From 53313ea02cfeec1e59dc5a67524c31ac67aa1425 Mon Sep 17 00:00:00 2001 From: Lewin Kelly Date: Fri, 7 Jul 2023 07:34:54 +0100 Subject: [PATCH] Add blog post system to load from markdown files with mdsvex and marked --- .gitignore | 1 + package.json | 8 +++- pages/blog/first.md | 14 ++++++ pages/blog/second.md | 14 ++++++ pages/pages.js | 62 ++++++++++++++++++++++++++ pnpm-lock.yaml | 57 +++++++++++++++++++++++ src/lib/components/Navbar.svelte | 3 +- src/routes/+error.svelte | 10 +++++ src/routes/+layout.svelte | 2 +- src/routes/+page.md | 8 ++++ src/routes/+page.svelte | 9 ---- src/routes/blog/+page.svelte | 26 +++++++++++ src/routes/blog/+page.ts | 18 ++++++++ src/routes/blog/[page]/+page.server.ts | 16 +++++++ src/routes/blog/[page]/+page.svelte | 22 +++++++++ svelte.config.js | 13 ++++++ vite.config.ts | 5 +++ 17 files changed, 275 insertions(+), 13 deletions(-) create mode 100644 pages/blog/first.md create mode 100644 pages/blog/second.md create mode 100644 pages/pages.js create mode 100644 src/routes/+error.svelte create mode 100644 src/routes/+page.md delete mode 100644 src/routes/+page.svelte create mode 100644 src/routes/blog/+page.svelte create mode 100644 src/routes/blog/+page.ts create mode 100644 src/routes/blog/[page]/+page.server.ts create mode 100644 src/routes/blog/[page]/+page.svelte diff --git a/.gitignore b/.gitignore index 6635cf5..a206d67 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ node_modules !.env.example vite.config.js.timestamp-* vite.config.ts.timestamp-* +/pagesjson diff --git a/package.json b/package.json index 53bfc6c..e106c71 100644 --- a/package.json +++ b/package.json @@ -4,18 +4,22 @@ "private": true, "scripts": { "dev": "vite dev", - "build": "vite build", + "build": "node pages/pages.js && vite build", "preview": "vite preview", - "buildview": "vite build && vite preview", + "buildview": "node pages/pages.js && vite build && vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "md": "node pages/pages.js", "lint": "prettier --plugin-search-dir . --check .", "format": "prettier --plugin-search-dir . --write ." }, "devDependencies": { "@sveltejs/adapter-auto": "^2.1.0", "@sveltejs/kit": "^1.21.0", + "@types/marked": "^5.0.0", "@unocss/transformer-directives": "^0.53.4", + "marked": "^5.1.0", + "mdsvex": "^0.11.0", "prettier": "^2.8.8", "prettier-plugin-svelte": "^2.10.1", "sass": "^1.63.6", diff --git a/pages/blog/first.md b/pages/blog/first.md new file mode 100644 index 0000000..9e7c611 --- /dev/null +++ b/pages/blog/first.md @@ -0,0 +1,14 @@ +First blog post +2023-06-07 + +I'm baby portland cred tote bag ethical glossier etsy fixie edison bulb retro irony. Helvetica beard humblebrag before they sold out photo booth yr cloud bread iceland ennui yes plz cold-pressed solarpunk tacos marxism. Yr occupy squid pug helvetica crucifix enamel pin subway tile bruh jean shorts fanny pack. Meditation gluten-free butcher PBR&B twee. Hammock selfies asymmetrical fixie before they sold out. + +Grailed iceland austin chicharrones sriracha 8-bit praxis kinfolk blog everyday carry trust fund DIY pour-over. Sriracha disrupt PBR&B fam gorpcore bodega boys adaptogen butcher. Master cleanse tumeric slow-carb activated charcoal jean shorts freegan artisan poke trust fund poutine paleo marxism viral sartorial. Wayfarers neutral milk hotel unicorn art party skateboard. Actually williamsburg chicharrones palo santo direct trade seitan kickstarter humblebrag church-key air plant tacos sriracha. Vape blackbird spyplane kickstarter +1 hexagon PBR&B. Organic copper mug aesthetic, XOXO marxism quinoa subway tile irony lumbersexual authentic disrupt kitsch solarpunk. + +Whatever JOMO organic artisan photo booth marfa, wayfarers yes plz cray. Keffiyeh gentrify thundercats affogato small batch retro you probably haven't heard of them drinking vinegar try-hard vibecession enamel pin. Tbh crucifix seitan, ennui jawn vice lo-fi DSA franzen fingerstache chillwave vape. Sartorial subway tile forage vaporware organic, XOXO letterpress. + +Mustache bicycle rights copper mug pitchfork af typewriter. Vinyl copper mug bitters sus brunch. Biodiesel copper mug vexillologist, butcher asymmetrical seitan man bun everyday carry. Cray humblebrag lumbersexual Brooklyn chambray vice. Taxidermy viral keytar XOXO hell of intelligentsia next level. + +Squid polaroid cold-pressed bitters, tousled enamel pin succulents. Seitan semiotics tumblr shabby chic heirloom salvia, beard gorpcore narwhal williamsburg forage. Austin synth locavore XOXO succulents artisan. Bodega boys bespoke bicycle rights shaman, mukbang leggings selvage irony yuccie polaroid kale chips activated charcoal chambray. Deep v migas pour-over edison bulb tilde chia vinyl, letterpress umami wolf hot chicken franzen taxidermy health goth tonx. Post-ironic gastropub heirloom, plaid literally dreamcatcher pop-up YOLO migas shoreditch brunch. Lo-fi glossier single-origin coffee, tattooed vegan hexagon kinfolk actually YOLO prism. + +Dummy text? More like dummy thicc text, amirite? diff --git a/pages/blog/second.md b/pages/blog/second.md new file mode 100644 index 0000000..3155d8a --- /dev/null +++ b/pages/blog/second.md @@ -0,0 +1,14 @@ +Second blog post +2023-06-07 + +I'm baby portland cred tote bag ethical glossier etsy fixie edison bulb retro irony. Helvetica beard humblebrag before they sold out photo booth yr cloud bread iceland ennui yes plz cold-pressed solarpunk tacos marxism. Yr occupy squid pug helvetica crucifix enamel pin subway tile bruh jean shorts fanny pack. Meditation gluten-free butcher PBR&B twee. Hammock selfies asymmetrical fixie before they sold out. + +Grailed iceland austin chicharrones sriracha 8-bit praxis kinfolk blog everyday carry trust fund DIY pour-over. Sriracha disrupt PBR&B fam gorpcore bodega boys adaptogen butcher. Master cleanse tumeric slow-carb activated charcoal jean shorts freegan artisan poke trust fund poutine paleo marxism viral sartorial. Wayfarers neutral milk hotel unicorn art party skateboard. Actually williamsburg chicharrones palo santo direct trade seitan kickstarter humblebrag church-key air plant tacos sriracha. Vape blackbird spyplane kickstarter +1 hexagon PBR&B. Organic copper mug aesthetic, XOXO marxism quinoa subway tile irony lumbersexual authentic disrupt kitsch solarpunk. + +Whatever JOMO organic artisan photo booth marfa, wayfarers yes plz cray. Keffiyeh gentrify thundercats affogato small batch retro you probably haven't heard of them drinking vinegar try-hard vibecession enamel pin. Tbh crucifix seitan, ennui jawn vice lo-fi DSA franzen fingerstache chillwave vape. Sartorial subway tile forage vaporware organic, XOXO letterpress. + +Mustache bicycle rights copper mug pitchfork af typewriter. Vinyl copper mug bitters sus brunch. Biodiesel copper mug vexillologist, butcher asymmetrical seitan man bun everyday carry. Cray humblebrag lumbersexual Brooklyn chambray vice. Taxidermy viral keytar XOXO hell of intelligentsia next level. + +Squid polaroid cold-pressed bitters, tousled enamel pin succulents. Seitan semiotics tumblr shabby chic heirloom salvia, beard gorpcore narwhal williamsburg forage. Austin synth locavore XOXO succulents artisan. Bodega boys bespoke bicycle rights shaman, mukbang leggings selvage irony yuccie polaroid kale chips activated charcoal chambray. Deep v migas pour-over edison bulb tilde chia vinyl, letterpress umami wolf hot chicken franzen taxidermy health goth tonx. Post-ironic gastropub heirloom, plaid literally dreamcatcher pop-up YOLO migas shoreditch brunch. Lo-fi glossier single-origin coffee, tattooed vegan hexagon kinfolk actually YOLO prism. + +Dummy text? More like dummy thicc text, amirite? diff --git a/pages/pages.js b/pages/pages.js new file mode 100644 index 0000000..a03851b --- /dev/null +++ b/pages/pages.js @@ -0,0 +1,62 @@ +import { marked } from "marked" +import fs from "fs" + +// Convert all markdown files in the pages directory and all subdirectories +// to HTML, and output as JSON files in the pagesjson directory. + +function walk(dir) { + let results = [] + + for (const file of fs.readdirSync(dir)) { + const name = `${dir}/${file}` + const stat = fs.statSync(name) + + if (stat && stat.isDirectory()) + // Recurse into a subdirectory + results = results.concat(walk(name)) + else if (file.endsWith(".md")) + // Is a file + results.push({ name }) + } + return results +} + +const allMdFiles = walk("./pages") + +fs.rmSync("./pagesjson", { recursive: true }) + +allMdFiles.forEach(file => { + let md = fs.readFileSync(file.name, "utf8") + + const lines = md.split("\n") + + // Remove the first line of the file + const title = lines.shift() + const date = new Date(lines.shift()) + lines.shift() // Remove the empty line + + md = lines.join("\n") + + const html = marked.parse(md, { + mangle: false, + headerIds: false, + }) + + const obj = { title, date, html } + + fs.mkdirSync( + file.name + .replace("/pages/", "/pagesjson/") + .replace(file.name.split("/").pop(), ""), + { recursive: true } + ) + + console.log(`Writing ${file.name.replace(".md", ".json")}`) + + fs.writeFileSync( + file.name.replace("/pages/", "/pagesjson/").replace(".md", ".json"), + JSON.stringify(obj) + ) +}) + +console.log("~ Done! ~") diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5eb02a..6abc213 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,9 +19,18 @@ devDependencies: '@sveltejs/kit': specifier: ^1.21.0 version: 1.21.0(svelte@4.0.3)(vite@4.3.9) + '@types/marked': + specifier: ^5.0.0 + version: 5.0.0 '@unocss/transformer-directives': specifier: ^0.53.4 version: 0.53.4 + marked: + specifier: ^5.1.0 + version: 5.1.0 + mdsvex: + specifier: ^0.11.0 + version: 0.11.0(svelte@4.0.3) prettier: specifier: ^2.8.8 version: 2.8.8 @@ -413,10 +422,18 @@ packages: /@types/estree@1.0.1: resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} + /@types/marked@5.0.0: + resolution: {integrity: sha512-YcZe50jhltsCq7rc9MNZC/4QB/OnA2Pd6hrOSTOFajtabN+38slqgDDCeE/0F83SjkKBQcsZUj7VLWR0H5cKRA==} + dev: true + /@types/pug@2.0.6: resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==} dev: true + /@types/unist@2.0.6: + resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} + dev: true + /@unocss/astro@0.53.4(vite@4.3.9): resolution: {integrity: sha512-fR1F0mNktoN79R+t4GD4y3cvfHUVxtV0+9/6vraZTw3SOXTOMdHeisdxDLjJb3N1yer7XoKX+2GHrKCt873IUA==} dependencies: @@ -1025,9 +1042,27 @@ packages: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 + /marked@5.1.0: + resolution: {integrity: sha512-z3/nBe7aTI8JDszlYLk7dDVNpngjw0o1ZJtrA9kIfkkHcIF+xH7mO23aISl4WxP83elU+MFROgahqdpd05lMEQ==} + engines: {node: '>= 18'} + hasBin: true + dev: true + /mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + /mdsvex@0.11.0(svelte@4.0.3): + resolution: {integrity: sha512-gJF1s0N2nCmdxcKn8HDn0LKrN8poStqAicp6bBcsKFd/zkUBGLP5e7vnxu+g0pjBbDFOscUyI1mtHz+YK2TCDw==} + peerDependencies: + svelte: '>=3 <5' + dependencies: + '@types/unist': 2.0.6 + prism-svelte: 0.4.7 + prismjs: 1.29.0 + svelte: 4.0.3 + vfile-message: 2.0.4 + dev: true + /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} dev: false @@ -1212,6 +1247,15 @@ packages: hasBin: true dev: true + /prism-svelte@0.4.7: + resolution: {integrity: sha512-yABh19CYbM24V7aS7TuPYRNMqthxwbvx6FF/Rw920YbyBWO3tnyPIqRMgHuSVsLmuHkkBS1Akyof463FVdkeDQ==} + dev: true + + /prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + dev: true + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -1495,6 +1539,12 @@ packages: busboy: 1.6.0 dev: true + /unist-util-stringify-position@2.0.3: + resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + dependencies: + '@types/unist': 2.0.6 + dev: true + /unocss@0.53.4(postcss@8.4.24)(vite@4.3.9): resolution: {integrity: sha512-UUEi+oh1rngHHP0DVESRS+ScoKMRF8q6GIQrElHb67gqG7GDEGpy3oocIA/6+1t71I4FFvnnxLMGIo9qAD0TEw==} engines: {node: '>=14'} @@ -1531,6 +1581,13 @@ packages: - vite dev: false + /vfile-message@2.0.4: + resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} + dependencies: + '@types/unist': 2.0.6 + unist-util-stringify-position: 2.0.3 + dev: true + /vite@4.3.9(sass@1.63.6): resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} engines: {node: ^14.18.0 || >=16.0.0} diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index 821a968..6bac70d 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -1,4 +1,4 @@ -
+ diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte new file mode 100644 index 0000000..0fb1384 --- /dev/null +++ b/src/routes/+error.svelte @@ -0,0 +1,10 @@ + + +
+
+

Error {$page.status}

+ {$page.error?.message} +
+
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index d6483e1..997289a 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -7,7 +7,7 @@
diff --git a/src/routes/+page.md b/src/routes/+page.md new file mode 100644 index 0000000..6f5bb72 --- /dev/null +++ b/src/routes/+page.md @@ -0,0 +1,8 @@ + +Revival Archive + + +# Revival Archive + +Visit [kit.svelte.dev](https://kit.svelte.dev) +to read the documentation diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte deleted file mode 100644 index 8326187..0000000 --- a/src/routes/+page.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - Revival Archive - - -

Welcome to SvelteKit

-

- Visit kit.svelte.dev - to read the documentation -

diff --git a/src/routes/blog/+page.svelte b/src/routes/blog/+page.svelte new file mode 100644 index 0000000..005e2d0 --- /dev/null +++ b/src/routes/blog/+page.svelte @@ -0,0 +1,26 @@ + + + + Blog • Revival Archive + + +

Posts

+ +{#each data.posts as post} + +
+

{post.title}

+

+ Published {new Date(post.date).toLocaleDateString("en-GB", { + year: "numeric", + month: "long", + day: "numeric", + hour: "numeric", + minute: "numeric", + })} +

+
+
+{/each} diff --git a/src/routes/blog/+page.ts b/src/routes/blog/+page.ts new file mode 100644 index 0000000..d21a368 --- /dev/null +++ b/src/routes/blog/+page.ts @@ -0,0 +1,18 @@ +export async function load() { + const allPostFiles = import.meta.glob("../../../pagesjson/blog/*.json") + + return { + posts: Promise.all( + Object.entries(allPostFiles).map(async ([path, resolver]) => { + const { title, date, html } = await resolver() + + return { + title, + date, + html, + path: path.match(/(\w+)\.json/)[1], + } + }) + ), + } +} diff --git a/src/routes/blog/[page]/+page.server.ts b/src/routes/blog/[page]/+page.server.ts new file mode 100644 index 0000000..65a9afb --- /dev/null +++ b/src/routes/blog/[page]/+page.server.ts @@ -0,0 +1,16 @@ +import { error } from "@sveltejs/kit" + +export async function load({ params }) { + try { + const { title, date, html } = await import( + `../../../../pagesjson/blog/${params.page}.json` + ) + return { + html, + title, + date, + } + } catch (e) { + throw error(404, "Post not found") + } +} diff --git a/src/routes/blog/[page]/+page.svelte b/src/routes/blog/[page]/+page.svelte new file mode 100644 index 0000000..f570a90 --- /dev/null +++ b/src/routes/blog/[page]/+page.svelte @@ -0,0 +1,22 @@ + + +
+ +

{data.title}

+ + Published {new Date(data.date).toLocaleDateString("en-GB", { + year: "numeric", + month: "long", + day: "numeric", + hour: "numeric", + minute: "numeric", + })} + +
+ +
+ {@html data.html} +
+
diff --git a/svelte.config.js b/svelte.config.js index eec6d32..e85f196 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,9 +1,12 @@ import adapter from "@sveltejs/adapter-auto" import { vitePreprocess } from "@sveltejs/kit/vite" import autoImport from "sveltekit-autoimport" +import { mdsvex } from "mdsvex" /** @type {import('@sveltejs/kit').Config} */ export default { + extensions: [".svelte", ".svelte.md", ".md", ".svx"], + // Consult https://kit.svelte.dev/docs/integrations#preprocessors // for more information about preprocessors preprocess: [ @@ -12,6 +15,16 @@ export default { components: ["./src/lib/components"], flat: true, }), + mdsvex({ + extensions: [".svelte.md", ".md", ".svx"], + + smartypants: { + dashes: "oldschool", + }, + + remarkPlugins: [], + rehypePlugins: [], + }), ], kit: { diff --git a/vite.config.ts b/vite.config.ts index 689061d..c9be25a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,4 +10,9 @@ export default defineConfig({ }), sveltekit(), ], + server: { + fs: { + allow: ["./pagesjson"], + }, + }, })