Files
diagrams-drawio/scripts/src/cli/commands.ts
T

145 lines
5.6 KiB
JavaScript

#!/usr/bin/env node
/**
* drawio-tools CLI dispatcher
*
* Usage:
* drawio-tools --file <path-to-drawio> --action <action-name> [--page <index>]
*
* Actions:
* summary — inventory of all shapes and edges for every page in the file
* page-summary — inventory of shapes and edges for a single page (use --page, default 0)
* page-hierarchy — containment tree of shapes (layers → subsystems → components)
* page-connectors-summary — summary of all connectors with type, labels, waypoints
* page-connectors-validation — detect connector-shape overlaps and connector crossings
* page-labels-validation — detect empty, duplicate, or overly long shape labels
* page-shape-bbox-validation — detect non-containment bounding box overlaps between shapes
* page-orphans — find isolated shapes and dangling connectors
* page-recommendations — bounding box analysis and page size recommendation
* page-negative-space-summary — free horizontal corridors (negative space) per nesting level
*
* Output is always YAML to stdout. Exit 0 on success, 1 on error.
*/
import { parseArgs } from "node:util";
import * as yaml from "js-yaml";
// ---------------------------------------------------------------------------
// Action registry
// ---------------------------------------------------------------------------
type ActionModule = {
run: (filePath: string, pageIndex?: number, outputPath?: string) => Record<string, unknown>;
};
const ACTIONS: Record<string, () => Promise<ActionModule>> = {
"build": () => import("../actions/build/action.js"),
"summary": () => import("../actions/summary/action.js"),
"page-summary": () => import("../actions/page-summary/action.js"),
"page-hierarchy": () => import("../actions/page-hierarchy/action.js"),
"page-connectors-summary": () => import("../actions/page-connectors-summary/action.js"),
"page-connectors-validation": () => import("../actions/page-connectors-validation/action.js"),
"page-labels-validation": () => import("../actions/page-labels-validation/action.js"),
"page-shape-bbox-validation": () => import("../actions/page-shape-bbox-validation/action.js"),
"page-orphans": () => import("../actions/page-orphans/action.js"),
"page-recommendations": () => import("../actions/page-recommendations/action.js"),
"page-hierarchy-full": () => import("../actions/page-hierarchy-full/action.js"),
"page-negative-space-summary":() => import("../actions/page-negative-space-summary/action.js"),
"validate": () => import("../actions/validate/action.js"),
"quality": () => import("../actions/quality/action.js"),
};
// ---------------------------------------------------------------------------
// CLI
// ---------------------------------------------------------------------------
async function main(): Promise<void> {
let values: { file?: string; action?: string; page?: string; output?: string; help?: boolean };
try {
({ values } = parseArgs({
args: process.argv.slice(2),
options: {
file: { type: "string", short: "f" },
action: { type: "string", short: "a" },
page: { type: "string", short: "p" },
output: { type: "string", short: "o" },
help: { type: "boolean", short: "h" },
},
strict: true,
}));
} catch (err) {
printError(String(err));
process.exit(1);
}
if (values.help) {
console.log(`drawio-tools --file <path> --action <action> [--page <index>] [--output <path>]
Actions: ${Object.keys(ACTIONS).join(", ")}`);
process.exit(0);
}
if (!values.file) {
printError("Missing required argument: --file");
process.exit(1);
}
if (!values.action) {
printError("Missing required argument: --action");
process.exit(1);
}
const actionName = values.action;
const filePath = values.file;
// Parse optional --page argument (0-based index, default 0)
let pageIndex = 0;
if (values.page !== undefined) {
if (!/^\d+$/.test(values.page)) {
printError(`--page must be a non-negative integer, got: ${values.page}`);
process.exit(1);
}
pageIndex = Number(values.page);
if (!Number.isSafeInteger(pageIndex)) {
printError(`--page must be a non-negative integer, got: ${values.page}`);
process.exit(1);
}
}
if (!ACTIONS[actionName]) {
printError(
`Unknown action '${actionName}'. Available: ${Object.keys(ACTIONS).join(", ")}`
);
process.exit(1);
}
try {
const mod = await ACTIONS[actionName]();
const result = mod.run(filePath, pageIndex, values.output);
process.stdout.write(
yaml.dump(result, { noRefs: true, sortKeys: false, lineWidth: 120 })
);
const summary = result.summary;
if (actionName === "validate" && summary && typeof summary === "object" && (summary as Record<string, unknown>).valid === false) {
process.exitCode = 1;
}
} catch (err) {
const errorOutput = {
error: true,
action: actionName,
file: filePath,
message: err instanceof Error ? err.message : String(err),
stack: err instanceof Error ? err.stack : undefined,
};
process.stdout.write(yaml.dump(errorOutput, { noRefs: true, sortKeys: false }));
process.exit(1);
}
}
function printError(msg: string): void {
process.stderr.write(`Error: ${msg}\n`);
process.stderr.write(
`Usage: drawio-tools --file <path> --action <${Object.keys(ACTIONS).join("|")}> [--page <index>] [--output <path>]\n`
);
}
main();