Skip to content

fix(scripts): fail fast when the spec-dist override's own deps don't resolve - #5995

Merged
yinlianghui-tw merged 1 commit into
mainfrom
claude/issue-5391-spec-dist-dep-validation
Aug 24, 2026
Merged

fix(scripts): fail fast when the spec-dist override's own deps don't resolve#5995
yinlianghui-tw merged 1 commit into
mainfrom
claude/issue-5391-spec-dist-dep-validation

Conversation

@yinlianghui-tw

Copy link
Copy Markdown
Collaborator

Fixes#5391

What changed

resolveSpecDistInjection (scripts/vite-objectstack-spec-dist.ts) validated that the override's exports targets resolve, but not that the override's own package.jsondependencies do. This adds assertSpecDependenciesResolve, called right after the package directory is located and before the exports map is read: it walks each dependencies entry up from packageDir's ancestor node_modules directories and fails loudly, naming both the override and every unresolved dependency, if any are missing.

Deliberately narrow, per the card: only the manifest's own dependencies (not peerDependencies — those are supplied by the consuming app by design, not the override's own tree — and not transitive dependencies, which fail the same way one frame further into their own resolution, still at build time).

Measurement note — the reproduction differs from the issue's description

Reproduced with a real dep-less copy of the installed @objectstack/spec (its dist/, no reachable node_modules) injected into a real apps/consolevite build. On this repo's current toolchain (vite 8.2.1 + rolldown 1.2.3, d8b48f495), the late failure was not the hard Rolldown failed to resolve import "zod" error the issue describes. Instead the build ran to completion of the transform phase (8613 modules) with zero warning or error for the missing zod — it was silently kept as an unresolved bare import ... from "zod" in the output chunk — and only crashed downstream, and only because of pg-connection-string (the other missing dependency), as an unrelated plugin's ENOENT reading a chunk file that was never written:

error during build:
Build failed with 1 error:
[plugin emit-eager-closure-report]
Error: ENOENT: no such file or directory, open '.../apps/console/dist/pg-connection-string'

So today the late failure is arguably worse than described — silent for zod specifically, and only incidentally loud (in a confusing, unrelated plugin) for pg-connection-string. This does not change the fix; it strengthens the case for it. Full before/after logs are in the dev report.

A second, load-bearing finding along the way

The first implementation used require.resolve(name, { paths: [packageDir] }), which is the obvious tool for this job and is what the file's own header suggests ("the same walk Node itself performs"). It passed the dep-less fixture cleanly — but only when invoked directly with node. Run through the real vite CLI it never caught anything, because pnpm's own apps/console/node_modules/.bin/vite shim exports NODE_PATH pointing at pnpm's flat .pnpm/node_modules hoist directory (which holds a copy of nearly every package the workspace has ever installed, zod included), and Node's paths option does not suppress NODE_PATHModule.globalPaths is consulted regardless. The shipped implementation instead does a manual ancestor node_modules walk (mirroring findSpecPackageDir's own idiom), which consults nothing global. A regression test (is not fooled by a dependency name that resolves globally but not from the package directory) pins this directly, using typescript — a name genuinely reachable elsewhere in the repo — as the stand-in.

Tests

  • scripts/__tests__/vite-objectstack-spec-dist.test.ts — 8 new cases: throws naming the override + dependency; names every unresolved dependency, not just the first; passes once the dependency is reachable (own node_modules and an ancestor's); ignores peerDependencies; inert when dependencies is absent; the real installed spec (which does declare zod + pg-connection-string) passes; and the NODE_PATH-blindness regression test above. All 33 tests in the file pass, including the 25 pre-existing ones.
  • A real apps/consolevite build exercising the injection path three ways: unset (baseline, unaffected, EXIT=0), override at a dep-less fixture (now fails in ~1s at config-load time instead of ~80s–2min into the build), override at the same fixture with its dependencies symlinked in (EXIT=0, injection still works end-to-end).

Gates run

Generated by Claude Code


Generated by Claude Code

…resolve
resolveSpecDistInjection validated that the override's exports-map targets
resolve, but not that the override's own package.json `dependencies` do.
A spec dist built without a reachable node_modules passed validation
cleanly and only failed deep inside the console build, in an error naming
a dependency (zod, observed) but never OBJECTSTACK_SPEC_DIST.
Adds assertSpecDependenciesResolve, called right after the package
directory is located: it reads the override's own `dependencies` (not
peerDependencies, not transitive deps) and confirms each resolves via a
manual ancestor node_modules walk, failing loudly with both the override
and every unresolved dependency named.
Measured and worth recording: a require.resolve(name, { paths: [packageDir] })
implementation of this same check passed silently every time it ran
through the real `vite` CLI, because pnpm's own `apps/console/node_modules/
.bin/vite` shim exports NODE_PATH pointing at the workspace's flat
`.pnpm/node_modules` hoist directory, and Node's `paths` option does not
suppress NODE_PATH. The manual directory walk consults nothing global and
is immune to it; a regression test pins the failure mode directly.
@yinlianghui-tw
yinlianghui-tw marked this pull request as ready for review August 24, 2026 13:44
@yinlianghui-tw
yinlianghui-tw added this pull request to the merge queueAug 24, 2026
Merged via the queue into main with commit b0de7a8Aug 24, 2026
21 checks passed
@yinlianghui-tw
yinlianghui-tw deleted the claude/issue-5391-spec-dist-dep-validation branch August 24, 2026 13:56
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@yinlianghui-tw@claude