Files
languageservices/workflow-parser/script/optimize-workflow-schema.js
T
eric sciple 7660f61777 Prune unused definitions from workflow schema
Remove 10 unreachable definitions from workflow-v1.0.json:
- workflow-root, on, on-mapping (non-strict variants)
- job-if-result, step-if-result
- boolean-needs-context, number-needs-context, string-needs-context
- boolean-steps-context, number-steps-context

Saves 2,039 bytes minified (2.9%), 146 bytes gzipped (1.2%).

Also adds script/prune-schema.cjs for future maintenance.
2025-12-06 22:57:10 +00:00

115 lines
3.2 KiB
JavaScript

#!/usr/bin/env node
/**
* Optimizes workflow-v1.0.json by pruning unused definitions.
*
* Removes definitions not reachable from the entry point (workflow-root-strict).
* Output is then minified by minify-json.js to produce the final .min.json file.
*
* Usage: node script/optimize-workflow-schema.js
*/
import {promises as fs} from "fs";
import path from "path";
import {fileURLToPath} from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ENTRY_POINT = "workflow-root-strict";
const inputPath = path.join(__dirname, "..", "src", "workflow-v1.0.json");
const outputPath = path.join(__dirname, "..", "src", "workflow-v1.0.optimized.json");
/**
* Find all type references in a definition.
*/
function findRefs(obj) {
const refs = [];
function visit(node) {
if (!node || typeof node !== "object") return;
if (Array.isArray(node)) {
for (const item of node) {
if (typeof item === "string") refs.push(item);
else visit(item);
}
return;
}
for (const [key, value] of Object.entries(node)) {
if (["type", "item-type", "loose-key-type", "loose-value-type"].includes(key)) {
if (typeof value === "string") refs.push(value);
} else if (key === "one-of") {
visit(value);
} else if (key === "properties") {
for (const propValue of Object.values(value)) {
if (typeof propValue === "string") refs.push(propValue);
else if (propValue && typeof propValue === "object") visit(propValue);
}
} else if (["mapping", "sequence", "string"].includes(key)) {
visit(value);
}
}
}
visit(obj);
return refs;
}
/**
* Find all definitions reachable from entry point.
*/
function findReachable(schema, entryPoint) {
const reachable = new Set();
const queue = [entryPoint];
while (queue.length > 0) {
const name = queue.shift();
if (reachable.has(name) || !schema.definitions[name]) continue;
reachable.add(name);
const refs = findRefs(schema.definitions[name]);
for (const ref of refs) {
if (!reachable.has(ref) && schema.definitions[ref]) {
queue.push(ref);
}
}
}
return reachable;
}
async function main() {
const content = await fs.readFile(inputPath, "utf8");
const schema = JSON.parse(content);
const reachable = findReachable(schema, ENTRY_POINT);
const allDefs = Object.keys(schema.definitions);
const unused = allDefs.filter((name) => !reachable.has(name));
console.log(`Entry point: ${ENTRY_POINT}`);
console.log(`Definitions: ${allDefs.length} -> ${reachable.size} (${unused.length} pruned)`);
if (unused.length > 0) {
console.log("\nPruned:");
unused.forEach((name) => console.log(` - ${name}`));
}
// Create pruned schema preserving definition order
const pruned = {version: schema.version, definitions: {}};
for (const name of allDefs) {
if (reachable.has(name)) {
pruned.definitions[name] = schema.definitions[name];
}
}
// Write output (will be minified by minify-json.js)
await fs.writeFile(outputPath, JSON.stringify(pruned));
console.log(`\nOutput: ${outputPath}`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});