Rework the manifest augmentation to use a file handle

This makes it more resilient to concurrent fs changes
This commit is contained in:
Quentin Gliech
2025-12-18 14:40:27 +01:00
parent 926721c279
commit aa6526dde8

View File

@@ -4,7 +4,7 @@
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
import { readFile, writeFile } from "node:fs/promises";
import { type FileHandle, open } from "node:fs/promises";
import { resolve } from "node:path";
import { promisify } from "node:util";
import zlib from "node:zlib";
@@ -138,34 +138,46 @@ export default defineConfig((env) => ({
async handler({ dir }): Promise<void> {
const manifestPath = resolve(dir, "manifest.json");
const manifest: Manifest | undefined = await readFile(
manifestPath,
"utf-8",
).then(JSON.parse, () => undefined);
if (manifest) {
const existing: Set<string> = new Set();
const needs: Set<string> = new Set();
for (const chunk of Object.values(manifest)) {
existing.add(chunk.file);
for (const css of chunk.css ?? []) needs.add(css);
for (const sub of chunk.assets ?? []) needs.add(sub);
}
const missing = Array.from(needs).filter((a) => !existing.has(a));
if (missing.length > 0) {
for (const asset of missing) {
manifest[asset] = {
file: asset,
integrity: "",
};
}
await writeFile(manifestPath, JSON.stringify(manifest, null, 2));
}
let manifestHandle: FileHandle;
try {
manifestHandle = await open(manifestPath, "r+");
} catch (error) {
// Manifest does not exist, nothing to do but still warn about
this.warn(`Failed to open manifest at ${manifestPath}: ${error}`);
return;
}
const rawManifest = await manifestHandle.readFile("utf-8");
const manifest = JSON.parse(rawManifest) as Manifest;
const existing: Set<string> = new Set();
const needs: Set<string> = new Set();
for (const chunk of Object.values(manifest)) {
existing.add(chunk.file);
for (const css of chunk.css ?? []) needs.add(css);
for (const sub of chunk.assets ?? []) needs.add(sub);
}
const missing = Array.from(needs).filter((a) => !existing.has(a));
if (missing.length > 0) {
for (const asset of missing) {
manifest[asset] = {
file: asset,
integrity: "",
};
}
// Overwrite the manifest with the new entries
await manifestHandle.truncate(0);
await manifestHandle.write(
JSON.stringify(manifest, null, 2),
0,
"utf-8",
);
}
await manifestHandle.close();
},
},
},