142 lines
3.8 KiB
JavaScript
142 lines
3.8 KiB
JavaScript
|
|
import fs from "fs";
|
||
|
|
import path from "path";
|
||
|
|
import { createRequire } from "module";
|
||
|
|
import { fileURLToPath } from "url";
|
||
|
|
|
||
|
|
const require = createRequire(import.meta.url);
|
||
|
|
const Ajv = require("ajv");
|
||
|
|
|
||
|
|
const __filename = fileURLToPath(import.meta.url);
|
||
|
|
const __dirname = path.dirname(__filename);
|
||
|
|
const repoRoot = path.resolve(__dirname, "..");
|
||
|
|
const contentRoot = path.join(repoRoot, "content");
|
||
|
|
const schemaRoot = path.join(contentRoot, "schema");
|
||
|
|
|
||
|
|
const ajv = new Ajv({
|
||
|
|
allErrors: true,
|
||
|
|
schemaId: "auto",
|
||
|
|
jsonPointers: true,
|
||
|
|
});
|
||
|
|
|
||
|
|
function normalizePath(filePath) {
|
||
|
|
return filePath.split(path.sep).join("/");
|
||
|
|
}
|
||
|
|
|
||
|
|
function readJson(filePath) {
|
||
|
|
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
||
|
|
}
|
||
|
|
|
||
|
|
function listFilesRecursive(dirPath) {
|
||
|
|
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
||
|
|
const files = [];
|
||
|
|
for (const entry of entries) {
|
||
|
|
const fullPath = path.join(dirPath, entry.name);
|
||
|
|
if (entry.isDirectory()) {
|
||
|
|
files.push(...listFilesRecursive(fullPath));
|
||
|
|
} else {
|
||
|
|
files.push(fullPath);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return files;
|
||
|
|
}
|
||
|
|
|
||
|
|
const jobs = [
|
||
|
|
{
|
||
|
|
schema: "abilities.schema.json",
|
||
|
|
files: [path.join(contentRoot, "abilities.json")],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "dialogues.schema.json",
|
||
|
|
files: [path.join(contentRoot, "dialogues.json")],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "factions.schema.json",
|
||
|
|
files: [path.join(contentRoot, "factions.json")],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "images.schema.json",
|
||
|
|
files: [path.join(contentRoot, "images.json")],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "items.schema.json",
|
||
|
|
files: [path.join(contentRoot, "items.json")],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "loot_tables.schema.json",
|
||
|
|
files: [path.join(contentRoot, "loot_tables.json")],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "monsters.schema.json",
|
||
|
|
files: [path.join(contentRoot, "monsters.json")],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "npc_templates.schema.json",
|
||
|
|
files: [path.join(contentRoot, "npc_templates.json")],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "quests.schema.json",
|
||
|
|
files: [path.join(contentRoot, "quests.json")],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "worlds.schema.json",
|
||
|
|
files: [path.join(contentRoot, "worlds.json")],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "world.schema.json",
|
||
|
|
files: listFilesRecursive(path.join(contentRoot, "worlds")).filter((filePath) => path.basename(filePath) === "world.json"),
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "world-bookmarks.schema.json",
|
||
|
|
files: listFilesRecursive(path.join(contentRoot, "worlds")).filter((filePath) => path.basename(filePath) === "bookmarks.json"),
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "world-chunk.schema.json",
|
||
|
|
files: listFilesRecursive(path.join(contentRoot, "worlds")).filter((filePath) => /^-?\d+_-?\d+\.json$/i.test(path.basename(filePath))),
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "dev_config.schema.json",
|
||
|
|
files: [path.join(contentRoot, "dev_config.json")],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
schema: "npcs.schema.json",
|
||
|
|
files: fs.existsSync(path.join(contentRoot, "npcs.json")) ? [path.join(contentRoot, "npcs.json")] : [],
|
||
|
|
},
|
||
|
|
];
|
||
|
|
|
||
|
|
let hasErrors = false;
|
||
|
|
let validatedCount = 0;
|
||
|
|
|
||
|
|
for (const job of jobs) {
|
||
|
|
const schemaPath = path.join(schemaRoot, job.schema);
|
||
|
|
const schema = readJson(schemaPath);
|
||
|
|
let validate;
|
||
|
|
try {
|
||
|
|
validate = ajv.compile(schema);
|
||
|
|
} catch (error) {
|
||
|
|
hasErrors = true;
|
||
|
|
console.error(`Schema compile failed: ${normalizePath(path.relative(repoRoot, schemaPath))}`);
|
||
|
|
console.error(String(error));
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const filePath of job.files) {
|
||
|
|
const payload = readJson(filePath);
|
||
|
|
const valid = validate(payload);
|
||
|
|
validatedCount += 1;
|
||
|
|
if (!valid) {
|
||
|
|
hasErrors = true;
|
||
|
|
console.error(`Validation failed: ${normalizePath(path.relative(repoRoot, filePath))}`);
|
||
|
|
for (const error of validate.errors || []) {
|
||
|
|
const instancePath = error.dataPath || "/";
|
||
|
|
console.error(` ${instancePath} ${error.message}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (hasErrors) {
|
||
|
|
process.exitCode = 1;
|
||
|
|
} else {
|
||
|
|
console.log(`Validated ${validatedCount} content file(s) successfully.`);
|
||
|
|
}
|