From 85163b6e676e3b4091c2d29cfda35fee789c8604 Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Tue, 13 Oct 2026 05:32:11 -0700 Subject: [PATCH] Reduce repeated compiler startup in monorepo QX formatting --- src/capability-language/tool-cli.ts | 24 +++++++++++++ test/qx-format-cli.test.ts | 55 +++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 test/qx-format-cli.test.ts diff --git a/src/capability-language/tool-cli.ts b/src/capability-language/tool-cli.ts index 7de8d1b..0f7930f 100644 --- a/src/capability-language/tool-cli.ts +++ b/src/capability-language/tool-cli.ts @@ -595,6 +595,30 @@ const main = async () => { process.stdout.write(`${JSON.stringify(plan, null, 2)}\n`); return; } + if (command === "format" && (args[0] === "--check" || args[0] === "--write")) { + const check = args[0] === "--check", + files = args.slice(1); + if (!files.length || files.some((file) => file.startsWith("-"))) + throw new Error("usage: quixos-qx format --check|--write FILE..."); + for (const file of files) { + try { + const source = await readFile(file, "utf8"), + formatted = formatQx(source); + if (formatted === source) continue; + if (check) { + console.error(`Needs formatting: ${file}`); + process.exitCode = 1; + } else { + await writeFile(file, formatted); + console.log(`Formatted ${file}`); + } + } catch (error) { + console.error(`${file}: ${error instanceof Error ? error.message : error}`); + process.exitCode = 1; + } + } + return; + } const [file, flag, ...rest] = args; if ( !file || diff --git a/test/qx-format-cli.test.ts b/test/qx-format-cli.test.ts new file mode 100644 index 0000000..7020ef9 --- /dev/null +++ b/test/qx-format-cli.test.ts @@ -0,0 +1,55 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { mkdtempSync, readFileSync, writeFileSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import test from "node:test"; +import { formatQx } from "../src/capability-language/source.js"; + +const cli = resolve(process.cwd(), "dist/src/capability-language/tool-cli.js"); +const run = (...args: string[]) => spawnSync(process.execPath, [cli, "format", ...args], { encoding: "utf8" }); +const source = `workspace Test id "w" revision "w@1" commit "${"1".repeat(40)}" {\n// 雪 🦊 comment preserved\n}\n`; + +test("QX batch checks are read-only; writes match single-file formatting and remain idempotent", (t) => { + const root = mkdtempSync(join(tmpdir(), "qx-format-")); + t.after(() => rmSync(root, { recursive: true, force: true })); + const files = [join(root, "one.qx"), join(root, "two 雪.qx")]; + for (const file of files) writeFileSync(file, source); + const check = run("--check", ...files); + assert.notEqual(check.status, 0, check.stderr); + for (const file of files) { + assert(check.stderr.includes(file)); + assert.equal(readFileSync(file, "utf8"), source); + } + const single = run(files[0]!); + assert.equal(single.status, 0, single.stderr); + assert.equal(single.stdout, formatQx(source)); + const write = run("--write", ...files); + assert.equal(write.status, 0, write.stderr); + for (const file of files) assert.equal(readFileSync(file, "utf8"), single.stdout); + assert.equal(run("--check", ...files).status, 0); + assert.equal(run("--write", ...files).stdout, ""); + writeFileSync(files[0]!, source); + assert.equal(run(files[0]!, "--write").status, 0); + assert.equal(readFileSync(files[0]!, "utf8"), single.stdout); +}); + +test("QX batch errors report every failure and do not truncate valid work", (t) => { + const root = mkdtempSync(join(tmpdir(), "qx-format-errors-")); + t.after(() => rmSync(root, { recursive: true, force: true })); + const broken = join(root, "broken.qx"), + missing = join(root, "missing.qx"), + valid = join(root, "valid.qx"); + writeFileSync(broken, "workspace { invalid"); + writeFileSync(valid, source); + const result = run("--write", broken, missing, valid); + assert.notEqual(result.status, 0); + assert(result.stderr.includes(broken)); + assert(result.stderr.includes(missing)); + assert.equal(readFileSync(broken, "utf8"), "workspace { invalid"); + assert.equal(readFileSync(valid, "utf8"), formatQx(source)); + writeFileSync(valid, source); + assert.notEqual(run("--write", valid, "--unknown").status, 0); + assert.equal(readFileSync(valid, "utf8"), source, "Validate arguments before writing any file"); + assert.notEqual(run("--check").status, 0); +});