generated from sofia/eleventy-base
Implement hashing of assets from scratch
This is a great idea... for sure
This commit is contained in:
parent
c5e968ce0d
commit
a60159ad68
2 changed files with 93 additions and 9 deletions
90
.eleventy.js
90
.eleventy.js
|
@ -1,7 +1,9 @@
|
|||
import { createRequire } from "module"
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
import { join } from "path";
|
||||
import { basename, dirname, extname, join, relative } from "path";
|
||||
import { copyFile, mkdir, readdir, readFile, stat, unlink } from "fs/promises";
|
||||
import { createHash } from "crypto";
|
||||
|
||||
import { DateTime } from "luxon";
|
||||
|
||||
|
@ -14,6 +16,70 @@ import markdownItAnchor from "markdown-it-anchor";
|
|||
const markdownItEmoji = require("markdown-it-emoji");
|
||||
import markdownItFootnote from "markdown-it-footnote";
|
||||
|
||||
const HASHED_ASSETS = [".css", ".js"]
|
||||
const assetsManifest = {}
|
||||
|
||||
async function hashFile(filePath) {
|
||||
const content = await readFile(filePath)
|
||||
return createHash("sha1").update(content).digest("hex").slice(0, 12)
|
||||
}
|
||||
|
||||
async function walk(dir) {
|
||||
let files = [];
|
||||
for (const item of await readdir(dir, { withFileTypes: true })) {
|
||||
const fullPath = join(dir, item.name);
|
||||
|
||||
if (item.isDirectory()) {
|
||||
files = files.concat(await walk(fullPath));
|
||||
} else if (item.isFile()) {
|
||||
files.push(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
async function prepareManifest(assetsPath, log = console.log) {
|
||||
const files = await walk(assetsPath);
|
||||
|
||||
for (const absPath of files) {
|
||||
if (!(await stat(absPath)).isFile()) continue;
|
||||
|
||||
const ext = extname(absPath)
|
||||
if (!HASHED_ASSETS.includes(ext)) {
|
||||
const rel = relative(assetsPath, absPath)
|
||||
assetsManifest[rel] = rel
|
||||
continue;
|
||||
}
|
||||
|
||||
const base = basename(absPath, ext)
|
||||
if (base.endsWith("__h")) continue;
|
||||
|
||||
const hash = await hashFile(absPath)
|
||||
const newBase = `${base}-${hash}__h${ext}`
|
||||
|
||||
const relDir = dirname(relative(assetsPath, absPath))
|
||||
const newRelPath = relDir === "."
|
||||
? newBase
|
||||
: join(relDir, newBase)
|
||||
|
||||
assetsManifest[relative(assetsPath, absPath)] = newRelPath
|
||||
log(`[sofi-assets] Manifest: '${absPath}' to '${newRelPath}'`)
|
||||
}
|
||||
}
|
||||
|
||||
async function processAssets(assetsPath, outputPath, log = console.log) {
|
||||
for (const [origRel, newRel] of Object.entries(assetsManifest)) {
|
||||
const src = join(assetsPath, origRel);
|
||||
const dest = join(outputPath, newRel);
|
||||
|
||||
await mkdir(dirname(dest), { recursive: true })
|
||||
|
||||
await copyFile(src, dest);
|
||||
log(`[sofi-assets] Copied: '${src}' to '${dest}'`);
|
||||
}
|
||||
}
|
||||
|
||||
const markdownItOptions = {
|
||||
html: true,
|
||||
linkify: true,
|
||||
|
@ -47,8 +113,26 @@ function dateDesc(a, b) {
|
|||
return DateTime.fromJSDate(b.data.date) - DateTime.fromJSDate(a.data.date);
|
||||
}
|
||||
|
||||
export default function (eleventyConfig) {
|
||||
eleventyConfig.addPassthroughCopy({ "assets": "/" })
|
||||
export default async function (eleventyConfig) {
|
||||
const log = eleventyConfig.logger.log.bind(eleventyConfig.logger)
|
||||
|
||||
eleventyConfig.addWatchTarget("./assets/")
|
||||
eleventyConfig.on("eleventy.before", async () => {
|
||||
prepareManifest("assets", log)
|
||||
})
|
||||
|
||||
eleventyConfig.on("eleventy.after", async () => {
|
||||
log("[sofi-assets] Build done, performing asset processing")
|
||||
await processAssets("assets", join(dirOptions.output, "/assets/"), log)
|
||||
})
|
||||
|
||||
eleventyConfig.addShortcode("assetPath", (asset) => {
|
||||
if (assetsManifest[asset] != null) {
|
||||
return `/assets/${assetsManifest[asset]}`
|
||||
} else {
|
||||
throw `Asset '${asset}' is not in asset manifest`
|
||||
}
|
||||
})
|
||||
|
||||
eleventyConfig.addCollection("weblog", (collection) => {
|
||||
return collection.getFilteredByGlob(join(dirOptions.input, "/weblog/*"))
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
<meta name="description" content="{{ description | default: site.description }}">
|
||||
<meta name="fediverse:creator" content="@sofiaritz@hachyderm.io">
|
||||
<link href="https://cdn.sofiaritz.com" rel="preconnect" crossorigin>
|
||||
<link rel="stylesheet" href="/styles/root.css">
|
||||
<link rel="stylesheet" href="/styles/fonts.css">
|
||||
<link rel="stylesheet" href="/styles/layout.css">
|
||||
<link rel="stylesheet" href="/styles/text.css">
|
||||
<link rel="stylesheet" href="/styles/components.css">
|
||||
<link href="/styles/prism-coldark-cold.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{% assetPath 'styles/root.css' %}">
|
||||
<link rel="stylesheet" href="{% assetPath 'styles/fonts.css' %}">
|
||||
<link rel="stylesheet" href="{% assetPath 'styles/layout.css' %}">
|
||||
<link rel="stylesheet" href="{% assetPath 'styles/text.css' %}">
|
||||
<link rel="stylesheet" href="{% assetPath 'styles/components.css' %}">
|
||||
<link rel="stylesheet" href="{% assetPath 'styles/prism-coldark-cold.css' %}">
|
||||
<title>{% if title %}{{ title }} - {% endif %}{{ site.title }}</title>
|
||||
<link rel="alternate" href="{{ site.url }}/feed.xml" type="application/atom+xml" title="{{ site.title }} Feed">
|
||||
</head>
|
||||
|
|
Loading…
Reference in a new issue