Source Map

Stage 0: Strawman,

This version:
https://tc39.es/source-map/
Previous Versions:
Author:
Asumu Takikawa (Igalia)
Former Authors:
Victor Porof (Google)
John Lenz (Google)
Nick Fitzgerald (Mozilla)

Abstract

A specification for mapping transpiled source code (primarily JavaScript) back to the original sources. This specification is a living document and describes a hardened version of the Source Map v3 specification.

License

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

Introduction

This document is a draft version of a hardened version of the Source Map v3 specification. In its current form, it’s not a defined standard and is subject to modifications. If you want to get involved you will find more information under the following Specification Repository.

1. Background

The original source map format (v1) was created by Joseph Schorr for use by Closure Inspector to enable source-level debugging of optimized JavaScript code (although the format itself is language agnostic). However, as the size of the projects using the source maps expanded the verbosity of the format started to become a problem. The v2 [V2Format] was created by trading some simplicity and flexibility to reduce the overall size of the source map. Even with the changes made with the v2 version of the format, the source map file size was limiting its usefulness. The v3 format is based on suggestions made by Pavel Podivilov (Google).

This document codifies the prior art that is Source Map v3 but is more specific about the precise meanings of the specification.

2. Terminology

Generated Code is the code which is generated by the compiler or transpiler.

Original Source is the source code which has not been passed through the compiler.

Base64 VLQ: [VLQ] is a [base64] value, where the most significant bit (the 6th bit) is used as the continuation bit, and the "digits" are encoded into the string least significant first, and where the least significant bit of the first digit is used as the sign bit.

Note: The values that can be represented by the VLQ Base64 encoded are limited to 32-bit quantities until some use case for larger values is presented. This means that values exceeding 32-bits are invalid and implementations may reject them. The sign bit is counted towards the limit, but the continuation bits are not.

Source Mapping URL refers to the URL referencing the location of a source map from the Generated code.

Column is the zero-based indexed offset within a line of the generated code. The definition for columns in source maps can depend on the content type.

3. General Goals

The goals for the v3 format of Source Maps:

4. Source Map Format

The source map is a JSON document containing a top-level JSON object with the following structure:

{
  "version" : 3,
  "file": "out.js",
  "sourceRoot": "",
  "sources": ["foo.js", "bar.js"],
  "sourcesContent": [null, null],
  "names": ["src", "maps", "are", "fun"],
  "mappings": "A,AAAB;;ABCDE"
  "ignoreList": [0]
}

4.1. Mappings Structure

The mappings data is broken down as follows:

The fields in each segment are:

  1. The zero-based starting column of the line in the generated code that the segment represents. If this is the first field of the first segment, or the first segment following a new generated line (;), then this field holds the whole Base64 VLQ. Otherwise, this field contains a Base64 VLQ that is relative to the previous occurrence of this field. Note that this is different than the fields below because the previous value is reset after every generated line.

  2. If present, a zero-based index into the sources list. This field is a Base64 VLQ relative to the previous occurrence of this field, unless this is the first occurrence of this field, in which case the whole value is represented.

  3. If present, the zero-based starting line in the original source is represented. This field is a Base64 VLQ relative to the previous occurrence of this field, unless this is the first occurrence of this field, in which case the whole value is represented. Always present if there is a source field.

  4. If present, the zero-based starting column of the line in the source represented. This field is a Base64 VLQ relative to the previous occurrence of this field unless this is the first occurrence of this field, in which case the whole value is represented. Always present if there is a source field.

  5. If present, the zero-based index into the names list associated with this segment. This field is a base 64 VLQ relative to the previous occurrence of this field unless this is the first occurrence of this field, in which case the whole value is represented.

Note: The purpose of this encoding is to reduce the source map size. VLQ encoding reduced source maps by 50% relative to the [V2Format] in tests performed using Google Calendar.

Note: Segments with one field are intended to represent generated code that is unmapped because there is no corresponding original source code, such as code that is generated by a compiler. Segments with four fields represent mapped code where a corresponding name does not exist. Segments with five fields represent mapped code that also has a mapped name.

