From 4b7c669ad0f8cb4e3b4cd83a5a34cb3e61b5bebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20=E2=80=9CAd=C3=A6dra=E2=80=9D=20Hamel?= Date: Mon, 24 Jun 2024 18:55:39 +0200 Subject: [PATCH] Remove pug and use Preact for templating --- lib/tasks/articles.ts | 59 ++++++++++----------- package-lock.json | 116 +++++++----------------------------------- package.json | 5 +- src/article.pug | 13 ----- src/article.tsx | 28 ++++++++++ src/constants.ts | 13 +++++ src/index.pug | 39 -------------- src/index.tsx | 84 ++++++++++++++++++++++++++++++ src/layout.pug | 15 ------ src/layout.tsx | 38 ++++++++++++++ tsconfig.json | 2 + 11 files changed, 212 insertions(+), 200 deletions(-) delete mode 100644 src/article.pug create mode 100644 src/article.tsx create mode 100644 src/constants.ts delete mode 100644 src/index.pug create mode 100644 src/index.tsx delete mode 100644 src/layout.pug create mode 100644 src/layout.tsx diff --git a/lib/tasks/articles.ts b/lib/tasks/articles.ts index e09386e..6ec0bf2 100644 --- a/lib/tasks/articles.ts +++ b/lib/tasks/articles.ts @@ -1,4 +1,3 @@ -import { compileFile } from "pug"; import { readFileSync } from "node:fs"; import { env } from "node:process"; import { join, basename, dirname } from "node:path"; @@ -16,6 +15,12 @@ import rehypeExternalLinks from "rehype-external-links"; import visit from "unist-util-visit"; import { src, dest } from "gulp"; import { readAssetManifest } from "../hash.js"; +import renderLayout from "../../src/layout.js"; +import renderArticleLayout from "../../src/article.js"; +import renderIndex from "../../src/index.js"; +import { renderToStaticMarkup } from "preact-render-to-string"; +import { SITE_TITLE, SITE_DESCRIPTION } from "../../src/constants.js"; +import { JSX } from "preact/jsx-runtime"; const Asciidoctor = asciidoctor(); const EXTENSION_REGISTRY = Asciidoctor.Extensions.create(); @@ -29,18 +34,6 @@ EXTENSION_REGISTRY.inlineMacro("abbr", function () { }); }); -const SITE_TITLE = "Ad\xE6dra's blog"; -const SITE_DESCRIPTION = [ - "Ad\xE6dra", - "Software Developper in Paris, France", - "Rust, Ruby, Typescript, Linux", -].join(" \u2022 "); -const SITE_DEFAULT_META = { - "og:image": "https://adaedra.blob.core.windows.net/blog-assets/og_preview.png", - "og:image:alt": "Ad\xE6dra's mascot", - "og:locale": "en_GB", -}; - function extractImages(gulp: Transform, file: File) { return function () { return function (tree) { @@ -61,10 +54,12 @@ function extractImages(gulp: Transform, file: File) { }; } +function renderDocument(root: JSX.Element): Buffer { + return Buffer.concat([Buffer.from(""), Buffer.from(renderToStaticMarkup(root))]); +} + function renderArticle() { const allArticles: [string, DateTime, Document][] = []; - const renderLayout = compileFile("src/layout.pug"); - const renderArticleLayout = compileFile("src/article.pug"); const assetManifest = readAssetManifest(); const asset = (path: string) => { @@ -103,31 +98,28 @@ function renderArticle() { .use(rehypeStringify) .process(article.convert()); const content = renderLayout({ - SITE_TITLE, asset, - title: article.getDoctitle({}), - meta: Object.assign({}, SITE_DEFAULT_META, { + title: article.getDoctitle({}) as string, + meta: { description: article.getAttribute("description"), keywords: article.getAttribute("keywords"), - "og:title": article.getDoctitle({}), + "og:title": article.getDoctitle({}) as string, "og:type": "article", "og:article:published_time": article.getAttribute("docdate"), "og:url": `https://adaedra.eu/${slug}/`, "og:description": article.getAttribute("description"), "og:site_name": SITE_TITLE, - }), - render() { - return renderArticleLayout({ + }, + Content: () => + renderArticleLayout({ asset, article, date, - DateTime, body: vfile.toString(), - }); - }, + }), }); - file.contents = Buffer.from(content); + file.contents = renderDocument(content); file.path = join(slug, "index.html"); file.base = null; @@ -140,22 +132,23 @@ function renderArticle() { final(callback) { try { allArticles.sort(([, a], [, b]) => b.diff(a).toMillis()); - const renderIndex = compileFile("src/index.pug"); const contents = renderLayout({ - SITE_TITLE, asset, - meta: Object.assign({}, SITE_DEFAULT_META, { + meta: { description: SITE_DESCRIPTION, "og:title": SITE_TITLE, "og:type": "website", "og:url": `https://adaedra.eu`, - }), - render() { - return renderIndex({ articles: allArticles, asset }); }, + Content: () => renderIndex({ articles: allArticles, asset }), }); - this.push(new File({ path: "index.html", contents: Buffer.from(contents) })); + this.push( + new File({ + path: "index.html", + contents: renderDocument(contents), + }), + ); callback(null); } catch (e) { console.error(e); diff --git a/package-lock.json b/package-lock.json index ba632bd..f228f36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,8 @@ "postcss": "^8.4.38", "postcss-import": "^16.1.0", "postcss-nesting": "^12.1.5", - "pug": "^3.0.3", + "preact": "^10.22.0", + "preact-render-to-string": "^6.5.5", "rehype": "^13.0.1", "rehype-external-links": "^3.0.0", "rehype-preset-minify": "^7.0.0", @@ -28,12 +29,10 @@ "unified": "^11.0.4" }, "devDependencies": { - "@types/express": "^4.17.21", "@types/glob": "^8.1.0", "@types/gulp": "^4.0.17", "@types/gulp-postcss": "^8.0.6", "@types/luxon": "^3.4.2", - "@types/pug": "^2.0.10", "@types/tmp": "^0.2.6", "prettier": "^3.3.1" } @@ -374,55 +373,12 @@ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/expect": { "version": "1.20.4", "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", "dev": true }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.3", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.3.tgz", - "integrity": "sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, "node_modules/@types/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", @@ -474,12 +430,6 @@ "@types/unist": "^2" } }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true - }, "node_modules/@types/luxon": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", @@ -494,12 +444,6 @@ "@types/unist": "*" } }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, "node_modules/@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", @@ -520,45 +464,6 @@ "integrity": "sha512-Yll76ZHikRFCyz/pffKGjrCwe/le2CDwOP5F210KQo27kpRE46U2rDnzikNlVn6/ezH3Mhn46bJMTfeVTtcYMg==", "dev": true }, - "node_modules/@types/pug": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", - "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", - "dev": true - }, - "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, "node_modules/@types/streamx": { "version": "2.9.5", "resolved": "https://registry.npmjs.org/@types/streamx/-/streamx-2.9.5.tgz", @@ -4341,6 +4246,23 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/preact": { + "version": "10.22.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.22.0.tgz", + "integrity": "sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.5.5.tgz", + "integrity": "sha512-KiMFTKNTmT/ccE79BURR/r6XRc2I2TCTZ0MpeWqHW2XnllbeghXvwGsdAfF/MzMilUcTfODtSmMxgoRFL9TM5g==", + "peerDependencies": { + "preact": ">=10" + } + }, "node_modules/prettier": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", diff --git a/package.json b/package.json index ea3d124..71a46ae 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "postcss": "^8.4.38", "postcss-import": "^16.1.0", "postcss-nesting": "^12.1.5", - "pug": "^3.0.3", + "preact": "^10.22.0", + "preact-render-to-string": "^6.5.5", "rehype": "^13.0.1", "rehype-external-links": "^3.0.0", "rehype-preset-minify": "^7.0.0", @@ -24,12 +25,10 @@ "unified": "^11.0.4" }, "devDependencies": { - "@types/express": "^4.17.21", "@types/glob": "^8.1.0", "@types/gulp": "^4.0.17", "@types/gulp-postcss": "^8.0.6", "@types/luxon": "^3.4.2", - "@types/pug": "^2.0.10", "@types/tmp": "^0.2.6", "prettier": "^3.3.1" }, diff --git a/src/article.pug b/src/article.pug deleted file mode 100644 index 6e5edbb..0000000 --- a/src/article.pug +++ /dev/null @@ -1,13 +0,0 @@ -nav - a(href="/" title="Back to home page") - img(src=asset("avatar.avif") alt="Adædra's avatar") - | Adædra - -article - header.article-header - = date.toLocaleString(DateTime.DATE_FULL) - h1!= article.getDocumentTitle() - - main - a(name="content") - != body diff --git a/src/article.tsx b/src/article.tsx new file mode 100644 index 0000000..2d2ca77 --- /dev/null +++ b/src/article.tsx @@ -0,0 +1,28 @@ +import { Document } from "asciidoctor"; +import { DateTime } from "luxon"; + +type Props = { + article: Document; + date: DateTime; + body: string; + asset(path: string): string; +}; + +export default ({ asset, article, date, body }: Props) => ( + <> + +
+
+ {date.toLocaleString(DateTime.DATE_FULL)} +

+

+ +
+
+ +); diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..17410db --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,13 @@ +export const SITE_TITLE = "Ad\xE6dra's blog"; +export const SITE_DESCRIPTION = [ + "Ad\xE6dra", + "Software Developper in Paris, France", + "Rust, Ruby, Typescript, Linux", +].join(" \u2022 "); +export const SITE_DEFAULT_META = { + viewport: "width=device-width, initial-scale=1", + "theme-color": "#DDCBA3", + "og:image": "https://adaedra.blob.core.windows.net/blog-assets/og_preview.png", + "og:image:alt": "Ad\xE6dra's mascot", + "og:locale": "en_GB", +}; diff --git a/src/index.pug b/src/index.pug deleted file mode 100644 index 30dde87..0000000 --- a/src/index.pug +++ /dev/null @@ -1,39 +0,0 @@ -header.index-header - div(style="flex: 1;") - h1 Adædra - | Software developper
- | Rust, Ruby, TypeScript, Linux
- | Paris, France - img(src=asset("cariboudev.avif") alt="Adædra's mascot")/ - -h2 Latest articles -ul.index-list - - for ([slug, date, article] of articles) - li - = date.toISODate() - |   - a(href=`/${slug}/`)!= article.getDocumentTitle() - -h2 Find me elsewhere -ul.index-list - li: a(href="https://gitea.adaedra.eu/adaedra" target="_blank") - svg(xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" aria-label="Git Logo"): use(href="/logos.svg#git")/ - | Gitea - li: a(href="https://nerdculture.de/@adaedra" target="_blank" rel="noopener") - svg(xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" aria-label="Mastodon Logo"): use(href="/logos.svg#mastodon")/ - | Mastodon - li: a(href="https://www.linkedin.com/in/thibault-hamel/" target="_blank" rel="noopener") - svg(xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" aria-label="LinkedIn Logo"): use(href="/logos.svg#linkedin")/ - | LinkedIn - -footer.index-footer - | Mascot drawn by  - a(href="https://linktr.ee/cutepencilcase" target="_blank" rel="noopener") CutePencilCase - |
Font  - a(href="https://github.com/be5invis/Iosevka" target="_blank" rel="noopener") Iosevka - | • Color theme  - a(href="https://github.com/morhetz/gruvbox-contrib" target="_blank" rel="noopener") Gruvbox - | • Social icons  - a(href="https://icons8.com/" target="_blank" rel="noopener") Icons8 - br/ - a(href="https://gitea.adaedra.eu/adaedra/blog" target="_blank") Source code diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 0000000..34e9358 --- /dev/null +++ b/src/index.tsx @@ -0,0 +1,84 @@ +import { Document } from "asciidoctor"; +import { DateTime } from "luxon"; + +type HeaderProps = { + asset(path: string): string; +}; + +const Header = ({ asset }: HeaderProps) => ( +
+
+

Adædra

+ Software developer +
+ Rust, Ruby, TypeScript, Linux +
+ Paris, France +
+ Adaedra's mascot +
+); + +type ArticleProps = { + articles: [string, DateTime, Document][]; +}; + +const Articles = ({ articles }: ArticleProps) => ( + <> +

Latest articles

+
+ +); + +type Props = HeaderProps & ArticleProps; + +const SOCIALS: [string, string, string][] = [ + ["Gitea", "https://gitea.adaedra.eu", "git"], + ["Mastodon", "https://nerdculture.de/@adaedra", "mastodon"], + ["LinkedIn", "https://www.linkedin.com/in/thibault-hamel/", "linkedin"], +]; + +const Socials = () => ( + <> +

Find me elsewhere

+
+ +); + +const Footer = () => ; + +export default ({ asset, articles }: Props) => ( + <> +
+ + +