Skip to content

fix: resolve relative sourceMappingURL against local path when remoteRoot/localRoot is set#2345

Open
chrisdp wants to merge 1 commit intomicrosoft:mainfrom
chrisdp:fix/remoteroot-relative-sourcemapurl
Open

fix: resolve relative sourceMappingURL against local path when remoteRoot/localRoot is set#2345
chrisdp wants to merge 1 commit intomicrosoft:mainfrom
chrisdp:fix/remoteroot-relative-sourcemapurl

Conversation

@chrisdp
Copy link
Copy Markdown

@chrisdp chrisdp commented Apr 17, 2026

Summary

When using remoteRoot/localRoot to debug a script on a remote device, a relative sourceMappingURL that navigates outside the remoteRoot subtree fails to load with ENOENT.

Root cause: sourceMapURL is resolved against the raw remote script URI before remoteRoot/localRoot path translation is applied. If the resolved path doesn't start with remoteRoot, no translation is applied and js-debug tries to open a non-existent device path as a local file.

Example: with remoteRoot: .../remote, localRoot: .../local, and sourceMappingURL: ../maps/index.js.map in remote/index.js:

Remote URI:  file://.../remote/index.js
Resolved:    file://.../maps/index.js.map   ← outside remoteRoot, ENOENT

The correct resolution uses the locally-translated script path as the base:

Local path:  .../local/index.js
Resolved:    .../local/maps/index.js.map    ✓

Fix

In SourceContainer.addSource, after urlToAbsolutePath produces the locally-translated absolutePath for the script, re-resolve a relative sourceMapURL against that local path. This only applies when:

  • absolutePath is non-empty (translation actually happened)
  • event.url is a file:// URL (excludes http/webpack dev-server cases where the map must be fetched from the remote server)
  • sourceMapURL is not a data URI

Absolute sourceMapURLs are unaffected — completeUrl ignores the base when the input is already an absolute URL.

This also fixes the secondary effect where absSourceRoot was derived from the remote script dirname instead of the local one, causing TypeScript source paths to resolve to nonexistent locations.

Test

Added an integration test that creates this fixture layout:

remote/
  index.js          ← deployed script, sourceMappingURL=../maps/index.js.map
local/
  maps/
    index.js.map    ← source map (only exists locally, never deployed)
  src/
    index.js        ← original source

With remoteRoot: .../remote and localRoot: .../local, the golden file asserts the stack frame references local/src/index.js (the mapped original source). Without the fix, the source map isn't found and the frame falls back to the compiled path.

Related issues

…Root/localRoot is set

When a script is translated via remoteRoot/localRoot and its sourceMappingURL
navigates outside the remoteRoot subtree, the resolved map URL pointed to a
non-existent device path instead of the locally-translated equivalent.

Fix: after urlToAbsolutePath produces a local absolutePath for the script,
re-resolve a relative sourceMapURL against that local path so the source map
can be found regardless of where it sits relative to remoteRoot.
@chrisdp
Copy link
Copy Markdown
Author

chrisdp commented Apr 17, 2026

@microsoft-github-policy-service agree

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