65 lines
2.2 KiB
JavaScript
65 lines
2.2 KiB
JavaScript
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
export const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
const webRoot = path.join(root, "web");
|
|
|
|
function walk(directory, output) {
|
|
if (!fs.existsSync(directory)) return;
|
|
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".cache" || entry.name === ".vite") continue;
|
|
const file = path.join(directory, entry.name);
|
|
if (entry.isDirectory()) walk(file, output);
|
|
else if (entry.isFile()) output.push(file);
|
|
}
|
|
}
|
|
|
|
export function inputFiles() {
|
|
const files = [
|
|
path.join(webRoot, "package.json"),
|
|
path.join(webRoot, "package-lock.json"),
|
|
path.join(webRoot, "tsconfig.json"),
|
|
path.join(webRoot, "app/vite.config.ts"),
|
|
];
|
|
walk(path.join(webRoot, "app/src"), files);
|
|
walk(path.join(webRoot, "protocol"), files);
|
|
walk(path.join(webRoot, "app/public"), files);
|
|
return [...new Set(files)].filter((file) => fs.existsSync(file)).sort();
|
|
}
|
|
|
|
export function fingerprint() {
|
|
const hash = crypto.createHash("sha256");
|
|
for (const file of inputFiles()) {
|
|
// Read nanosecond metadata as bigint. Without the explicit option
|
|
// `mtimeNs` is undefined in Node, so same-sized edits can reuse stale
|
|
// bundles indefinitely.
|
|
const stat = fs.statSync(file, { bigint: true });
|
|
hash.update(path.relative(root, file));
|
|
hash.update("\0");
|
|
hash.update(stat.size.toString());
|
|
hash.update("\0");
|
|
hash.update(stat.mtimeNs.toString());
|
|
hash.update("\n");
|
|
}
|
|
return hash.digest("hex");
|
|
}
|
|
|
|
export function statePath(name) {
|
|
return path.join(webRoot, ".cache", name);
|
|
}
|
|
|
|
export function readState(name) {
|
|
try { return JSON.parse(fs.readFileSync(statePath(name), "utf8")); }
|
|
catch { return null; }
|
|
}
|
|
|
|
export function writeState(name, value) {
|
|
const file = statePath(name);
|
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
const temporary = `${file}.tmp-${process.pid}`;
|
|
fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`);
|
|
fs.renameSync(temporary, file);
|
|
}
|