Skip to content

Skip URI-like module names in typings-location resolution - #65340

Open
Ryan Cavanaugh (RyanCavanaugh) wants to merge 1 commit into
microsoft:mainfrom
RyanCavanaugh:fix-typings-location-url-fragment
Open

Ryan Cavanaugh (RyanCavanaugh) wants to merge 1 commit into
microsoft:mainfrom
RyanCavanaugh:fix-typings-location-url-fragment

Conversation

@RyanCavanaugh

Copy link
Copy Markdown
Member

Part of #65068 (panic: path must not contain a URL query or fragment in tryResolveFromTypingsLocation during program construction, which takes down the language server)

Analysis

When automatic type acquisition is on (for example, in inferred projects), DefaultResolver.resolveModuleName runs 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 calls loadModuleFromImmediateNodeModulesDirectory(extensionsDeclaration, typingsLocation, ...), which calls resolutionCandidateFromDirectoryPath(nodeModulesDir, moduleName) and then RootedDirectoryPath.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:

// index.ts, opened in an inferred project with a typings cache
import "https://esm.sh/preact?dev";
import "https://esm.sh/preact#main";

The primary lookup avoids this. resolveNodeLikeWorker returns 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, loadModuleFromGlobalCache also lacked the check. It didn't crash only because combinePaths returns the URL unchanged and the resulting fileExists checks fail, so the module stays unresolved.

Fix

Add the same strings.Contains(moduleName, ":") check to the early return in tryResolveFromTypingsLocation. 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 like node:fs or virtual:foo never 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:

  • Weakening the tspath assertion. Rejected: the assertion is correct, and the caller should never build such a path.
  • Adding the guard lower down, in 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_fragment session test in tsc/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:

  • npx hereby validate
  • npx hereby validate --api (for TypeScript API changes)

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
Copilot AI balanced review requested due to automatic review settings November 5, 2026 18:25
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Nov 5, 2026
@typescript-automation typescript-automation Bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Nov 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 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

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this true? What about node:foo or jsr:bar etc? I guess they still can't...

@github-project-automation github-project-automation Bot moved this from Not started to Needs merge in PR Backlog Nov 5, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

Status: Needs merge

Development

Successfully merging this pull request may close these issues.

3 participants