diff --git a/CHANGELOG.md b/CHANGELOG.md index 6465b46a141..aef578926a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +* Fill in `null` entries in input source maps ([#3144](https://github.com/evanw/esbuild/issues/3144)) + + If esbuild bundles input files with source maps and those source maps contain a `sourcesContent` array with `null` entries, esbuild previously copied those `null` entries over to the output source map. With this release, esbuild will now attempt to fill in those `null` entries by looking for a file on the file system with the corresponding name from the `sources` array. This matches esbuild's existing behavior that automatically generates the `sourcesContent` array from the file system if the entire `sourcesContent` array is missing. + ## 0.18.0 **This release deliberately contains backwards-incompatible changes.** To avoid automatically picking up releases like this, you should either be pinning the exact version of `esbuild` in your `package.json` file (recommended) or be using a version range syntax that only accepts patch upgrades such as `^0.17.0` or `~0.17.0`. See npm's documentation about [semver](https://docs.npmjs.com/cli/v6/using-npm/semver/) for more information. diff --git a/internal/bundler/bundler.go b/internal/bundler/bundler.go index 2d6e2b3c51c..ee31eaf2c12 100644 --- a/internal/bundler/bundler.go +++ b/internal/bundler/bundler.go @@ -480,23 +480,33 @@ func parseFile(args parseArgs) { } } - // If "sourcesContent" isn't present, try filling it in using the file system - if sourceMap != nil && sourceMap.SourcesContent == nil && !args.options.ExcludeSourcesContent { - for _, source := range sourceMap.Sources { - var absPath string - if args.fs.IsAbs(source) { - absPath = source - } else if path.Namespace == "file" { - absPath = args.fs.Join(args.fs.Dir(path.Text), source) - } else { - sourceMap.SourcesContent = append(sourceMap.SourcesContent, sourcemap.SourceContent{}) - continue - } - var sourceContent sourcemap.SourceContent - if contents, err, _ := args.caches.FSCache.ReadFile(args.fs, absPath); err == nil { - sourceContent.Value = helpers.StringToUTF16(contents) + // If "sourcesContent" entries aren't present, try filling them in + // using the file system. This includes both generating the entire + // "sourcesContent" array if it's absent as well as filling in + // individual null entries in the array if the array is present. + if sourceMap != nil && !args.options.ExcludeSourcesContent { + // Make sure "sourcesContent" is big enough + if len(sourceMap.SourcesContent) < len(sourceMap.Sources) { + slice := make([]sourcemap.SourceContent, len(sourceMap.Sources)) + copy(slice, sourceMap.SourcesContent) + sourceMap.SourcesContent = slice + } + + // Attempt to fill in null entries using the file system + for i, source := range sourceMap.Sources { + if sourceMap.SourcesContent[i].Value == nil { + var absPath string + if args.fs.IsAbs(source) { + absPath = source + } else if path.Namespace == "file" { + absPath = args.fs.Join(args.fs.Dir(path.Text), source) + } else { + continue + } + if contents, err, _ := args.caches.FSCache.ReadFile(args.fs, absPath); err == nil { + sourceMap.SourcesContent[i].Value = helpers.StringToUTF16(contents) + } } - sourceMap.SourcesContent = append(sourceMap.SourcesContent, sourceContent) } } diff --git a/scripts/verify-source-map.js b/scripts/verify-source-map.js index da1becd831f..40b66f89752 100644 --- a/scripts/verify-source-map.js +++ b/scripts/verify-source-map.js @@ -402,6 +402,34 @@ const toSearchMissingSourcesContent = { bar: 'src/foo.ts', } +// The "null" should be filled in by the contents of "bar.ts" +const testCaseNullSourcesContent = { + 'entry.js': `import './foo.js'\n`, + 'foo.ts': `import './bar.ts'\nconsole.log("foo")`, + 'bar.ts': `console.log("bar")\n`, + 'foo.js': `(() => { + // bar.ts + console.log("bar"); + + // foo.ts + console.log("foo"); +})(); +//# sourceMappingURL=foo.js.map +`, + 'foo.js.map': `{ + "version": 3, + "sources": ["bar.ts", "foo.ts"], + "sourcesContent": [null, "import './bar.ts'\\nconsole.log(\\"foo\\")"], + "mappings": ";;AAAA,UAAQ,IAAI,KAAK;;;ACCjB,UAAQ,IAAI,KAAK;", + "names": [] +} +`, +} + +const toSearchNullSourcesContent = { + bar: 'bar.ts', +} + async function check(kind, testCase, toSearch, { ext, flags, entryPoints, crlf, followUpFlags = [] }) { let failed = 0 @@ -856,13 +884,21 @@ async function main() { crlf, }), - // Checks for loading missing sources content in nested source maps + // Checks for loading missing "sourcesContent" in nested source maps check('missing-sources-content' + suffix, testCaseMissingSourcesContent, toSearchMissingSourcesContent, { ext: 'js', flags: flags.concat('--outfile=out.js', '--bundle'), entryPoints: ['foo.js'], crlf, }), + + // Checks for null entries in "sourcesContent" in nested source maps + check('null-sources-content' + suffix, testCaseNullSourcesContent, toSearchNullSourcesContent, { + ext: 'js', + flags: flags.concat('--outfile=out.js', '--bundle'), + entryPoints: ['foo.js'], + crlf, + }), ) } }