Note: Using file offsets was considered but rejected in favor of using line/column data to avoid becoming misaligned with the original due to platform-specific line endings.

4.2. Resolving Sources

If the sources are not absolute URLs after prepending the sourceRoot, the sources are resolved relative to the SourceMap (like resolving the script src attribute in an HTML document).

4.3. Extensions

Source map consumers must ignore any additional unrecognized properties, rather than causing the source map to be rejected, so that additional features can be added to this format without breaking existing users.

5. Index Map

To support concatenating generated code and other common post-processing, an alternate representation of a map is supported:

{
  "version" : 3,
  "file": "app.js",
  "sections": [
    {
      "offset": {"line": 0, "column": 0},
      "map": {
        "version" : 3,
        "file": "section.js",
        "sources": ["foo.js", "bar.js"],
        "names": ["src", "maps", "are", "fun"],
        "mappings": "AAAA,E;;ABCDE"
      }
    },
    {
      "offset": {"line": 100, "column": 10},
      "map": {
        "version" : 3,
        "file": "another_section.js",
        "sources": ["more.js"],
        "names": ["more", "is", "better"],
        "mappings": "AAAA,E;AACA,C;ABCDE"
      }
    }
  ]
}

The index map follows the form of the standard map. Like the regular source map, the file format is JSON with a top-level object. It shares the version and file field from the regular source map, but gains a new sections field.

sections is an array of Section objects.

5.1. Section

Section objects have the following fields:

The sections must be sorted by starting position and the represented sections must not overlap.

6. Conventions

The following conventions should be followed when working with source maps or when generating them.

6.1. Source Map Naming

Commonly, a source map will have the same name as the generated file but with a .map extension. For example, for page.js a source map named page.js.map would be generated.

6.2. Linking generated code to source maps

While the source map format is intended to be language and platform agnostic, it is useful to have some conventions for the expected use-case of web server-hosted JavaScript.

There are two suggested ways to link source maps to the output. The first requires server support in order to add an HTTP header and the second requires an annotation in the source.

Source maps are linked through URLs as defined in [URL]; in particular, characters outside the set permitted to appear in URIs must be percent-encoded and it may be a data URI. Using a data URI along with sourcesContent allows for a completely self-contained source map.

The HTTP sourcemap header has precedence over a source annotation, and if both are present, the header URL should be used to resolve the source map file.

Regardless of the method used to retrieve the Source Mapping URL the same process is used to resolve it, which is as follows:

When the Source Mapping URL is not absolute, then it is relative to the generated code’s source origin. The source origin is determined by one of the following cases:

6.2.1. Linking through HTTP headers

If a file is served through HTTP(S) with a sourcemap header, the value of the header is the URL of the linked source map.

sourcemap: <url>

Note: Previous revisions of this document recommended a header name of x-sourcemap. This is now deprecated; sourcemap is now expected.

6.2.2. Linking through inline annotations

The generated code should include a comment, or the equivalent construct depending on its language or format, named sourceMappingURL and that contains the URL of the source map. This specification defines how the comment should look like for JavaScript, CSS, and WebAssembly. Other languages should follow a similar convention.

For a given language there can be multiple ways of detecting the sourceMappingURL comment, to allow for different implementations to choose what is less complex for them. The generated code unambiguously links to a source map if the result of all the extraction methods is the same.

If a tool consumes one or more source files that unambiguously links to a source map and it produces an output file that links to a source map, it must do so unambiguously.

The following JavaScript code links to a source map, but it does not do so unambiguously:
let a = `
//# sourceMappingURL=foo.js.map
//`;

Extracing a Source Map URL from it through parsing gives null, while without parsing gives foo.js.map.

6.2.2.1. Extraction methods for JavaScript sources

