Fix auto-import of subpath imports starting with #/ in supported module resolution modes#3123
Conversation
…d streamline completion checks
There was a problem hiding this comment.
Pull request overview
This PR investigates #2984 (auto-import preferring deep relative paths over shorter package.json#imports root wildcard aliases) and applies a minimal fix so #/* mappings can contribute #/... module specifiers during auto-import generation.
Changes:
- Add investigation notes documenting repro, root cause, and fix.
- Update
tryGetModuleNameFromPackageJsonImportsto allow the#/*root wildcard imports key. - Add a focused fourslash test covering the
#/*->#/...auto-import preference for bothshortestandnon-relative.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| investigations/issue-2984-autoimport-root-wildcard.md | Documents repro, diagnosis, and validation for #2984. |
| internal/modulespecifiers/specifiers.go | Adjusts imports-key validation to permit #/* root wildcard. |
| internal/fourslash/tests/autoImport_issue2984_rootWildcardVitest_test.go | Adds regression test asserting #/... is top-ranked over deep relative specifiers. |
You can also share your feedback on Copilot code review. Take the survey.
… not just #/* Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Restructure validation logic for clarity - Rewrite fourslash tests using BaselineAutoImports - Delete investigation notes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
#/ in supported module resolution modes
| ```ts | ||
| // @FileName: /src/features/deep/consumer.ts | ||
| entit/**/ | ||
| ``````ts |
There was a problem hiding this comment.
This seems broken, and the next like is missing a file path - what's going on here?
There was a problem hiding this comment.
These baselines have always been missing a newline. I'll fix them all in a separate PR but the baseline still shows everything is working.
| entries := top.Entries() | ||
| for k, value := range entries { | ||
| if !strings.HasPrefix(k, "#") || k == "#" || strings.HasPrefix(k, "#/") { | ||
| if k == "#" || k == "#/" || !strings.HasPrefix(k, "#") { |
There was a problem hiding this comment.
Yeah, --moduleResolution node20 doesn't exist yet. Everything else is right.
There was a problem hiding this comment.
But this logic says that "#" and "#/" are invalid entries in "imports" - so am I misunderstanding?
There was a problem hiding this comment.
They are, aren't they? You need something after that prefix.
There was a problem hiding this comment.
Sorry, to be explicit - the blog post example uses a bare # entry, which is not being used as a prefix.
There was a problem hiding this comment.
Omg, sorry, I totally missed that. I was only focused on the highlighted text. Yes, the blog post needs to be updated:
Developer/eg/imports v24.14.0
❯ node index.js
node:internal/modules/esm/resolve:698
throw new ERR_INVALID_MODULE_SPECIFIER(name, reason, fileURLToPath(base));
^
TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module "#" is not a valid internal imports specifier name imported from /Users/andrew/Developer/eg/imports/index.js
at packageImportsResolve (node:internal/modules/esm/resolve:698:11)
at moduleResolve (node:internal/modules/esm/resolve:848:16)
at defaultResolve (node:internal/modules/esm/resolve:991:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:719:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:736:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:765:52)
at #resolve (node:internal/modules/esm/loader:701:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:621:35)
at ModuleJob.syncLink (node:internal/modules/esm/module_job:160:33)
at ModuleJob.link (node:internal/modules/esm/module_job:245:17) {
code: 'ERR_INVALID_MODULE_SPECIFIER'
}
Node.js v24.14.0
Developer/eg/imports v24.14.0
❯ node index.js
node:internal/modules/esm/resolve:698
throw new ERR_INVALID_MODULE_SPECIFIER(name, reason, fileURLToPath(base));
^
TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module "#/" is not a valid internal imports specifier name imported from /Users/andrew/Developer/eg/imports/index.js
at packageImportsResolve (node:internal/modules/esm/resolve:698:11)
at moduleResolve (node:internal/modules/esm/resolve:848:16)
at defaultResolve (node:internal/modules/esm/resolve:991:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:719:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:736:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:765:52)
at #resolve (node:internal/modules/esm/loader:701:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:621:35)
at ModuleJob.syncLink (node:internal/modules/esm/module_job:160:33)
at ModuleJob.link (node:internal/modules/esm/module_job:245:17) {
code: 'ERR_INVALID_MODULE_SPECIFIER'
}
Node.js v24.14.0

Auto-imports was never updated to allow subpath imports starting with
#/after microsoft/TypeScript#62844 landed. Same as with module resolution, they're only considered valid in--moduleResolution nodenextandbundler.Fixes #2984