fix(test): explain an unresolvable workspace specifier - #752
Merged
sroussey merged 2 commits intoAug 13, 2026
Merged
Conversation
sroussey
force-pushed
the
claude/coverage-dist-bundle-fix-ew0vj8
branch
from
August 13, 2026 03:53
ced2825 to
97a0b75
Compare
The source-redirect plugin rewrites the RESULT of resolution, so resolution itself still goes through the package's `exports`, which point at ./dist/*. With no built entry there, Vite's resolution fails first, `this.resolve` yields nothing, the plugin returns null, and the run dies with a generic `Cannot find package '@workglow/ai/worker' imported from …` that blames the manifest and names neither the plugin nor anything to do about it. Keeps the workspace list as WorkspacePackage rather than bare names, so the owning directory is available at the point resolution fails, and throws a message naming the specifier, the owner, the importer and the remedy. Throwing rather than warning is right: an unresolvable @workglow/* specifier already fails the run, so this replaces a misleading message with an actionable one. The remedy branches on whether the owner's dist holds any built entries, since "never built" and "a new exports subpath was added without rebuilding" call for different actions and the second reads as wrong advice to someone looking at a populated dist. An empty dist directory — what `bun run clean` and `use-dist --no-build` both leave behind — counts as never built. ownerOf and unresolvedWorkspaceMessage are separated out as pure functions because resolveId needs Vite's plugin context to drive and cannot be unit tested; the message was additionally verified end to end by emptying dist and running a suite. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H797qbH356jjznKgUax63o
sroussey
force-pushed
the
claude/coverage-resolve-diagnostic
branch
from
August 13, 2026 03:55
007fc3b to
02e3cdb
Compare
…ew0vj8' into tmp-resolve-752 # Conflicts: # scripts/workspaceSource.test.ts
sroussey
merged commit Aug 13, 2026
71afe15
into
claude/coverage-dist-bundle-fix-ew0vj8
3 checks passed
Coverage Report
File CoverageNo changed files found. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Based on #741 (retarget to
mainonce that merges). Rebased ontoorigin/main(67bed681). Independent of #748 and #749.What
The source-redirect plugin rewrites the result of resolution — that ordering is the whole trick, since it lets conditional
exportspick the right target first. The consequence is that resolution still goes throughexports, which point at./dist/*, sodist/<entry>.jshas to exist even though the plugin immediately rewrites it tosrc.When it does not, Vite's resolution fails before the rewrite can happen,
this.resolveyields nothing, the plugin returnsnull, and the run dies with a generic message that blames the manifest and names neither the plugin nor the remedy. Observed first-hand in this checkout:Nothing there tells you that a workspace plugin is involved, that
@workglow/aiis the package to look at, or thatbun run build/bun run use-sourcefixes it.Why this fix
The plugin kept only package names, so at the moment resolution failed it could not say which package's
distto look at. It now keepsWorkspacePackage[], which makes an actionable message possible at all. After the fix, the same failure reads:Throwing, not warning. An unresolvable
@workglow/*specifier already fails the run a moment later; this only replaces the message.The remedy branches, because the two cases need opposite responses: a
distwith no built entries means the package was never built, whereas a populateddistthat still lacks this entry is the "added anexportssubpath and did not rebuild" case — and there, "run build" on its own reads as wrong advice to someone staring at a directory full of bundles.Beyond the original plan: the branch tests for built entries, not for the directory. The first draft used
existsSync(dir/dist)and, when actually exercised, produced the wrong branch —bun run use-dist --no-build(andbun run clean) leave the directory in place but empty, so a never-built tree was told itsdistwas stale. Caught only by running it.Tests
resolveIdneeds Vite's plugin context and cannot be unit tested, so the two pure helpers were separated out and tested directly (5 cases inscripts/workspaceSource.test.ts):ownerOf(packages, "@workglow/util/schema")→ the@workglow/utilentry; the bare name too.ownerOf(packages, "@workglow/utilities")→undefined— a string prefix that is not a package boundary; attributing it would point the diagnostic at an unrelated directory."vitest"likewise.missing or empty/never been builtvscarries built entries but none for this specifier/stale rather than absent).All five fail pre-fix (
TypeError: ownerOf is not a function— neither helper exists).Actually executed:
vitest --project scripts: 3 files, 19 passed.bun run use-dist --no-buildto empty everydist, then ran a suite and confirmed the new error text (quoted above) replaces the generic one. Thenbun run use-sourceand re-confirmed green.prettier --checkclean on bothscripts/files..claude/CLAUDE.mdfailsprettier --checkon the unmodified base branch too — pre-existing, verified by stashing.Risk / blast radius
The one behaviour change in this stack: a workspace specifier that fails to resolve is now a hard error at the plugin, where it was previously a soft
nullthat let Vite continue. If some call site legitimately expects an optional@workglow/*specifier to fail resolution and be handled downstream, this converts that into a thrown error. No such call site was found.Confined to
scripts/lib/workspaceSource.ts. The success path is byte-identical —ownerOfreplaces an inlinednames.some(...)with the same boundary semantics.Unverified
distis absent" is now observed, not merely reasoned — but only for one specifier shape (a subpath export under vite-node). Other resolution paths may still produce Vite's own message.buildfailure inherited frommain(see the note at the top).