From 22b8a26e3906f1e51381848ca2ce2ec000846170 Mon Sep 17 00:00:00 2001 From: "Ricardo Q. Bazan" Date: Mon, 15 Jun 2026 18:04:42 -0500 Subject: [PATCH 1/3] feat: honor pretscheck as primary pre-script alias for tsc Now that the typecheck task is canonically named tscheck (#240), a package's pretscheck script runs before the type check, taking precedence over the legacy pretsc/pretypecheck aliases (still honored as fallbacks). Adds integration coverage for the pre-script feature, which previously had none. Co-Authored-By: Claude Opus 4.8 (1M context) --- .changeset/tsc-pretscheck-prescript.md | 7 ++++ run-run/cli/src/actions/tsc.ts | 4 +-- run-run/cli/test/integration/tsc.test.ts | 42 ++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 .changeset/tsc-pretscheck-prescript.md diff --git a/.changeset/tsc-pretscheck-prescript.md b/.changeset/tsc-pretscheck-prescript.md new file mode 100644 index 00000000..45de1a26 --- /dev/null +++ b/.changeset/tsc-pretscheck-prescript.md @@ -0,0 +1,7 @@ +--- +"@rrlab/cli": patch +--- + +Honor `pretscheck` as the primary pre-script alias for `rr tsc`. + +Now that the typecheck task is canonically named `tscheck` (#240), a package's `pretscheck` script runs before the type check, taking precedence over the legacy `pretsc` and `pretypecheck` aliases (which still work as fallbacks). diff --git a/run-run/cli/src/actions/tsc.ts b/run-run/cli/src/actions/tsc.ts index 0e852c9b..fc4c35e9 100644 --- a/run-run/cli/src/actions/tsc.ts +++ b/run-run/cli/src/actions/tsc.ts @@ -11,14 +11,14 @@ export type TscActionConfig = { type Scripts = Record | undefined; -const getPreScript = (scripts: Scripts) => scripts?.pretsc ?? scripts?.pretypecheck; +const getPreScript = (scripts: Scripts) => scripts?.pretscheck ?? scripts?.pretsc ?? scripts?.pretypecheck; export async function tscAction({ ctx, tsc }: TscActionConfig): Promise { const { appPkg, shell } = ctx; const isTsProject = (dir: string) => appPkg.hasFile("tsconfig.json", dir); - // A package's `pretsc`/`pretypecheck` runs captured, inside the task, so its + // A package's `pretscheck`/`pretsc`/`pretypecheck` runs captured, inside the task, so its // output stays grouped with that package. It may use shell features, so it // goes through `/bin/sh -c`. A failing pre-script fails the task before tsc. const typecheckTask = (label: string, dir: string, scripts: Scripts): BoardTask => diff --git a/run-run/cli/test/integration/tsc.test.ts b/run-run/cli/test/integration/tsc.test.ts index 05c761da..8ed62e21 100644 --- a/run-run/cli/test/integration/tsc.test.ts +++ b/run-run/cli/test/integration/tsc.test.ts @@ -59,4 +59,46 @@ describe("rr tsc", () => { expect(r.stdout + r.stderr).toMatch(/Type 'string' is not assignable to type 'number'/); }); }); + + describe("pre-script", () => { + const pkgWithScripts = (scripts: Record) => + `${JSON.stringify({ name: "rr-test-fixture", version: "0.0.0", private: true, scripts }, null, 2)}\n`; + + test("runs `pretscheck` before tsc and fails the task when it fails", () => { + fixture = makeFixture("tsc-pretscheck", { + "package.json": pkgWithScripts({ pretscheck: "echo MARK_TSCHECK && exit 1" }), + "run-run.config.mts": fixtures.config(["ts"]), + "tsconfig.json": fixtures.tsconfig(), + "src/ok.ts": "export const ok: number = 1;\n", + }); + + const r = cli("tsc", { cwd: fixture.dir }); + const combined = r.stdout + r.stderr; + expect(r.status).not.toBe(0); + // The task fails on the pre-script, before tsc runs; its captured output is surfaced. + expect(combined).toContain("pre-script"); + expect(combined).toContain("MARK_TSCHECK"); + }); + + test("prefers `pretscheck` over the legacy `pretsc`/`pretypecheck` aliases", () => { + fixture = makeFixture("tsc-pretscheck-precedence", { + "package.json": pkgWithScripts({ + pretscheck: "echo MARK_TSCHECK && exit 1", + pretsc: "echo MARK_LEGACY_TSC && exit 0", + pretypecheck: "echo MARK_LEGACY_TYPECHECK && exit 0", + }), + "run-run.config.mts": fixtures.config(["ts"]), + "tsconfig.json": fixtures.tsconfig(), + "src/ok.ts": "export const ok: number = 1;\n", + }); + + const r = cli("tsc", { cwd: fixture.dir }); + const combined = r.stdout + r.stderr; + // Only `pretscheck` runs: it fails the task and the legacy aliases never fire. + expect(r.status).not.toBe(0); + expect(combined).toContain("MARK_TSCHECK"); + expect(combined).not.toContain("MARK_LEGACY_TSC"); + expect(combined).not.toContain("MARK_LEGACY_TYPECHECK"); + }); + }); }); From 1b355e638a44aadc14556ae2d624b50e18b8e58f Mon Sep 17 00:00:00 2001 From: "Ricardo Q. Bazan" Date: Mon, 15 Jun 2026 18:09:18 -0500 Subject: [PATCH 2/3] test: raise CLI integration test timeout to 30s Integration tests spawn the real rr bin and run actual toolchains (tsc, biome, oxlint), which exceeds vitest's 5s default on contended CI runners and fails the job by timeout. Set testTimeout/hookTimeout to 30s for the integration project so the budget covers all of them in one place, and drop the one-off inline 15s override in only.test.ts (the oxc tsgolint dispatch) so it inherits that single budget too. Co-Authored-By: Claude Opus 4.8 (1M context) --- run-run/cli/test/integration/only.test.ts | 2 +- run-run/cli/vitest.config.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/run-run/cli/test/integration/only.test.ts b/run-run/cli/test/integration/only.test.ts index 19655ecb..65720285 100644 --- a/run-run/cli/test/integration/only.test.ts +++ b/run-run/cli/test/integration/only.test.ts @@ -47,7 +47,7 @@ describe("plugin { only } narrowing", () => { // output: either its `N warnings and M errors` summary or the `tsgolint` // type-checker it shells out to — both are oxlint-exclusive markers. expect(combined).toMatch(/\d+ warnings? and \d+ errors?|tsgolint/i); - }, 15_000); + }); test("rr jsc composes biome's lint+format (biome's direct jsc was narrowed away)", () => { const r = cli("jsc", { cwd: fixture.dir }); diff --git a/run-run/cli/vitest.config.ts b/run-run/cli/vitest.config.ts index 2312481f..825842d2 100644 --- a/run-run/cli/vitest.config.ts +++ b/run-run/cli/vitest.config.ts @@ -15,6 +15,12 @@ export default defineConfig({ test: { name: "integration", include: ["./test/integration/**/*.test.ts"], + // Integration tests spawn the real `rr` bin against a tmp fixture and + // run actual toolchains (tsc, biome, oxlint), so they're far slower + // than the 5s default — and slower still on contended CI runners. + // A generous per-test/hook budget keeps them from timing out flakily. + testTimeout: 30_000, + hookTimeout: 30_000, }, }, ], From 5a5faa9d1644eeba99a089f5f507429d9cecb69a Mon Sep 17 00:00:00 2001 From: "Ricardo Q. Bazan" Date: Mon, 15 Jun 2026 18:39:17 -0500 Subject: [PATCH 3/3] chore: remove comment --- run-run/cli/vitest.config.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/run-run/cli/vitest.config.ts b/run-run/cli/vitest.config.ts index 825842d2..1264e8ea 100644 --- a/run-run/cli/vitest.config.ts +++ b/run-run/cli/vitest.config.ts @@ -15,10 +15,6 @@ export default defineConfig({ test: { name: "integration", include: ["./test/integration/**/*.test.ts"], - // Integration tests spawn the real `rr` bin against a tmp fixture and - // run actual toolchains (tsc, biome, oxlint), so they're far slower - // than the 5s default — and slower still on contended CI runners. - // A generous per-test/hook budget keeps them from timing out flakily. testTimeout: 30_000, hookTimeout: 30_000, },