Skip to content

fix(compiler): dynamic-import lowering resolves bare project specifiers - #200

Open
techfreaque wants to merge 1 commit into
vercel-labs:mainfrom
techfreaque:fix-dynamic-import-resolution
Open

fix(compiler): dynamic-import lowering resolves bare project specifiers#200
techfreaque wants to merge 1 commit into
vercel-labs:mainfrom
techfreaque:fix-dynamic-import-resolution

Conversation

@techfreaque

Copy link
Copy Markdown

Problem

scriptc has two independent resolvers: the type-checker (a spawned tsgo process) resolves imports one way, and packages/compiler/src/frontend/resolve.ts's hand-written resolveProjectImport() resolves them again, separately, for the dynamic-import codegen/lowering path (packages/compiler/src/frontend/lowering/lower-modules.ts, used to decide whether a dynamic import() targets a compiled program module). These two resolvers can disagree, producing two distinct bugs.

Bug A. dynamicImportProgramTargetOf() only calls the checker-based resolveImport() when the specifier is relative (./foo) or absolute (/foo). For a bare specifier that resolves via a project's own package.json self-name "exports" (a package importing its own name, e.g. import("my-package/foo") from within my-package itself) — which the type-checker resolves successfully — the lowering path returned null unconditionally, producing:

error: dynamic import of the program's own module 'my-package/foo' is not part of the compiled module graph

even though the checker resolved the exact same specifier moments earlier.

Bug B. resolveProjectImport()'s final fallback only tried loadAsFile(path) then a bare isFile(path) check — never loadAsDirectory(path) (i.e. path/index.ts), even though loadAsDirectory already exists in this same file and is used by every other resolver here (see resolveRelativeModule). So a package.json "exports"/"imports" target that itself points at a directory (e.g. a wildcard subpath landing on "./src/foo", meant to be answered by "./src/foo/index.ts") failed to resolve through this path, while the identical directory resolves fine one character away as a plain relative import.

Root cause

Both bugs stem from resolveProjectImport (resolve.ts) and the checker's own resolution independently reimplementing bundler-style resolution and drifting apart on two specific cases: dynamic-import lowering never tried the project resolver for bare specifiers at all (Bug A), and the project resolver itself was missing a fallback arm every sibling resolver in the file already has (Bug B).

Fix

  • lower-modules.ts: dynamicImportProgramTargetOf now falls back to resolveProjectImport for non-relative/non-absolute specifiers, mapping the resolved path to a program ts.SourceFile via program.getSourceFile.
  • resolve.ts: resolveProjectImport's final fallback now tries loadAsDirectory between loadAsFile and the bare isFile check, matching the pattern used elsewhere in this file.
  • resolve.ts / program.ts: adds a paths-alias fallback registry (setTsconfigPaths / resolveViaTsconfigPaths), consulted only when the package.json-exports walk finds nothing, and wired from program.ts's loadProgram7. This makes the same resolver used here also answer tsconfig paths aliases, which is useful once a project's paths are adopted into the type-checker (a separate, independent PR). Until that lands, config.options["paths"] is typically undefined here, so the registry stays empty and this fallback is inert — safe to land standalone.

Verification

  • cd packages/compiler && node node_modules/typescript5/bin/tsc -p tsconfig.json — 0 errors.
  • node_modules/.bin/vitest run packages/compiler/test/ts7/resolver-parity.test.ts — same 4 pre-existing failures with and without this change (confirmed via git stash), all a Windows path-separator artifact unrelated to this fix (\ vs / in resolved paths); the resolveProjectImport-specific tests in this file pass.
  • node_modules/.bin/vitest run tests/harness/diagnostics.test.ts (full 114-program diagnostics corpus, which exercises the frontend/lowering pipeline without requiring a native toolchain) — same 114/114 failures with and without this change (also the identical Windows path-separator artifact, confirmed via git stash); no diagnostic content changed.
  • Full native-build differential/corpus suites require a native toolchain (ar etc.) not available in this environment, so they were not run end-to-end; the change is scoped to the frontend resolution layer covered above.

@vercel

vercel Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

@mabr-pcvisit is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

Two bugs in the dynamic-import codegen path's own resolver
(resolveProjectImport in resolve.ts), independent from the type-checker's
resolution but expected to agree with it:

- dynamicImportProgramTargetOf only ever tried the checker-based
  resolveImport for relative/absolute specifiers, never for bare ones. A
  package importing its own name (`import("my-package/foo")` from within
  my-package, resolved via its package.json self-name "exports") is
  something the checker resolves fine, but the lowering path returned null
  unconditionally and reported "dynamic import of the program's own module
  ... is not part of the compiled module graph" even though the exact
  specifier had just resolved moments earlier.

- resolveProjectImport's final fallback only tried loadAsFile then a bare
  isFile check, never loadAsDirectory (unlike every other resolver in this
  file) — so an exports/imports target landing on a directory answered
  null instead of falling back to its index file.

Also adds a tsconfig `paths` fallback registry (setTsconfigPaths /
resolveViaTsconfigPaths), consulted only when the package.json-exports walk
finds nothing. It pairs with a separate, independent PR adopting a
project's `paths` into the type-checker; until that lands the registry
stays empty and this fallback is inert.
@techfreaque
techfreaque force-pushed the fix-dynamic-import-resolution branch from 8ff9d75 to b4eb36b Compare August 22, 2026 09:29
techfreaque added a commit to techfreaque/scriptc that referenced this pull request Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant