From b17b5716f60d59de229b0ceaaf63ca5a7583b971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez?= Date: Mon, 17 Feb 2025 18:23:46 +0100 Subject: [PATCH 1/6] Guides --- packages/nextjs/app/guides/[slug]/page.tsx | 28 ++++++++++++ packages/nextjs/guides/getting-started.md | 24 +++++++++++ packages/nextjs/package.json | 1 + packages/nextjs/services/guides.ts | 37 ++++++++++++++++ yarn.lock | 50 +++++++++++++++++++++- 5 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 packages/nextjs/app/guides/[slug]/page.tsx create mode 100644 packages/nextjs/guides/getting-started.md create mode 100644 packages/nextjs/services/guides.ts diff --git a/packages/nextjs/app/guides/[slug]/page.tsx b/packages/nextjs/app/guides/[slug]/page.tsx new file mode 100644 index 00000000..62772791 --- /dev/null +++ b/packages/nextjs/app/guides/[slug]/page.tsx @@ -0,0 +1,28 @@ +import { notFound } from "next/navigation"; +import { MDXRemote } from "next-mdx-remote/rsc"; +import { getAllGuidesSlugs, getGuideBySlug } from "~~/services/guides"; + +export async function generateStaticParams() { + const slugs = await getAllGuidesSlugs(); + + return slugs; +} + +export default function GuidePage({ params }: { params: { slug: string } }) { + const guide = getGuideBySlug(params.slug); + + if (!guide) { + return notFound(); + } + + return ( +
+
+

{guide.title}

+
+
+ +
+
+ ); +} diff --git a/packages/nextjs/guides/getting-started.md b/packages/nextjs/guides/getting-started.md new file mode 100644 index 00000000..481bffcc --- /dev/null +++ b/packages/nextjs/guides/getting-started.md @@ -0,0 +1,24 @@ +--- +title: Getting Started with SpeedRunEthereum +description: Learn how to get started with SpeedRunEthereum challenges +--- + +Welcome to SpeedRunEthereum! This guide will help you get started with the challenges and set up your development environment. + +## Prerequisites + +Before you begin, make sure you have: + +- Node.js installed (v18 or higher) +- Git installed +- Basic knowledge of Ethereum and smart contracts + +## Setting Up Your Environment + +1. Clone the repository +2. Install dependencies +3. Start coding! + +## Next Steps + +Check out our first challenge to begin your journey into Ethereum development! diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index e0bdc484..e77e09f5 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -28,6 +28,7 @@ "daisyui": "4.12.10", "dotenv": "^16.4.7", "drizzle-orm": "^0.39.1", + "gray-matter": "^4.0.3", "next": "~14.2.11", "next-mdx-remote": "^5.0.0", "next-nprogress-bar": "~2.3.13", diff --git a/packages/nextjs/services/guides.ts b/packages/nextjs/services/guides.ts new file mode 100644 index 00000000..f3ff65f8 --- /dev/null +++ b/packages/nextjs/services/guides.ts @@ -0,0 +1,37 @@ +import fs from "fs"; +import matter from "gray-matter"; +import path from "path"; + +const guidesDirectory = path.join(process.cwd(), "guides"); + +export type GuideMetadata = { + title: string; + description: string; + slug: string; +}; + +export type Guide = GuideMetadata & { + content: string; +}; + +export function getAllGuidesSlugs(): string[] { + return fs.readdirSync(guidesDirectory).map(fileName => fileName.replace(/\.md$/, "")); +} + +export function getGuideBySlug(slug: string): Guide | null { + const fullPath = path.join(guidesDirectory, `${slug}.md`); + + try { + const fileContents = fs.readFileSync(fullPath, "utf8"); + const { data, content } = matter(fileContents); + + return { + ...(data as GuideMetadata), + content, + slug, + }; + } catch (error) { + // Not found. + return null; + } +} diff --git a/yarn.lock b/yarn.lock index 1fdad9e8..1e332e9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2643,6 +2643,7 @@ __metadata: eslint-config-next: ~14.2.15 eslint-config-prettier: ~8.10.0 eslint-plugin-prettier: ~5.2.1 + gray-matter: ^4.0.3 next: ~14.2.11 next-mdx-remote: ^5.0.0 next-nprogress-bar: ~2.3.13 @@ -8268,6 +8269,15 @@ __metadata: languageName: node linkType: hard +"extend-shallow@npm:^2.0.1": + version: 2.0.1 + resolution: "extend-shallow@npm:2.0.1" + dependencies: + is-extendable: ^0.1.0 + checksum: 8fb58d9d7a511f4baf78d383e637bd7d2e80843bd9cd0853649108ea835208fb614da502a553acc30208e1325240bb7cc4a68473021612496bb89725483656d8 + languageName: node + linkType: hard + "extend@npm:^3.0.0": version: 3.0.2 resolution: "extend@npm:3.0.2" @@ -9009,6 +9019,18 @@ __metadata: languageName: node linkType: hard +"gray-matter@npm:^4.0.3": + version: 4.0.3 + resolution: "gray-matter@npm:4.0.3" + dependencies: + js-yaml: ^3.13.1 + kind-of: ^6.0.2 + section-matter: ^1.0.0 + strip-bom-string: ^1.0.0 + checksum: 37717bd424344487d655392251ce8d8878a1275ee087003e61208fba3bfd59cbb73a85b2159abf742ae95e23db04964813fdc33ae18b074208428b2528205222 + languageName: node + linkType: hard + "h3@npm:^1.13.0": version: 1.14.0 resolution: "h3@npm:1.14.0" @@ -9778,6 +9800,13 @@ __metadata: languageName: node linkType: hard +"is-extendable@npm:^0.1.0": + version: 0.1.1 + resolution: "is-extendable@npm:0.1.1" + checksum: 3875571d20a7563772ecc7a5f36cb03167e9be31ad259041b4a8f73f33f885441f778cee1f1fe0085eb4bc71679b9d8c923690003a36a6a5fdf8023e6e3f0672 + languageName: node + linkType: hard + "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -10125,7 +10154,7 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:3.x": +"js-yaml@npm:3.x, js-yaml@npm:^3.13.1": version: 3.14.1 resolution: "js-yaml@npm:3.14.1" dependencies: @@ -10325,7 +10354,7 @@ __metadata: languageName: node linkType: hard -"kind-of@npm:^6.0.2": +"kind-of@npm:^6.0.0, kind-of@npm:^6.0.2": version: 6.0.3 resolution: "kind-of@npm:6.0.3" checksum: 3ab01e7b1d440b22fe4c31f23d8d38b4d9b91d9f291df683476576493d5dfd2e03848a8b05813dd0c3f0e835bc63f433007ddeceb71f05cb25c45ae1b19c6d3b @@ -13850,6 +13879,16 @@ __metadata: languageName: node linkType: hard +"section-matter@npm:^1.0.0": + version: 1.0.0 + resolution: "section-matter@npm:1.0.0" + dependencies: + extend-shallow: ^2.0.1 + kind-of: ^6.0.0 + checksum: 3cc4131705493b2955729b075dcf562359bba66183debb0332752dc9cad35616f6da7a23e42b6cab45cd2e4bb5cda113e9e84c8f05aee77adb6b0289a0229101 + languageName: node + linkType: hard + "semver@npm:6.3.1, semver@npm:^6.0.0, semver@npm:^6.3.0, semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" @@ -14559,6 +14598,13 @@ __metadata: languageName: node linkType: hard +"strip-bom-string@npm:^1.0.0": + version: 1.0.0 + resolution: "strip-bom-string@npm:1.0.0" + checksum: 5635a3656d8512a2c194d6c8d5dee7ef0dde6802f7be9413b91e201981ad4132506656d9cf14137f019fd50f0269390d91c7f6a2601b1bee039a4859cfce4934 + languageName: node + linkType: hard + "strip-bom@npm:^3.0.0": version: 3.0.0 resolution: "strip-bom@npm:3.0.0" From 78de8d36168c43740ff90e7ad4b6730119a713d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez?= Date: Mon, 17 Feb 2025 18:26:54 +0100 Subject: [PATCH 2/6] remove export --- packages/nextjs/services/guides.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nextjs/services/guides.ts b/packages/nextjs/services/guides.ts index f3ff65f8..fca5dd3a 100644 --- a/packages/nextjs/services/guides.ts +++ b/packages/nextjs/services/guides.ts @@ -4,13 +4,13 @@ import path from "path"; const guidesDirectory = path.join(process.cwd(), "guides"); -export type GuideMetadata = { +type GuideMetadata = { title: string; description: string; slug: string; }; -export type Guide = GuideMetadata & { +type Guide = GuideMetadata & { content: string; }; From a84b6a7cb9831fe014888802b9d4111e693ba668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez?= Date: Mon, 17 Feb 2025 18:27:46 +0100 Subject: [PATCH 3/6] TODO: metadata --- packages/nextjs/app/guides/[slug]/page.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/nextjs/app/guides/[slug]/page.tsx b/packages/nextjs/app/guides/[slug]/page.tsx index 62772791..234babb4 100644 --- a/packages/nextjs/app/guides/[slug]/page.tsx +++ b/packages/nextjs/app/guides/[slug]/page.tsx @@ -2,6 +2,8 @@ import { notFound } from "next/navigation"; import { MDXRemote } from "next-mdx-remote/rsc"; import { getAllGuidesSlugs, getGuideBySlug } from "~~/services/guides"; +// ToDo: Metadata + export async function generateStaticParams() { const slugs = await getAllGuidesSlugs(); From f5f9efc445fba145d3daef9c63be3edb197a2c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez?= Date: Mon, 17 Feb 2025 18:53:37 +0100 Subject: [PATCH 4/6] Use built in frontmatter instead of gray-matter --- packages/nextjs/app/guides/[slug]/page.tsx | 9 ++-- packages/nextjs/package.json | 1 - packages/nextjs/services/guides.ts | 20 +++++---- yarn.lock | 50 +--------------------- 4 files changed, 17 insertions(+), 63 deletions(-) diff --git a/packages/nextjs/app/guides/[slug]/page.tsx b/packages/nextjs/app/guides/[slug]/page.tsx index 234babb4..1d2f45a8 100644 --- a/packages/nextjs/app/guides/[slug]/page.tsx +++ b/packages/nextjs/app/guides/[slug]/page.tsx @@ -1,5 +1,4 @@ import { notFound } from "next/navigation"; -import { MDXRemote } from "next-mdx-remote/rsc"; import { getAllGuidesSlugs, getGuideBySlug } from "~~/services/guides"; // ToDo: Metadata @@ -10,8 +9,8 @@ export async function generateStaticParams() { return slugs; } -export default function GuidePage({ params }: { params: { slug: string } }) { - const guide = getGuideBySlug(params.slug); +export default async function GuidePage({ params }: { params: { slug: string } }) { + const guide = await getGuideBySlug(params.slug); if (!guide) { return notFound(); @@ -22,9 +21,7 @@ export default function GuidePage({ params }: { params: { slug: string } }) {

