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/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/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"); + }); + }); }); diff --git a/run-run/cli/vitest.config.ts b/run-run/cli/vitest.config.ts index 2312481f..1264e8ea 100644 --- a/run-run/cli/vitest.config.ts +++ b/run-run/cli/vitest.config.ts @@ -15,6 +15,8 @@ export default defineConfig({ test: { name: "integration", include: ["./test/integration/**/*.test.ts"], + testTimeout: 30_000, + hookTimeout: 30_000, }, }, ],