Files
jp-editor/scripts/generate-fixture-manifest.mjs
2026-09-22 16:33:35 +08:00

83 lines
2.7 KiB
JavaScript

import { readdir, readFile, writeFile } from "node:fs/promises";
import { createHash } from "node:crypto";
import { basename, join, relative } from "node:path";
const rawRoot = "research/jpw7/demo";
const decodedRoot = "research/jpw7/decoded";
const manifestPath = "fixtures/manifest.json";
const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
const sorted = (values) => [...values].sort((left, right) => left.localeCompare(right, "zh-Hans-CN"));
const walk = async (root, prefix = "") => {
const entries = await readdir(join(root, prefix), { withFileTypes: true });
const paths = [];
for (const entry of entries) {
const child = join(prefix, entry.name);
if (entry.isDirectory()) paths.push(...await walk(root, child));
else paths.push(child);
}
return paths;
};
const rawPaths = sorted((await walk(rawRoot)).filter((path) => path.toLowerCase().endsWith(".jpwabc")));
const samples = [];
for (const rawRelativePath of rawPaths) {
const rawName = basename(rawRelativePath);
const candidates = [
`${rawName}.txt`,
`${rawName.replace(/\.jpwabc$/i, "")}.txt`,
`${rawRelativePath}.txt`,
rawName
];
let decodedName;
for (const candidate of candidates) {
try {
await readFile(join(decodedRoot, candidate));
decodedName = candidate;
break;
} catch {
// Try the next conventional decoded filename.
}
}
if (!decodedName) {
throw new Error(`No decoded pair found for ${rawName}`);
}
const rawPath = join(rawRoot, rawRelativePath);
const decodedPath = join(decodedRoot, decodedName);
const rawBytes = await readFile(rawPath);
const decodedBytes = await readFile(decodedPath);
samples.push({
id: rawRelativePath.replace(/\.jpwabc$/i, "").replaceAll("/", "::"),
sourceReadOnly: true,
rawPath: relative(".", rawPath),
decodedPath: relative(".", decodedPath),
rawBytes: rawBytes.byteLength,
decodedBytes: decodedBytes.byteLength,
rawSha256: sha256(rawBytes),
decodedSha256: sha256(decodedBytes),
classification: {
status: "pending",
firstResponsibleMilestone: "M1",
expected: [
"encoding-and-bom",
"section-inventory",
"core-semantics",
"unknown-content-retained",
"diagnostics-emitted"
]
}
});
}
const manifest = {
manifestVersion: 1,
purpose: "M0 read-only fixture baseline",
sampleCount: samples.length,
generatedBy: "scripts/generate-fixture-manifest.mjs",
sourceRoots: { raw: rawRoot, decoded: decodedRoot },
samples
};
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
console.log(`Wrote ${manifestPath} with ${samples.length} sample pairs`);