{guide.title}

-
- -
+
{guide.content}
); } diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index e77e09f5..e0bdc484 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -28,7 +28,6 @@ "daisyui": "4.12.10", "dotenv": "^16.4.7", "drizzle-orm": "^0.39.1", - "gray-matter": "^4.0.3", "next": "~14.2.11", "next-mdx-remote": "^5.0.0", "next-nprogress-bar": "~2.3.13", diff --git a/packages/nextjs/services/guides.ts b/packages/nextjs/services/guides.ts index fca5dd3a..67ea750f 100644 --- a/packages/nextjs/services/guides.ts +++ b/packages/nextjs/services/guides.ts @@ -1,5 +1,6 @@ +import { ReactElement } from "react"; import fs from "fs"; -import matter from "gray-matter"; +import { compileMDX } from "next-mdx-remote/rsc"; import path from "path"; const guidesDirectory = path.join(process.cwd(), "guides"); @@ -7,28 +8,31 @@ const guidesDirectory = path.join(process.cwd(), "guides"); type GuideMetadata = { title: string; description: string; - slug: string; }; type Guide = GuideMetadata & { - content: string; + content: ReactElement; }; export function getAllGuidesSlugs(): string[] { return fs.readdirSync(guidesDirectory).map(fileName => fileName.replace(/\.md$/, "")); } -export function getGuideBySlug(slug: string): Guide | null { +export async function getGuideBySlug(slug: string): Promise { const fullPath = path.join(guidesDirectory, `${slug}.md`); try { const fileContents = fs.readFileSync(fullPath, "utf8"); - const { data, content } = matter(fileContents); + const { frontmatter, content } = await compileMDX({ + source: fileContents, + options: { parseFrontmatter: true }, + }); + + console.log("content", content); return { - ...(data as GuideMetadata), - content, - slug, + ...frontmatter, + content: content, }; } catch (error) { // Not found. diff --git a/yarn.lock b/yarn.lock index 1e332e9d..1fdad9e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2643,7 +2643,6 @@ __metadata: eslint-config-next: ~14.2.15 eslint-config-prettier: ~8.10.0 eslint-plugin-prettier: ~5.2.1 - gray-matter: ^4.0.3 next: ~14.2.11 next-mdx-remote: ^5.0.0 next-nprogress-bar: ~2.3.13 @@ -8269,15 +8268,6 @@ __metadata: languageName: node linkType: hard -"extend-shallow@npm:^2.0.1": - version: 2.0.1 - resolution: "extend-shallow@npm:2.0.1" - dependencies: - is-extendable: ^0.1.0 - checksum: 8fb58d9d7a511f4baf78d383e637bd7d2e80843bd9cd0853649108ea835208fb614da502a553acc30208e1325240bb7cc4a68473021612496bb89725483656d8 - languageName: node - linkType: hard - "extend@npm:^3.0.0": version: 3.0.2 resolution: "extend@npm:3.0.2" @@ -9019,18 +9009,6 @@ __metadata: languageName: node linkType: hard -"gray-matter@npm:^4.0.3": - version: 4.0.3 - resolution: "gray-matter@npm:4.0.3" - dependencies: - js-yaml: ^3.13.1 - kind-of: ^6.0.2 - section-matter: ^1.0.0 - strip-bom-string: ^1.0.0 - checksum: 37717bd424344487d655392251ce8d8878a1275ee087003e61208fba3bfd59cbb73a85b2159abf742ae95e23db04964813fdc33ae18b074208428b2528205222 - languageName: node - linkType: hard - "h3@npm:^1.13.0": version: 1.14.0 resolution: "h3@npm:1.14.0" @@ -9800,13 +9778,6 @@ __metadata: languageName: node linkType: hard -"is-extendable@npm:^0.1.0": - version: 0.1.1 - resolution: "is-extendable@npm:0.1.1" - checksum: 3875571d20a7563772ecc7a5f36cb03167e9be31ad259041b4a8f73f33f885441f778cee1f1fe0085eb4bc71679b9d8c923690003a36a6a5fdf8023e6e3f0672 - languageName: node - linkType: hard - "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -10154,7 +10125,7 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:3.x, js-yaml@npm:^3.13.1": +"js-yaml@npm:3.x": version: 3.14.1 resolution: "js-yaml@npm:3.14.1" dependencies: @@ -10354,7 +10325,7 @@ __metadata: languageName: node linkType: hard -"kind-of@npm:^6.0.0, kind-of@npm:^6.0.2": +"kind-of@npm:^6.0.2": version: 6.0.3 resolution: "kind-of@npm:6.0.3" checksum: 3ab01e7b1d440b22fe4c31f23d8d38b4d9b91d9f291df683476576493d5dfd2e03848a8b05813dd0c3f0e835bc63f433007ddeceb71f05cb25c45ae1b19c6d3b @@ -13879,16 +13850,6 @@ __metadata: languageName: node linkType: hard -"section-matter@npm:^1.0.0": - version: 1.0.0 - resolution: "section-matter@npm:1.0.0" - dependencies: - extend-shallow: ^2.0.1 - kind-of: ^6.0.0 - checksum: 3cc4131705493b2955729b075dcf562359bba66183debb0332752dc9cad35616f6da7a23e42b6cab45cd2e4bb5cda113e9e84c8f05aee77adb6b0289a0229101 - languageName: node - linkType: hard - "semver@npm:6.3.1, semver@npm:^6.0.0, semver@npm:^6.3.0, semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" @@ -14598,13 +14559,6 @@ __metadata: languageName: node linkType: hard -"strip-bom-string@npm:^1.0.0": - version: 1.0.0 - resolution: "strip-bom-string@npm:1.0.0" - checksum: 5635a3656d8512a2c194d6c8d5dee7ef0dde6802f7be9413b91e201981ad4132506656d9cf14137f019fd50f0269390d91c7f6a2601b1bee039a4859cfce4934 - languageName: node - linkType: hard - "strip-bom@npm:^3.0.0": version: 3.0.0 resolution: "strip-bom@npm:3.0.0" From 90827251e2ff12dc33701719f5b21e78e1a7bc6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez?= Date: Mon, 17 Feb 2025 18:55:08 +0100 Subject: [PATCH 5/6] remove log --- packages/nextjs/services/guides.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/nextjs/services/guides.ts b/packages/nextjs/services/guides.ts index 67ea750f..0ca3f22f 100644 --- a/packages/nextjs/services/guides.ts +++ b/packages/nextjs/services/guides.ts @@ -28,8 +28,6 @@ export async function getGuideBySlug(slug: string): Promise { options: { parseFrontmatter: true }, }); - console.log("content", content); - return { ...frontmatter, content: content, From 0ad7563f243e3bb538bed5dff627cc0217ade64d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez?= Date: Tue, 18 Feb 2025 10:15:28 +0100 Subject: [PATCH 6/6] Fix static params --- packages/nextjs/app/guides/[slug]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/app/guides/[slug]/page.tsx b/packages/nextjs/app/guides/[slug]/page.tsx index 1d2f45a8..81e20167 100644 --- a/packages/nextjs/app/guides/[slug]/page.tsx +++ b/packages/nextjs/app/guides/[slug]/page.tsx @@ -6,7 +6,7 @@ import { getAllGuidesSlugs, getGuideBySlug } from "~~/services/guides"; export async function generateStaticParams() { const slugs = await getAllGuidesSlugs(); - return slugs; + return slugs.map(slug => ({ slug })); } export default async function GuidePage({ params }: { params: { slug: string } }) {