diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0cb3ad8..486bd65 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: - run: pnpm typecheck - run: pnpm lint - run: pnpm format:check - - run: pnpm test + - run: pnpm test:coverage - run: pnpm build - run: pnpm pack:smoke diff --git a/.gitignore b/.gitignore index 30ee432..30d4524 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules/ dist/ coverage/ +.vitest/ .clawpatch/runs/ .clawpatch/findings/ .clawpatch/patches/ diff --git a/AGENTS.md b/AGENTS.md index b3a9436..d273caa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,7 +10,8 @@ User documentation is in `docs/`; the static website assets are in `website/`. ## Build, Test, and Development Commands -Use pnpm with Node 22 or newer. +Use pnpm with Node 22.x (22.12+), 24.x, or 26+ for Vitest 5 development. The published +CLI continues to support Node 22 or newer. - `pnpm build`: clean and compile the package with `tsconfig.build.json`. - `pnpm typecheck`: run TypeScript checks without emitting files. @@ -18,6 +19,7 @@ Use pnpm with Node 22 or newer. - `pnpm format`: rewrite files with Oxfmt. - `pnpm format:check`: verify formatting without writing changes. - `pnpm test`: run the Vitest suite. +- `pnpm test:coverage`: run the full suite with V8 coverage reports in `coverage/`. - `pnpm test src/mapper.test.ts`: run one focused test file. For local CLI checks, build first, then run `node dist/cli.js `. diff --git a/CHANGELOG.md b/CHANGELOG.md index afc9cc1..309b7a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 0.8.1 - Unreleased +- Completed Vitest 5 tooling with matching V8 coverage, a full-suite CI coverage run, ignored report artifacts, and documented development Node requirements while retaining Node 22/24/26 CI and the CLI's Node 22 floor. - Updated Nano ID to 3.3.19, Magic String to 1.3.1, and Obug to 2.2.1 within the 48-hour dependency release-age policy. - Fixed nested-project repairs to ignore their own state and sibling changes, fingerprint project-relative source paths, and record both sides of renames. - Preserved observed source edits in failed patch attempts when a provider writes files before exiting with an error. diff --git a/README.md b/README.md index ab1b290..b95e3e9 100644 --- a/README.md +++ b/README.md @@ -92,11 +92,16 @@ pnpm typecheck pnpm lint pnpm format:check pnpm test +pnpm test:coverage pnpm build pnpm pack:smoke ``` -Use Node.js 22 or newer and the pnpm version declared in `package.json`. +Use Node.js 22.x (22.12+), 24.x, or 26+ and the pnpm version declared in `package.json` +for development with Vitest 5. CI tests Node.js 22, 24, and 26; the published CLI +continues to support Node.js 22 or newer. `pnpm test:coverage` runs the full suite +with V8 coverage, prints a text report, and writes JSON summary and HTML reports +to `coverage/`. ## License diff --git a/docs/spec.md b/docs/spec.md index 2d66e88..6335e0d 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -109,11 +109,17 @@ Provider restrictions vary; [Safety](safety.md) describes their limits. ## Development and verification -Use Node.js 22 or newer and the pnpm version pinned in `package.json`. Source is +Use Node.js 22.x (22.12+), 24.x, or 26+ for Vitest 5 and the pnpm version pinned in +`package.json`. The published CLI still supports Node.js 22 or newer. Source is strict ESM TypeScript, checked with TypeScript and Oxlint and formatted with Oxfmt. Tests live beside their modules and use Vitest; mapper behavior is grouped by language and framework. +`pnpm test:coverage` runs the full suite with the matching Vitest 5 V8 provider, +covering production TypeScript under `src/`, including files not imported by +tests. Coverage prints a text report and writes JSON summary and HTML reports to +`coverage/`; the Node.js 26 CI job runs coverage as well. + Run the commands in [README development](../README.md#development) before submitting changes. `pnpm pack:smoke` builds and installs a local package, then runs its CLI against a synthetic mixed-language project. It does not publish. diff --git a/package.json b/package.json index 9265207..812d4b1 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "format:check": "oxfmt --check .", "website:smoke": "node scripts/website-smoke.mjs", "test": "vitest run", + "test:coverage": "vitest run --coverage", "pack:smoke": "node scripts/package-smoke.mjs", "check:changed": "pnpm run test", "test:changed": "pnpm run test", @@ -42,6 +43,7 @@ "devDependencies": { "@types/node": "^22.20.2", "@types/proper-lockfile": "^4.1.4", + "@vitest/coverage-v8": "^5.0.0", "oxfmt": "^0.67.0", "oxlint": "^1.82.0", "typescript": "^7.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4e6a43..04083c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,6 +28,9 @@ importers: '@types/proper-lockfile': specifier: ^4.1.4 version: 4.1.4 + '@vitest/coverage-v8': + specifier: ^5.0.0 + version: 5.0.0(vitest@5.0.0) oxfmt: specifier: ^0.67.0 version: 0.67.0 @@ -39,10 +42,31 @@ importers: version: 7.0.2 vitest: specifier: ^5.0.0 - version: 5.0.0(@types/node@22.20.2)(vite@8.3.0(@types/node@22.20.2)) + version: 5.0.0(@types/node@22.20.2)(@vitest/coverage-v8@5.0.0)(vite@8.3.0(@types/node@22.20.2)) packages: + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -537,6 +561,23 @@ packages: cpu: [x64] os: [win32] + '@vitest/coverage-v8@5.0.0': + resolution: {integrity: sha512-toMg6PZGCIa/lQNCDoASrfb1ly4hsUKXFtFYC9kD4t78o5Y6LyNJU7AENt8eHPr3quYdxaxK7hj2mnbFfUk9NA==} + peerDependencies: + '@vitest/browser': 5.0.0 + vitest: 5.0.0 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/istanbul-lib-coverage@1.0.1': + resolution: {integrity: sha512-k3DJZ8LhMBK9NS4SclF1ASD3OgXEWDorbIcPTRDK0/Zae6fRvu+fJRxtFdLfHsa9Y24beCdPnoNZ4LviTNstfA==} + engines: {node: '>=22'} + + '@vitest/istanbul-lib-report@1.0.1': + resolution: {integrity: sha512-1EOLRfsTMnyAr3+kEAsP4o9dhaDlGPpD7H5iLBBeq//YpNB1VIahkPhB+eRp9N2Dkfw8oySROjE3yf9XDeaIkQ==} + engines: {node: '>=22'} + '@vitest/mocker@5.0.0': resolution: {integrity: sha512-66PGTMIiVJP3t4a5yxU9qPtf7MdTBs8jmToMvy+HVflB3Yy13WJZTtPePdvU+wjRV02SKK5doLbSA6o9pwOmiA==} peerDependencies: @@ -555,6 +596,9 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-v8-to-istanbul@1.0.6: + resolution: {integrity: sha512-fvpl29helSO2w/z7utIbrkNXILdrLwDwAMH2I/zPKlGf5244+gf+B4cyS1sANcrPY2h+hWCGSgC8N61s/+AF9A==} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -670,6 +714,9 @@ packages: magic-string@1.3.1: resolution: {integrity: sha512-rm91zr2Ou+XueDTohjQQjdQEcYM6zVi8KVUCG8Ec3vHwUEKrhSdCNyfuIywkA6hcCAteIn0ZOtAHA6eGpiX+Pg==} + magicast@0.5.5: + resolution: {integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==} + nanoid@3.3.19: resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -760,6 +807,10 @@ packages: resolution: {integrity: sha512-9YodfrxS9g9IbFr/KOjE5bAeJ0p61n3bW6mqvy0jtoeKd1kTW1Cxm0oulm6KX2lyM9Gl6WIe8nEbY7LWv5ZJww==} engines: {node: ^20.0.0 || >=22.0.0} + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} + engines: {node: '>=14.0.0'} + typescript@7.0.2: resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} engines: {node: '>=16.20.0'} @@ -862,6 +913,21 @@ packages: snapshots: + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@bcoe/v8-coverage@1.0.2': {} + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.6.0': {} @@ -1113,6 +1179,24 @@ snapshots: '@typescript/typescript-win32-x64@7.0.2': optional: true + '@vitest/coverage-v8@5.0.0(vitest@5.0.0)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/istanbul-lib-coverage': 1.0.1 + '@vitest/istanbul-lib-report': 1.0.1 + ast-v8-to-istanbul: 1.0.6 + magicast: 0.5.5 + obug: 2.2.1 + std-env: 4.2.0 + tinyrainbow: 3.1.1 + vitest: 5.0.0(@types/node@22.20.2)(@vitest/coverage-v8@5.0.0)(vite@8.3.0(@types/node@22.20.2)) + + '@vitest/istanbul-lib-coverage@1.0.1': {} + + '@vitest/istanbul-lib-report@1.0.1': + dependencies: + '@vitest/istanbul-lib-coverage': 1.0.1 + '@vitest/mocker@5.0.0(vite@8.3.0(@types/node@22.20.2))': dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -1126,6 +1210,12 @@ snapshots: assertion-error@2.0.1: {} + ast-v8-to-istanbul@1.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + chai@6.2.2: {} detect-libc@2.1.2: {} @@ -1202,6 +1292,12 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 + magicast@0.5.5: + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + source-map-js: 1.2.1 + nanoid@3.3.19: {} obug@2.2.1: {} @@ -1312,6 +1408,8 @@ snapshots: tinypool@2.1.2: {} + tinyrainbow@3.1.1: {} + typescript@7.0.2: optionalDependencies: '@typescript/typescript-aix-ppc64': 7.0.2 @@ -1348,7 +1446,7 @@ snapshots: '@types/node': 22.20.2 fsevents: 2.3.3 - vitest@5.0.0(@types/node@22.20.2)(vite@8.3.0(@types/node@22.20.2)): + vitest@5.0.0(@types/node@22.20.2)(@vitest/coverage-v8@5.0.0)(vite@8.3.0(@types/node@22.20.2)): dependencies: '@types/chai': 5.2.3 '@vitest/mocker': 5.0.0(vite@8.3.0(@types/node@22.20.2)) @@ -1366,6 +1464,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.20.2 + '@vitest/coverage-v8': 5.0.0(vitest@5.0.0) transitivePeerDependencies: - msw diff --git a/vitest.config.ts b/vitest.config.ts index 8d59bf5..685d6de 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -5,5 +5,11 @@ export default defineConfig({ include: ["src/**/*.test.ts"], restoreMocks: true, testTimeout: 30_000, + coverage: { + provider: "v8", + include: ["src/**/*.ts"], + exclude: ["src/**/*.test.ts", "src/test-helpers.ts"], + reporter: ["text", "json-summary", "html"], + }, }, });