Skip to content

Commit

Permalink
fix #650: add "--sourcemap=both"
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jan 7, 2021
1 parent 90cb795 commit e220328
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 14 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

In that case, transforming the code by inverting the condition and moving the following statements inside the branch is not valid because the function is no longer hoisted to above the branch condition. This release fixes the regression by avoiding this optimization in cases like this.

* Add the option `--sourcemap=both` ([#650](https://github.com/evanw/esbuild/issues/650))

This new option puts the generated source map both an inline `//# sourceMappingURL=` data URL comment inside the output file and in an external file next to the output file. Using it is also possible with the transform API, which will cause it to return both an inline data URL comment in the `code` value and the source map JSON in the `map` value.

* Tree-shake unused code with `--format=iife` ([#639](https://github.com/evanw/esbuild/issues/639))

When the output format is IIFE (which wraps the code in an immediately-invoked function expression), esbuild now assumes that it's safe to remove unused code. This is an assumption that esbuild always makes when bundling but that esbuild previously didn't make when not bundling. Now esbuild will remove code even when not bundling as long as the output format is IIFE.
Expand Down
18 changes: 15 additions & 3 deletions internal/bundler/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -3820,15 +3820,27 @@ func (repr *chunkReprJS) generate(c *linkerContext, chunk *chunkInfo) func([]ast

if c.options.SourceMap != config.SourceMapNone {
sourceMap := c.generateSourceMapForChunk(compileResultsForSourceMap, chunkAbsDir, dataForSourceMaps)

// Store the generated source map
var writeDataURL bool
var writeFile bool
switch c.options.SourceMap {
case config.SourceMapInline:
writeDataURL = true
case config.SourceMapLinkedWithComment, config.SourceMapExternalWithoutComment:
writeFile = true
case config.SourceMapInlineAndExternal:
writeDataURL = true
writeFile = true
}

// Write the generated source map as an inline comment
if writeDataURL {
j.AddString("//# sourceMappingURL=data:application/json;base64,")
j.AddString(base64.StdEncoding.EncodeToString(sourceMap))
j.AddString("\n")
}

case config.SourceMapLinkedWithComment, config.SourceMapExternalWithoutComment:
// Write the generated source map as an external file
if writeFile {
// Optionally add metadata about the file
var jsonMetadataChunk []byte
if c.options.AbsMetadataFile != "" {
Expand Down
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const (
SourceMapInline
SourceMapLinkedWithComment
SourceMapExternalWithoutComment
SourceMapInlineAndExternal
)

type Loader int
Expand Down
2 changes: 1 addition & 1 deletion lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export type Charset = 'ascii' | 'utf8';
export type TreeShaking = true | 'ignore-annotations';

interface CommonOptions {
sourcemap?: boolean | 'inline' | 'external';
sourcemap?: boolean | 'inline' | 'external' | 'both';
sourcesContent?: boolean;

format?: Format;
Expand Down
1 change: 1 addition & 0 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const (
SourceMapInline
SourceMapLinked
SourceMapExternal
SourceMapInlineAndExternal
)

type SourcesContent uint8
Expand Down
2 changes: 2 additions & 0 deletions pkg/api/api_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ func validateSourceMap(value SourceMap) config.SourceMap {
return config.SourceMapInline
case SourceMapExternal:
return config.SourceMapExternalWithoutComment
case SourceMapInlineAndExternal:
return config.SourceMapInlineAndExternal
default:
panic("Invalid source map")
}
Expand Down
24 changes: 14 additions & 10 deletions pkg/cli/cli_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,19 +127,23 @@ func parseOptionsImpl(osArgs []string, buildOpts *api.BuildOptions, transformOpt
}
hasBareSourceMapFlag = true

case arg == "--sourcemap=external":
if buildOpts != nil {
buildOpts.Sourcemap = api.SourceMapExternal
} else {
transformOpts.Sourcemap = api.SourceMapExternal
case strings.HasPrefix(arg, "--sourcemap="):
value := arg[len("--sourcemap="):]
var sourcemap api.SourceMap
switch value {
case "inline":
sourcemap = api.SourceMapInline
case "external":
sourcemap = api.SourceMapExternal
case "both":
sourcemap = api.SourceMapInlineAndExternal
default:
return fmt.Errorf("Invalid sourcemap: %q (valid: inline, external, both)", value)
}
hasBareSourceMapFlag = false

case arg == "--sourcemap=inline":
if buildOpts != nil {
buildOpts.Sourcemap = api.SourceMapInline
buildOpts.Sourcemap = sourcemap
} else {
transformOpts.Sourcemap = api.SourceMapInline
transformOpts.Sourcemap = sourcemap
}
hasBareSourceMapFlag = false

Expand Down
26 changes: 26 additions & 0 deletions scripts/js-api-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,24 @@ let buildTests = {
assert.strictEqual(json.sourcesContent[0], content)
},

async sourceMapBoth({ esbuild, testDir }) {
const input = path.join(testDir, 'in.js')
const output = path.join(testDir, 'out.js')
const content = 'exports.foo = 123'
await writeFileAsync(input, content)
await esbuild.build({ entryPoints: [input], outfile: output, sourcemap: 'both' })
const result = require(output)
assert.strictEqual(result.foo, 123)
const outputFile = await readFileAsync(output, 'utf8')
const match = /\/\/# sourceMappingURL=data:application\/json;base64,(.*)/.exec(outputFile)
const json = JSON.parse(Buffer.from(match[1], 'base64').toString())
assert.strictEqual(json.version, 3)
assert.strictEqual(json.sources[0], path.basename(input))
assert.strictEqual(json.sourcesContent[0], content)
const outputFileMap = await readFileAsync(output + '.map', 'utf8')
assert.strictEqual(Buffer.from(match[1], 'base64').toString(), outputFileMap)
},

async sourceMapIncludeSourcesContent({ esbuild, testDir }) {
const input = path.join(testDir, 'in.js')
const output = path.join(testDir, 'out.js')
Expand Down Expand Up @@ -2010,6 +2028,14 @@ let transformTests = {
await assertSourceMap(Buffer.from(base64.trim(), 'base64').toString(), 'afile.js')
},

async sourceMapBothWithName({ service }) {
const { code, map } = await service.transform(`let x`, { sourcemap: 'both', sourcefile: 'afile.js' })
assert(code.startsWith(`let x;\n//# sourceMappingURL=`))
await assertSourceMap(map, 'afile.js')
const base64 = code.slice(code.indexOf('base64,') + 'base64,'.length)
await assertSourceMap(Buffer.from(base64.trim(), 'base64').toString(), 'afile.js')
},

async numericLiteralPrinting({ service }) {
async function checkLiteral(text) {
const { code } = await service.transform(`return ${text}`, { minify: true })
Expand Down

0 comments on commit e220328

Please sign in to comment.