You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
finding: childEnv() forwards NODE_PATH to spawned children, so a spawned resolution pin that routes through CJS is as vacuous as the in-process one it replaced #11773
Filed unassigned while measuring #11412. Recording only — not graded here.
The mechanism
A vitest worker runs with NODE_PATH set to pnpm's dirs, the last of which is the
hoisted store node_modules/.pnpm/node_modules — which holds everything
transitively reachable anywhere in the workspace.
packages/cli/test/helpers/serve-process.ts's childEnv() strips exactly the
vitest worker family (TEST, VITEST, VITEST_*) and forwards everything else,
so NODE_PATH rides into every spawned child. Measured from inside a vitest
worker: childEnv() keeps NODE_PATH and drops VITEST, and the child sees the
store.
Why it matters
Spawning a real Node child is the repo's remedy for #11412's base collapse. It
escapes Vite's rewrite — but not this. Measured in one spawned child anchored at packages/types, NODE_PATH inherited:
esm chalk -> MISS (base preserved — ESM ignores NODE_PATH)
cjs chalk -> HIT (base erased — CJS honours it)
esm/cjs @os-fixture/... -> MISS, MISS (control: the probe could say either)
NODE_PATH is a fallback, not an override — the node_modules walk wins when
it hits, so the store can only turn a MISS into a HIT. The dangerous direction is
an ACCEPTANCE claim ("this base CAN reach X"): green because the store supplied X,
not because the base did.
The narrow escape, and why it is luck
test/serve-host-fallback-base.e2e.test.ts's CONTROL case is sound only
because createHostImporter's fallback leg is an ESM import(). Had it been
CJS — as createHostRequire is — the inherited NODE_PATH would have kept it
green through the very ablation it exists to fail, i.e. the spawned remedy would
have reproduced the defect it was adopted to escape.
Not measured here
Whether childEnv() should strip NODE_PATH by default. ⚠️ Not obvious and not a tidy-up: real serve/dev children DO inherit NODE_PATH from pnpm
bin shims — that is createHostRequire 认 NODE_PATH,于是 #4699 立下的「host app 必须自己声明」在 pnpm 工作区里根本没被强制 #4719's entire history — so an e2e reproducing production
faithfully may want it kept. This looks like a per-test opt-out
(childEnv({ NODE_PATH: undefined })) rather than a global strip, but that is a
judgement, not a measurement.
Whether check:cli-test-child-env should flag a spawned CJS-resolution claim
that does not strip it. Today's population of such pins is zero, so this is
a guard against a future shape, not a live defect.
Today the hazard is recorded, not enforced: the mechanism and its controls are
in packages/cli/test/vitest-resolution-base-collapse.e2e.test.ts and referenced
from scripts/check-test-source-alias.mjs's header (PR #11771).
Related: #11412 (where it was measured) · #4719 (NODE_PATH reachability vs
declaration, the same variable one layer down) · #11595 · #11441 · #11464 (the packages/cli/test spawner-env family; this is a different variable and a
different consequence)
Filed unassigned while measuring #11412. Recording only — not graded here.
The mechanism
A vitest worker runs with
NODE_PATHset to pnpm's dirs, the last of which is thehoisted store
node_modules/.pnpm/node_modules— which holds everythingtransitively reachable anywhere in the workspace.
packages/cli/test/helpers/serve-process.ts'schildEnv()strips exactly thevitest worker family (
TEST,VITEST,VITEST_*) and forwards everything else,so
NODE_PATHrides into every spawned child. Measured from inside a vitestworker:
childEnv()keepsNODE_PATHand dropsVITEST, and the child sees thestore.
Why it matters
Spawning a real Node child is the repo's remedy for #11412's base collapse. It
escapes Vite's rewrite — but not this. Measured in one spawned child anchored at
packages/types,NODE_PATHinherited:NODE_PATHis a fallback, not an override — thenode_moduleswalk wins whenit hits, so the store can only turn a MISS into a HIT. The dangerous direction is
an ACCEPTANCE claim ("this base CAN reach X"): green because the store supplied X,
not because the base did.
The narrow escape, and why it is luck
test/serve-host-fallback-base.e2e.test.ts'sCONTROLcase is sound onlybecause
createHostImporter's fallback leg is an ESMimport(). Had it beenCJS — as
createHostRequireis — the inheritedNODE_PATHwould have kept itgreen through the very ablation it exists to fail, i.e. the spawned remedy would
have reproduced the defect it was adopted to escape.
Not measured here
childEnv()should stripNODE_PATHby default.not a tidy-up: real
serve/devchildren DO inheritNODE_PATHfrom pnpmbin shims — that is
createHostRequire认 NODE_PATH,于是 #4699 立下的「host app 必须自己声明」在 pnpm 工作区里根本没被强制 #4719's entire history — so an e2e reproducing productionfaithfully may want it kept. This looks like a per-test opt-out
(
childEnv({ NODE_PATH: undefined })) rather than a global strip, but that is ajudgement, not a measurement.
check:cli-test-child-envshould flag a spawned CJS-resolution claimthat does not strip it. Today's population of such pins is zero, so this is
a guard against a future shape, not a live defect.
Today the hazard is recorded, not enforced: the mechanism and its controls are
in
packages/cli/test/vitest-resolution-base-collapse.e2e.test.tsand referencedfrom
scripts/check-test-source-alias.mjs's header (PR #11771).Related: #11412 (where it was measured) · #4719 (
NODE_PATHreachability vsdeclaration, the same variable one layer down) · #11595 · #11441 · #11464 (the
packages/cli/testspawner-env family; this is a different variable and adifferent consequence)