Repository navigation
Skip URI-like module names in typings-location resolution - #65340
Open
Ryan Cavanaugh (RyanCavanaugh) wants to merge 1 commit into
Open
Ryan Cavanaugh (RyanCavanaugh) wants to merge 1 commit into
Ryan Cavanaugh (RyanCavanaugh) wants to merge 1 commit into
Conversation
The extra typings-cache resolution pass resolved non-relative names like "https://esm.sh/pkg?dev" against the typings node_modules directory, panicking in tspath because the resulting path is an absolute URL containing a query or fragment. The primary node_modules lookup already skips names containing ':' as absolute URIs; apply the same check to the typings-location pass. Part of microsoft#65068 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dec07792-6b96-4d6d-b2c8-c8a49abd0ce6
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues remain for the typings-cache resolution change.
No open issues 🎉
What changed in this PR
Prevents a language-server crash during program construction by skipping URI-like module names in typings-cache resolution, addressing part of #65068.
Changes:
- Adds a colon guard to the typings-location fallback, matching the primary module lookup.
- Adds a session regression test for URL imports containing queries or fragments, verifying that both remain unresolved without crashing.
| File | Description |
|---|---|
tsc/internal/project/session_test.go |
Tests URL imports in an inferred project with a typings cache. |
tsc/internal/module/resolver.go |
Skips URI-like names before constructing typings-cache paths. |
Review effort: Balanced
Jake Bailey (jakebailey)
approved these changes
Nov 5, 2026
| if r.typingsLocation == "" || | ||
| tspath.IsExternalModuleNameRelative(moduleName) || | ||
| // Like the primary node_modules lookup, skip names that look like absolute URIs | ||
| // (e.g. "https://esm.sh/pkg?dev"); they can never resolve within the typings cache. |
There was a problem hiding this comment.
Is this true? What about node:foo or jsr:bar etc? I guess they still can't...
This branch has not been deployed
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.
Part of #65068 (
panic: path must not contain a URL query or fragmentintryResolveFromTypingsLocationduring program construction, which takes down the language server)Analysis
When automatic type acquisition is on (for example, in inferred projects),
DefaultResolver.resolveModuleNameruns a second resolution pass against the global typings cache for any non-relative import that didn't resolve to a TS file (tryResolveFromTypingsLocation). That pass callsloadModuleFromImmediateNodeModulesDirectory(extensionsDeclaration, typingsLocation, ...), which callsresolutionCandidateFromDirectoryPath(nodeModulesDir, moduleName)and thenRootedDirectoryPath.ResolveDirectory.If the module name is an absolute URL with a query or fragment, it hits the panic in
tspath.ToRootedPath, because the URL can't be a rooted path:The primary lookup avoids this.
resolveNodeLikeWorkerreturns early for any non-relative name containing:and logs "Skipping module '...' that looks like an absolute URI". The typings-location pass had no matching check, so it was the first code to build a node_modules path from these names.In Strada,
loadModuleFromGlobalCachealso lacked the check. It didn't crash only becausecombinePathsreturns the URL unchanged and the resultingfileExistschecks fail, so the module stays unresolved.Fix
Add the same
strings.Contains(moduleName, ":")check to the early return intryResolveFromTypingsLocation. URI-like names now skip the typings-cache pass, the same way they skip the primary node_modules lookup, and the original (unresolved) result is returned, as in Strada. Names likenode:fsorvirtual:foonever resolved through the typings cache before (the extra lookups always failed, and their failed lookups were discarded), so the only visible change is that the crash is gone.Alternatives considered:
tspathassertion. Rejected: the assertion is correct, and the caller should never build such a path.loadModuleFromSpecificNodeModulesDirectory. This would make the typings pass behave differently from the primary lookup, which already filters these names before reaching that function.Test: a new
TestSession/DidOpenFile/inferred_project_with_URL_imports_containing_query_or_fragmentsession test intsc/internal/project/session_test.go. It opens a file with URL imports that include?and#in an inferred project with a typings cache. Before the fix it panicked with the same stack as the report; now it passes and both imports stay unresolved.Copilot Checklist
I successfully ran the applicable command at the end of my session, and it completed without error: