feat: make mode creation preview-only and cache-safe
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
import { cpSync, copyFileSync, existsSync, mkdirSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { createHash } from "node:crypto";
|
||||
import {
|
||||
cpSync,
|
||||
copyFileSync,
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { basename, dirname, extname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
@@ -45,6 +53,74 @@ const runtimeFiles = [
|
||||
"codepen-glass-theme.css",
|
||||
"vue-ui.js"
|
||||
];
|
||||
const fingerprintedRuntimeFiles = runtimeFiles.filter((file) => (
|
||||
/\.(?:css|js)$/.test(file)
|
||||
&& !["service-worker.js", "speech-audio-worklet.js"].includes(file)
|
||||
));
|
||||
const rewrittenHtmlFiles = [
|
||||
"index.html",
|
||||
"login.html",
|
||||
"hardware-control.html",
|
||||
"firmware-workbench.html",
|
||||
];
|
||||
|
||||
function contentHash(value) {
|
||||
return createHash("sha256").update(value).digest("hex").slice(0, 12);
|
||||
}
|
||||
|
||||
function hashedRuntimeName(file, hash) {
|
||||
const extension = extname(file);
|
||||
return `${basename(file, extension)}.${hash}${extension}`;
|
||||
}
|
||||
|
||||
function rewriteRuntimeReferences(value, manifest) {
|
||||
let rewritten = value;
|
||||
for (const [source, target] of Object.entries(manifest)) {
|
||||
const escaped = source.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
rewritten = rewritten.replace(
|
||||
new RegExp(`/${escaped}(?:\\?[^\\s\"'<>)]*)?`, "g"),
|
||||
`/${target}`,
|
||||
);
|
||||
}
|
||||
return rewritten;
|
||||
}
|
||||
|
||||
function fingerprintRuntimeAssets() {
|
||||
const manifest = {};
|
||||
for (const file of fingerprintedRuntimeFiles) {
|
||||
const source = resolve(outputRoot, file);
|
||||
if (!existsSync(source)) continue;
|
||||
const contents = readFileSync(source);
|
||||
const hashedName = hashedRuntimeName(file, contentHash(contents));
|
||||
writeFileSync(resolve(outputRoot, hashedName), contents);
|
||||
manifest[file] = hashedName;
|
||||
}
|
||||
|
||||
for (const file of rewrittenHtmlFiles) {
|
||||
const target = resolve(outputRoot, file);
|
||||
if (!existsSync(target)) continue;
|
||||
const source = readFileSync(target, "utf8");
|
||||
writeFileSync(target, rewriteRuntimeReferences(source, manifest), "utf8");
|
||||
}
|
||||
|
||||
const serviceWorkerPath = resolve(outputRoot, "service-worker.js");
|
||||
const buildHash = contentHash(JSON.stringify(manifest));
|
||||
if (existsSync(serviceWorkerPath)) {
|
||||
const serviceWorker = rewriteRuntimeReferences(
|
||||
readFileSync(serviceWorkerPath, "utf8"),
|
||||
manifest,
|
||||
).replace(
|
||||
/const CACHE_VERSION = "[^"]+";/,
|
||||
`const CACHE_VERSION = "cognitive-agent-pad-build-${buildHash}";`,
|
||||
);
|
||||
writeFileSync(serviceWorkerPath, serviceWorker, "utf8");
|
||||
}
|
||||
writeFileSync(
|
||||
resolve(outputRoot, "runtime-assets.json"),
|
||||
`${JSON.stringify({ build_hash: buildHash, assets: manifest }, null, 2)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
}
|
||||
|
||||
function copyRuntimeAssets() {
|
||||
return {
|
||||
@@ -64,6 +140,7 @@ function copyRuntimeAssets() {
|
||||
mkdirSync(dirname(target), { recursive: true });
|
||||
copyFileSync(source, target);
|
||||
}
|
||||
fingerprintRuntimeAssets();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user