To extract a Source Map URL from JavaScript through parsing a string source, run the following steps:

  1. Let tokens be the list of tokens obtained by parsing source according to [ECMA-262].

  2. For each token in tokens, in reverse order:

    1. If token is not a single-line comment or a multi-line comment, return null.

    2. Let comment be the content of token.

    3. If matching a Source Map URL in comment returns a string, return it.

  3. Return null.

To extract a Source Map URL from JavaScript without parsing a string source, run the following steps:

  1. Let lines be the result of strictly splitting source on ECMAScript line terminator code points.

  2. Let lastURL be null.

  3. For each line in lines:

    1. Let position be a position variable for line, initially pointing at the start of line.

    2. While position doesn’t point past the end of line:

      1. Collect a sequence of code points that are ECMAScript white space code points from line given position.

        NOTE: The collected code points are not used, but position is still updated.

      2. If position points past the end of line, break.

      3. Let first be the code point of line at position.

      4. Increment position by 1.

      5. If first is U+002F (/) and position does not point past the end of line, then:

        1. Let second be the code point of line at position.

        2. Increment position by 1.

        3. If second is U+002F (/), then:

          1. Let comment be the code point substring from position to the end of line.

          2. If matching a Source Map URL in comment returns a string, set lastURL to it.

          3. Break.

        4. Else if second is U+002A (*), then:

          1. Let comment be the empty string.

          2. While position + 1 doesn’t point past the end of line:

            1. Let c1 be the code point of line at position.

            2. Increment position by 1.

            3. Let c2 be the code point of line at position.

            4. If c1 is U+002A (*) and c2 is U+002F (/), then:

              1. If matching a Source Map URL in comment returns a string, set lastURL to it.

              2. Increment position by 1.

            5. Append c1 to comment.

        5. Else, set lastURL to null.

      6. Else, set lastURL to null.

      Note: We reset lastURL to null whenever we find a non-comment code character.

  4. Return lastURL.

NOTE: The algorithm above has been designed so that the source lines can be iterated in reverse order, returning early after scanning through a line that contains a sourceMappingURL comment.

Note: The algorithm above is equivalent to the following JavaScript implementation:
const JS_NEWLINE = /^/m;

// This RegExp will always match one of the following:
// - single-line comments
// - "single-line" multi-line comments
// - unclosed multi-line comments
// - just trailing whitespaces
// - a code character
// The loop below differentiates between all these cases.
const JS_COMMENT =
  /\s*(?:\/\/(?<single>.*)|\/\*(?<multi>.*?)\*\/|\/\*.*|$|(?<code>[^\/]+))/uym;

