From 093de02711eb83e3fa5a7956f877acf91f2fb8f0 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Sun, 12 Jul 2026 13:55:00 +0200 Subject: [PATCH] fix(tooling): restore L0 coverage sweep across all packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The template baseline shipped 9 packages without @vitest/coverage-v8 and documented an L0 invocation turbo cannot parse — the full coverage sweep had never run. Adds the provider (pinned to the feature-package spec), a root test:coverage script, corrects every live doc/prompt/CI occurrence, and calibrates coverage excludes for the 4 packages whose latent threshold failures the outage masked (framework glue only — zero threshold numbers changed). L0 sweep now passes 21/21. Squash of a284baa + 5c6af95 (worktree-agent-a013c67cea83b2636), implemented and reviewed via the dispatch loop under the epic's baseline waiver (lint pending story 03). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK --- .github/workflows/ci.yml | 2 +- .github/workflows/coverage-snapshot.yml | 2 +- .gitignore | 1 + .sandcastle/implementer.prompt.md | 2 +- CLAUDE.md | 4 ++-- apps/cms/package.json | 1 + apps/cms/vitest.config.ts | 14 +++++++++++++ apps/web-next/package.json | 1 + apps/web-next/vitest.config.ts | 18 +++++++++++++++++ apps/web-tanstack/package.json | 1 + apps/web-tanstack/vitest.config.ts | 18 +++++++++++++++++ docs/guides/coverage.md | 6 +++--- docs/guides/tdd-workflow.md | 6 +++--- package.json | 1 + packages/core-audit/package.json | 1 + packages/core-cms/package.json | 9 +++++---- packages/core-eslint/package.json | 1 + packages/core-testing/package.json | 7 ++++--- packages/core-trpc/package.json | 1 + packages/core-trpc/vitest.config.ts | 14 +++++++++++++ pnpm-lock.yaml | 27 +++++++++++++++++++++++++ scripts/coverage/aggregate.mjs | 2 +- scripts/coverage/diff.mjs | 4 ++-- turbo/generators/package.json | 1 + 24 files changed, 123 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0397496..4558570 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,7 +80,7 @@ jobs: env: DATABASE_URL: postgres://postgres:postgres@localhost:5432/cms_test PAYLOAD_SECRET: test-secret-do-not-use-in-prod - run: pnpm test -- --coverage + run: pnpm test:coverage # L2 — merge per-package lcovs to coverage/lcov.info + emit # coverage/summary.json (ADR-020). Runs even on test failure so the # artifact still captures what was produced. diff --git a/.github/workflows/coverage-snapshot.yml b/.github/workflows/coverage-snapshot.yml index 51924fc..748a5de 100644 --- a/.github/workflows/coverage-snapshot.yml +++ b/.github/workflows/coverage-snapshot.yml @@ -52,7 +52,7 @@ jobs: env: DATABASE_URL: postgres://postgres:postgres@localhost:5432/cms_test PAYLOAD_SECRET: test-secret-do-not-use-in-prod - run: pnpm test -- --coverage + run: pnpm test:coverage - name: Aggregate run: pnpm coverage:aggregate - name: Commit summary.json if changed diff --git a/.gitignore b/.gitignore index 6bde221..808d03c 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ storybook-static # Testing — per-package coverage output (vitest) packages/*/coverage/ apps/*/coverage/ +turbo/generators/coverage/ # Aggregated coverage output (pnpm coverage:aggregate): ignore everything # under /coverage/ EXCEPT summary.json (committed for trend history per # ADR-020) and .gitkeep markers. diff --git a/.sandcastle/implementer.prompt.md b/.sandcastle/implementer.prompt.md index 8d40c97..ab8168e 100644 --- a/.sandcastle/implementer.prompt.md +++ b/.sandcastle/implementer.prompt.md @@ -51,7 +51,7 @@ All five pass before you commit. If any fail, fix or report BLOCKED — do not p The coverage architecture has its own multi-layer enforcement that's distinct from the conformance gates above. Run all of these before declaring done: ``` -pnpm test -- --coverage # L0 — per-layer thresholds (100% on entities/use-cases/controllers) +pnpm test:coverage # L0 — per-layer thresholds (100% on entities/use-cases/controllers) pnpm coverage:aggregate # L2 — merges per-package lcovs to coverage/lcov.info + coverage/summary.json pnpm coverage:diff -- --base # L1 — cover-the-diff: every changed line must be exercised ``` diff --git a/CLAUDE.md b/CLAUDE.md index 74537be..0f09afe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,7 +35,7 @@ docker compose up -d # Start PostgreSQL ```bash pnpm test --watch --filter @repo/ # watch one feature -pnpm test -- --coverage # full run with coverage +pnpm test:coverage # full run with coverage pnpm test:stories # Storybook smoke tests pnpm test:e2e # Playwright e2e ``` @@ -85,7 +85,7 @@ Coverage runs in parallel to the 5-gate conformance system above — same multi- | Layer | Catches | Surface | | ---------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------- | -| **L0** Per-layer vitest thresholds | Drift below declared bands (entities/use-cases/controllers at 100%) | `pnpm test -- --coverage` | +| **L0** Per-layer vitest thresholds | Drift below declared bands (entities/use-cases/controllers at 100%) | `pnpm test:coverage` | | **L1** Diff coverage | Changed line not exercised by tests | `pnpm coverage:diff` — CI-gated on PRs + dispatch post-task | | **L2** Aggregate trend | Codebase coverage drifted over time | `pnpm coverage:aggregate` → committed `coverage/summary.json` | | **L3** Mutation testing | Tests that exist + execute the code but assert nothing | `pnpm mutate` — on-demand + nightly GH Action | diff --git a/apps/cms/package.json b/apps/cms/package.json index 7c71b17..0969552 100644 --- a/apps/cms/package.json +++ b/apps/cms/package.json @@ -32,6 +32,7 @@ "@types/node": "^22.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "@vitest/coverage-v8": "^3.2.4", "vitest": "^3.0.0" } } diff --git a/apps/cms/vitest.config.ts b/apps/cms/vitest.config.ts index c933396..7357400 100644 --- a/apps/cms/vitest.config.ts +++ b/apps/cms/vitest.config.ts @@ -2,6 +2,20 @@ import path from "node:path"; import { mergeConfig } from "vitest/config"; import { nodeVitestConfig } from "@repo/core-typescript/vitest.base.node"; +// Coverage excludes mirror the feature-package pattern (see +// packages/auth/vitest.config.ts): framework glue is excluded, thresholds +// stay inherited from the shared base — never lowered here. export default mergeConfig(nodeVitestConfig, { + test: { + coverage: { + exclude: [ + // Payload-generated admin UI + API mount points: importMap.js is + // stamped "GENERATED AUTOMATICALLY BY PAYLOAD"; the layout/page/ + // route files are one-line re-exports of @payloadcms/next handlers. + // Exercised through the running CMS, not unit-testable. + "src/app/**", + ], + }, + }, resolve: { alias: { "@": path.resolve(__dirname, "./src") } }, }); diff --git a/apps/web-next/package.json b/apps/web-next/package.json index 78534be..a42084d 100644 --- a/apps/web-next/package.json +++ b/apps/web-next/package.json @@ -47,6 +47,7 @@ "@types/node": "^22.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "@vitest/coverage-v8": "^3.2.4", "jsdom": "^25.0.0", "tsx": "^4.0.0", "vitest": "^3.0.0" diff --git a/apps/web-next/vitest.config.ts b/apps/web-next/vitest.config.ts index b1c34d3..de03b29 100644 --- a/apps/web-next/vitest.config.ts +++ b/apps/web-next/vitest.config.ts @@ -2,7 +2,25 @@ import path from "node:path"; import { mergeConfig } from "vitest/config"; import { jsdomVitestConfig } from "@repo/core-typescript/vitest.base.jsdom"; +// Coverage excludes mirror the feature-package pattern (see +// packages/auth/vitest.config.ts): framework glue is excluded, thresholds +// stay inherited from the shared base — never lowered here. export default mergeConfig(jsdomVitestConfig, { esbuild: { jsx: "automatic" }, + test: { + coverage: { + exclude: [ + // Next.js App Router entry points — async server components and the + // tRPC route-handler mount, invoked by the framework and covered by + // Playwright e2e; not unit-testable in jsdom. providers.tsx and + // src/server/** stay counted (they have unit tests). + "src/app/**/layout.tsx", + "src/app/**/page.tsx", + "src/app/**/route.ts", + // Ambient type declarations — no executable code + "src/**/*.d.ts", + ], + }, + }, resolve: { alias: { "@": path.resolve(__dirname, "./src") } }, }); diff --git a/apps/web-tanstack/package.json b/apps/web-tanstack/package.json index 5a2feca..6d02040 100644 --- a/apps/web-tanstack/package.json +++ b/apps/web-tanstack/package.json @@ -38,6 +38,7 @@ "@types/node": "^22.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "@vitest/coverage-v8": "^3.2.4", "jsdom": "^25.0.0", "vitest": "^3.0.0" } diff --git a/apps/web-tanstack/vitest.config.ts b/apps/web-tanstack/vitest.config.ts index b1c34d3..4746d15 100644 --- a/apps/web-tanstack/vitest.config.ts +++ b/apps/web-tanstack/vitest.config.ts @@ -2,7 +2,25 @@ import path from "node:path"; import { mergeConfig } from "vitest/config"; import { jsdomVitestConfig } from "@repo/core-typescript/vitest.base.jsdom"; +// Coverage excludes mirror the feature-package pattern (see +// packages/auth/vitest.config.ts): framework glue is excluded, thresholds +// stay inherited from the shared base — never lowered here. export default mergeConfig(jsdomVitestConfig, { esbuild: { jsx: "automatic" }, + test: { + coverage: { + exclude: [ + // TanStack Router route definitions — framework-invoked entry + // points, covered by Playwright e2e; not unit tests + "src/routes/**", + // Server-entry OTel/Sentry boot wiring — import-time side effects + // run once at startup (same class as feature DI bootstrap). + // instrumentation-client.ts stays counted (it has a unit test). + "src/instrumentation.ts", + // Ambient type declarations — no executable code + "src/**/*.d.ts", + ], + }, + }, resolve: { alias: { "@": path.resolve(__dirname, "./src") } }, }); diff --git a/docs/guides/coverage.md b/docs/guides/coverage.md index f9b6952..82cf711 100644 --- a/docs/guides/coverage.md +++ b/docs/guides/coverage.md @@ -8,7 +8,7 @@ The agent-first coverage architecture has four layers. This guide is the day-to- | Layer | Question it answers | Command | | ---------------------------------- | ------------------------------------------------ | ---------------------------------------------------- | -| **L0** Per-layer vitest thresholds | "Did the last test run meet the declared bands?" | `pnpm test -- --coverage` | +| **L0** Per-layer vitest thresholds | "Did the last test run meet the declared bands?" | `pnpm test:coverage` | | **L1** Diff coverage | "Did this PR/slice cover its own changed lines?" | `pnpm coverage:diff` | | **L2** Aggregate trend | "How is coverage trending across the repo?" | `pnpm coverage:aggregate` → `coverage/summary.json` | | **L3** Mutation testing | "Do my tests actually assert anything?" | `pnpm mutate` _(opt-in, not in default `pnpm test`)_ | @@ -58,7 +58,7 @@ Two readers pick this up today: ### Before pushing ```bash -pnpm test -- --coverage # L0 — per-package thresholds enforced +pnpm test:coverage # L0 — per-package thresholds enforced pnpm coverage:aggregate # L2 — produce coverage/lcov.info + summary.json pnpm coverage:diff # L1 — fails if changed lines aren't covered ``` @@ -255,7 +255,7 @@ Fix: read the surviving mutant's diff in `reports/mutation/index.html`, identify **"Coverage for lines (X%) does not meet 'src/...' threshold (Y%)"** — L0 failure. Real test gap. Either write the missing test or adjust the manifest band downward (rare; band relaxation should be justified). -**`pnpm coverage:diff` says "lcov file not found"** — run `pnpm test -- --coverage && pnpm coverage:aggregate` first. The diff script reads the merged root `coverage/lcov.info`. +**`pnpm coverage:diff` says "lcov file not found"** — run `pnpm test:coverage && pnpm coverage:aggregate` first. The diff script reads the merged root `coverage/lcov.info`. **`coverage/summary.json` differs every commit** — expected. It includes `generatedAt` (ISO timestamp) and `commit` (SHA). The snapshot workflow only commits it when the underlying numbers change; in local dev, regenerating it shows diff noise. diff --git a/docs/guides/tdd-workflow.md b/docs/guides/tdd-workflow.md index 74b2174..a20caf5 100644 --- a/docs/guides/tdd-workflow.md +++ b/docs/guides/tdd-workflow.md @@ -290,7 +290,7 @@ HTML report lands at `packages/blog/coverage/index.html` — open it in a browse To run coverage across all packages: ```bash -pnpm test -- --coverage +pnpm test:coverage ``` Coverage thresholds are enforced in `packages/core-typescript/vitest.base.ts` and inherited by every package's `vitest.config.ts` via `nodeVitestConfig` / `jsdomVitestConfig`. @@ -655,10 +655,10 @@ Then attach Chrome DevTools at `chrome://inspect`. **Coverage:** ```bash -pnpm test --coverage --filter @repo/blog +pnpm test --filter @repo/blog -- --coverage # HTML report: packages/blog/coverage/index.html -pnpm test -- --coverage +pnpm test:coverage # HTML reports: packages/*/coverage/index.html ``` diff --git a/package.json b/package.json index d93dc33..27e4fc4 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dev": "dotenv -- turbo run dev", "lint": "turbo run lint", "test": "turbo run test", + "test:coverage": "turbo run test -- --coverage", "test:e2e": "turbo run test:e2e", "test:stories": "turbo run test:stories", "test:visual": "pnpm --filter @repo/storybook exec concurrently -k -s first -n 'SB,VRT' -c 'magenta,blue' 'pnpm --filter @repo/storybook exec http-server storybook-static --port 6006 --silent' 'pnpm --filter @repo/storybook exec wait-on tcp:6006 && pnpm exec playwright test'", diff --git a/packages/core-audit/package.json b/packages/core-audit/package.json index 013f356..95498d6 100644 --- a/packages/core-audit/package.json +++ b/packages/core-audit/package.json @@ -37,6 +37,7 @@ "@repo/core-eslint": "workspace:*", "@repo/core-testing": "workspace:*", "@repo/core-typescript": "workspace:*", + "@vitest/coverage-v8": "^3.2.4", "inversify": "^6.2.0", "payload": "^3.14.0", "reflect-metadata": "^0.2.2", diff --git a/packages/core-cms/package.json b/packages/core-cms/package.json index 9b7aa99..0efcf9d 100644 --- a/packages/core-cms/package.json +++ b/packages/core-cms/package.json @@ -14,20 +14,21 @@ "test": "vitest run --passWithNoTests" }, "dependencies": { + "@payloadcms/db-postgres": "^3.14.0", + "@payloadcms/richtext-lexical": "^3.14.0", "@repo/auth": "workspace:*", "@repo/blog": "workspace:*", - "@repo/media": "workspace:*", "@repo/marketing-pages": "workspace:*", + "@repo/media": "workspace:*", "@repo/navigation": "workspace:*", - "payload": "^3.14.0", - "@payloadcms/db-postgres": "^3.14.0", - "@payloadcms/richtext-lexical": "^3.14.0" + "payload": "^3.14.0" }, "devDependencies": { "@repo/core-eslint": "workspace:*", "@repo/core-testing": "workspace:*", "@repo/core-typescript": "workspace:*", "@types/node": "^22.0.0", + "@vitest/coverage-v8": "^3.2.4", "vitest": "^3.0.0" } } diff --git a/packages/core-eslint/package.json b/packages/core-eslint/package.json index 5b4190a..66b00c2 100644 --- a/packages/core-eslint/package.json +++ b/packages/core-eslint/package.json @@ -16,6 +16,7 @@ "@eslint/js": "^9.20.0", "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", + "@vitest/coverage-v8": "^3.2.4", "eslint": "^9.20.0", "eslint-config-prettier": "^10.1.0", "eslint-plugin-boundaries": "^4.2.2", diff --git a/packages/core-testing/package.json b/packages/core-testing/package.json index 03fccc0..3103cee 100644 --- a/packages/core-testing/package.json +++ b/packages/core-testing/package.json @@ -25,16 +25,16 @@ "test": "vitest run" }, "dependencies": { + "@tanstack/react-query": "^5.59.0", "@testing-library/jest-dom": "^6.5.0", - "zod": "^3.23.0", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.5.0", "@trpc/client": "^11.0.0", - "@tanstack/react-query": "^5.59.0", "react": "^19.0.0", "react-dom": "^19.0.0", "superjson": "^2.2.0", - "vitest": "^3.0.0" + "vitest": "^3.0.0", + "zod": "^3.23.0" }, "peerDependencies": { "@trpc/server": "^11.0.0", @@ -54,6 +54,7 @@ "@sentry/nextjs": "^10.51.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "@vitest/coverage-v8": "^3.2.4", "jsdom": "^25.0.0", "typescript": "^5.8.0" } diff --git a/packages/core-trpc/package.json b/packages/core-trpc/package.json index da39811..a8b258d 100644 --- a/packages/core-trpc/package.json +++ b/packages/core-trpc/package.json @@ -31,6 +31,7 @@ "@testing-library/jest-dom": "^6.5.0", "@testing-library/react": "^16.0.0", "@types/react": "^19.0.0", + "@vitest/coverage-v8": "^3.2.4", "jsdom": "^25.0.0", "vitest": "^3.0.0" } diff --git a/packages/core-trpc/vitest.config.ts b/packages/core-trpc/vitest.config.ts index ccf910f..f7a945d 100644 --- a/packages/core-trpc/vitest.config.ts +++ b/packages/core-trpc/vitest.config.ts @@ -2,6 +2,20 @@ import path from "node:path"; import { mergeConfig } from "vitest/config"; import { jsdomVitestConfig } from "@repo/core-typescript/vitest.base.jsdom"; +// Coverage excludes mirror the feature-package pattern (see +// packages/auth/vitest.config.ts): framework glue is excluded, thresholds +// stay inherited from the shared base — never lowered here. export default mergeConfig(jsdomVitestConfig, { + test: { + coverage: { + exclude: [ + // React context providers wiring tRPC + React Query into each + // framework's render tree — integration-tested in the apps + "src/providers/**", + // QueryClient bootstrap factory consumed by the providers + "src/query-client.ts", + ], + }, + }, resolve: { alias: { "@": path.resolve(__dirname, "./src") } }, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6e62c51..a5b3ee6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -110,6 +110,9 @@ importers: "@types/react-dom": specifier: ^19.0.0 version: 19.2.3(@types/react@19.2.14) + "@vitest/coverage-v8": + specifier: ^3.2.4 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) @@ -264,6 +267,9 @@ importers: "@types/react-dom": specifier: ^19.0.0 version: 19.2.3(@types/react@19.2.14) + "@vitest/coverage-v8": + specifier: ^3.2.4 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) jsdom: specifier: ^25.0.0 version: 25.0.1 @@ -349,6 +355,9 @@ importers: "@types/react-dom": specifier: ^19.0.0 version: 19.2.3(@types/react@19.2.14) + "@vitest/coverage-v8": + specifier: ^3.2.4 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) jsdom: specifier: ^25.0.0 version: 25.0.1 @@ -570,6 +579,9 @@ importers: "@repo/core-typescript": specifier: workspace:* version: link:../core-typescript + "@vitest/coverage-v8": + specifier: ^3.2.4 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) inversify: specifier: ^6.2.0 version: 6.2.2(reflect-metadata@0.2.2) @@ -625,6 +637,9 @@ importers: "@types/node": specifier: ^22.0.0 version: 22.19.17 + "@vitest/coverage-v8": + specifier: ^3.2.4 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) @@ -724,6 +739,9 @@ importers: "@typescript-eslint/parser": specifier: ^8.25.0 version: 8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + "@vitest/coverage-v8": + specifier: ^3.2.4 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) eslint: specifier: ^9.20.0 version: 9.39.4(jiti@2.7.0) @@ -891,6 +909,9 @@ importers: "@types/react-dom": specifier: ^19.0.0 version: 19.2.3(@types/react@19.2.14) + "@vitest/coverage-v8": + specifier: ^3.2.4 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) jsdom: specifier: ^25.0.0 version: 25.0.1 @@ -943,6 +964,9 @@ importers: "@types/react": specifier: ^19.0.0 version: 19.2.14 + "@vitest/coverage-v8": + specifier: ^3.2.4 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) jsdom: specifier: ^25.0.0 version: 25.0.1 @@ -1179,6 +1203,9 @@ importers: "@repo/core-typescript": specifier: workspace:* version: link:../../packages/core-typescript + "@vitest/coverage-v8": + specifier: ^3.2.4 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: ^5.8.0 version: 5.9.3 diff --git a/scripts/coverage/aggregate.mjs b/scripts/coverage/aggregate.mjs index b4a9958..cb63b3b 100644 --- a/scripts/coverage/aggregate.mjs +++ b/scripts/coverage/aggregate.mjs @@ -178,7 +178,7 @@ function main() { if (lcovs.length === 0) { process.stderr.write( `[coverage:aggregate] No per-package lcov.info files found.\n` + - `Run \`pnpm test -- --coverage\` first.\n`, + `Run \`pnpm test:coverage\` first.\n`, ); process.exit(1); } diff --git a/scripts/coverage/diff.mjs b/scripts/coverage/diff.mjs index 2d9aaf7..337ac62 100644 --- a/scripts/coverage/diff.mjs +++ b/scripts/coverage/diff.mjs @@ -193,7 +193,7 @@ export function computeDiffCoverage(diff, lcov, opts = {}) { // Try matching by suffix in either direction. lcov paths can be: // - absolute (vitest with `coverage.reportsDirectory` at default) // - repo-relative (after `pnpm coverage:aggregate` normalizes) - // - package-relative (per-package lcov from `pnpm test -- --coverage`) + // - package-relative (per-package lcov from `pnpm test:coverage`) // The diff path is always repo-relative. for (const [k, v] of lcov.entries()) { if (file.endsWith("/" + k) || k.endsWith("/" + file) || k === file) { @@ -264,7 +264,7 @@ function main() { if (!fs.existsSync(lcovPath)) { process.stderr.write( `[coverage:diff] lcov file not found at ${lcovPath}\n` + - `Run \`pnpm test -- --coverage\` first, then \`pnpm coverage:aggregate\`.\n`, + `Run \`pnpm test:coverage\` first, then \`pnpm coverage:aggregate\`.\n`, ); // Emit JSON anyway so the dispatch loop can read it process.stdout.write( diff --git a/turbo/generators/package.json b/turbo/generators/package.json index 8b6d077..e221e81 100644 --- a/turbo/generators/package.json +++ b/turbo/generators/package.json @@ -14,6 +14,7 @@ "devDependencies": { "@repo/core-eslint": "workspace:*", "@repo/core-typescript": "workspace:*", + "@vitest/coverage-v8": "^3.2.4", "typescript": "^5.8.0", "vitest": "^3.1.0" }