npm runs the `postbuild` hook after every `npm run build`, and the desktop
compiler (scripts/build/desktop.mjs) already runs checkDistBuilt on the
scratch product before it publishes dist/. Every update and every pack
therefore re-verified the bytes it had just verified; with the per-chunk
syntax check that was a second ~960-process pass (#123216).
Drop the hook; nix's checkPhase calls the guard directly.
`assert-dist-built.mjs` validated the emitted renderer chunks by spawning
`node --input-type=module --check` once per chunk. A real bundle here emits
~960 `.js` chunks (31 MB), so the postbuild step performed ~960 sequential
process creations: 47-80 s wall clock measured on an idle machine (median
77 ms per spawn, worst 446 ms). That made the update's build step depend on
~960 successful process creations.
On Windows one spawn stalled past its 60 s budget and returned ETIMEDOUT
(`could not run node to syntax-check nord-D92QzZWU.js`), which failed
`npm run build` and aborted the entire update (receipt outcome "failed",
exit 1) even though vite had reported `built in 54.73s`. Nothing was wrong
with the bundle or with node - a single process creation was slow.
Parse every chunk in one child instead: `scripts/check-chunks-parse.mjs`
uses `vm.SourceTextModule` - the in-process module parser, hence
`--experimental-vm-modules` - and writes a JSON verdict on stdout. It
reports the same early errors as `node --check`: invalid destructuring
patterns and duplicate declarations still fail, so the guard keeps catching
the corrupted-silent-fail bundle it was written for. Same verification now
costs one process creation and ~1.5 s instead of ~960 spawns and 47-80 s.
The verdict carries `kind`, and the guard reports `harness` when the check
could not run versus `bundle` when the build is defective. The line is drawn
at the checker: only a SyntaxError is a bundle defect; an unreadable chunk or
an unavailable vm API is a failure of the check itself. Previously every
failure printed "The renderer bundle is missing or incomplete ... Re-run the
build", so a blocked or stalled node process was reported as a bundle defect
and sent at least one investigation after a problem that did not exist.
- assert-dist-built now parses every emitted dist/assets/*.js chunk as an
ES module (node --input-type=module --check via spawnSync) before the
build reports success
- a silent bundler output loss (observed twice: a 10-byte identifier token
missing from the main chunk) previously shipped a syntactically invalid
bundle that white-screens Electron with Uncaught SyntaxError
- the loud failure feeds the existing update-path honest-retry and
stage-and-swap, so a corrupted build retries instead of shipping
- vitest coverage: invalid chunk fails with the chunk name in the error,
valid chunks (incl. import.meta / template literals) still pass
Fixes#105184
The packaged app crashed at launch with 'No QueryClient set, use
QueryClientProvider to set one': useQuery in a lazy chunk (session-list-density)
read a second @tanstack/react-query runtime whose QueryClientContext was never
populated by the entry's QueryClientProvider. The source tree was correct — the
duplication happened at build time, because react-query was the one
context-bearing runtime not pinned to a shared vendor chunk, and rolldown's
merge heuristics inline the spare copy into a lazy chunk depending on toolchain
version.
- vite.config.ts: add @tanstack/react-query to the vendor-react
advancedChunks group + dev dedupe list, mirroring the react-router fix.
- assert-dist-built.mjs: fail the build when the 'No QueryClient set'
invariant appears in more than one JS asset (launch-smoke guard).
- assert-dist-built.test.mjs: unit tests for the new invariant check.
- launch-packaged-app.spec.ts: e2e smoke test asserting the packaged app
boots to real UI, not the QueryClient error boundary.