const PATTERN = /^[@#]\s*sourceMappingURL=(\S*?)\s*$/;

let lastURL = null;
for (const line of source.split(JS_NEWLINE)) {
  JS_COMMENT.lastIndex = 0;
  while (JS_COMMENT.lastIndex < line.length) {
    let commentMatch = JS_COMMENT.exec(line).groups;
    let comment = commentMatch.single ?? commentMatch.multi;
    if (comment != null) {
      let match = PATTERN.exec(comment);
      if (match !== null) lastURL = match[1];
    } else if (commentMatch.code != null) {
      lastURL = null;
    } else {
      // We found either trailing whitespaces or an unclosed comment.
      // Assert: JS_COMMENT.lastIndex === line.length
    }
  }
}
return lastURL;

To match a Source Map URL in a comment comment (a string), run the following steps:

  1. Let pattern be the regular expression /^[@#]\s*sourceMappingURL=(\S*?)\s*$/.

  2. Let match be ! RegExpBuiltInExec(pattern, comment).

  3. If match is not null, return match[1].

  4. Return null.

Note: The prefix for this annotation was initially //@ however this conflicts with Internet Explorer’s Conditional Compilation and was changed to //#.

Source map generators must only emit //# while source map consumers must accept both //@ and //#.

6.2.2.2. Extraction methods for CSS sources

Extracting source mapping URLs from CSS is similar to JavaScript, with the exception that CSS only supports /* ... */-style comments.

6.2.2.3. Extraction methods for WebAssembly binaries

To extract a Source Map URL from a WebAssembly source given a byte sequence bytes, run the following steps:

  1. Let module be module_decode(bytes).

  2. If module is error, return null.

  3. For each custom section customSection of module,

    1. Let name be the name of customSection, decoded as UTF-8.

    2. If name is "sourceMappingURL", then:

      1. Let value be the bytes of customSection, decoded as UTF-8.

      2. If value is failure, return null.

      3. Return value.

  4. Return null.

Since WebAssembly is not a textual format and it does not support comments, it supports a single unambiguous extraction method. The URL is encoded using [WasmNamesBinaryFormat], and it’s placed as the content of the custom section. It is invalid for tools that generate WebAssembly code to generate two or more custom sections with the "sourceMappingURL" name.

6.3. Linking eval’d code to named generated code

There is an existing convention that should be supported for the use of source maps with eval’d code, it has the following form:

//# sourceURL=foo.js

It is described in [EvalSourceURL].

7. Language Neutral Stack Mapping Notes

Stack tracing mapping without knowledge of the source language is not covered by this document.

8. Multi-level Mapping Notes

It is getting more common to have tools generate sources from some DSL (templates) or compile TypeScript -> JavaScript -> minified JavaScript, resulting in multiple translations before the final source map is created. This problem can be handled in one of two ways. The easy but lossy way is to ignore the intermediate steps in the process for the purposes of debugging, the source location information from the translation is either ignored (the intermediate translation is considered the “Original Source”) or the source location information is carried through (the intermediate translation hidden). The more complete way is to support multiple levels of mapping: if the Original Source also has a source map reference, the user is given the choice of using that as well.

However, It is unclear what a "source map reference" looks like in anything other than JavaScript. More specifically, what a source map reference looks like in a language that doesn’t support JavaScript-style single-line comments.

9. Fetching Source Maps

To fetch a source map given a URL url, run the following steps:

  1. Let promise be a new promise.

  2. Let request be a new request whose URL is url.

  3. Fetch request with processResponseConsumeBody set to the following steps given response response and null, failure, or a byte sequence bodyBytes:

    1. If bodyBytes is null or failure, reject promise with a TypeError and abort these steps.

    2. If url’s scheme is an HTTP(S) scheme and bodyBytes starts with `)]}'`, then:

      1. While bodyBytes’s length is not 0 and bodyBytes’s 0th byte is not an HTTP newline byte:

        1. remove the 0th byte from bodyBytes.

        Note: For historic reasons, when delivering source maps over HTTP(S), servers may prepend a line starting with the string )]}' to the source map.
        )]}'garbage here
        {"version": 3, ...}
        

        is interpreted as

        {"version": 3, ...}
        
    3. Let sourceMap be the result of parsing JSON bytes to a JavaScript value given bodyBytes.

    4. If the previous step threw an error, reject promise with that error.

    5. Otherwise, resolve promise with sourceMap.

  4. Return promise.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[ENCODING]
Anne van Kesteren. Encoding Standard. Living Standard. URL: https://encoding.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[URL]
URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

Informative References

[BASE64]
The Base16, Base32, and Base64 Data Encodings. Standards Track. URL: https://www.ietf.org/rfc/rfc4648.txt
[ECMA-262]
ECMAScript® Language Specification. Standards Track. URL: https://tc39.es/ecma262/
[EvalSourceURL]
Give your eval a name with //@ sourceURL. archive. URL: https://web.archive.org/web/20120814122523/http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/
[V2Format]
Source Map Revision 2 Proposal. URL: https://docs.google.com/document/d/1xi12LrcqjqIHTtZzrzZKmQ3lbTv9mKrN076UB-j3UZQ/edit?hl=en_US
[VLQ]
Variable-length quantity. reference article. URL: https://en.wikipedia.org/wiki/Variable-length_quantity
[WasmNamesBinaryFormat]
WebAssembly Names binary format. Living Standard. URL: https://www.w3.org/TR/wasm-core-2/#names%E2%91%A2