Skip to content

fix(test): rebuild dist/ once before every vitest run - #2555

Merged
carlos-alm merged 1 commit into
mainfrom
fix/issue-2439
Aug 17, 2026
Merged

fix(test): rebuild dist/ once before every vitest run#2555
carlos-alm merged 1 commit into
mainfrom
fix/issue-2439

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

The WASM engine's worker always loads the compiled dist/domain/wasm-worker-entry.js, even when a test imports src/*.ts directly (Node's worker_threads loader doesn't apply vitest/ts-node transforms). pretest only runs npm run doctor (no build), and npm install's prepare script builds dist/ once, not on every subsequent extractor edit — so editing an extractor under src/ silently exercised stale compiled code in every WASM-engine integration test, while parser-level tests (which import src/domain/parser.ts directly) saw the live edit.

Why this bites: the two disagree silently, and the failure is maximally misleading. In PR #2432 this presented as "the native engine is correct and WASM reproduces the old buggy behaviour" — indistinguishable from a genuine engine-parity bug, and would reasonably be reported as one.

Fix

Added a vitest globalSetup (scripts/vitest-global-setup.ts) that runs npm run build once before the whole test run — regardless of invocation style. This matters because a pretest-only fix (adding npm run build to the pretest npm-lifecycle script) would only cover npm test, not a direct npx vitest run <file> — which bypasses pretest entirely and is how individual test files get iterated on during actual development.

A hand-rolled staleness check was tried first and reverted

My first attempt compared dist/domain/wasm-worker-entry.js's mtime against every .ts file under src/, throwing if any were newer. This is wrong for this codebase: tsconfig.json sets incremental: true, so tsc skips re-emitting an output file whose compiled content wouldn't change. dist/domain/wasm-worker-entry.js's own mtime reflects when that specific file was last actually recompiled — not when the project was last built — and is routinely older than unrelated src/ files even in a fully up-to-date build. Confirmed via local verification: this produced false positives on essentially every run, failing 114 test files.

Just running the build sidesteps this entirely by deferring to tsc's own incremental engine — the only thing that actually knows what's stale — instead of reimplementing it. A no-op incremental rebuild costs about a second, dominated by process startup rather than compilation, and runs once per test invocation (not once per test file).

Tests

  • tests/unit/vitest-global-setup.test.ts: mocks node:child_process's execFileSync and verifies the setup hook invokes npm run build with the correct cwd.
  • Revert-verified: temporarily changed the invoked script to doctor and confirmed the test fails; restored and confirmed it passes.
  • Full suite (npm test): 337 files / 5399 tests pass in ~57s (unchanged from baseline — the one-time rebuild cost is negligible against the full suite's runtime).

Closes #2439

The WASM engine's worker always loads the compiled dist/domain/wasm-worker-entry.js,
even when a test imports src/*.ts directly. pretest only runs `npm run doctor`
(no build), and `npm install`'s prepare script builds dist/ once, not on
every subsequent extractor edit — so an edit silently exercised stale
compiled code while parser-level tests saw the live change. In PR #2432
this presented as "the native engine is correct and WASM reproduces the
old buggy behaviour", indistinguishable from a genuine engine-parity bug.

Added a vitest globalSetup that runs `npm run build` once before the whole
run, regardless of invocation style (`npm test` or a direct `npx vitest run
<file>` — the latter bypasses pretest entirely, which a pretest-only fix
would have missed).

A hand-rolled mtime-based staleness check was tried first and reverted:
tsconfig.json sets `incremental: true`, so tsc skips re-emitting an output
file whose compiled content wouldn't change. dist/domain/wasm-worker-entry.js's
own mtime reflects when THAT file was last actually recompiled, not when
the project was last built, and is routinely older than unrelated src/
files even in a fully up-to-date build — comparing raw mtimes produced
false positives on essentially every run (confirmed: 114 failing test
files during local verification). Just running the build defers to tsc's
own incremental engine, which is the only thing that actually knows what's
stale, instead of reimplementing it. A no-op incremental rebuild costs
about a second, dominated by process startup rather than compilation.

docs check acknowledged

Impact: 1 functions changed, 0 affected
@greptile-apps

greptile-apps Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR ensures Vitest rebuilds compiled JavaScript before collecting tests, preventing WASM integration tests from using stale extractor code.

  • Adds a global setup hook that runs the incremental TypeScript build once per Vitest invocation.
  • Registers the hook in the root Vitest configuration so direct and npm-driven runs share the behavior.
  • Adds a focused unit test for the build command and repository working directory.

Confidence Score: 5/5

The PR appears safe to merge, with the setup consistently covering repository Vitest invocations and no actionable failures identified.

The build command does not recurse into tests or clean required artifacts, all identified Vitest commands inherit the root configuration, and the npm spawning pattern matches existing cross-platform repository code.

Important Files Changed

Filename Overview
scripts/vitest-global-setup.ts Adds a synchronous, cross-platform setup hook that rebuilds dist from the repository root before test collection.
tests/unit/vitest-global-setup.test.ts Verifies that the setup invokes the build script with the expected repository working directory and inherited output.
vitest.config.ts Registers the setup hook in the sole root Vitest configuration, covering existing repository test invocations.

Sequence Diagram

sequenceDiagram
  participant User
  participant Vitest
  participant Setup as globalSetup
  participant Build as npm run build
  participant Tests
  User->>Vitest: Start test run
  Vitest->>Setup: Execute once
  Setup->>Build: Rebuild dist/
  Build-->>Setup: Updated compiled artifacts
  Setup-->>Vitest: Complete
  Vitest->>Tests: Collect and execute tests
  Tests->>Tests: WASM worker loads fresh dist/
Loading

Reviews (1): Last reviewed commit: "fix(test): rebuild dist/ once before eve..." | Re-trigger Greptile

@github-actions

Copy link
Copy Markdown
Contributor

Codegraph Impact Analysis

1 functions changed0 callers affected across 0 files

  • setup in scripts/vitest-global-setup.ts:43 (0 transitive callers)

@carlos-alm
carlos-alm merged commit 36014af into main Aug 17, 2026
27 checks passed
@carlos-alm
carlos-alm deleted the fix/issue-2439 branch August 17, 2026 11:08
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

follow-up: npm test silently exercises a stale dist/ for the WASM engine path

1 participant