Skip to content

Commit

Permalink
fix #4078: prepend namespaces to source map paths
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Mar 10, 2025
1 parent ccf3dd7 commit f9b3952
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

This fix was contributed by [@jridgewell](https://github.com/jridgewell).

* Fix a regression with non-file source map paths ([#4078](https://github.com/evanw/esbuild/issues/4078))

The format of paths in source maps that aren't in the `file` namespace was unintentionally changed in version 0.25.0. Path namespaces is an esbuild-specific concept that is optionally available for plugins to use to distinguish paths from `file` paths and from paths meant for other plugins. Previously the namespace was prepended to the path joined with a `:` character, but version 0.25.0 unintentionally failed to prepend the namespace. The previous behavior has been restored.

* Fix a crash with `switch` optimization ([#4088](https://github.com/evanw/esbuild/issues/4088))

The new code in the previous release to optimize dead code in switch statements accidentally introduced a crash in the edge case where one or more switch case values include a function expression. This is because esbuild now visits the case values first to determine whether any cases are dead code, and then visits the case values once the dead code status is known. That triggered some internal asserts that guard against traversing the AST in an unexpected order. This crash has been fixed by changing esbuild to expect the new traversal ordering. Here's an example of affected code:
Expand Down
32 changes: 32 additions & 0 deletions internal/linker/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -6980,7 +6980,39 @@ func (c *linkerContext) generateSourceMapForChunk(
}
source := file.InputFile.Source.KeyPath.Text
if file.InputFile.Source.KeyPath.Namespace == "file" {
// Serialize the file path as a "file://" URL, since source maps encode
// sources as URLs. While we could output absolute "file://" URLs, it
// will be turned into a relative path when it's written out below for
// better readability and to be independent of build directory.
source = helpers.FileURLFromFilePath(source).String()
} else {
// If the path for this file isn't in the "file" namespace, then write
// out something arbitrary instead. Source maps encode sources as URLs
// but plugins are allowed to put almost anything in the "namespace"
// and "path" fields, so we don't attempt to control whether this forms
// a valid URL or not.
//
// The approach used here is to join the namespace with the path text
// using a ":" character. It's important to include the namespace
// because esbuild considers paths with different namespaces to have
// separate identities. And using a ":" means that the path is more
// likely to form a valid URL in the source map.
//
// For example, you could imagine a plugin that uses the "https"
// namespace and path text like "//example.com/foo.js", which would
// then be joined into the URL "https://example.com/foo.js" here.
//
// Note that this logic is currently mostly the same as the pretty-
// printed paths that esbuild shows to humans in error messages.
// However, this code has been forked below as these source map URLs
// are intended for code instead of humans, and we don't want the
// changes for humans to unintentionally break code that uses them.
//
// See https://github.com/evanw/esbuild/issues/4078 for more info.
if ns := file.InputFile.Source.KeyPath.Namespace; ns != "" {
source = fmt.Sprintf("%s:%s", ns, source)
}
source += file.InputFile.Source.KeyPath.IgnoredSuffix
}
items = append(items, item{
source: source,
Expand Down
32 changes: 32 additions & 0 deletions scripts/plugin-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -2675,6 +2675,38 @@ console.log(foo_default, foo_default2);
}],
})
},

async sourceMapNamespacePrefixIssue4078({ esbuild, testDir }) {
const entry = path.join(testDir, 'entry.js')
await writeFileAsync(entry, `
import 'foo'
console.log('entry')
`)

const result = await esbuild.build({
entryPoints: [entry],
bundle: true,
sourcemap: true,
write: false,
outdir: path.join(testDir, 'out'),
plugins: [{
name: 'example',
setup(build) {
build.onResolve({ filter: /foo/ }, () => {
return { path: 'lib/foo', namespace: 'mynamespace' }
})
build.onLoad({ filter: /foo/, namespace: 'mynamespace' }, () => {
return { contents: 'console.log("foo")' }
})
},
}],
})

assert.strictEqual(result.outputFiles.length, 2)
const map = result.outputFiles.find(file => file.path.endsWith('.js.map'))
const json = JSON.parse(map.text)
assert.deepStrictEqual(json.sources, ['mynamespace:lib/foo', '../entry.js'])
},
}

const makeRebuildUntilPlugin = () => {
Expand Down

0 comments on commit f9b3952

Please sign in to comment.