Compare commits
No commits in common. "80912cb6e2e33399010b2f5fd1034fc24f221c44" and "9fd97d17d8e319d48ac6069ebd732d7cefe51b4e" have entirely different histories.
80912cb6e2
...
9fd97d17d8
|
@ -1,27 +0,0 @@
|
|||
import { globSync } from "glob";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { env } from "node:process";
|
||||
import { join } from "node:path";
|
||||
|
||||
export const assetMap: Map<string, string> = new Map();
|
||||
|
||||
export function reloadAssets() {
|
||||
assetMap.clear();
|
||||
|
||||
globSync("dist/_assets/*.manifest")
|
||||
.map(
|
||||
(manifest) =>
|
||||
JSON.parse(readFileSync(manifest).toString()) as { [orig: string]: string },
|
||||
)
|
||||
.forEach((mapping) =>
|
||||
Object.entries(mapping).forEach(([orig, hashed]) => assetMap.set(orig, hashed)),
|
||||
);
|
||||
}
|
||||
|
||||
export function asset(path: string): string {
|
||||
const realPath = assetMap.has(path) ? assetMap.get(path) : path;
|
||||
|
||||
return env.ASSETS_HOSTS
|
||||
? new URL(realPath, env.ASSETS_HOSTS).toString()
|
||||
: join("/_assets/", realPath);
|
||||
}
|
11
lib/hash.ts
11
lib/hash.ts
|
@ -1,8 +1,19 @@
|
|||
import { Transform } from "node:stream";
|
||||
import { createHash } from "node:crypto";
|
||||
import File from "vinyl";
|
||||
import { globSync } from "glob";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { PRODUCTION } from "./environment.js";
|
||||
|
||||
export function readAssetManifest(): { [entry: string]: string } {
|
||||
return Object.assign(
|
||||
{},
|
||||
...globSync("dist/_assets/*.manifest").map((path) =>
|
||||
JSON.parse(readFileSync(path).toString()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function fileHash(buffer: Buffer) {
|
||||
const hash = createHash("sha256");
|
||||
hash.update(buffer.toString());
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import Postcss from "postcss";
|
||||
import { reloadAssets, assetMap } from "../assets.js";
|
||||
import { readAssetManifest } from "../hash.js";
|
||||
|
||||
export default (): Postcss.Plugin => {
|
||||
export default (opts: any = {}): Postcss.Plugin => {
|
||||
return {
|
||||
postcssPlugin: "postcss-hashes",
|
||||
Once() {
|
||||
reloadAssets();
|
||||
opts._mappings = readAssetManifest();
|
||||
},
|
||||
Declaration(decl: Postcss.Declaration) {
|
||||
decl.value = decl.value.replace(/url\("([^"]+)"\)/, (v, url) => {
|
||||
if (assetMap.has(url)) {
|
||||
return v.replace(url, assetMap.get(url));
|
||||
if (opts._mappings.hasOwnProperty(url)) {
|
||||
return v.replace(url, opts._mappings[url]);
|
||||
}
|
||||
|
||||
return v;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { compileFile } from "pug";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { env } from "node:process";
|
||||
import { join, basename, dirname } from "node:path";
|
||||
import { Transform } from "node:stream";
|
||||
import asciidoctor, { Document } from "asciidoctor";
|
||||
|
@ -13,13 +15,7 @@ import { PRODUCTION, DEFAULT_DATE } from "../environment.js";
|
|||
import rehypeExternalLinks from "rehype-external-links";
|
||||
import visit from "unist-util-visit";
|
||||
import { src, dest } from "gulp";
|
||||
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";
|
||||
import { reloadAssets } from "../assets.js";
|
||||
import { readAssetManifest } from "../hash.js";
|
||||
|
||||
const Asciidoctor = asciidoctor();
|
||||
const EXTENSION_REGISTRY = Asciidoctor.Extensions.create();
|
||||
|
@ -33,6 +29,18 @@ 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) {
|
||||
|
@ -53,13 +61,21 @@ function extractImages(gulp: Transform, file: File) {
|
|||
};
|
||||
}
|
||||
|
||||
function renderDocument(root: JSX.Element): Buffer {
|
||||
return Buffer.concat([Buffer.from("<!doctype html>"), Buffer.from(renderToStaticMarkup(root))]);
|
||||
}
|
||||
|
||||
function renderArticle() {
|
||||
const allArticles: [string, DateTime, Document][] = [];
|
||||
reloadAssets();
|
||||
const renderLayout = compileFile("src/layout.pug");
|
||||
const renderArticleLayout = compileFile("src/article.pug");
|
||||
const assetManifest = readAssetManifest();
|
||||
|
||||
const asset = (path: string) => {
|
||||
if (assetManifest.hasOwnProperty(path)) {
|
||||
path = assetManifest[path];
|
||||
}
|
||||
|
||||
return env.ASSETS_HOSTS
|
||||
? new URL(path, env.ASSETS_HOSTS).toString()
|
||||
: join("/_assets/", path);
|
||||
};
|
||||
|
||||
return new Transform({
|
||||
readableObjectMode: true,
|
||||
|
@ -87,26 +103,31 @@ function renderArticle() {
|
|||
.use(rehypeStringify)
|
||||
.process(article.convert());
|
||||
const content = renderLayout({
|
||||
title: article.getDoctitle({}) as string,
|
||||
meta: {
|
||||
SITE_TITLE,
|
||||
asset,
|
||||
title: article.getDoctitle({}),
|
||||
meta: Object.assign({}, SITE_DEFAULT_META, {
|
||||
description: article.getAttribute("description"),
|
||||
keywords: article.getAttribute("keywords"),
|
||||
"og:title": article.getDoctitle({}) as string,
|
||||
"og:title": article.getDoctitle({}),
|
||||
"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,
|
||||
},
|
||||
Content: () =>
|
||||
renderArticleLayout({
|
||||
}),
|
||||
render() {
|
||||
return renderArticleLayout({
|
||||
asset,
|
||||
article,
|
||||
date,
|
||||
DateTime,
|
||||
body: vfile.toString(),
|
||||
}),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
file.contents = renderDocument(content);
|
||||
file.contents = Buffer.from(content);
|
||||
file.path = join(slug, "index.html");
|
||||
file.base = null;
|
||||
|
||||
|
@ -119,22 +140,22 @@ function renderArticle() {
|
|||
final(callback) {
|
||||
try {
|
||||
allArticles.sort(([, a], [, b]) => b.diff(a).toMillis());
|
||||
const renderIndex = compileFile("src/index.pug");
|
||||
const contents = renderLayout({
|
||||
meta: {
|
||||
SITE_TITLE,
|
||||
asset,
|
||||
meta: Object.assign({}, SITE_DEFAULT_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 }),
|
||||
});
|
||||
|
||||
this.push(
|
||||
new File({
|
||||
path: "index.html",
|
||||
contents: renderDocument(contents),
|
||||
}),
|
||||
);
|
||||
this.push(new File({ path: "index.html", contents: Buffer.from(contents) }));
|
||||
callback(null);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
|
116
package-lock.json
generated
116
package-lock.json
generated
|
@ -19,8 +19,7 @@
|
|||
"postcss": "^8.4.38",
|
||||
"postcss-import": "^16.1.0",
|
||||
"postcss-nesting": "^12.1.5",
|
||||
"preact": "^10.22.0",
|
||||
"preact-render-to-string": "^6.5.5",
|
||||
"pug": "^3.0.3",
|
||||
"rehype": "^13.0.1",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"rehype-preset-minify": "^7.0.0",
|
||||
|
@ -29,10 +28,12 @@
|
|||
"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"
|
||||
}
|
||||
|
@ -373,12 +374,55 @@
|
|||
"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",
|
||||
|
@ -430,6 +474,12 @@
|
|||
"@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",
|
||||
|
@ -444,6 +494,12 @@
|
|||
"@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",
|
||||
|
@ -464,6 +520,45 @@
|
|||
"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",
|
||||
|
@ -4246,23 +4341,6 @@
|
|||
"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",
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
"postcss": "^8.4.38",
|
||||
"postcss-import": "^16.1.0",
|
||||
"postcss-nesting": "^12.1.5",
|
||||
"preact": "^10.22.0",
|
||||
"preact-render-to-string": "^6.5.5",
|
||||
"pug": "^3.0.3",
|
||||
"rehype": "^13.0.1",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"rehype-preset-minify": "^7.0.0",
|
||||
|
@ -25,10 +24,12 @@
|
|||
"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"
|
||||
},
|
||||
|
|
13
src/article.pug
Normal file
13
src/article.pug
Normal file
|
@ -0,0 +1,13 @@
|
|||
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
|
|
@ -1,28 +0,0 @@
|
|||
import { Document } from "asciidoctor";
|
||||
import { DateTime } from "luxon";
|
||||
import { asset } from "../lib/assets.js";
|
||||
|
||||
type Props = {
|
||||
article: Document;
|
||||
date: DateTime;
|
||||
body: string;
|
||||
};
|
||||
|
||||
export default ({ article, date, body }: Props) => (
|
||||
<>
|
||||
<nav>
|
||||
<a href="/" title="Back to home page">
|
||||
<img src={asset("avatar.avif")} alt="Adædra's avatar" />
|
||||
Adædra
|
||||
</a>
|
||||
</nav>
|
||||
<article>
|
||||
<header class="article-header">
|
||||
{date.toLocaleString(DateTime.DATE_FULL)}
|
||||
<h1 dangerouslySetInnerHTML={{ __html: article.getDocumentTitle() as string }} />
|
||||
</header>
|
||||
<a name="content" />
|
||||
<main dangerouslySetInnerHTML={{ __html: body }} />
|
||||
</article>
|
||||
</>
|
||||
);
|
|
@ -1,13 +0,0 @@
|
|||
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",
|
||||
};
|
39
src/index.pug
Normal file
39
src/index.pug
Normal file
|
@ -0,0 +1,39 @@
|
|||
header.index-header
|
||||
div(style="flex: 1;")
|
||||
h1 Adædra
|
||||
| Software developper<br />
|
||||
| Rust, Ruby, TypeScript, Linux<br />
|
||||
| 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
|
||||
| <br />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
|
|
@ -1,79 +0,0 @@
|
|||
import { Document } from "asciidoctor";
|
||||
import { DateTime } from "luxon";
|
||||
import { asset } from "../lib/assets.js";
|
||||
|
||||
const Header = () => (
|
||||
<header class="index-header">
|
||||
<div style={{ flex: 1 }}>
|
||||
<h1>Adædra</h1>
|
||||
Software developer
|
||||
<br />
|
||||
Rust, Ruby, TypeScript, Linux
|
||||
<br />
|
||||
Paris, France
|
||||
</div>
|
||||
<img src={asset("cariboudev.avif")} alt="Adaedra's mascot" />
|
||||
</header>
|
||||
);
|
||||
|
||||
type Props = {
|
||||
articles: [string, DateTime, Document][];
|
||||
};
|
||||
|
||||
const Articles = ({ articles }: Props) => (
|
||||
<>
|
||||
<h2>Latest articles</h2>
|
||||
<ul class="index-list">
|
||||
{articles.map(([slug, date, article]) => (
|
||||
<li>
|
||||
{date.toISODate()}
|
||||
<a
|
||||
href={`/${slug}/`}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: article.getDocumentTitle() as string,
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
|
||||
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 = () => (
|
||||
<>
|
||||
<h2>Find me elsewhere</h2>
|
||||
<ul class="index-list">
|
||||
{SOCIALS.map(([name, url, svgId]) => (
|
||||
<li>
|
||||
<a href={url} target="_blank" rel="noopener">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 32 32"
|
||||
aria-label={`${name} Logo`}
|
||||
>
|
||||
<use href={`/logos.svg#${svgId}`} />
|
||||
</svg>
|
||||
{name}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
|
||||
const Footer = () => <footer class="index-footer"></footer>;
|
||||
|
||||
export default (props: Props) => (
|
||||
<>
|
||||
<Header />
|
||||
<Articles {...props} />
|
||||
<Socials />
|
||||
<Footer />
|
||||
</>
|
||||
);
|
15
src/layout.pug
Normal file
15
src/layout.pug
Normal file
|
@ -0,0 +1,15 @@
|
|||
doctype html
|
||||
html(lang="en")
|
||||
head
|
||||
meta(charset="utf-8")/
|
||||
title
|
||||
if title
|
||||
!= `${title} – `
|
||||
= SITE_TITLE
|
||||
meta(name="viewport" content="width=device-width, initial-scale=1")/
|
||||
meta(name="theme-color" content="#DDCBA3")/
|
||||
- for([name, content] of Object.entries(meta))
|
||||
meta(name=name content=content)/
|
||||
link(rel="stylesheet" type="text/css" href=asset("index.css"))/
|
||||
body: .main
|
||||
!= render()
|
|
@ -1,35 +0,0 @@
|
|||
import { SITE_DEFAULT_META, SITE_TITLE } from "./constants.js";
|
||||
import { JSX } from "preact/jsx-runtime";
|
||||
import { asset } from "../lib/assets.js";
|
||||
|
||||
type Props = {
|
||||
title?: string;
|
||||
meta?: { [name: string]: string };
|
||||
Content: () => JSX.Element;
|
||||
};
|
||||
|
||||
export default ({ title, meta, Content }: Props) => {
|
||||
const metaTags = Object.entries(Object.assign({}, SITE_DEFAULT_META, meta || {})).map(
|
||||
([name, value]) => <meta name={name} value={value} />,
|
||||
);
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: [title, SITE_TITLE].filter(Boolean).join(" – "),
|
||||
}}
|
||||
/>
|
||||
{metaTags}
|
||||
<link rel="stylesheet" type="text/css" href={asset("index.css")} />
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<Content />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
};
|
|
@ -7,7 +7,5 @@
|
|||
},
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "preact"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user