68 lines
2.8 KiB
JavaScript
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;
|
|
});
|