Split site definitions and generate default collection
This commit is contained in:
parent
3ccdaaf953
commit
40895e05db
10 changed files with 169 additions and 14 deletions
65
tools/generate-definitions.mjs
Normal file
65
tools/generate-definitions.mjs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/env node
|
||||
import { readFileSync, readdirSync, writeFileSync } from "node:fs";
|
||||
import { basename, dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const repositoryRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const definitionsDir = join(repositoryRoot, "src", "content", "definitions");
|
||||
const sitesDir = join(definitionsDir, "sites");
|
||||
const outputFile = join(definitionsDir, "default.json");
|
||||
const checkOnly = process.argv.includes("--check");
|
||||
|
||||
function readJson(file) {
|
||||
try {
|
||||
return JSON.parse(readFileSync(file, "utf8"));
|
||||
} catch (error) {
|
||||
throw new Error(`Cannot read valid JSON from ${file}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function requireString(value, field, file) {
|
||||
if (typeof value !== "string" || !value.trim()) throw new Error(`${file}: ${field} must be a non-empty string.`);
|
||||
}
|
||||
|
||||
function validateDefinition(definition, file) {
|
||||
if (!definition || typeof definition !== "object" || Array.isArray(definition)) throw new Error(`${file}: a site definition must be an object.`);
|
||||
requireString(definition.id, "id", file);
|
||||
requireString(definition.name, "name", file);
|
||||
if (!Array.isArray(definition.urlPatterns) || definition.urlPatterns.length === 0) throw new Error(`${file}: urlPatterns must be a non-empty array.`);
|
||||
if (!Array.isArray(definition.requiredSelectors) || definition.requiredSelectors.length === 0) throw new Error(`${file}: requiredSelectors must be a non-empty array.`);
|
||||
if (!definition.post || typeof definition.post !== "object" || Array.isArray(definition.post)) throw new Error(`${file}: post must be an object.`);
|
||||
}
|
||||
|
||||
const collection = readJson(join(definitionsDir, "collection.json"));
|
||||
if (!collection || typeof collection !== "object" || Array.isArray(collection)) throw new Error("collection.json must be an object.");
|
||||
if (collection.schemaVersion !== 1) throw new Error("collection.json: schemaVersion must be 1.");
|
||||
requireString(collection.collectionId, "collectionId", "collection.json");
|
||||
requireString(collection.name, "name", "collection.json");
|
||||
|
||||
const definitions = readdirSync(sitesDir)
|
||||
.filter((file) => file.endsWith(".json"))
|
||||
.sort()
|
||||
.map((file) => {
|
||||
const definition = readJson(join(sitesDir, file));
|
||||
validateDefinition(definition, join("sites", file));
|
||||
return definition;
|
||||
});
|
||||
|
||||
if (definitions.length === 0) throw new Error("No site definition JSON files were found.");
|
||||
|
||||
const ids = new Set();
|
||||
for (const definition of definitions) {
|
||||
if (ids.has(definition.id)) throw new Error(`Duplicate definition ID: ${definition.id}.`);
|
||||
ids.add(definition.id);
|
||||
}
|
||||
|
||||
const generated = `${JSON.stringify({ ...collection, definitions }, null, 2)}\n`;
|
||||
if (checkOnly) {
|
||||
let current;
|
||||
try { current = readFileSync(outputFile, "utf8"); } catch { throw new Error(`${basename(outputFile)} is missing. Run npm run definitions:generate.`); }
|
||||
if (current !== generated) throw new Error(`${basename(outputFile)} is out of date. Run npm run definitions:generate and commit the result.`);
|
||||
console.log("Definition collection is up to date.");
|
||||
} else {
|
||||
writeFileSync(outputFile, generated);
|
||||
console.log(`Generated ${basename(outputFile)} from ${definitions.length} site definition(s).`);
|
||||
}
|
||||
Loading…
Reference in a new issue