From b54a91ac140b5e4f314e0840fc04ac25fc6f76ff Mon Sep 17 00:00:00 2001 From: Clemence Kyara Date: Mon, 30 Sep 2024 09:22:01 +0300 Subject: [PATCH 1/7] Rename Article to Post --- .../components/Article/Article.tsx | 40 --------------- .../components/Article/ArticleHeader.tsx | 49 ------------------- .../components/Article/ArticleSxProps.tsx | 9 ---- apps/techlabblog/components/Article/index.ts | 5 -- .../components/ArticleList/ArticleList.tsx | 38 -------------- .../components/ArticleList/index.ts | 5 -- .../components/PostHeader/PostHeader.tsx | 37 ++++++++++++++ .../components/PostHeader/PostSxProps.tsx | 9 ++++ .../components/PostHeader/index.ts | 5 ++ .../ArticleCard.tsx => PostList/PostCard.tsx} | 21 +++++--- .../components/PostList/PostList.tsx | 39 +++++++++++++++ apps/techlabblog/components/PostList/index.ts | 5 ++ 12 files changed, 110 insertions(+), 152 deletions(-) delete mode 100644 apps/techlabblog/components/Article/Article.tsx delete mode 100644 apps/techlabblog/components/Article/ArticleHeader.tsx delete mode 100644 apps/techlabblog/components/Article/ArticleSxProps.tsx delete mode 100644 apps/techlabblog/components/Article/index.ts delete mode 100644 apps/techlabblog/components/ArticleList/ArticleList.tsx delete mode 100644 apps/techlabblog/components/ArticleList/index.ts create mode 100644 apps/techlabblog/components/PostHeader/PostHeader.tsx create mode 100644 apps/techlabblog/components/PostHeader/PostSxProps.tsx create mode 100644 apps/techlabblog/components/PostHeader/index.ts rename apps/techlabblog/components/{ArticleList/ArticleCard.tsx => PostList/PostCard.tsx} (67%) create mode 100644 apps/techlabblog/components/PostList/PostList.tsx create mode 100644 apps/techlabblog/components/PostList/index.ts diff --git a/apps/techlabblog/components/Article/Article.tsx b/apps/techlabblog/components/Article/Article.tsx deleted file mode 100644 index 0baaeeaa7..000000000 --- a/apps/techlabblog/components/Article/Article.tsx +++ /dev/null @@ -1,40 +0,0 @@ -"use client"; - -import { Section } from "@commons-ui/core"; -import React from "react"; - -import Markdown from "@/techlabblog/components/Markdown"; -import type { ArticleSxProps } from "./ArticleSxProps"; -import ArticleHeader from "./ArticleHeader"; - -const Article = React.forwardRef(function Article( - props: ArticleSxProps, - ref: React.Ref, -) { - const { content, excerpt, publishedDate, sx, title } = props; - - return ( -
- - -
- ); -}); - -export default Article; diff --git a/apps/techlabblog/components/Article/ArticleHeader.tsx b/apps/techlabblog/components/Article/ArticleHeader.tsx deleted file mode 100644 index d66201a1a..000000000 --- a/apps/techlabblog/components/Article/ArticleHeader.tsx +++ /dev/null @@ -1,49 +0,0 @@ -"use client"; - -import { Typography } from "@mui/material"; -import { styled } from "@mui/material/styles"; -import React from "react"; - -import type { ArticleSxProps } from "./ArticleSxProps"; - -type ArticleHeaderSxProps = Omit< - ArticleSxProps, - "content" | "featuredImage" | "slug" ->; - -const ArticleHeaderRoot = styled("header")({}); - -const ArticleHeader = React.forwardRef(function ArticleHeader( - props: ArticleHeaderSxProps, - ref: React.Ref, -) { - const { publishedDate, excerpt, sx, title } = props; - - return ( - - - {publishedDate} - - - {title} - - - {excerpt} - - - ); -}); - -export default ArticleHeader; diff --git a/apps/techlabblog/components/Article/ArticleSxProps.tsx b/apps/techlabblog/components/Article/ArticleSxProps.tsx deleted file mode 100644 index 1ff4070cd..000000000 --- a/apps/techlabblog/components/Article/ArticleSxProps.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import type { SxProps, Theme } from "@mui/material/styles"; - -import { ArticleProps } from "@/techlabblog/lib/data"; - -interface ArticleSxProps extends ArticleProps { - sx?: SxProps; -} - -export type { ArticleSxProps }; diff --git a/apps/techlabblog/components/Article/index.ts b/apps/techlabblog/components/Article/index.ts deleted file mode 100644 index 655f96df3..000000000 --- a/apps/techlabblog/components/Article/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import Article from "./Article"; -import ArticleHeader from "./ArticleHeader"; - -export { ArticleHeader }; -export default Article; diff --git a/apps/techlabblog/components/ArticleList/ArticleList.tsx b/apps/techlabblog/components/ArticleList/ArticleList.tsx deleted file mode 100644 index 7391c87bd..000000000 --- a/apps/techlabblog/components/ArticleList/ArticleList.tsx +++ /dev/null @@ -1,38 +0,0 @@ -"use client"; - -import { Section } from "@commons-ui/core"; -import { Grid } from "@mui/material"; -import React from "react"; - -import { ArticleWithoutContentProps } from "@/techlabblog/lib/data"; -import ArticleCard from "./ArticleCard"; - -const ArticleList = React.forwardRef(function ArtilceList( - { - articles, - }: { - articles: ArticleWithoutContentProps[]; - }, - ref: React.Ref, -) { - if (!articles?.length) { - return null; - } - return ( -
- - {articles?.map((article) => ( - - - - ))} - -
- ); -}); - -export default ArticleList; diff --git a/apps/techlabblog/components/ArticleList/index.ts b/apps/techlabblog/components/ArticleList/index.ts deleted file mode 100644 index c66f68313..000000000 --- a/apps/techlabblog/components/ArticleList/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import ArticleCard from "./ArticleCard"; -import ArticleList from "./ArticleList"; - -export { ArticleCard }; -export default ArticleList; diff --git a/apps/techlabblog/components/PostHeader/PostHeader.tsx b/apps/techlabblog/components/PostHeader/PostHeader.tsx new file mode 100644 index 000000000..61378d2c8 --- /dev/null +++ b/apps/techlabblog/components/PostHeader/PostHeader.tsx @@ -0,0 +1,37 @@ +import { Box, Typography } from "@mui/material"; +import React from "react"; + +import type { PostSxProps } from "./PostSxProps"; + +const PostHeader = React.forwardRef(function ArticleHeader( + props: PostSxProps, + ref: React.Ref, +) { + const { publishedDate, excerpt, sx, title } = props; + + return ( + + + {publishedDate} + + + {title} + + + {excerpt} + + + ); +}); + +export default PostHeader; diff --git a/apps/techlabblog/components/PostHeader/PostSxProps.tsx b/apps/techlabblog/components/PostHeader/PostSxProps.tsx new file mode 100644 index 000000000..17de1522c --- /dev/null +++ b/apps/techlabblog/components/PostHeader/PostSxProps.tsx @@ -0,0 +1,9 @@ +import type { SxProps, Theme } from "@mui/material/styles"; + +import { PostFrontMatterProps } from "@/techlabblog/lib/data"; + +interface PostSxProps extends PostFrontMatterProps { + sx?: SxProps; +} + +export type { PostSxProps }; diff --git a/apps/techlabblog/components/PostHeader/index.ts b/apps/techlabblog/components/PostHeader/index.ts new file mode 100644 index 000000000..c2dbd1f5d --- /dev/null +++ b/apps/techlabblog/components/PostHeader/index.ts @@ -0,0 +1,5 @@ +import PostHeader from "./PostHeader"; +import type { PostSxProps } from "./PostSxProps"; + +export type { PostSxProps as PostProps }; +export default PostHeader; diff --git a/apps/techlabblog/components/ArticleList/ArticleCard.tsx b/apps/techlabblog/components/PostList/PostCard.tsx similarity index 67% rename from apps/techlabblog/components/ArticleList/ArticleCard.tsx rename to apps/techlabblog/components/PostList/PostCard.tsx index 8b8a3df4c..7a8a47d2b 100644 --- a/apps/techlabblog/components/ArticleList/ArticleCard.tsx +++ b/apps/techlabblog/components/PostList/PostCard.tsx @@ -1,6 +1,6 @@ "use client"; -import { StyledLink } from "@/commons-ui/next/Link"; +import { StyledLink as Link } from "@commons-ui/next"; import { Card, CardActionArea, @@ -8,14 +8,21 @@ import { CardMedia, Typography, } from "@mui/material"; +import type { SxProps, Theme } from "@mui/material/styles"; import React from "react"; -import { ArticleWithoutContentProps } from "@/techlabblog/lib/data"; +import { PostFrontMatterProps } from "@/techlabblog/lib/data"; -const ArticleCard = React.forwardRef(function ArticleCard( - { title, publishedDate, featuredImage, slug }: ArticleWithoutContentProps, +interface PostCardProps extends PostFrontMatterProps { + sx?: SxProps; +} + +const PostCard = React.forwardRef(function ArticleCard( + props: PostCardProps, ref: React.Ref, ) { + const { title, publishedDate, featuredImage, slug, sx } = props; + return ( - + ; +} + +const PostList = React.forwardRef(function PostList( + props: PostListProps, + ref: React.Ref, +) { + const { posts, sx } = props; + + if (!posts?.length) { + return null; + } + return ( + + {posts.map((post) => ( + + + + ))} + + ); +}); + +export default PostList; diff --git a/apps/techlabblog/components/PostList/index.ts b/apps/techlabblog/components/PostList/index.ts new file mode 100644 index 000000000..33aff0f29 --- /dev/null +++ b/apps/techlabblog/components/PostList/index.ts @@ -0,0 +1,5 @@ +import type { PostCardProps } from "./PostCard"; +import PostList from "./PostList"; + +export type { PostCardProps }; +export default PostList; From 7315e4000c4633c61e04290513f01aaeed67cc1b Mon Sep 17 00:00:00 2001 From: Clemence Kyara Date: Mon, 30 Sep 2024 09:23:02 +0300 Subject: [PATCH 2/7] Switch to using mdx in both Nextjs config and data --- apps/techlabblog/lib/data/index.ts | 165 +++++++++++++++++++--------- apps/techlabblog/mdx-components.tsx | 49 +++++++++ apps/techlabblog/mdx.config.mjs | 26 +++++ apps/techlabblog/next.config.mjs | 7 +- 4 files changed, 195 insertions(+), 52 deletions(-) create mode 100644 apps/techlabblog/mdx.config.mjs diff --git a/apps/techlabblog/lib/data/index.ts b/apps/techlabblog/lib/data/index.ts index 700d576b4..c93132983 100644 --- a/apps/techlabblog/lib/data/index.ts +++ b/apps/techlabblog/lib/data/index.ts @@ -1,56 +1,84 @@ -import { format } from "date-fns"; +import { compile, run } from "@mdx-js/mdx"; import { promises as fs } from "fs"; -import matter from "gray-matter"; +import type { MDXModule } from "mdx/types"; import path from "path"; +import * as runtime from "react/jsx-runtime"; -type MdFileContentProps = { +import { useMDXComponents } from "@/techlabblog/mdx-components"; +import { rehypePlugins, remarkPlugins } from "@/techlabblog/mdx.config.mjs"; + +export type AuthorFrontMatterProps = { + fullName: string; + twitter: string; + slug: string; +}; + +export type PostFrontMatterProps = { title: string; excerpt: string; publishedDate: string; featuredImage: string; - content: string; -}; - -export interface ArticleProps extends MdFileContentProps { slug: string; -} - -export type ArticleWithoutContentProps = Omit; + authors: AuthorFrontMatterProps[] | null; +}; -async function readMdFile(filePath: string) { +async function readMdFile(filePath: string): Promise { const fileContent = await fs.readFile(filePath, "utf8"); - return matter(fileContent); + const vfile = await compile(fileContent, { + outputFormat: "function-body", + providerImportSource: "../../mdx-components.tsx", + rehypePlugins, + remarkPlugins, + }); + return run(vfile, { ...runtime, useMDXComponents }); } -async function readArticleFile(filePath: string): Promise { - const { data, content } = await readMdFile(filePath); +async function listMdxFilesRecursively(dir: string): Promise { + const files = await fs.readdir(dir, { recursive: true }); + return files.filter((f) => + [".md", ".mdx"].includes(path.extname(f.toLowerCase())), + ); +} - return { - title: data.title, - excerpt: data.excerpt, - publishedDate: format(new Date(data.publishedDate), "MMM dd, yyyy"), - featuredImage: data.featuredImage, - content, - }; +export async function getAuthors(): Promise { + const cwd = process.cwd(); + const postsDir = path.join(cwd, "content", "publication", "authors"); + const mdxFiles = await listMdxFilesRecursively(postsDir); + const authorsPromises = mdxFiles.map(async (fileName) => { + const filePath = path.join(postsDir, fileName); + const { frontmatter } = await readMdFile(filePath); + + return { + ...frontmatter, + slug: fileName.replace(/\.mdx$/, ""), + }; + }); + return ( + (await Promise.allSettled(authorsPromises)) + // TODO: log/send to Sentry those that fail + .filter((p) => p.status === "fulfilled") + .map((p) => p.value) + .sort( + (a, b) => + new Date(b.publishedDate).getTime() - + new Date(a.publishedDate).getTime(), + ) + ); } -export async function getAllContents(): Promise { - const contentDir = path.join(process.cwd(), "content"); - const files = await fs.readdir(contentDir); - const contentsPromises = files - .filter((fileName) => fileName.endsWith(".mdx")) - .map(async (fileName) => { - const filePath = path.join(contentDir, fileName); - const { content, ...fileContent } = await readArticleFile(filePath); - - return { - ...fileContent, - slug: fileName.replace(/\.mdx$/, ""), - }; - }); - const contents = ( - await Promise.allSettled(contentsPromises) - ) +export async function getPosts(): Promise { + const postsDir = path.join(process.cwd(), "content", "publication", "posts"); + const mdxFiles = await listMdxFilesRecursively(postsDir); + const postsPromises = mdxFiles.map(async (fileName) => { + const filePath = path.join(postsDir, fileName); + const { frontmatter } = await readMdFile(filePath); + + return { + ...frontmatter, + slug: fileName.replace(/\.mdx$/, ""), + }; + }); + const posts = (await Promise.allSettled(postsPromises)) // TODO: log/send to Sentry those that fail .filter((p) => p.status === "fulfilled") .map((p) => p.value) @@ -59,16 +87,51 @@ export async function getAllContents(): Promise { new Date(b.publishedDate).getTime() - new Date(a.publishedDate).getTime(), ); - return contents; + if (!posts.length) { + return posts; + } + const authors = await getAuthors(); + return posts.map((post) => { + const { authors: authorsSlugs } = post; + const postAuthors = + authorsSlugs?.flatMap((aS) => { + return authors.find((a) => a.slug == aS) || []; + }) ?? null; + return { + ...post, + authors: postAuthors, + }; + }); } -export async function getContent(slug: string): Promise { - const filePath = path.join(process.cwd(), "content", `${slug}.mdx`); - const fileContent = await readArticleFile(filePath); - +export async function getPost(slug: string): Promise { + const postsDir = path.join(process.cwd(), "content", "publication", "posts"); + const mdxFiles = await listMdxFilesRecursively(postsDir); + const postFiles = mdxFiles.filter( + (f) => f.toLowerCase().replace(/\.mdx?$/, "") === slug, + ); + if (postFiles.length !== 1) { + return Promise.resolve(); + } + + const postPath = path.join(postsDir, postFiles[0]); + const post = await readMdFile(postPath); + const authors = await getAuthors(); + if (!authors?.length) { + return post; + } + const { frontmatter } = post; + const { authors: authorsSlugs } = frontmatter; + const postAuthors = + authorsSlugs?.flatMap((aS) => { + return authors.find((a) => a.slug == aS) || []; + }) ?? null; return { - ...fileContent, - slug, + ...post, + frontmatter: { + ...frontmatter, + authors: postAuthors, + }, }; } @@ -120,14 +183,14 @@ type SettingsProps = { }; async function readSettingsFile(filePath: string): Promise { - const { data } = await readMdFile(filePath); + const { frontmatter } = await readMdFile(filePath); return { - analytics: data.analytics, - connect: data.connect, - primaryNavigation: data.primaryNavigation, - secondaryNavigation: data.secondaryNavigation, - title: data.title, + analytics: frontmatter.analytics, + connect: frontmatter.connect, + primaryNavigation: frontmatter.primaryNavigation, + secondaryNavigation: frontmatter.secondaryNavigation, + title: frontmatter.title, }; } diff --git a/apps/techlabblog/mdx-components.tsx b/apps/techlabblog/mdx-components.tsx index 9ff722919..f5e02f101 100644 --- a/apps/techlabblog/mdx-components.tsx +++ b/apps/techlabblog/mdx-components.tsx @@ -1,7 +1,56 @@ +import { Link, Typography } from "@mui/material"; +import type { Variant } from "@mui/material/styles/createTypography"; import type { MDXComponents } from "mdx/types"; +function createHeading(variant: Variant): HTMLHeadingElement { + return ({ children, ...others }) => ( + a": { + opacity: 0.65, + }, + "& .icon.icon-link": { + display: "none", + paddingLeft: 1, + // position: "relative" + }, + "&:hover": { + "& .icon.icon-link": { + display: "initial", + }, + }, + "& .icon.icon-link:before": { + // position: "absolute", + content: '"#"', + }, + }} + > + {children} + + ); +} + export function useMDXComponents(components: MDXComponents): MDXComponents { return { + h1: createHeading("h1"), + h2: createHeading("h2"), + h3: createHeading("h3"), + h4: createHeading("h4"), + h5: createHeading("h5"), + h6: createHeading("h6"), + p: ({ children, ...others }) => ( + + {children} + + ), + a: ({ children, ...others }) => ( + + {children} + + ), ...components, }; } diff --git a/apps/techlabblog/mdx.config.mjs b/apps/techlabblog/mdx.config.mjs new file mode 100644 index 000000000..14e781a14 --- /dev/null +++ b/apps/techlabblog/mdx.config.mjs @@ -0,0 +1,26 @@ +import rehypeAutolinkHeadings from "rehype-autolink-headings"; +import rehypePrettyCode from "rehype-pretty-code"; +import rehypeSlug from "rehype-slug"; +import remarkFrontmatter from "remark-frontmatter"; +import remarkMdxFrontmatter from "remark-mdx-frontmatter"; + +/** @type {import('rehype-pretty-code').Options} */ +const rehypePrettyCodeOptions = { + keepBackground: true, + theme: { + dark: "catppuccin-mocha", + light: "catppuccin-latte", + }, +}; + +/** @type {import('rehype-autolink-headings').Options} */ +const rehypeAutolinkHeadingsOptions = { + behavior: "append", +}; + +export const remarkPlugins = [remarkFrontmatter, remarkMdxFrontmatter]; +export const rehypePlugins = [ + [rehypePrettyCode, rehypePrettyCodeOptions], + rehypeSlug, + [rehypeAutolinkHeadings, rehypeAutolinkHeadingsOptions], +]; diff --git a/apps/techlabblog/next.config.mjs b/apps/techlabblog/next.config.mjs index 378cf0147..6a4c4cd64 100644 --- a/apps/techlabblog/next.config.mjs +++ b/apps/techlabblog/next.config.mjs @@ -1,5 +1,7 @@ import createMDX from "@next/mdx"; +import { rehypePlugins, remarkPlugins } from "./mdx.config.mjs"; + /** @type {import('next').NextConfig} */ const nextConfig = { pageExtensions: ["mdx", "tsx"], @@ -30,7 +32,10 @@ const nextConfig = { }; const withMDX = createMDX({ - // Add markdown plugins here, as desired + options: { + remarkPlugins, + rehypePlugins, + }, }); export default withMDX(nextConfig); From c4a98b1911298176cdaca4385a02c75744888b80 Mon Sep 17 00:00:00 2001 From: Clemence Kyara Date: Mon, 30 Sep 2024 09:23:42 +0300 Subject: [PATCH 3/7] Organise content --- .../publication/authors/davidtheprogrammer.mdx | 4 ++++ .../content/publication/authors/kelvinkipruto.mdx | 4 ++++ .../content/publication/authors/kilemensi.mdx | 4 ++++ .../{ => publication/posts}/exploring-next-js.mdx | 4 +++- .../posts}/javascript-generators.mdx | 8 +++++--- .../{ => publication/posts}/mastering-docker.mdx | 3 +++ ...pt-generators.jpeg => javascript-generators.jpg} | Bin 7 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 apps/techlabblog/content/publication/authors/davidtheprogrammer.mdx create mode 100644 apps/techlabblog/content/publication/authors/kelvinkipruto.mdx create mode 100644 apps/techlabblog/content/publication/authors/kilemensi.mdx rename apps/techlabblog/content/{ => publication/posts}/exploring-next-js.mdx (98%) rename apps/techlabblog/content/{ => publication/posts}/javascript-generators.mdx (93%) rename apps/techlabblog/content/{ => publication/posts}/mastering-docker.mdx (99%) rename apps/techlabblog/public/blog/{javascript-generators.jpeg => javascript-generators.jpg} (100%) diff --git a/apps/techlabblog/content/publication/authors/davidtheprogrammer.mdx b/apps/techlabblog/content/publication/authors/davidtheprogrammer.mdx new file mode 100644 index 000000000..9d57b8529 --- /dev/null +++ b/apps/techlabblog/content/publication/authors/davidtheprogrammer.mdx @@ -0,0 +1,4 @@ +--- +fullName: David Muma +twitter: davidtheprogrammer +--- diff --git a/apps/techlabblog/content/publication/authors/kelvinkipruto.mdx b/apps/techlabblog/content/publication/authors/kelvinkipruto.mdx new file mode 100644 index 000000000..51c915477 --- /dev/null +++ b/apps/techlabblog/content/publication/authors/kelvinkipruto.mdx @@ -0,0 +1,4 @@ +--- +fullName: Kelvin Kipruto +twitter: kelvinkipruto +--- diff --git a/apps/techlabblog/content/publication/authors/kilemensi.mdx b/apps/techlabblog/content/publication/authors/kilemensi.mdx new file mode 100644 index 000000000..e603a34cd --- /dev/null +++ b/apps/techlabblog/content/publication/authors/kilemensi.mdx @@ -0,0 +1,4 @@ +--- +fullName: Clemence Kyara +twitter: kilemensi +--- diff --git a/apps/techlabblog/content/exploring-next-js.mdx b/apps/techlabblog/content/publication/posts/exploring-next-js.mdx similarity index 98% rename from apps/techlabblog/content/exploring-next-js.mdx rename to apps/techlabblog/content/publication/posts/exploring-next-js.mdx index f6457a2a8..230844e1f 100644 --- a/apps/techlabblog/content/exploring-next-js.mdx +++ b/apps/techlabblog/content/publication/posts/exploring-next-js.mdx @@ -3,6 +3,8 @@ title: "Exploring Next.js: The React Framework for Production" publishedDate: "2020-01-01" excerpt: "Next.js has rapidly become one of the most popular frameworks for building modern web applications. Created by Vercel, it extends the capabilities of React, providing developers with a powerful toolkit to build fast, scalable, and SEO-friendly websites with ease." featuredImage: "/blog/exploring-next-js.png" +authors: + - kelvinkipruto --- ### What is Next.js? @@ -33,7 +35,7 @@ Whether you're building a static site, a dynamic web app, or a full-stack soluti To get started with Next.js, all you need is Node.js installed on your machine. Run the following command to create a new Next.js project: -```bash +```sh pnpx create-next-app my-nextjs-app ``` diff --git a/apps/techlabblog/content/javascript-generators.mdx b/apps/techlabblog/content/publication/posts/javascript-generators.mdx similarity index 93% rename from apps/techlabblog/content/javascript-generators.mdx rename to apps/techlabblog/content/publication/posts/javascript-generators.mdx index 1eaebf47f..e7e9efc77 100644 --- a/apps/techlabblog/content/javascript-generators.mdx +++ b/apps/techlabblog/content/publication/posts/javascript-generators.mdx @@ -2,7 +2,9 @@ title: "Understanding JavaScript Generators: A Deep Dive" publishedDate: "2020-02-02" excerpt: "JavaScript is a versatile language, and one of its lesser-known yet powerful features is Generators. Introduced in ECMAScript 6 (ES6), Generators provide a unique way to handle functions, offering more control over the execution flow. In this blog, we'll explore what generators are, how they work, and practical use cases." -featuredImage: "/blog/javascript-generators.jpeg" +featuredImage: "/blog/javascript-generators.jpg" +authors: + - kelvinkipruto --- ### What are Generators? @@ -11,9 +13,9 @@ Generators are a special type of function in JavaScript that can be paused and r ### Syntax -A generator function is defined using the `function*`syntax, where the asterisk (\*) distinguishes it from a regular function. Inside the function body, the yield keyword is used to pause the function and return a value. +A generator function is defined using the `function*` syntax, where the asterisk (\*) distinguishes it from a regular function. Inside the function body, the yield keyword is used to pause the function and return a value. -```javascript +```js showLineNumbers {1-3,4} caption="Caption is here" title="Title is here" function* myGenerator() { yield 1; yield 2; diff --git a/apps/techlabblog/content/mastering-docker.mdx b/apps/techlabblog/content/publication/posts/mastering-docker.mdx similarity index 99% rename from apps/techlabblog/content/mastering-docker.mdx rename to apps/techlabblog/content/publication/posts/mastering-docker.mdx index 79210c88e..8625ddf73 100644 --- a/apps/techlabblog/content/mastering-docker.mdx +++ b/apps/techlabblog/content/publication/posts/mastering-docker.mdx @@ -3,6 +3,9 @@ title: "Mastering Docker: A Comprehensive Guide for Developers" publishedDate: "2020-03-03" excerpt: "Docker has revolutionized the way we build, ship, and run applications. It simplifies software development by creating isolated environments, known as containers, that bundle an application and its dependencies together. This ensures consistency across multiple environments and reduces the 'it works on my machine' problem. In this article, we'll dive deep into Docker, exploring its architecture, benefits, common use cases, and best practices." featuredImage: "/blog/mastering-docker.png" +authors: + - davidtheprogrammer + - kilemensi --- ### What is Docker? diff --git a/apps/techlabblog/public/blog/javascript-generators.jpeg b/apps/techlabblog/public/blog/javascript-generators.jpg similarity index 100% rename from apps/techlabblog/public/blog/javascript-generators.jpeg rename to apps/techlabblog/public/blog/javascript-generators.jpg From 77461061aa2792af02b459fc7478868ffe9b1729 Mon Sep 17 00:00:00 2001 From: Clemence Kyara Date: Mon, 30 Sep 2024 09:34:28 +0300 Subject: [PATCH 4/7] Update components to use mdxjs + deps cleanup --- apps/civicsignalblog/package.json | 1 - apps/codeforafrica/package.json | 1 - apps/techlabblog/app/[slug]/page.tsx | 21 +- apps/techlabblog/app/layout.tsx | 4 +- apps/techlabblog/app/page.tsx | 12 +- .../components/Markdown/CopyCodeButton.tsx | 48 - .../components/Markdown/Markdown.tsx | 102 - apps/techlabblog/components/Markdown/index.ts | 3 - apps/techlabblog/package.json | 16 +- apps/techlabblog/theme/index.ts | 48 + pnpm-lock.yaml | 9277 ++++++++--------- pnpm-workspace.yaml | 15 +- 12 files changed, 4421 insertions(+), 5127 deletions(-) delete mode 100644 apps/techlabblog/components/Markdown/CopyCodeButton.tsx delete mode 100644 apps/techlabblog/components/Markdown/Markdown.tsx delete mode 100644 apps/techlabblog/components/Markdown/index.ts diff --git a/apps/civicsignalblog/package.json b/apps/civicsignalblog/package.json index f95dc937b..5abf3beeb 100644 --- a/apps/civicsignalblog/package.json +++ b/apps/civicsignalblog/package.json @@ -60,7 +60,6 @@ "dotenv": "catalog:", "express": "catalog:", "fast-equals": "catalog:", - "gray-matter": "catalog:", "js-yaml": "catalog:", "jsdom": "catalog:", "next": "catalog:", diff --git a/apps/codeforafrica/package.json b/apps/codeforafrica/package.json index 977d8c376..7cd1afa60 100644 --- a/apps/codeforafrica/package.json +++ b/apps/codeforafrica/package.json @@ -57,7 +57,6 @@ "dotenv": "catalog:", "express": "catalog:", "fast-equals": "catalog:", - "gray-matter": "catalog:", "js-yaml": "catalog:", "jsdom": "catalog:", "next": "catalog:", diff --git a/apps/techlabblog/app/[slug]/page.tsx b/apps/techlabblog/app/[slug]/page.tsx index e17e39344..1ae70c70b 100644 --- a/apps/techlabblog/app/[slug]/page.tsx +++ b/apps/techlabblog/app/[slug]/page.tsx @@ -1,11 +1,20 @@ -import { Box } from "@mui/material"; +import { Section } from "@commons-ui/core"; -import Article from "@/techlabblog/components/Article"; -import { ArticleProps, getContent } from "@/techlabblog/lib/data"; +import { getPost } from "@/techlabblog/lib/data"; +import PostHeader from "@/techlabblog/components/PostHeader"; export default async function Page({ params }: { params: { slug: string } }) { - const post: ArticleProps = await getContent(params.slug); + const { default: PostContent, frontmatter } = await getPost(params.slug); - // TODO: Check that the post does exist, return 404 otherwise - return
; + return ( +
+ + +
+ ); } diff --git a/apps/techlabblog/app/layout.tsx b/apps/techlabblog/app/layout.tsx index 6919d9424..3af4b51ad 100644 --- a/apps/techlabblog/app/layout.tsx +++ b/apps/techlabblog/app/layout.tsx @@ -39,9 +39,9 @@ export default async function RootLayout({ {children}