feat(check-parser): resolve and bundle Node.js subpath #imports [RED-688] [show]#1388
Merged
Conversation
Add hasImports()/resolveImportPath() to PackageJsonFile, resolving Node.js subpath import specifiers (#-prefixed) against the package's `imports` map. The `imports` and `exports` fields share the same conditional-target shape, so the private exports resolver is generalized to #resolveConditionalTargets and reused. Unlike exports, no subpath normalization is applied: `imports` keys and the specifier are both #-prefixed and matched verbatim. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P3ajE9Vq8Mw8C5YNAzQwn8
Replace the resolver's TODO that silently skipped `#`-prefixed import specifiers. `#imports` now resolve against the nearest package.json's `imports` map (the importing file's package scope): relative targets are bundled as local files, and bare targets are treated as external packages or workspace neighbors. Previously the mapped files were never discovered, so subpath imports failed at runtime unless the user forced the files into the bundle with a manual `include`. This makes Playwright Check Suites resolve them automatically. The workspace-neighbor/external-fallback logic is extracted into a shared closure so both ordinary specifiers and bare `#import` targets reuse it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P3ajE9Vq8Mw8C5YNAzQwn8
Exports resolution was covered by resolveExportPath unit tests but never exercised through parser.parse(). Add an e2e fixture whose package exposes its entry point only via a conditional `exports` map (no `main`), so the target is discovered solely through exports resolution, and assert the matching `node` condition is selected over the non-matching `browser` and the `default` fallback. Covered in both unrestricted and restricted modes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P3ajE9Vq8Mw8C5YNAzQwn8
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.
Linear: RED-688
What
Resolve and bundle Node.js subpath imports (the
package.jsonimportsfield, referenced via#-prefixed specifiers likeimport x from '#utils/foo').The parser already read the
importsfield into its schema, but the dependency resolver had an explicit// TODOthat silently skipped every#-specifier. The mapped files were therefore never discovered or bundled, so subpath imports failed at runtime unless the user forced the files into the bundle with a manualinclude. This makes Playwright Check Suites resolve them automatically.How
PackageJsonFilegainshasImports()/resolveImportPath(), reusing the existing conditional-target + wildcard machinery used forexports(the two fields share the same target shape, so the private resolver is generalized to#resolveConditionalTargets). Unlikeexports, no subpath normalization is applied —importskeys and the specifier are both#-prefixed and matched verbatim.#-specifier against the nearestpackage.json'simportsmap (the importing file's package scope). Relative targets are bundled as local files (a missing target is reported); bare targets are treated as external packages or workspace neighbors via a shared closure extracted from the existing loop-tail logic. Every#-specifier path terminates incontinue resolve, so an unmatched#foois never misrecorded as an external npm dependency.Tests
resolveImportPath(relative, conditional import/require, wildcard, external/bare, unmatched).#-specifiers never leak into the dependency set.Known limitation
In restricted mode (Api/Browser/MultiStep checks) the nearest
package.jsonis deliberately not bundled, so theimportsmap is unavailable at runtime. Playwright Check Suites — the target use case — are unrestricted, so this fully addresses the ticket; the restricted-mode case is left as a documented limitation.Other changes
Adds end-to-end coverage for the
exportsfield (previously only unit-tested viaresolveExportPath): a fixture whose package exposes its entry point solely through a conditionalexportsmap is now resolved throughparser.parse(), asserting the matchingnodecondition wins over a non-matchingbrowsercondition and thedefaultfallback.