Files
healer-man/scripts/manastorm-assets/validate-package-glbs.cjs
2026-10-06 22:25:34 -04:00

68 lines
2.8 KiB
JavaScript

const fs = require("node:fs");
const path = require("node:path");
const validator = require("gltf-validator");
function findGlbs(directory, root = directory) {
return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
const entryPath = path.join(directory, entry.name);
if (entry.isDirectory()) return findGlbs(entryPath, root);
if (!entry.isFile() || path.extname(entry.name).toLowerCase() !== ".glb") return [];
return [{
file: path.relative(root, entryPath).replaceAll("\\", "/"),
path: entryPath,
}];
});
}
async function main() {
const directory = process.argv[2];
if (!directory) throw new Error("Usage: node validate-package-glbs.cjs <directory>");
const files = findGlbs(directory).sort((left, right) => left.file.localeCompare(right.file));
if (!files.length) throw new Error(`No GLB files found below ${path.resolve(directory)}.`);
const rows = [];
for (const file of files) {
const report = await validator.validateBytes(
new Uint8Array(fs.readFileSync(file.path)),
// Large KTX2 packages produce many known validator tooling messages.
// Keep the issue limit above those so later mesh errors are still read.
{ uri: file.file, maxIssues: 100_000 },
);
const validatorMessages = report.issues.messages;
const hasBasisu = validatorMessages.some((issue) => (
issue.code === "UNSUPPORTED_EXTENSION" && issue.message.includes("'KHR_texture_basisu'")
));
const actionable = validatorMessages.filter((issue) => {
if (issue.code === "UNSUPPORTED_EXTENSION"
&& /'(?:EXT_mesh_gpu_instancing|KHR_texture_basisu)'/.test(issue.message)) return false;
if (issue.code === "VALUE_NOT_IN_LIST"
&& hasBasisu
&& /^\/images\/\d+\/mimeType$/.test(issue.pointer ?? "")
&& issue.message.includes("'image/ktx2'")) return false;
if (issue.code === "IMAGE_UNRECOGNIZED_FORMAT"
&& hasBasisu
&& /^\/images\/\d+$/.test(issue.pointer ?? "")) return false;
return true;
});
rows.push({
file: file.file,
errors: actionable.filter((issue) => issue.severity === 0).length,
warnings: actionable.filter((issue) => issue.severity === 1).length,
ignoredKnownToolingIssues: validatorMessages.length - actionable.length,
});
}
const result = {
files: rows.length,
errors: rows.reduce((sum, row) => sum + row.errors, 0),
warnings: rows.reduce((sum, row) => sum + row.warnings, 0),
ignoredKnownToolingIssues: rows.reduce((sum, row) => sum + row.ignoredKnownToolingIssues, 0),
nonGreen: rows.filter((row) => row.errors || row.warnings),
};
console.log(JSON.stringify(result, null, 2));
if (result.errors || result.warnings) process.exitCode = 1;
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});