From c51617351dc91140da1d0cec9c1c4fd5e7aa2fe3 Mon Sep 17 00:00:00 2001 From: Tinson Lai Date: Wed, 15 Jul 2026 11:37:34 +0000 Subject: [PATCH 1/6] chore(scripts): migrate source and test analysis tools to .mts Signed-off-by: Tinson Lai --- .github/actions/ci-cli-coverage-merge/action.yaml | 4 ++-- .github/actions/ci-cli-coverage-shard/action.yaml | 2 +- .github/actions/ci-plugin-coverage/action.yaml | 2 +- .pre-commit-config.yaml | 4 ++-- package.json | 14 +++++++------- ...erage-ratchet.ts => check-coverage-ratchet.mts} | 0 ...ist-sourcemaps.ts => check-dist-sourcemaps.mts} | 3 ++- ...e-budget.ts => check-test-file-size-budget.mts} | 0 ...-shape-tests.ts => find-source-shape-tests.mts} | 2 +- ...-conditionals.ts => find-test-conditionals.mts} | 2 +- ...helper-uses.ts => list-command-helper-uses.mts} | 10 +++++----- ...safety-hotspots.ts => type-safety-hotspots.mts} | 0 test/coverage-ratchet.test.ts | 2 +- test/docker-abstraction-guard.test.ts | 2 +- test/list-command-helper-uses.test.ts | 2 +- test/pr-workflow-contract.test.ts | 6 +++--- 16 files changed, 28 insertions(+), 27 deletions(-) rename scripts/{check-coverage-ratchet.ts => check-coverage-ratchet.mts} (100%) rename scripts/{check-dist-sourcemaps.ts => check-dist-sourcemaps.mts} (95%) rename scripts/{check-test-file-size-budget.ts => check-test-file-size-budget.mts} (100%) rename scripts/{find-source-shape-tests.ts => find-source-shape-tests.mts} (99%) rename scripts/{find-test-conditionals.ts => find-test-conditionals.mts} (99%) rename scripts/{list-command-helper-uses.ts => list-command-helper-uses.mts} (97%) rename scripts/{type-safety-hotspots.ts => type-safety-hotspots.mts} (100%) diff --git a/.github/actions/ci-cli-coverage-merge/action.yaml b/.github/actions/ci-cli-coverage-merge/action.yaml index c24686e3fe6..6174078aad1 100644 --- a/.github/actions/ci-cli-coverage-merge/action.yaml +++ b/.github/actions/ci-cli-coverage-merge/action.yaml @@ -51,7 +51,7 @@ runs: shell: bash run: | test -s dist/nemoclaw.js - npx tsx scripts/check-dist-sourcemaps.ts dist + npx tsx scripts/check-dist-sourcemaps.mts dist - name: Download CLI shard blob reports uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -95,7 +95,7 @@ runs: --coverage.include="src/**/*.ts" \ --coverage.exclude="test/**/*.js" \ --coverage.exclude="test/**/*.ts" - npx tsx scripts/check-coverage-ratchet.ts coverage/cli/coverage-summary.json ci/coverage-threshold-cli.json "CLI coverage" + npx tsx scripts/check-coverage-ratchet.mts coverage/cli/coverage-summary.json ci/coverage-threshold-cli.json "CLI coverage" - name: Upload CLI coverage report if: ${{ always() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) }} diff --git a/.github/actions/ci-cli-coverage-shard/action.yaml b/.github/actions/ci-cli-coverage-shard/action.yaml index 0d433054c0e..de8bc2b8a1c 100644 --- a/.github/actions/ci-cli-coverage-shard/action.yaml +++ b/.github/actions/ci-cli-coverage-shard/action.yaml @@ -102,7 +102,7 @@ runs: run: | node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })" npm run build:cli - npx tsx scripts/check-dist-sourcemaps.ts dist + npx tsx scripts/check-dist-sourcemaps.mts dist - name: Upload compiled CLI artifact if: ${{ steps.validate-shard-inputs.outputs.upload_build_artifact == 'true' && success() }} diff --git a/.github/actions/ci-plugin-coverage/action.yaml b/.github/actions/ci-plugin-coverage/action.yaml index ddbb9e77f8b..149144afa68 100644 --- a/.github/actions/ci-plugin-coverage/action.yaml +++ b/.github/actions/ci-plugin-coverage/action.yaml @@ -31,7 +31,7 @@ runs: --coverage.include="nemoclaw/src/**/*.ts" \ --coverage.include="nemoclaw/src/**/*.cts" \ --coverage.exclude="**/*.test.ts" - npx tsx scripts/check-coverage-ratchet.ts coverage/plugin/coverage-summary.json ci/coverage-threshold-plugin.json "Plugin coverage" + npx tsx scripts/check-coverage-ratchet.mts coverage/plugin/coverage-summary.json ci/coverage-threshold-plugin.json "Plugin coverage" - name: Upload plugin coverage report if: ${{ always() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 13afa4df8e2..6e305a4e1d8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -306,7 +306,7 @@ repos: entry: npm run source-shape:check language: system pass_filenames: false - files: ^(.+\.(test|spec)\.(js|ts|mjs|mts|cjs|cts)|scripts/find-source-shape-tests\.ts|ci/source-shape-test-budget\.json)$ + files: ^(.+\.(test|spec)\.(js|ts|mjs|mts|cjs|cts)|scripts/find-source-shape-tests\.mts|ci/source-shape-test-budget\.json)$ priority: 20 - id: test-file-size-budget @@ -314,7 +314,7 @@ repos: entry: npm run test-size:check language: system pass_filenames: false - files: ^(test/|src/.*\.(test|spec)\.(ts|js|mts|mjs|cts|cjs)$|nemoclaw/src/.*\.(test|spec)\.(ts|js|mts|mjs|cts|cjs)$|scripts/check-test-file-size-budget\.ts$|ci/test-file-size-budget\.json$) + files: ^(test/|src/.*\.(test|spec)\.(ts|js|mts|mjs|cts|cjs)$|nemoclaw/src/.*\.(test|spec)\.(ts|js|mts|mjs|cts|cjs)$|scripts/check-test-file-size-budget\.mts$|ci/test-file-size-budget\.json$) priority: 20 - id: test-skills-yaml diff --git a/package.json b/package.json index 30e4108765b..77cca767c47 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,8 @@ "test:diagnose:leaks": "vitest run --project cli --project plugin --project e2e-support --detectAsyncLeaks --coverage=false --reporter=default --reporter=hanging-process", "test:integration": "npm run clean:cli && npm run build:cli && vitest run --project integration --project installer-integration", "test:package": "npm run clean:cli && npm --prefix nemoclaw run clean && npm run build:cli && npm --prefix nemoclaw run build && vitest run --project package-contract", - "test:coverage:cli": "npm run clean:cli && npm run build:cli && tsx scripts/check-dist-sourcemaps.ts dist && vitest run --project cli --project integration --coverage --coverage.reporter=text-summary --coverage.reporter=json-summary --coverage.reportsDirectory=coverage/cli --coverage.include=\"bin/**/*.js\" --coverage.include=\"src/**/*.ts\" --coverage.exclude=\"test/**/*.js\" --coverage.exclude=\"test/**/*.ts\" && tsx scripts/check-coverage-ratchet.ts coverage/cli/coverage-summary.json ci/coverage-threshold-cli.json \"CLI coverage\"", - "test:coverage:plugin": "vitest run --project plugin --coverage --coverage.reporter=text-summary --coverage.reporter=json-summary --coverage.reportsDirectory=coverage/plugin --coverage.include=\"nemoclaw/src/**/*.ts\" --coverage.include=\"nemoclaw/src/**/*.cts\" --coverage.exclude=\"**/*.test.ts\" && tsx scripts/check-coverage-ratchet.ts coverage/plugin/coverage-summary.json ci/coverage-threshold-plugin.json \"Plugin coverage\"", + "test:coverage:cli": "npm run clean:cli && npm run build:cli && tsx scripts/check-dist-sourcemaps.mts dist && vitest run --project cli --project integration --coverage --coverage.reporter=text-summary --coverage.reporter=json-summary --coverage.reportsDirectory=coverage/cli --coverage.include=\"bin/**/*.js\" --coverage.include=\"src/**/*.ts\" --coverage.exclude=\"test/**/*.js\" --coverage.exclude=\"test/**/*.ts\" && tsx scripts/check-coverage-ratchet.mts coverage/cli/coverage-summary.json ci/coverage-threshold-cli.json \"CLI coverage\"", + "test:coverage:plugin": "vitest run --project plugin --coverage --coverage.reporter=text-summary --coverage.reporter=json-summary --coverage.reportsDirectory=coverage/plugin --coverage.include=\"nemoclaw/src/**/*.ts\" --coverage.include=\"nemoclaw/src/**/*.cts\" --coverage.exclude=\"**/*.test.ts\" && tsx scripts/check-coverage-ratchet.mts coverage/plugin/coverage-summary.json ci/coverage-threshold-plugin.json \"Plugin coverage\"", "test:live-e2e": "npm run clean:cli && npm run build:cli && NEMOCLAW_RUN_LIVE_E2E=1 vitest run --project e2e-live", "test:imports:check": "tsx scripts/checks/no-test-dist-imports.ts", "test:projects:check": "tsx scripts/checks/vitest-project-overlap.ts", @@ -54,11 +54,11 @@ "clean:cli": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"", "typecheck:cli": "tsc -p tsconfig.cli.json", "validate:configs": "tsx scripts/validate-configs.ts", - "type-safety:hotspots": "tsx scripts/type-safety-hotspots.ts", - "source-shape:scan": "tsx scripts/find-source-shape-tests.ts --metrics", - "source-shape:check": "tsx scripts/find-source-shape-tests.ts --check", - "test-size:check": "tsx scripts/check-test-file-size-budget.ts", - "test-conditionals:scan": "tsx scripts/find-test-conditionals.ts", + "type-safety:hotspots": "tsx scripts/type-safety-hotspots.mts", + "source-shape:scan": "tsx scripts/find-source-shape-tests.mts --metrics", + "source-shape:check": "tsx scripts/find-source-shape-tests.mts --check", + "test-size:check": "tsx scripts/check-test-file-size-budget.mts", + "test-conditionals:scan": "tsx scripts/find-test-conditionals.mts", "bump:version": "tsx scripts/bump-version.ts", "release:plan": "tsx scripts/release-plan.ts", "release:cut": "bash scripts/release-cut-tag.sh", diff --git a/scripts/check-coverage-ratchet.ts b/scripts/check-coverage-ratchet.mts similarity index 100% rename from scripts/check-coverage-ratchet.ts rename to scripts/check-coverage-ratchet.mts diff --git a/scripts/check-dist-sourcemaps.ts b/scripts/check-dist-sourcemaps.mts similarity index 95% rename from scripts/check-dist-sourcemaps.ts rename to scripts/check-dist-sourcemaps.mts index 3b7dbf22f7d..17634aefeca 100644 --- a/scripts/check-dist-sourcemaps.ts +++ b/scripts/check-dist-sourcemaps.mts @@ -3,6 +3,7 @@ import fs from "node:fs"; import path from "node:path"; +import { fileURLToPath } from "node:url"; type SourceMap = { sources?: unknown; @@ -76,6 +77,6 @@ function main(): void { process.exit(1); } -if (require.main === module) { +if (fileURLToPath(import.meta.url) === path.resolve(process.argv[1] ?? "")) { main(); } diff --git a/scripts/check-test-file-size-budget.ts b/scripts/check-test-file-size-budget.mts similarity index 100% rename from scripts/check-test-file-size-budget.ts rename to scripts/check-test-file-size-budget.mts diff --git a/scripts/find-source-shape-tests.ts b/scripts/find-source-shape-tests.mts similarity index 99% rename from scripts/find-source-shape-tests.ts rename to scripts/find-source-shape-tests.mts index 54521b41402..bfbb40f4f0e 100755 --- a/scripts/find-source-shape-tests.ts +++ b/scripts/find-source-shape-tests.mts @@ -2109,7 +2109,7 @@ function isDirectInvocation(): boolean { const invoked = process.argv[1]; return Boolean( invoked && - (import.meta.url === `file://${invoked}` || invoked.endsWith("find-source-shape-tests.ts")), + (import.meta.url === `file://${invoked}` || invoked.endsWith("find-source-shape-tests.mts")), ); } diff --git a/scripts/find-test-conditionals.ts b/scripts/find-test-conditionals.mts similarity index 99% rename from scripts/find-test-conditionals.ts rename to scripts/find-test-conditionals.mts index f817387b83b..285756b9493 100755 --- a/scripts/find-test-conditionals.ts +++ b/scripts/find-test-conditionals.mts @@ -557,7 +557,7 @@ function parseArgs(argv: readonly string[]): CliOptions { roots.push(argv[++index] ?? ""); } else if (arg === "--help" || arg === "-h") { console.log( - `Usage: tsx scripts/find-test-conditionals.ts [--top N] [--min-score N] [--root PATH] [--json]\n\nScans test/spec files under test, src, and nemoclaw/src by default.`, + `Usage: tsx scripts/find-test-conditionals.mts [--top N] [--min-score N] [--root PATH] [--json]\n\nScans test/spec files under test, src, and nemoclaw/src by default.`, ); process.exit(0); } else { diff --git a/scripts/list-command-helper-uses.ts b/scripts/list-command-helper-uses.mts similarity index 97% rename from scripts/list-command-helper-uses.ts rename to scripts/list-command-helper-uses.mts index e839aefcc78..ba0c6a939a1 100644 --- a/scripts/list-command-helper-uses.ts +++ b/scripts/list-command-helper-uses.mts @@ -168,13 +168,13 @@ function parseArgs(argv: string[]): Options { function printHelp(): void { console.log( - "Usage: tsx scripts/list-command-helper-uses.ts [--root ] [--names run,runInteractive,...] [--include-tests] [--list-calls] [--markdown] [--json] [path ...]\n\n" + + "Usage: tsx scripts/list-command-helper-uses.mts [--root ] [--names run,runInteractive,...] [--include-tests] [--list-calls] [--markdown] [--json] [path ...]\n\n" + "Lists AST-level callsites and assignments for command helper names such as run(), runInteractive(), runCapture(), runShell(), execFileSync(), spawnSync(), and runCommand(). By default it excludes test files and groups results by inferred command head.\n\n" + "Examples:\n" + - " tsx scripts/list-command-helper-uses.ts\n" + - " tsx scripts/list-command-helper-uses.ts --markdown src\n" + - " tsx scripts/list-command-helper-uses.ts --include-tests --list-calls --json src test\n" + - " tsx scripts/list-command-helper-uses.ts --names run,runInteractive src test\n", + " tsx scripts/list-command-helper-uses.mts\n" + + " tsx scripts/list-command-helper-uses.mts --markdown src\n" + + " tsx scripts/list-command-helper-uses.mts --include-tests --list-calls --json src test\n" + + " tsx scripts/list-command-helper-uses.mts --names run,runInteractive src test\n", ); } diff --git a/scripts/type-safety-hotspots.ts b/scripts/type-safety-hotspots.mts similarity index 100% rename from scripts/type-safety-hotspots.ts rename to scripts/type-safety-hotspots.mts diff --git a/test/coverage-ratchet.test.ts b/test/coverage-ratchet.test.ts index c63097708c5..8731c55e535 100644 --- a/test/coverage-ratchet.test.ts +++ b/test/coverage-ratchet.test.ts @@ -51,7 +51,7 @@ describe("coverage ratchet", () => { [ "--import", "tsx", - "scripts/check-coverage-ratchet.ts", + "scripts/check-coverage-ratchet.mts", relative(process.cwd(), summaryPath), relative(process.cwd(), thresholdPath), "Test coverage", diff --git a/test/docker-abstraction-guard.test.ts b/test/docker-abstraction-guard.test.ts index 98fe989ab7f..56a44f895a1 100644 --- a/test/docker-abstraction-guard.test.ts +++ b/test/docker-abstraction-guard.test.ts @@ -8,7 +8,7 @@ import { describe, expect, it } from "vitest"; const REPO_ROOT = path.join(import.meta.dirname, ".."); const TSX = path.join(REPO_ROOT, "node_modules", ".bin", "tsx"); -const INVENTORY_SCRIPT = path.join(REPO_ROOT, "scripts", "list-command-helper-uses.ts"); +const INVENTORY_SCRIPT = path.join(REPO_ROOT, "scripts", "list-command-helper-uses.mts"); const DOCKER_ABSTRACTION_PREFIX = "src/lib/adapters/docker/"; type CommandUse = { diff --git a/test/list-command-helper-uses.test.ts b/test/list-command-helper-uses.test.ts index 3a82f78fb56..fe3c69ca2f2 100644 --- a/test/list-command-helper-uses.test.ts +++ b/test/list-command-helper-uses.test.ts @@ -10,7 +10,7 @@ import { describe, expect, it } from "vitest"; const REPO_ROOT = path.join(import.meta.dirname, ".."); const TSX = path.join(REPO_ROOT, "node_modules", ".bin", "tsx"); -const SCRIPT = path.join(REPO_ROOT, "scripts", "list-command-helper-uses.ts"); +const SCRIPT = path.join(REPO_ROOT, "scripts", "list-command-helper-uses.mts"); type HelperMatch = { filePath: string; diff --git a/test/pr-workflow-contract.test.ts b/test/pr-workflow-contract.test.ts index 87a1260f0e9..b714ed949e9 100644 --- a/test/pr-workflow-contract.test.ts +++ b/test/pr-workflow-contract.test.ts @@ -567,7 +567,7 @@ describe("pull request and main workflow contracts", () => { "test/example.test.ts", "src/lib/example.spec.ts", "nemoclaw/src/example.test.ts", - "scripts/find-source-shape-tests.ts", + "scripts/find-source-shape-tests.mts", "ci/source-shape-test-budget.json", ]) { expect(files.test(path), path).toBe(true); @@ -655,7 +655,7 @@ describe("pull request and main workflow contracts", () => { ); expect(cliCoverageCalls[4]).toEqual([ "tsx", - "scripts/check-coverage-ratchet.ts", + "scripts/check-coverage-ratchet.mts", "coverage/cli/coverage-summary.json", "ci/coverage-threshold-cli.json", "CLI coverage", @@ -670,7 +670,7 @@ describe("pull request and main workflow contracts", () => { ); expect(pluginCoverageCalls[1]).toEqual([ "tsx", - "scripts/check-coverage-ratchet.ts", + "scripts/check-coverage-ratchet.mts", "coverage/plugin/coverage-summary.json", "ci/coverage-threshold-plugin.json", "Plugin coverage", From 69ee0a671b0b76d3b8999cbdb52e608f54ba0a80 Mon Sep 17 00:00:00 2001 From: Tinson Lai Date: Wed, 15 Jul 2026 12:40:26 +0000 Subject: [PATCH 2/6] test(scripts): import .mts analysis tools with explicit extension Signed-off-by: Tinson Lai --- test/codebase-growth-guardrails-conditionals.test.ts | 2 +- test/coverage-ratchet.test.ts | 2 +- test/dist-sourcemaps.test.ts | 2 +- test/source-shape-scanner.test.ts | 2 +- test/test-conditionals-scanner.test.ts | 2 +- test/test-file-size-budget.test.ts | 2 +- test/type-safety-hotspots.test.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/codebase-growth-guardrails-conditionals.test.ts b/test/codebase-growth-guardrails-conditionals.test.ts index ceb9eaae4e9..2b84c905f81 100644 --- a/test/codebase-growth-guardrails-conditionals.test.ts +++ b/test/codebase-growth-guardrails-conditionals.test.ts @@ -8,7 +8,7 @@ import path from "node:path"; import { describe, expect, it } from "vitest"; -import { scanTextForTestConditionals } from "../scripts/find-test-conditionals"; +import { scanTextForTestConditionals } from "../scripts/find-test-conditionals.mts"; const WORKFLOW_PATH = ".github/workflows/codebase-growth-guardrails.yaml"; const STEP_NAME = "Require changed test files not to add if statements"; diff --git a/test/coverage-ratchet.test.ts b/test/coverage-ratchet.test.ts index 8731c55e535..843679283f7 100644 --- a/test/coverage-ratchet.test.ts +++ b/test/coverage-ratchet.test.ts @@ -7,7 +7,7 @@ import { tmpdir } from "node:os"; import { join, relative } from "node:path"; import { describe, expect, it } from "vitest"; -import { findCoverageFailures } from "../scripts/check-coverage-ratchet"; +import { findCoverageFailures } from "../scripts/check-coverage-ratchet.mts"; const thresholds = { lines: 71.2, diff --git a/test/dist-sourcemaps.test.ts b/test/dist-sourcemaps.test.ts index 938d45f8a46..5c92f498aa8 100644 --- a/test/dist-sourcemaps.test.ts +++ b/test/dist-sourcemaps.test.ts @@ -7,7 +7,7 @@ import path from "node:path"; import { describe, expect, it } from "vitest"; -import { findMissingDistSourcemapSources } from "../scripts/check-dist-sourcemaps"; +import { findMissingDistSourcemapSources } from "../scripts/check-dist-sourcemaps.mts"; describe("dist sourcemap checks", () => { it("reports JavaScript sourcemaps pointing at missing source files", () => { diff --git a/test/source-shape-scanner.test.ts b/test/source-shape-scanner.test.ts index 410b0893ea8..a00add8ade1 100644 --- a/test/source-shape-scanner.test.ts +++ b/test/source-shape-scanner.test.ts @@ -8,7 +8,7 @@ import { scanTextForTest, scanTextForTestReport, sourceShapeSummary, -} from "../scripts/find-source-shape-tests"; +} from "../scripts/find-source-shape-tests.mts"; function detectedCaseNames(source: string): string[] { return scanTextForTest("test/virtual-source-shape.test.ts", source).map((entry) => entry.name); diff --git a/test/test-conditionals-scanner.test.ts b/test/test-conditionals-scanner.test.ts index 665ca8d7876..0ddc5012bd1 100644 --- a/test/test-conditionals-scanner.test.ts +++ b/test/test-conditionals-scanner.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest"; -import { scanTextForTestConditionals } from "../scripts/find-test-conditionals"; +import { scanTextForTestConditionals } from "../scripts/find-test-conditionals.mts"; describe("test conditional scanner", () => { it("detects real if statements without matching strings or comments", () => { diff --git a/test/test-file-size-budget.test.ts b/test/test-file-size-budget.test.ts index 435cb1fdf6a..4be37554b40 100644 --- a/test/test-file-size-budget.test.ts +++ b/test/test-file-size-budget.test.ts @@ -8,7 +8,7 @@ import { evaluateTestFileSizeBudget, formatViolations, parseBudget, -} from "../scripts/check-test-file-size-budget"; +} from "../scripts/check-test-file-size-budget.mts"; describe("test file size budget", () => { it("counts trailing-newline and non-trailing-newline files consistently", () => { diff --git a/test/type-safety-hotspots.test.ts b/test/type-safety-hotspots.test.ts index 933475c1119..3235a1218b1 100644 --- a/test/type-safety-hotspots.test.ts +++ b/test/type-safety-hotspots.test.ts @@ -11,7 +11,7 @@ import { analyzeTypeSafetyHotspots, parseArgs, renderTextReport, -} from "../scripts/type-safety-hotspots"; +} from "../scripts/type-safety-hotspots.mts"; const tempDirs: string[] = []; From 24508f539b31c8d0b495321e0679740a55c6b365 Mon Sep 17 00:00:00 2001 From: Tinson Lai Date: Wed, 15 Jul 2026 13:12:45 +0000 Subject: [PATCH 3/6] test(scripts): cover the .mts CLI entrypoints for sourcemap and source-shape checks Signed-off-by: Tinson Lai --- test/dist-sourcemaps.test.ts | 93 ++++++++++++++++++++++++++----- test/source-shape-scanner.test.ts | 49 ++++++++++++++++ 2 files changed, 128 insertions(+), 14 deletions(-) diff --git a/test/dist-sourcemaps.test.ts b/test/dist-sourcemaps.test.ts index 5c92f498aa8..310c53abb07 100644 --- a/test/dist-sourcemaps.test.ts +++ b/test/dist-sourcemaps.test.ts @@ -1,35 +1,100 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { spawnSync } from "node:child_process"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; +import { pathToFileURL } from "node:url"; import { describe, expect, it } from "vitest"; import { findMissingDistSourcemapSources } from "../scripts/check-dist-sourcemaps.mts"; -describe("dist sourcemap checks", () => { - it("reports JavaScript sourcemaps pointing at missing source files", () => { - const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-sourcemap-check-")); - const distLib = path.join(root, "dist", "lib"); - const srcLib = path.join(root, "src", "lib"); - fs.mkdirSync(distLib, { recursive: true }); - fs.mkdirSync(srcLib, { recursive: true }); - fs.writeFileSync(path.join(srcLib, "present.ts"), "export {};\n"); - fs.writeFileSync( - path.join(distLib, "present.js.map"), - JSON.stringify({ version: 3, sources: ["../../src/lib/present.ts"], mappings: "" }), - ); +function writeFixtureDist(root: string, { includeMissing }: { includeMissing: boolean }): string { + const distLib = path.join(root, "dist", "lib"); + const srcLib = path.join(root, "src", "lib"); + fs.mkdirSync(distLib, { recursive: true }); + fs.mkdirSync(srcLib, { recursive: true }); + fs.writeFileSync(path.join(srcLib, "present.ts"), "export {};\n"); + fs.writeFileSync( + path.join(distLib, "present.js.map"), + JSON.stringify({ version: 3, sources: ["../../src/lib/present.ts"], mappings: "" }), + ); + if (includeMissing) { fs.writeFileSync( path.join(distLib, "missing.js.map"), JSON.stringify({ version: 3, sources: ["../../src/lib/missing.ts"], mappings: "" }), ); + } + return path.join(root, "dist"); +} - expect(findMissingDistSourcemapSources(path.join(root, "dist"))).toEqual([ - `${path.join(distLib, "missing.js.map")} -> ../../src/lib/missing.ts`, +describe("dist sourcemap checks", () => { + it("reports JavaScript sourcemaps pointing at missing source files", () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-sourcemap-check-")); + const distDir = writeFixtureDist(root, { includeMissing: true }); + + expect(findMissingDistSourcemapSources(distDir)).toEqual([ + `${path.join(distDir, "lib", "missing.js.map")} -> ../../src/lib/missing.ts`, ]); fs.rmSync(root, { recursive: true, force: true }); }); + + it("invoking the .mts entrypoint directly exits 0 for a clean dist directory", () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-sourcemap-cli-clean-")); + const distDir = writeFixtureDist(root, { includeMissing: false }); + + const result = spawnSync( + process.execPath, + ["--import", "tsx", "scripts/check-dist-sourcemaps.mts", distDir], + { cwd: process.cwd(), encoding: "utf8" }, + ); + + expect(result.status).toBe(0); + expect(result.stdout).toContain( + `All JavaScript sourcemaps in ${distDir} reference existing sources.`, + ); + + fs.rmSync(root, { recursive: true, force: true }); + }); + + it("invoking the .mts entrypoint directly exits 1 and reports stale sources", () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-sourcemap-cli-stale-")); + const distDir = writeFixtureDist(root, { includeMissing: true }); + + const result = spawnSync( + process.execPath, + ["--import", "tsx", "scripts/check-dist-sourcemaps.mts", distDir], + { cwd: process.cwd(), encoding: "utf8" }, + ); + + expect(result.status).toBe(1); + expect(result.stderr).toContain(`Stale JavaScript sourcemap sources found in ${distDir}:`); + expect(result.stderr).toContain( + `${path.join(distDir, "lib", "missing.js.map")} -> ../../src/lib/missing.ts`, + ); + + fs.rmSync(root, { recursive: true, force: true }); + }); + + it("importing the .mts entrypoint does not run its CLI main", () => { + const scriptUrl = pathToFileURL(path.resolve("scripts/check-dist-sourcemaps.mts")).href; + const result = spawnSync( + process.execPath, + [ + "--import", + "tsx", + "-e", + `import(${JSON.stringify(scriptUrl)}).then(() => { console.log("IMPORT_ONLY_OK"); });`, + ], + { cwd: process.cwd(), encoding: "utf8" }, + ); + + expect(result.status).toBe(0); + expect(result.stdout.trim()).toBe("IMPORT_ONLY_OK"); + expect(result.stdout).not.toContain("reference existing sources"); + expect(result.stdout).not.toContain("Stale JavaScript sourcemap sources found"); + }); }); diff --git a/test/source-shape-scanner.test.ts b/test/source-shape-scanner.test.ts index a00add8ade1..97e673c4691 100644 --- a/test/source-shape-scanner.test.ts +++ b/test/source-shape-scanner.test.ts @@ -1,6 +1,10 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { spawnSync } from "node:child_process"; +import path from "node:path"; +import { pathToFileURL } from "node:url"; + import { describe, expect, it } from "vitest"; import { @@ -666,3 +670,48 @@ describe("source-shape scanner", () => { ).toEqual([expect.stringContaining("duplicate source-shape exception identity")]); }); }); + +describe("source-shape scanner CLI entrypoint", () => { + function runCli(...args: string[]): { status: number | null; stdout: string; stderr: string } { + const result = spawnSync( + process.execPath, + ["--import", "tsx", "scripts/find-source-shape-tests.mts", ...args], + { cwd: process.cwd(), encoding: "utf8" }, + ); + return { status: result.status, stdout: result.stdout, stderr: result.stderr }; + } + + it("invoking the .mts entrypoint with --check prints the human report, metrics, and preserves the budget exit status", () => { + const { status, stdout } = runCli("--check"); + expect(status).toBe(0); + expect(stdout).toMatch( + /No source-shape tests detected\.|Detected \d+ source-shape test cases:/, + ); + expect(stdout).toContain("METRIC source_shape_cases="); + }, 90_000); + + it("invoking the .mts entrypoint with --json prints a parsable report and exits 0", () => { + const { status, stdout } = runCli("--json"); + expect(status).toBe(0); + const report = JSON.parse(stdout) as { summary: { source_shape_cases: number } }; + expect(typeof report.summary.source_shape_cases).toBe("number"); + }, 90_000); + + it("importing the .mts entrypoint does not run its CLI main", () => { + const scriptUrl = pathToFileURL(path.resolve("scripts/find-source-shape-tests.mts")).href; + const result = spawnSync( + process.execPath, + [ + "--import", + "tsx", + "-e", + `import(${JSON.stringify(scriptUrl)}).then(() => { console.log("IMPORT_ONLY_OK"); });`, + ], + { cwd: process.cwd(), encoding: "utf8" }, + ); + + expect(result.status).toBe(0); + expect(result.stdout.trim()).toBe("IMPORT_ONLY_OK"); + expect(result.stdout).not.toContain("METRIC source_shape_cases="); + }); +}); From 1108ee69e98131e462f553519f51d4eca7961e5d Mon Sep 17 00:00:00 2001 From: Tinson Lai Date: Wed, 15 Jul 2026 13:19:12 +0000 Subject: [PATCH 4/6] test(scripts): keep sourcemap fixture builders branch-free Signed-off-by: Tinson Lai --- test/dist-sourcemaps.test.ts | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/test/dist-sourcemaps.test.ts b/test/dist-sourcemaps.test.ts index 310c53abb07..bf957fbd6ee 100644 --- a/test/dist-sourcemaps.test.ts +++ b/test/dist-sourcemaps.test.ts @@ -11,7 +11,7 @@ import { describe, expect, it } from "vitest"; import { findMissingDistSourcemapSources } from "../scripts/check-dist-sourcemaps.mts"; -function writeFixtureDist(root: string, { includeMissing }: { includeMissing: boolean }): string { +function writeCleanFixtureDist(root: string): string { const distLib = path.join(root, "dist", "lib"); const srcLib = path.join(root, "src", "lib"); fs.mkdirSync(distLib, { recursive: true }); @@ -21,19 +21,22 @@ function writeFixtureDist(root: string, { includeMissing }: { includeMissing: bo path.join(distLib, "present.js.map"), JSON.stringify({ version: 3, sources: ["../../src/lib/present.ts"], mappings: "" }), ); - if (includeMissing) { - fs.writeFileSync( - path.join(distLib, "missing.js.map"), - JSON.stringify({ version: 3, sources: ["../../src/lib/missing.ts"], mappings: "" }), - ); - } return path.join(root, "dist"); } +function writeStaleFixtureDist(root: string): string { + const distDir = writeCleanFixtureDist(root); + fs.writeFileSync( + path.join(distDir, "lib", "missing.js.map"), + JSON.stringify({ version: 3, sources: ["../../src/lib/missing.ts"], mappings: "" }), + ); + return distDir; +} + describe("dist sourcemap checks", () => { it("reports JavaScript sourcemaps pointing at missing source files", () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-sourcemap-check-")); - const distDir = writeFixtureDist(root, { includeMissing: true }); + const distDir = writeStaleFixtureDist(root); expect(findMissingDistSourcemapSources(distDir)).toEqual([ `${path.join(distDir, "lib", "missing.js.map")} -> ../../src/lib/missing.ts`, @@ -44,7 +47,7 @@ describe("dist sourcemap checks", () => { it("invoking the .mts entrypoint directly exits 0 for a clean dist directory", () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-sourcemap-cli-clean-")); - const distDir = writeFixtureDist(root, { includeMissing: false }); + const distDir = writeCleanFixtureDist(root); const result = spawnSync( process.execPath, @@ -62,7 +65,7 @@ describe("dist sourcemap checks", () => { it("invoking the .mts entrypoint directly exits 1 and reports stale sources", () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-sourcemap-cli-stale-")); - const distDir = writeFixtureDist(root, { includeMissing: true }); + const distDir = writeStaleFixtureDist(root); const result = spawnSync( process.execPath, From e345456e9659bffcb15e35c5b2820374b59d2d76 Mon Sep 17 00:00:00 2001 From: Tinson Lai Date: Wed, 15 Jul 2026 17:54:54 +0000 Subject: [PATCH 5/6] fix(ci): restore trusted-action compatibility shims for coverage scripts Signed-off-by: Tinson Lai --- scripts/check-coverage-ratchet.ts | 21 +++++++++++++++++++++ scripts/check-dist-sourcemaps.ts | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 scripts/check-coverage-ratchet.ts create mode 100644 scripts/check-dist-sourcemaps.ts diff --git a/scripts/check-coverage-ratchet.ts b/scripts/check-coverage-ratchet.ts new file mode 100644 index 00000000000..241ec1ac315 --- /dev/null +++ b/scripts/check-coverage-ratchet.ts @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { spawnSync } from "node:child_process"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); + +const result = spawnSync( + process.execPath, + [ + "--import", + "tsx", + path.join(REPO_ROOT, "scripts/check-coverage-ratchet.mts"), + ...process.argv.slice(2), + ], + { cwd: REPO_ROOT, stdio: "inherit" }, +); + +process.exit(result.status ?? 1); diff --git a/scripts/check-dist-sourcemaps.ts b/scripts/check-dist-sourcemaps.ts new file mode 100644 index 00000000000..cf1ec98ef6d --- /dev/null +++ b/scripts/check-dist-sourcemaps.ts @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { spawnSync } from "node:child_process"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); + +const result = spawnSync( + process.execPath, + [ + "--import", + "tsx", + path.join(REPO_ROOT, "scripts/check-dist-sourcemaps.mts"), + ...process.argv.slice(2), + ], + { cwd: REPO_ROOT, stdio: "inherit" }, +); + +process.exit(result.status ?? 1); From 739259e50f45bf93687d30aa4e98c3da79147c27 Mon Sep 17 00:00:00 2001 From: Tinson Lai Date: Wed, 15 Jul 2026 18:15:27 +0000 Subject: [PATCH 6/6] fix(scripts): report the .mts usage path in coverage-ratchet CLI errors Signed-off-by: Tinson Lai --- scripts/check-coverage-ratchet.mts | 2 +- test/coverage-ratchet.test.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/scripts/check-coverage-ratchet.mts b/scripts/check-coverage-ratchet.mts index a28b3aacaa9..a92a6144a7d 100755 --- a/scripts/check-coverage-ratchet.mts +++ b/scripts/check-coverage-ratchet.mts @@ -69,7 +69,7 @@ function main(): void { const [summaryPath, thresholdPath, label = "coverage"] = process.argv.slice(2); if (!summaryPath || !thresholdPath) { throw new Error( - "Usage: coverage-ratchet.ts [label]", + "Usage: check-coverage-ratchet.mts [label]", ); } diff --git a/test/coverage-ratchet.test.ts b/test/coverage-ratchet.test.ts index 843679283f7..1b343427a1c 100644 --- a/test/coverage-ratchet.test.ts +++ b/test/coverage-ratchet.test.ts @@ -66,4 +66,17 @@ describe("coverage ratchet", () => { rmSync(directory, { force: true, recursive: true }); } }); + + it("reports the .mts usage path when required arguments are missing (#6922)", () => { + const result = spawnSync( + process.execPath, + ["--import", "tsx", "scripts/check-coverage-ratchet.mts"], + { cwd: process.cwd(), encoding: "utf8" }, + ); + + expect(result.status).toBe(1); + expect(result.stderr).toContain( + "Usage: check-coverage-ratchet.mts [label]", + ); + }); });