Skip to content

Commit

Permalink
Add Source Map sourceRoot Support
Browse files Browse the repository at this point in the history
This adds the `--sourcemap-root`, `sourcemapRoot` (JS), and `SourcemapRoot` (Go) options to insert a `sourceRoot` field in any output source maps. When a `sourceRoot` exists on the map, any sources are resolved relative to it.

 This is particularly useful when hosting compiled code on a server, and pointing the source files to a GitHub repo. Eg, as [AMP does](https://cdn.ampproject.org/v0.mjs.map).
  • Loading branch information
jridgewell committed Mar 21, 2021
1 parent 512eb19 commit f45c1e4
Show file tree
Hide file tree
Showing 9 changed files with 52 additions and 0 deletions.
2 changes: 2 additions & 0 deletions cmd/esbuild/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ var helpText = func(colors logger.Colors) string {
--sourcefile=... Set the source file for the source map (for stdin)
--sourcemap=external Do not link to the source map with a comment
--sourcemap=inline Emit the source map with an inline data URL
--sourcemap-root=... The root path from which source files are looked up
in a source map.
--sources-content=false Omit "sourcesContent" in generated source maps
--tree-shaking=... Set to "ignore-annotations" to work with packages
that have incorrect tree-shaking annotations
Expand Down
5 changes: 5 additions & 0 deletions internal/bundler/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -4841,6 +4841,11 @@ func (c *linkerContext) generateSourceMapForChunk(
}
j.AddString("]")

if c.options.SourceMapRoot != "" {
j.AddString(",\n \"sourceRoot\": ")
j.AddBytes(js_printer.QuoteForJSON(c.options.SourceMapRoot, c.options.ASCIIOnly))
}

// Write the sourcesContent
if !c.options.ExcludeSourcesContent {
j.AddString(",\n \"sourcesContent\": [")
Expand Down
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ type Options struct {
NeedsMetafile bool

SourceMap SourceMap
SourceMapRoot string
ExcludeSourcesContent bool

Stdin *StdinInfo
Expand Down
2 changes: 2 additions & 0 deletions lib/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ function pushLogFlags(flags: string[], options: CommonOptions, keys: OptionKeys,
}

function pushCommonFlags(flags: string[], options: CommonOptions, keys: OptionKeys): void {
let sourcemapRoot = getFlag(options, keys, 'sourcemapRoot', mustBeString);
let sourcesContent = getFlag(options, keys, 'sourcesContent', mustBeBoolean);
let target = getFlag(options, keys, 'target', mustBeStringOrArray);
let format = getFlag(options, keys, 'format', mustBeString);
Expand All @@ -110,6 +111,7 @@ function pushCommonFlags(flags: string[], options: CommonOptions, keys: OptionKe
let pure = getFlag(options, keys, 'pure', mustBeArray);
let keepNames = getFlag(options, keys, 'keepNames', mustBeBoolean);

if (sourcemapRoot !== void 0) flags.push(`--sourcemap-root=${sourcemapRoot}`);
if (sourcesContent !== void 0) flags.push(`--sources-content=${sourcesContent}`);
if (target) {
if (Array.isArray(target)) flags.push(`--target=${Array.from(target).map(validateTarget).join(',')}`)
Expand Down
1 change: 1 addition & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type TreeShaking = true | 'ignore-annotations';

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

format?: Format;
Expand Down
2 changes: 2 additions & 0 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ type BuildOptions struct {
LogLevel LogLevel

Sourcemap SourceMap
SourcemapRoot string
SourcesContent SourcesContent

Target Target
Expand Down Expand Up @@ -318,6 +319,7 @@ type TransformOptions struct {
LogLevel LogLevel

Sourcemap SourceMap
SourcemapRoot string
SourcesContent SourcesContent

Target Target
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 @@ -737,6 +737,7 @@ func rebuildImpl(
InjectedDefines: injectedDefines,
Platform: validatePlatform(buildOpts.Platform),
SourceMap: validateSourceMap(buildOpts.Sourcemap),
SourceMapRoot: buildOpts.SourcemapRoot,
ExcludeSourcesContent: buildOpts.SourcesContent == SourcesContentExclude,
MangleSyntax: buildOpts.MinifySyntax,
RemoveWhitespace: buildOpts.MinifyWhitespace,
Expand Down Expand Up @@ -1188,6 +1189,7 @@ func transformImpl(input string, transformOpts TransformOptions) TransformResult
Defines: defines,
InjectedDefines: injectedDefines,
SourceMap: validateSourceMap(transformOpts.Sourcemap),
SourceMapRoot: transformOpts.SourcemapRoot,
ExcludeSourcesContent: transformOpts.SourcesContent == SourcesContentExclude,
OutputFormat: validateFormat(transformOpts.Format),
GlobalName: validateGlobalName(log, transformOpts.GlobalName),
Expand Down
8 changes: 8 additions & 0 deletions pkg/cli/cli_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,14 @@ func parseOptionsImpl(
}
hasBareSourceMapFlag = false

case strings.HasPrefix(arg, "--sourcemap-root="):
sourceRoot := arg[len("--sourcemap-root="):]
if buildOpts != nil {
buildOpts.SourcemapRoot = sourceRoot
} else {
transformOpts.SourcemapRoot = sourceRoot
}

case strings.HasPrefix(arg, "--sources-content="):
value := arg[len("--sources-content="):]
var sourcesContent api.SourcesContent
Expand Down
29 changes: 29 additions & 0 deletions scripts/js-api-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,29 @@ let buildTests = {
assert.strictEqual(json.sourcesContent, void 0)
},

async sourceMapSourceRoot({ 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: true,
sourcemapRoot: 'https://example.com/'
})
const result = require(output)
assert.strictEqual(result.foo, 123)
const outputFile = await readFileAsync(output, 'utf8')
const match = /\/\/# sourceMappingURL=(.*)/.exec(outputFile)
assert.strictEqual(match[1], 'out.js.map')
const resultMap = await readFileAsync(output + '.map', 'utf8')
const json = JSON.parse(resultMap)
assert.strictEqual(json.version, 3)
assert.strictEqual(json.sources[0], path.basename(input))
assert.strictEqual(json.sourceRoot, 'https://example.com/')
},

async sourceMapWithDisabledFile({ esbuild, testDir }) {
const input = path.join(testDir, 'in.js')
const disabled = path.join(testDir, 'disabled.js')
Expand Down Expand Up @@ -3152,6 +3175,12 @@ let transformTests = {
await assertSourceMap(Buffer.from(base64.trim(), 'base64').toString(), 'afile.js')
},

async sourceMapRoot({ esbuild }) {
const { code, map } = await esbuild.transform(`let x`, { sourcemap: true, sourcefile: 'afile.js', sourcemapRoot: "https://example.com/" })
assert.strictEqual(code, `let x;\n`)
assert.strictEqual(JSON.parse(map).sourceRoot, 'https://example.com/');
},

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

0 comments on commit f45c1e4

Please sign in to comment.