diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3cc96c20e552..368afb0cf307 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3939,6 +3939,16 @@ jobs: install-bun: "false" dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && runner.environment == 'self-hosted' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} + - name: Restore test-type incremental state + id: test-type-cache + if: matrix.task == 'test-types' && needs.preflight.outputs.cache_mode != 'off' && needs.preflight.outputs.frozen_target != 'true' + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .artifacts/tsgo-cache + key: ${{ runner.os }}-${{ runner.arch }}-tsgo-v1-${{ matrix.task }}-${{ hashFiles('pnpm-lock.yaml', 'tsconfig*.json', 'test/tsconfig/*.json', 'scripts/run-tsgo*', 'scripts/lib/local-check-runtime.mts') }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-${{ runner.arch }}-tsgo-v1-${{ matrix.task }}-${{ hashFiles('pnpm-lock.yaml', 'tsconfig*.json', 'test/tsconfig/*.json', 'scripts/run-tsgo*', 'scripts/lib/local-check-runtime.mts') }}- + - name: Compute extension boundary input fingerprint id: extension-boundary-inputs if: matrix.task == 'lint' @@ -4279,6 +4289,14 @@ jobs: ;; esac + - name: Save test-type incremental state + if: success() && matrix.task == 'test-types' && needs.preflight.outputs.cache_write_allowed == 'true' && needs.preflight.outputs.cache_mode != 'off' && needs.preflight.outputs.frozen_target != 'true' && steps.test-type-cache.outputs.cache-hit != 'true' + continue-on-error: true + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .artifacts/tsgo-cache + key: ${{ steps.test-type-cache.outputs.cache-primary-key }} + # Keep five core stripes; the runner isolates large targets into serial child processes. # Ordinary hybrid runs serialize the stripes across two hosted jobs. # GitHub, frozen and manual runs retain five jobs. All-Blacksmith keeps one lint job. @@ -4371,7 +4389,7 @@ jobs: done # Pair four original core stripes; check-test-types owns the fifth and its tail. - # Each stripe retains fresh compiler processes and its two-child limit; + # Each row retains fresh compiler processes and its two-child limit; # all-Blacksmith mode uses one 16-vCPU test-types job for the full workload. check-test-types-hosted-core-shard: permissions: @@ -4396,7 +4414,19 @@ jobs: install-bun: "false" dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && runner.environment == 'self-hosted' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} + - name: Restore core test-type incremental state + id: test-type-cache + if: needs.preflight.outputs.cache_mode != 'off' && needs.preflight.outputs.frozen_target != 'true' + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .artifacts/tsgo-cache + key: ${{ runner.os }}-${{ runner.arch }}-tsgo-v1-core-${{ matrix.stripe }}-${{ hashFiles('pnpm-lock.yaml', 'tsconfig*.json', 'test/tsconfig/*.json', 'scripts/run-tsgo*', 'scripts/lib/local-check-runtime.mts') }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-${{ runner.arch }}-tsgo-v1-core-${{ matrix.stripe }}-${{ hashFiles('pnpm-lock.yaml', 'tsconfig*.json', 'test/tsconfig/*.json', 'scripts/run-tsgo*', 'scripts/lib/local-check-runtime.mts') }}- + - name: Run hosted core test-types stripe + env: + FROZEN_TARGET: ${{ needs.preflight.outputs.frozen_target }} shell: bash run: | set -euo pipefail @@ -4406,9 +4436,24 @@ jobs: echo "[skip] target does not support core test-type stripes" exit 0 fi - for stripe in "$((2 * ${{ matrix.stripe }} - 1))" "$((2 * ${{ matrix.stripe }}))"; do - node scripts/run-tsgo-core-test-shards.mjs --stripe "$stripe/5" --concurrency 2 - done + first="$((2 * ${{ matrix.stripe }} - 1))" + last="$((2 * ${{ matrix.stripe }}))" + if [ "$FROZEN_TARGET" = "true" ]; then + for stripe in "$first" "$last"; do + node scripts/run-tsgo-core-test-shards.mjs --stripe "$stripe/5" --concurrency 2 + done + else + # One queue fills an idle slot across the former stripe barrier. + node scripts/run-tsgo-core-test-shards.mjs --stripe "$first-$last/5" --concurrency 2 + fi + + - name: Save core test-type incremental state + if: success() && needs.preflight.outputs.cache_write_allowed == 'true' && needs.preflight.outputs.cache_mode != 'off' && needs.preflight.outputs.frozen_target != 'true' && steps.test-type-cache.outputs.cache-hit != 'true' + continue-on-error: true + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .artifacts/tsgo-cache + key: ${{ steps.test-type-cache.outputs.cache-primary-key }} check-additional-shard: permissions: diff --git a/docs/ci.md b/docs/ci.md index 043f8739c07d..c738f2279ba9 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -17,7 +17,7 @@ Docs-only `main` pushes skip CI and cache warming. The cache warmer publishes de Core-test-only PRs use targeted type checks only when every selected test exists in the checkout. Deleting a core test keeps the full type-check plan, including the existing core stripes on GitHub and hybrid profiles. -Core lint includes `src/**/*.test-support.cjs` in type-aware checks through the bounded `src/tsconfig.json` discovery project. Other source files retain the root TypeScript project; unrelated JavaScript files are not added to this test-support project. +Core lint discovers separate source and UI TypeScript projects, retaining shared ambient declarations and imported dependencies. The source project also includes `src/**/*.test-support.cjs`; unrelated JavaScript files are not added as roots. See [local checks](/ci/local-proof#local-equivalents). Android native resource preparation uses the Mermaid renderer's filtered dependency install, including optional build tooling. Pnpm retains root dependencies but omits unrelated plugin packages; Gradle still builds the assets and runs the selected native tests and lint. Historical targets keep their compatibility path. diff --git a/docs/ci/local-proof.md b/docs/ci/local-proof.md index 43fdfde4b3f7..41234a5760df 100644 --- a/docs/ci/local-proof.md +++ b/docs/ci/local-proof.md @@ -24,6 +24,25 @@ local runs, Windows, explicit plugin stripes, and explicit serial selections kee eight-directory chunks. Explicit split-core and parallel execution selections remain unchanged. +Oxlint's type-aware backend discovers `src/tsconfig.json` and `ui/tsconfig.json` +separately. Both inherit the root compiler options, include shared ambient +declarations, and follow imported dependencies. Every existing lint target and +rule still runs, including source CommonJS test preloads. The CLI `--tsconfig` +option controls import resolution; it does not replace these discovery projects. + +Current CI core-test rows combine their paired stripes into one queue of fresh +compiler processes, with the same two-child limit. Each independent graph runs +in incremental project mode: solution-build mode can miss an added root whose +timestamp predates restored build information. Frozen targets keep their +original stripe invocations. Per-graph elapsed times appear in the job log. + +The test-type jobs restore their own `.artifacts/tsgo-cache` state across runs. +Cache keys separate compiler/dependency/configuration versions and CI rows; +the compiler still validates every selected graph after a hit. Pull requests +only restore state, while the existing trusted cache writer policy controls +publication after successful checks. Cache-off and frozen-target runs retain +their original behavior. Lint programs do not share these compiler caches. + Oxlint keeps `eslint/no-redeclare` enabled for JavaScript. For `.ts`, `.tsx`, `.mts`, and `.cts`, `tsgo` owns declaration validity, including intentional type/value pairs with the same public name. `eslint/no-var` remains enabled diff --git a/scripts/lib/tsgo-core-test-shards.mts b/scripts/lib/tsgo-core-test-shards.mts index 515a79b7acf1..696c4cd7a776 100644 --- a/scripts/lib/tsgo-core-test-shards.mts +++ b/scripts/lib/tsgo-core-test-shards.mts @@ -147,16 +147,24 @@ export function selectTsgoCoreTestShards( export function selectTsgoCoreTestStripe( stripeSpec: string, ): readonly { name: string; config: string }[] | undefined { - const match = /^([1-9]\d*)\/([1-9]\d*)$/u.exec(stripeSpec); + const match = /^([1-9]\d*)(?:-([1-9]\d*))?\/([1-9]\d*)$/u.exec(stripeSpec); if (!match) { return undefined; } const stripe = Number(match[1]); - const stripeCount = Number(match[2]); - if (stripe > stripeCount) { + const lastStripe = Number(match[2] ?? match[1]); + const stripeCount = Number(match[3]); + if ( + ![stripe, lastStripe, stripeCount].every(Number.isSafeInteger) || + stripe > lastStripe || + lastStripe > stripeCount + ) { return undefined; } - return TSGO_CORE_TEST_SHARDS.filter((_, index) => index % stripeCount === stripe - 1); + return TSGO_CORE_TEST_SHARDS.filter((_, index) => { + const owner = (index % stripeCount) + 1; + return owner >= stripe && owner <= lastStripe; + }); } export function findTsgoCoreTestShardViolations(params: { diff --git a/scripts/run-tsgo-core-test-shards.mts b/scripts/run-tsgo-core-test-shards.mts index d34c0c9fa76b..130511d26a91 100644 --- a/scripts/run-tsgo-core-test-shards.mts +++ b/scripts/run-tsgo-core-test-shards.mts @@ -26,7 +26,9 @@ function runShard(config: string, env: NodeJS.ProcessEnv): Promise { shell: false, args: distArtifactEntryArgs( path.join(repoRoot, "scripts/run-tsgo.mts"), - ["-b", config, "--builders", "1"], + // These graphs have no project references. Project mode rechecks root + // membership even when a restored build-info file is newer than a new root. + ["-p", config, "--incremental"], { native: true }, ), cwd: repoRoot, @@ -54,10 +56,14 @@ async function runTsgoCoreTestShards( if (!shard || failureCode !== 0) { return; } + const startedAt = performance.now(); const code = await runShard(shard.config, env).catch((error: unknown) => { failureCode = 1; throw error; }); + console.error( + `[tsgo:${shard.name}] ${code === 0 ? "passed" : `failed (exit ${code})`} in ${((performance.now() - startedAt) / 1000).toFixed(1)}s`, + ); if (code !== 0 && failureCode === 0) { failureCode = code; } @@ -142,7 +148,7 @@ if (isDirectRunUrl(process.argv[1], import.meta.url)) { const stripeSpec = process.argv[stripeFlagIndex + 1] ?? ""; shards = selectTsgoCoreTestStripe(stripeSpec); if (!shards) { - console.error(`Invalid core test stripe (expected i/n): ${stripeSpec}`); + console.error(`Invalid core test stripe (expected i/n or first-last/n): ${stripeSpec}`); process.exit(1); } } else { diff --git a/src/tsconfig.json b/src/tsconfig.json index 362d17cf2708..56d15b0c4441 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -3,6 +3,21 @@ "compilerOptions": { "allowJs": true }, - // Oxlint discovers this project for CommonJS test preloads; other sources use the root project. - "include": ["**/*.test-support.cjs"] + // Keep lint's source graph separate from UI and plugins, including shared ambient types. + "include": [ + "**/*.ts", + "**/*.tsx", + "**/*.mts", + "**/*.cts", + "**/*.test-support.cjs", + "../packages/**/*.d.ts", + "../packages/**/*.d.mts", + "../packages/**/*.d.cts", + "../ui/**/*.d.ts", + "../ui/**/*.d.mts", + "../ui/**/*.d.cts", + "../extensions/**/*.d.ts", + "../extensions/**/*.d.mts", + "../extensions/**/*.d.cts" + ] } diff --git a/test/scripts/ci-workflow-guards.test.ts b/test/scripts/ci-workflow-guards.test.ts index 60cbed47ce68..4cb9bbe0bec3 100644 --- a/test/scripts/ci-workflow-guards.test.ts +++ b/test/scripts/ci-workflow-guards.test.ts @@ -206,6 +206,7 @@ function evaluateWorkflowExpression( ); return runInNewContext(evaluableSource, { always: () => true, + success: () => !context.failed && !context.cancelled, failure: () => context.failed ?? false, cancelled: () => context.cancelled ?? false, // GitHub expression builtins the runner-routing clauses use. @@ -2191,7 +2192,8 @@ function runCheckShardFixture(options: { `import { appendFileSync } from "node:fs"; const args = process.argv.slice(2); appendFileSync(process.env.TYPE_CALLS, [process.env.TYPE_ROW, process.env.OPENCLAW_LOCAL_CHECK ?? "", "node " + args.join(" ")].join("\\t") + "\\n"); -if (args[args.indexOf("--stripe") + 1] === process.env.FAIL_TYPE_STRIPE) process.exit(17); +const stripe = args[args.indexOf("--stripe") + 1]; +if (stripe === process.env.FAIL_TYPE_STRIPE || stripe?.replace(/-\\d+\\//, "/") === process.env.FAIL_TYPE_STRIPE) process.exit(17); `, ); } @@ -12948,6 +12950,61 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" } }); + it("reuses isolated test-type caches without bypassing compilation or cache authority", () => { + const workflow = readCiWorkflow(); + for (const [jobId, runName] of [ + ["check-shard", "Run check shard"], + ["check-test-types-hosted-core-shard", "Run hosted core test-types stripe"], + ] as const) { + const steps = workflow.jobs[jobId].steps as WorkflowStep[]; + const restore = expectDefined( + steps.find((step) => step.id === "test-type-cache"), + `${jobId} compiler cache`, + ); + const save = expectDefined( + steps.find( + (step) => step.name?.startsWith("Save") && step.with?.path === ".artifacts/tsgo-cache", + ), + `${jobId} compiler cache writer`, + ); + const run = expectDefined( + steps.find((step) => step.name === runName), + `${jobId} compiler`, + ); + expect(steps.indexOf(restore)).toBeLessThan(steps.indexOf(run)); + expect(steps.indexOf(save)).toBeGreaterThan(steps.indexOf(run)); + expect(run.if).toBeUndefined(); + expect(run.run).not.toContain("cache-hit"); + expect(restore.with?.path).toBe(".artifacts/tsgo-cache"); + expect(restore.with?.key).toContain("pnpm-lock.yaml"); + expect(restore.with?.key).toContain("test/tsconfig/*.json"); + expect(restore.with?.key).toContain( + jobId === "check-shard" ? "matrix.task" : "matrix.stripe", + ); + expect(save.with?.key).toBe("${{ steps.test-type-cache.outputs.cache-primary-key }}"); + for (const [cacheMode, writable, frozen, failed, canRestore, canSave] of [ + ["restore", false, false, false, true, false], + ["restore", true, false, false, true, true], + ["off", true, false, false, false, false], + ["restore", true, true, false, false, false], + ["restore", true, false, true, true, false], + ] as const) { + const context: Parameters[1] = { + eventName: writable ? "push" : "pull_request", + repository: "openclaw/openclaw", + runAttempt: 1, + frozenTarget: frozen, + failed, + matrix: { task: "test-types", stripe: 1 }, + preflightOutputs: { cache_mode: cacheMode, cache_write_allowed: String(writable) }, + steps: { "test-type-cache": { outputs: { "cache-hit": "false" } } }, + }; + expect(evaluateWorkflowExpression("${{ " + restore.if + " }}", context)).toBe(canRestore); + expect(evaluateWorkflowExpression("${{ " + save.if + " }}", context)).toBe(canSave); + } + } + }); + it.each([ ["hybrid", "pull_request", false, true, true, true], ["github", "workflow_dispatch", false, true, true, true], @@ -12976,7 +13033,11 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" .filter((call) => call.row === row.name) .map((call) => call.command.split(" ")[2]), ), - ).toEqual([["1/5", "2/5"], ["3/5", "4/5"], ["5/5"]]); + ).toEqual( + frozenTarget + ? [["1/5", "2/5"], ["3/5", "4/5"], ["5/5"]] + : [["1-2/5"], ["3-4/5"], ["5/5"]], + ); for (const call of stripes) { const args = call.command.split(" ").slice(1); expect(args).toEqual(["--stripe", expect.any(String), "--concurrency", "2"]); @@ -13006,7 +13067,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" expect(result.rows.filter((row) => row.status === 0)).toHaveLength(2); expect( result.typeCalls.filter((call) => call.row === failed[0]!.name).map((call) => call.command), - ).toEqual([`node --stripe ${failStripe} --concurrency 2`]); + ).toEqual([`node --stripe ${failStripe === "1/5" ? "1-2/5" : failStripe} --concurrency 2`]); }); it.each(["main", "trunk/release"])( diff --git a/test/scripts/dist-artifact-ownership.test.ts b/test/scripts/dist-artifact-ownership.test.ts index 6524c737b658..0fb59a7c9262 100644 --- a/test/scripts/dist-artifact-ownership.test.ts +++ b/test/scripts/dist-artifact-ownership.test.ts @@ -372,7 +372,7 @@ describe("native check launchers in paths with spaces", () => { ); expect(child.argv).toEqual( compiler - ? ["-b", TSGO_CORE_TEST_SHARDS[0].config, "--builders", "1"] + ? ["-p", TSGO_CORE_TEST_SHARDS[0].config, "--incremental"] : ["--mode=package-boundary"], ); const lock = resolveDistArtifactLockPath(root); diff --git a/test/scripts/e2e-mock-config-limits.test.ts b/test/scripts/e2e-mock-config-limits.test.ts index 0dd62af60562..2761ab1089a4 100644 --- a/test/scripts/e2e-mock-config-limits.test.ts +++ b/test/scripts/e2e-mock-config-limits.test.ts @@ -939,8 +939,9 @@ describe("mock OpenAI response markers", () => { } }); - it("resumes the MCP Code Mode fixture until the latest result completes", async () => { - await withMockServer(mockOpenAiPath, {}, async (baseUrl) => { + it.each(["current", "legacy"])("resumes the MCP Code Mode fixture (%s catalog)", async (mode) => { + const env = { OPENCLAW_FROZEN_TARGET_MCP_CODE_MODE_CATALOG_MODE: mode }; + await withMockServer(mockOpenAiPath, env, async (baseUrl) => { const input: Record[] = [ { content: "mcp code mode api file qa check", role: "user" }, ]; @@ -963,9 +964,13 @@ describe("mock OpenAI response markers", () => { }; const first = await request(); expect(first.output?.[0]).toMatchObject({ name: "exec", type: "function_call" }); - expect(JSON.parse(first.output[0].arguments)).toEqual({ + const execArguments = JSON.parse(first.output[0].arguments); + expect(execArguments).toEqual({ code: expect.stringContaining('MCP.fixture.lookupNote({ id: "alpha" })'), }); + expect(execArguments.code).toContain( + mode === "legacy" ? "ALL_TOOLS.some(" : "catalog.all().some(", + ); for (const reason of ["pending_tools", "yield"]) { input.push({ diff --git a/test/scripts/mcp-code-mode-gateway-client.test.ts b/test/scripts/mcp-code-mode-gateway-client.test.ts index 2da8faae9c43..13ec8d6f4d2a 100644 --- a/test/scripts/mcp-code-mode-gateway-client.test.ts +++ b/test/scripts/mcp-code-mode-gateway-client.test.ts @@ -531,7 +531,6 @@ describe("MCP code-mode gateway Docker client result validation", () => { id: "mcp-code-mode-exec", name: "exec", arguments: { - language: "javascript", code: 'return await MCP.fixture.lookupNote({ id: "alpha" });', }, }, diff --git a/test/scripts/oxlint-config.test.ts b/test/scripts/oxlint-config.test.ts index fd2938cd8d9d..515527ba9d0e 100644 --- a/test/scripts/oxlint-config.test.ts +++ b/test/scripts/oxlint-config.test.ts @@ -362,6 +362,99 @@ describe("oxlint config", () => { ); }); + it("keeps source and UI lint projects bounded with imported and ambient types", () => { + const tempRoot = fs.realpathSync(createTempDir("openclaw-oxlint-core-projects-")); + for (const file of [ + ".oxlintrc.json", + "tsconfig.json", + "src/tsconfig.json", + "ui/tsconfig.json", + ]) { + if (fs.existsSync(file)) { + const target = path.join(tempRoot, file); + fs.mkdirSync(path.dirname(target), { recursive: true }); + fs.copyFileSync(file, target); + } + } + fs.symlinkSync(path.resolve("node_modules"), path.join(tempRoot, "node_modules"), "junction"); + const source = [ + 'import { work } from "../packages/imported.js";', + "work(); fromCore(); fromUi(); fromPackage(); fromPlugin(); fromMts(); fromCts();", + ].join("\n"); + for (const [file, content] of Object.entries({ + "src/owner.ts": source, + "ui/owner.ts": source, + "packages/imported.ts": "export function work(): Promise { return Promise.resolve(); }", + "src/contracts.d.ts": "declare function fromCore(): Promise;", + "ui/contracts.d.ts": "declare function fromUi(): Promise;", + "packages/contracts.d.ts": "declare function fromPackage(): Promise;", + "extensions/contracts.d.ts": "declare function fromPlugin(): Promise;", + "packages/contracts.d.mts": + "export {}; declare global { function fromMts(): Promise; }", + "packages/contracts.d.cts": + "export {}; declare global { function fromCts(): Promise; }", + })) { + const target = path.join(tempRoot, file); + fs.mkdirSync(path.dirname(target), { recursive: true }); + fs.writeFileSync(target, content); + } + const selected = ["src/owner.ts", "ui/owner.ts"]; + const result = spawnSync( + process.execPath, + [ + path.resolve("node_modules/oxlint/bin/oxlint"), + "--type-aware", + "--format", + "json", + "--threads=1", + ...selected, + ], + { + cwd: tempRoot, + encoding: "utf8", + timeout: 10_000, + env: { + ...process.env, + OXC_LOG: "debug", + GOMAXPROCS: "2", + OXLINT_TSGOLINT_PATH: path.resolve( + "node_modules/.bin", + process.platform === "win32" ? "tsgolint.CMD" : "tsgolint", + ), + }, + }, + ); + expect(result.error).toBeUndefined(); + expect(result.status, result.stderr).toBe(1); + const report = JSON.parse(result.stdout) as { + diagnostics: Array<{ filename: string; code: string }>; + }; + for (const file of selected) { + expect( + report.diagnostics + .filter((diagnostic) => diagnostic.filename.replaceAll("\\", "/") === file) + .map((diagnostic) => diagnostic.code), + ).toEqual(Array.from({ length: 7 }, () => "typescript(no-floating-promises)")); + const owner = path.dirname(file); + expect(result.stderr.replaceAll("\\", "/")).toContain( + `Got tsconfig for file ${path.join(tempRoot, file).replaceAll("\\", "/")}: ${path.join(tempRoot, owner, "tsconfig.json").replaceAll("\\", "/")}`, + ); + const project = spawnSync( + process.execPath, + [ + path.resolve("node_modules/typescript-native/bin/tsc"), + "--showConfig", + "-p", + `${owner}/tsconfig.json`, + ], + { cwd: tempRoot, encoding: "utf8", timeout: 10_000 }, + ); + expect(project.status, project.stdout + project.stderr).toBe(0); + const parsed = JSON.parse(project.stdout) as { files: string[] }; + expect(parsed.files).not.toContain(`../${owner === "src" ? "ui" : "src"}/owner.ts`); + } + }); + it("checks unbound methods in TypeScript and CommonJS source test support", () => { const tempRoot = fs.realpathSync(createTempDir("openclaw-oxlint-source-support-")); for (const file of [".oxlintrc.json", "tsconfig.json", "src/tsconfig.json"]) { diff --git a/test/scripts/tsgo-core-test-shards.test.ts b/test/scripts/tsgo-core-test-shards.test.ts index d1c3a61707c1..d6d50dd5c1ac 100644 --- a/test/scripts/tsgo-core-test-shards.test.ts +++ b/test/scripts/tsgo-core-test-shards.test.ts @@ -36,6 +36,7 @@ describe("tsgo core test shards", () => { throw new Error(`Could not parse ${config}`); } expect(parsed.errors, config).toEqual([]); + expect(parsed.projectReferences ?? [], config).toEqual([]); return parsed.fileNames .filter((file) => /\.test\.tsx?$/u.test(file)) .map((file) => path.relative(process.cwd(), file).replaceAll(path.sep, "/")); @@ -108,6 +109,19 @@ describe("tsgo core test shards", () => { expect(selectTsgoCoreTestStripe("0/2")).toBeUndefined(); expect(selectTsgoCoreTestStripe("3/2")).toBeUndefined(); expect(selectTsgoCoreTestStripe("src")).toBeUndefined(); + expect(selectTsgoCoreTestStripe("2-1/5")).toBeUndefined(); + expect(selectTsgoCoreTestStripe("1-6/5")).toBeUndefined(); + const paired = ["1-2/5", "3-4/5", "5/5"].flatMap( + (stripe) => selectTsgoCoreTestStripe(stripe) ?? [], + ); + expect(paired.map((shard) => shard.name).toSorted()).toEqual( + TSGO_CORE_TEST_SHARDS.map((shard) => shard.name).toSorted(), + ); + expect(selectTsgoCoreTestStripe("1-2/5")).toEqual( + TSGO_CORE_TEST_SHARDS.filter((shard) => + ["1/5", "2/5"].some((stripe) => selectTsgoCoreTestStripe(stripe)?.includes(shard)), + ), + ); }); it("accepts an exact once-only partition within the root budget", () => { @@ -398,9 +412,10 @@ process.exit(result.status??1); expect(calls.filter((args) => args.includes("--listFilesOnly"))).toHaveLength( TSGO_CORE_GRAPHS.length, ); + // Discovery and diagnostic checks both use project mode. const builds = calls - .filter((args) => args.includes("-b")) - .map((args) => args[args.indexOf("-b") + 1]); + .filter((args) => !args.includes("--listFilesOnly") && !args.includes("--showConfig")) + .map((args) => args[args.indexOf("-p") + 1]); return { result, builds }; }; const initial = await check(); diff --git a/ui/tsconfig.json b/ui/tsconfig.json new file mode 100644 index 000000000000..422aaa5fad9b --- /dev/null +++ b/ui/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../tsconfig.json", + "include": [ + "**/*.ts", + "**/*.tsx", + "**/*.mts", + "**/*.cts", + "../src/**/*.d.ts", + "../src/**/*.d.mts", + "../src/**/*.d.cts", + "../packages/**/*.d.ts", + "../packages/**/*.d.mts", + "../packages/**/*.d.cts", + "../extensions/**/*.d.ts", + "../extensions/**/*.d.mts", + "../extensions/**/*.d.cts" + ] +}