title: Source map format specification
shortname: ECMA-426
status: draft
location: https://tc39.es/ecma426/
boilerplate:
copyright: alternative

Introduction
This Ecma Standard defines the Source map format, used for mapping transpiled source code back to the original sources.
The source map format has the following goals:
- Support source-level debugging allowing bidirectional mapping
- Support server-side stack trace deobfuscation
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 source maps expanded, the verbosity of the format started to become a problem. The v2 format (Source Map Revision 2 Proposal) 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).
The source map format does not have version numbers anymore, and it is instead hard-coded to always be “3”.
In 2023-2024, the source map format was developed into a more precise Ecma standard, with significant contributions from many people. Further iteration on the source map format is expected to come from TC39-TG4.
Asumu Takikawa, Nicolò Ribaudo, Jon Kuperman
ECMA-426, 1st edition, Project Editors
Scope
This Standard defines the source map format, used by different types of developer tools to improve the debugging experience of code compiled to JavaScript, WebAssembly, and CSS.
Conformance
A conforming source map document is a JSON document that conforms to the structure detailed in this specification.
A conforming source map generator should generate documents which are conforming source map documents, and can be decoded by the algorithms in this specification without reporting any errors (even those which are specified as optional).
A conforming source map consumer should implement the algorithms specified in this specification for retrieving (where applicable) and decoding source map documents. A conforming consumer is permitted to ignore errors or report them without terminating where the specification indicates that an algorithm may optionally report an error.
References
The following documents are referred to in the text in such a way that some or all of their content constitutes requirements of this document. For dated references, only the edition cited applies. For undated references, the latest edition of the referenced document (including any amendments) applies.
Normative References
ECMA-262, ECMAScript® Language Specification.
https://tc39.es/ecma262/
ECMA-404, The JSON Data Interchange Format.
https://www.ecma-international.org/publications-and-standards/standards/ecma-404/
Informative References
IETF RFC 4648, The Base16, Base32, and Base64 Data Encodings.
https://datatracker.ietf.org/doc/html/rfc4648
WebAssembly Core Specification.
https://www.w3.org/TR/wasm-core-2/
WHATWG Encoding.
https://encoding.spec.whatwg.org/
WHATWG Fetch.
https://fetch.spec.whatwg.org/
WHATWG Infra.
https://infra.spec.whatwg.org/
WHATWG URL.
https://url.spec.whatwg.org/
Notational Conventions
This specification follows the same notational conventions as defined by ECMA-262 (Notational conventions), with the extensions defined in this section.
Algorithm Conventions
Implicit Completions
All abstract operations declared in this specification are implicitly assumed to either return a normal completion containing the algorithm's declared return type, or a throw completion. For example, an abstract operation declared as
GetTheAnswer (
_input_: an integer,
): an integer
is equivalent to:
GetTheAnswer2 (
_input_: an integer,
): either a normal completion containing an integer or a throw completion
All calls to abstract operations that return completion records are implicitly assumed to be wrapped by ECMA-262's `?` shorthand for unwrapping completion records, unless they are wrapped by an explicit Completion call. For example:
1. Let _result_ be GetTheAnswer(_value_).
1. Let _second_ be Completion(GetTheAnswer(_value_)).
is equivalent to:
1. Let _result_ be ? GetTheAnswer(_value_).
1. Let _second_ be Completion(GetTheAnswer(_value_)).
Optional Errors
Whenever an algorithm is to optionally report an error, an implementation may choose one of the following behaviours:
- Continue executing the rest of the algorithm.
- Report an error to the user (for example, in the browser console), and continue executing the rest of the algorithm.
- Return a ThrowCompletion.
An implementation can choose different behaviours for different optional errors.
Grammar Notation
This specification follows the same grammar notation convention as defined by ECMA-262 (Grammar Notation), with the following caveats:
Terms and Definitions
For the purposes of this document, the following terms and definitions apply.
- generated code
-
code which is generated by the compiler or transpiler.
- original source
-
source code which has not been passed through a compiler or transpiler.
- source map URL
-
URL referencing the location of a source map from the generated code.
- column
-
zero-based indexed offset within a line of the generated code, computed as UTF-16 code units for JavaScript and CSS source maps, and as byte indices in the binary content (represented as a single line) for WebAssembly source maps.
That means that “A” (`LATIN CAPITAL LETTER A`) measures as 1 code unit, and “🔥” (`FIRE`) measures as 2 code units. Source maps for other content types may diverge from this.
base64 VLQ
A base64 VLQ is a base64-encoded variable-length quantity, 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.
The values that can be represented by the base64 VLQ encoding 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.
The string `"iB"` represents a base64 VLQ with two digits. The first digit `"i"` encodes the bit pattern `0b100010`, which has a continuation bit of `1` (the VLQ continues), a sign bit of `0` (non-negative), and the value bits `0b0001`. The second digit `B` encodes the bit pattern `0b000001`, which has a continuation bit of `0`, no sign bit, and value bits `0b00001`. The decoding of this VLQ string is the number 17.
The string `"V"` represents a base64 VLQ with one digit. The digit `"V"` encodes the bit pattern `0b010101`, which has a continuation bit of `0` (no continuation), a sign bit of `1` (negative), and the value bits `0b1010`. The decoding of this VLQ string is the number -10.
A base64 VLQ adheres to the following lexical grammar:
Vlq ::
VlqDigitList
VlqDigitList ::
TerminalDigit
ContinuationDigit VlqDigitList
TerminalDigit ::
`A` `B` `C` `D` `E` `F` `G` `H` `I` `J` `K` `L` `M` `N` `O` `P` `Q` `R`
`S` `T` `U` `V` `W` `X` `Y` `Z` `a` `b` `c` `d` `e` `f`
ContinuationDigit ::
`g` `h` `i` `j` `k` `l` `m` `n` `o` `p` `q` `r` `s` `t` `u` `v` `w` `x`
`y` `z` `0` `1` `2` `3` `4` `5` `6` `7` `8` `9` `+` `/`
VLQSignedValue ( ): an integer
Vlq :: VlqDigitList
1. Let _unsigned_ be the VLQUnsignedValue of |VlqDigitList|.
1. If _unsigned_ modulo 2 = 1, let _sign_ be -1.
1. Else, let _sign_ be 1.
1. Let _value_ be floor(_unsigned_ / 2).
1. If _value_ is 0 and _sign_ is -1, return -231.
1. [id="step-VLQSignedValue-boundary-check"] If _value_ is ≥ 231, throw an error.
1. Return _sign_ × _value_.
The check in step is needed because _unsigned_ is the VLQUnsignedValue of |VlqDigitList|, not of |Vlq|.
VLQUnsignedValue ( ): an non-negative integer
Vlq :: VlqDigitList
1. Let _value_ be the VLQUnsignedValue of |VlqDigitList|.
1. If _value_ is ≥ 232, throw an error.
1. Return _value_.
VlqDigitList ::
ContinuationDigit VlqDigitList
1. Let _left_ be the VLQUnsignedValue of |ContinuationDigit|.
1. Let _right_ be the VLQUnsignedValue of |VlqDigitList|.
1. Return _left_ + _right_ × 25.
TerminalDigit ::
`A` `B` `C` `D` `E` `F` `G` `H` `I` `J` `K` `L` `M` `N` `O` `P` `Q` `R`
`S` `T` `U` `V` `W` `X` `Y` `Z` `a` `b` `c` `d` `e` `f`
1. Let _digit_ be the character matched by this production.
1. Let _value_ be the integer corresponding to _digit_, according to the base64 encoding as defined by IETF RFC 4648.
1. Assert: _value_ < 32.
1. Return _value_.
ContinuationDigit ::
`g` `h` `i` `j` `k` `l` `m` `n` `o` `p` `q` `r` `s` `t` `u` `v` `w` `x`
`y` `z` `0` `1` `2` `3` `4` `5` `6` `7` `8` `9` `+` `/`
1. Let _digit_ be the character matched by this production.
1. Let _value_ be the integer corresponding to _digit_, according to the base64 encoding as defined by IETF RFC 4648.
1. Assert: 32 ≤ _value_ < 64.
1. Return _value_ - 32.
JSON values utilities
While this specification's algorithms are defined on top of ECMA-262 internals, it is meant to be easily implementable by non-JavaScript platforms. This section contains utilities for working with JSON values, abstracting away ECMA-262 details from the rest of the document.
A JSON value is either a JSON object, a JSON array, a String, a Number, a Boolean, or *null*.
A JSON object is an Object such that each of its properties:
- is a data property,
- has a String key,
- has JSON value value.
A JSON array is a JSON object such that:
- it has a property whose key is *"length"* and whose value is a Number,
- all its other properties' keys are integer indices.
ParseJSON (
_string_: a String,
): a JSON value
1. Let _result_ be Call(%JSON.parse%, *null*, « _string_ »).
1. Assert: _result_ is a JSON value.
1. Return _result_.
This abstract operation is in the process of being exposed by ECMA-262 itself, at tc39/ecma262#3540.
JSONObjectGet (
_object_: a JSON object,
_key_: a String,
): a JSON value or ~missing~
1. If _object_ does not have an own property with key _key_, return ~missing~.
1. Let _prop_ be _object_'s own property whose key is _key_.
1. Return _prop_'s [[Value]] attribute.
JSONArrayIterate (
_array_: a JSON array,
): a List of JSON values
1. Let _length_ be JSONObjectGet(_array_, *"length"*).
1. Assert: _length_ is a non-negative integral Number.
1. Let _list_ be a new empty List.
1. Let _i_ be 0.
1. Repeat, while _i_ < ℝ(_length_),
1. Let _value_ be JSONObjectGet(_array_, ToString(𝔽(_i_))).
1. Assert: _value_ is not ~missing~.
1. Append _value_ to _list_.
1. Set _i_ to _i_ + 1.
1. Return _list_.
StringSplit (
_string_: a String,
_separators_: a List of non-empty Strings,
): a List of Strings
1. Let _parts_ be a new empty List.
1. Let _strLen_ be the length of _string_.
1. Let _lastStart_ be 0.
1. Let _i_ be 0.
1. Repeat, while _i_ < _strLen_,
1. Let _matched_ be *false*.
1. For each String _sep_ of _separators_, do
1. Let _sepLen_ be the length of _sep_.
1. Let _candidate_ be the substring of _string_ from _i_ to min(_i_ + _sepLen_, _strLen_).
1. If _candidate_ = _sep_ and _matched_ is *false*, then
1. Let _chunk_ be the substring of _string_ from _lastStart_ to _i_.
1. Append _chunk_ to _parts_.
1. Set _lastStart_ to _i_ + _sepLen_.
1. Set _i_ to _i_ + _sepLen_.
1. Set _matched_ to *true*.
1. If _matched_ is *false*, set _i_ to _i_ + 1.
1. Let _chunk_ be the substring of _string_ from _lastStart_ to _strLen_.
1. Append _chunk_ to _parts_.
1. Return _parts_.
Position types
Position Record
A Position Record is a tuple of a non-negative line and non-negative column number:
| Field Name |
Value Type |
| [[Line]] |
a non-negative integral Number |
| [[Column]] |
a non-negative integral Number |
Original Position Record
A Original Position Record is a tuple of a Decoded Source Record, a non-negative line and non-negative column number. It is similar to a Position Record but describes a source position in a concrete original source file.
| Field Name |
Value Type |
| [[Source]] |
a Decoded Source Record |
| [[Line]] |
a non-negative integral Number |
| [[Column]] |
a non-negative integral Number |
ComparePositions (
_first_: a Position Record or a Original Position Record,
_second_: a Position Record or a Original Position Record,
): ~lesser~, ~equal~ or ~greater~
1. If _first_.[[Line]] < _second_.[[Line]], return ~lesser~.
1. If _first_.[[Line]] > _second_.[[Line]], return ~greater~.
1. Assert: _first_.[[Line]] is equal to _second_.[[Line]].
1. If _first_.[[Column]] < _second_.[[Column]], return ~lesser~.
1. If _first_.[[Column]] > _second_.[[Column]], return ~greater~.
1. Return ~equal~.
Source map format
A 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]
}
- The
version field shall always be the number 3 as an integer. The source map may be rejected if the field has any other value.
- The
file field is an optional name of the generated code that this source map is associated with. It's not specified if this can be an URL, relative path name, or just a base name. Source map generators may choose the appropriate interpretation for their contexts of use.
- The
sourceRoot field is an optional source root string, used for relocating source files on a server or removing repeated values in the sources entry. This value is prepended to the individual entries in the sources field.
- The
sources field is a list of original sources used by the mappings field. Each entry is either a string that is a (potentially relative) URL or *null* if the source name is not known.
- The
sourcesContent field is an optional list of source content (i.e. the original source) strings, used when the source cannot be hosted. The contents are listed in the same order as in the sources field. Entries may be *null* if some original sources should be retrieved by name.
- The
names field is an optional list of symbol names which may be used by the mappings field.
- The
mappings field is a string with the encoded mapping data (see section ).
- The
ignoreList field is an optional list of indices of files that should be considered third party code, such as framework code or bundler-generated code. This allows developer tools to avoid code that developers likely don't want to see or step through, without requiring developers to configure this beforehand. It refers to the sources field and lists the indices of all the known third-party sources in the source map. Some browsers may also use the deprecated `x_google_ignoreList` field if `ignoreList` is not present.
Decoding source maps
A Decoded Source Map Record has the following fields:
|
Field Name
|
Value Type
|
| [[File]] |
a String or *null* |
| [[Sources]] |
a List of Decoded Source Records |
| [[Mappings]] |
a List of Decoded Mapping Records |
A Decoded Source Record has the following fields:
|
Field Name
|
Value Type
|
| [[URL]] |
a URL or *null* |
| [[Content]] |
a String or *null* |
| [[Ignored]] |
a Boolean |
ParseSourceMap (
_string_: a String,
_baseURL_: an URL,
): a Decoded Source Map Record
1. Let _json_ be ParseJSON(_string_).
1. If _json_ is not a JSON object, throw an error.
1. If JSONObjectGet(_json_, *"sections"*) is not ~missing~, then
1. Return DecodeIndexSourceMap(_json_, _baseURL_).
1. Return DecodeSourceMap(_json_, _baseURL_).
DecodeSourceMap (
_json_: a JSON object,
_baseURL_: an URL,
): a Decoded Source Map Record
1. If JSONObjectGet(_json_, *"version"*) is not *3*𝔽, optionally report an error.
1. Let _mappingsField_ be JSONObjectGet(_json_, *"mappings"*).
1. If _mappingsField_ is not a String, throw an error.
1. If JSONObjectGet(_json_, *"sources"*) is not a JSON array, throw an error.
1. Let _fileField_ be GetOptionalString(_json_, *"file"*).
1. Let _sourceRootField_ be GetOptionalString(_json_, *"sourceRoot"*).
1. Let _sourcesField_ be GetOptionalListOfOptionalStrings(_json_, *"sources"*).
1. Let _sourcesContentField_ be GetOptionalListOfOptionalStrings(_json_, *"sourcesContent"*).
1. Let _ignoreListField_ be GetOptionalListOfArrayIndexes(_json_, *"ignoreList"*).
1. Let _sources_ be DecodeSourceMapSources(_baseURL_, _sourceRootField_, _sourcesField_, _sourcesContentField_, _ignoreListField_).
1. Let _namesField_ be GetOptionalListOfStrings(_json_, *"names"*).
1. Let _mappings_ be DecodeMappings(_mappingsField_, _namesField_, _sources_).
1. [declared="a,b"] Sort _mappings_ in ascending order, with a Decoded Mapping Record _a_ being less than a Decoded Mapping Record _b_ if ComparePositions(_a_.[[GeneratedPosition]], _b_.[[GeneratedPosition]]) is ~lesser~.
1. Return the Decoded Source Map Record { [[File]]: _fileField_, [[Sources]]: _sources_, [[Mappings]]: _mappings_ }.
GetOptionalString (
_object_: a JSON object,
_key_: a String,
): a String or *null*
1. Let _value_ be JSONObjectGet(_object_, _key_).
1. If _value_ is a String, return _value_.
1. If _value_ is not ~missing~, optionally report an error.
1. Return *null*.
GetOptionalListOfStrings (
_object_: a JSON object,
_key_: a String,
): a List of Strings
1. Let _list_ be a new empty List.
1. Let _values_ be JSONObjectGet(_object_, _key_).
1. If _values_ is ~missing~, return _list_.
1. If _values_ is not a JSON array, then
1. Optionally report an error.
1. Return _list_.
1. For each element _item_ of JSONArrayIterate(_values_), do
1. If _item_ is a String, then
1. Append _item_ to _list_.
1. Else,
1. Optionally report an error.
1. Append the empty String to _list_.
1. Return _list_.
GetOptionalListOfOptionalStrings (
_object_: a JSON object,
_key_: a String,
): a List of either Strings or *null*
1. Let _list_ be a new empty List.
1. Let _values_ be JSONObjectGet(_object_, _key_).
1. If _values_ is ~missing~, return _list_.
1. If _values_ is not a JSON array, then
1. Optionally report an error.
1. Return _list_.
1. For each element _item_ of JSONArrayIterate(_values_), do
1. If _item_ is a String, then
1. Append _item_ to _list_.
1. Else,
1. If _item_ ≠ *null*, optionally report an error.
1. Append *null* to _list_.
1. Return _list_.
GetOptionalListOfArrayIndexes (
_object_: an Object,
_key_: a String,
): a List of non-negative integers
1. Let _list_ be a new empty List.
1. Let _values_ be JSONObjectGet(_object_, _key_).
1. If _values_ is ~missing~, return _list_.
1. If _values_ is not a JSON array, then
1. Optionally report an error.
1. Return _list_.
1. For each element _item_ of JSONArrayIterate(_values_), do
1. If _item_ is an integral Number, and _item_ ≥ *+0*𝔽, then
1. Append ℝ(_item_) to _list_.
1. Else,
1. Optionally report an error.
1. Return _list_.
Mappings structure
The mappings field data is broken down as follows:
- each group representing a line in the generated file is separated by a semicolon (`;`)
- each segment is separated by a comma (`,`)
- each segment is made up of 1, 4, or 5 variable length fields.
The fields in each segment are:
- 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 from the subsequent fields below because the previous value is reset after every generated line.
- If present, the zero-based index into the sources list. This field contains a base64 VLQ relative to the previous occurrence of this field, unless it is the first occurrence of this field, in which case the whole value is represented.
- If present, the zero-based starting line in the original source. This field contains a base64 VLQ relative to the previous occurrence of this field, unless it is the first occurrence of this field, in which case the whole value is represented. Shall be present if there is a source field.
- If present, the zero-based starting column of the line in the original source. This field contains a base64 VLQ relative to the previous occurrence of this field, unless it is the first occurrence of this field, in which case the whole value is represented. Shall be present if there is a source field.
- If present, the zero-based index into the names list associated with this segment. This field contains a base64 VLQ relative to the previous occurrence of this field, unless it is the first occurrence of this field, in which case the whole value is represented.
The purpose of this encoding is to reduce the source map size. VLQ encoding reduced source maps by 50% relative to the Source Map Revision 2 Proposal in tests performed using Google Calendar.
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.
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.
A Decoded Mapping Record has the following fields:
|
Field Name
|
Value Type
|
| [[GeneratedPosition]] |
a Position Record |
| [[OriginalPosition]] |
a Original Position Record or *null* |
| [[Name]] |
a String or *null* |
Mappings grammar
The mappings String must adhere to the following grammar:
MappingsField :
LineList
LineList :
Line
Line `;` LineList
Line :
MappingList?
MappingList :
Mapping
Mapping `,` MappingList
Mapping :
GeneratedColumn
GeneratedColumn OriginalSource OriginalLine OriginalColumn Name?
GeneratedColumn :
Vlq
OriginalSource :
Vlq
OriginalLine :
Vlq
OriginalColumn :
Vlq
Name :
Vlq
A Decode Mapping State Record has the following fields:
|
Field Name
|
Value Type
|
| [[GeneratedLine]] |
a non-negative integer |
| [[GeneratedColumn]] |
a non-negative integer |
| [[SourceIndex]] |
a non-negative integer |
| [[OriginalLine]] |
a non-negative integer |
| [[OriginalColumn]] |
a non-negative integer |
| [[NameIndex]] |
a non-negative integer |
DecodeMappingsField (
_state_: a Decode Mapping State Record,
_mappings_: a List of Decoded Mapping Records,
_names_: a List of Strings,
_sources_: a List of Decoded Source Records,
)
LineList :
Line `;` LineList
1. Perform DecodeMappingsField of |Line| with arguments _state_, _mappings_, _names_ and _sources_.
1. Set _state_.[[GeneratedLine]] to _state_.[[GeneratedLine]] + 1.
1. Set _state_.[[GeneratedColumn]] to 0.
1. Perform DecodeMappingsField of |LineList| with arguments _state_, _mappings_, _names_ and _sources_.
Line : [empty]
1. Return.
MappingList :
Mapping `,` MappingList
1. Perform DecodeMappingsField of |Mapping| with arguments _state_, _mappings_, _names_ and _sources_.
1. Perform DecodeMappingsField of |MappingList| with arguments _state_, _mappings_, _names_ and _sources_.
Mapping :
GeneratedColumn
1. Perform DecodeMappingsField of |GeneratedColumn| with arguments _state_, _mappings_, _names_ and _sources_.
1. If _state_.[[GeneratedColumn]] < 0, then
1. Optionally report an error.
1. Return.
1. Let _position_ be a new Position Record { [[Line]]: _state_.[[GeneratedLine]], [[Column]]: _state_.[[GeneratedColumn]] }.
1. Let _decodedMapping_ be a new DecodedMappingRecord { [[GeneratedPosition]]: _position_, [[OriginalPosition]]: *null*, [[Name]]: *null* }.
1. Append _decodedMapping_ to _mappings_.
Mapping :
GeneratedColumn OriginalSource OriginalLine OriginalColumn Name?
1. Perform DecodeMappingsField of |GeneratedColumn| with arguments _state_, _mappings_, _names_ and _sources_.
1. If _state_.[[GeneratedColumn]] < 0, then
1. Optionally report an error.
1. Return.
1. Let _generatedPosition_ be a new Position Record { [[Line]]: _state_.[[GeneratedLine]], [[Column]]: _state_.[[GeneratedColumn]] }.
1. Perform DecodeMappingsField of |OriginalSource| with arguments _state_, _mappings_, _names_ and _sources_.
1. Perform DecodeMappingsField of |OriginalLine| with arguments _state_, _mappings_, _names_ and _sources_.
1. Perform DecodeMappingsField of |OriginalColumn| with arguments _state_, _mappings_, _names_ and _sources_.
1. If _state_.[[SourceIndex]] < 0 or _state_.[[SourceIndex]] ≥ the number of elements of _sources_ or _state_.[[OriginalLine]] < 0 or _state_.[[OriginalColumn]] < 0, then
1. Optionally report an error.
1. Let _originalPosition_ be *null*.
1. Else,
1. Let _originalPosition_ be a new Original Position Record { [[Source]]: _sources_[_state_.[[SourceIndex]]], [[Line]]: _state_.[[OriginalLine]], [[Column]]: _state_.[[OriginalColumn]] }.
1. Let _name_ be *null*.
1. If |Name| is present, then
1. Perform DecodeMappingsField of |Name| with arguments _state_, _mappings_, _names_ and _sources_.
1. If _state_.[[NameIndex]] < 0 or _state_.[[NameIndex]] ≥ the number of elements of _names_, optionally report an error.
1. Else, set _name_ to _names_[_state_.[[NameIndex]]].
1. Let _decodedMapping_ be a new DecodedMappingRecord { [[GeneratedPosition]]: _generatedPosition_, [[OriginalPosition]]: _originalPosition_, [[Name]]: _name_ }.
1. Append _decodedMapping_ to _mappings_.
GeneratedColumn :
Vlq
1. Let _relativeColumn_ be the VLQSignedValue of |Vlq|.
1. Set _state_.[[GeneratedColumn]] to _state_.[[GeneratedColumn]] + _relativeColumn_.
OriginalSource :
Vlq
1. Let _relativeSourceIndex_ be the VLQSignedValue of |Vlq|.
1. Set _state_.[[SourceIndex]] to _state_.[[SourceIndex]] + _relativeSourceIndex_.
OriginalLine :
Vlq
1. Let _relativeLine_ be the VLQSignedValue of |Vlq|.
1. Set _state_.[[OriginalLine]] to _state_.[[OriginalLine]] + _relativeLine_.
OriginalColumn :
Vlq
1. Let _relativeColumn_ be the VLQSignedValue of |Vlq|.
1. Set _state_.[[OriginalColumn]] to _state_.[[OriginalColumn]] + _relativeColumn_.
Name :
Vlq
1. Let _relativeName_ be the VLQSignedValue of |Vlq|.
1. Set _state_.[[NameIndex]] to _state_.[[NameIndex]] + _relativeName_.
DecodeMappings (
_rawMappings_: a String,
_names_: a List of Strings,
_sources_: a List of Decoded Source Records,
): a List of Decoded Mapping Record
1. Let _mappings_ be a new empty List.
1. Let _mappingsNode_ be the root Parse Node when parsing _rawMappings_ using |MappingsField| as the goal symbol.
1. If parsing failed, then
1. Optionally report an error.
1. Return _mappings_.
1. Let _state_ be a new Decode Mapping State Record with all fields set to 0.
1. Perform DecodeMappingsField of _mappingsNode_ with arguments _state_, _mappings_, _names_ and _sources_.
1. Return _mappings_.
Mappings for generated JavaScript code
Generated code positions that may have mapping entries are defined in terms of input elements, as per the ECMAScript Lexical Grammar. Mapping entries shall point to either:
- the first code point of the source text matched by |IdentifierName|, |PrivateIdentifier|, |Punctuator|, |DivPunctuator|, |RightBracePunctuator|, |NumericLiteral| and |RegularExpressionLiteral|.
- any code point of the source text matched by |Comment|, |HashbangComment|, |StringLiteral|, |Template|, |TemplateSubstitutionTail|, |WhiteSpace| and |LineTerminator|.
Names for generated JavaScript code
Source map generators should create a mapping entry with a [[Name]] field for a JavaScript token, if:
- The original source language construct maps semantically to the generated JavaScript code.
- The original source language construct has a name.
Then the [[Name]] of the mapping entry should be the name of the original source language construct. A mapping with a non-null [[Name]] is called a named mapping.
A minifier renaming functions and variables or removing function names from immediately invoked function expressions.
The following enumeration lists productions of the ECMAScript Syntactic Grammar and the respective token or non-terminal (on the right-hand side of the production) for which source map generators should emit a named mapping. The mapping entry created for such tokens shall follow section .
The enumeration should be understood as the “minimum”. In general, source map generators are free to emit any additional named mappings.
The enumeration also lists tokens where generators “may” emit named mappings in addition to the tokens where they “should”. These reflect the reality where existing tooling emits or expects named mappings. The duplicated named mapping is comparably cheap: Indices into names are encoded relative to each other so subsequent mappings to the same name are encoded as 0 (`A`).
-
The |BindingIdentifier|(s) for |LexicalDeclaration|, |VariableStatement| and |FormalParameterList|.
-
The |BindingIdentifier| for |FunctionDeclaration|, |FunctionExpression|, |AsyncFunctionDeclaration|, |AsyncFunctionExpression|, |GeneratorDeclaration|, |GeneratorExpression|, |AsyncGeneratorDeclaration|, and |AsyncGeneratorExpression| if it exists, or the opening parenthesis `(` preceding the |FormalParameters| otherwise.
Source map generators may chose to emit a named mapping on the opening parenthesis regardless of
the presence of the |BindingIdentifier|.
-
For an |ArrowFunction| or |AsyncArrowFunction|:
-
The `=>` token where |ArrowFunction| is produced with a single |BindingIdentifier| for |ArrowParameters| or |AsyncArrowFunction| is produced with an |AsyncArrowBindingIdentifier|.
This describes the case of (async) arrow functions with a single parameter, where that single parameter is not wrapped in parenthesis.
-
The opening parenthesis `(` where |ArrowFunction| or |AsyncArrowFunction| is produced with |ArrowFormalParameters|.
Source map generators may chose to additionally emit a named mapping on the `=>` token for consistency with the previous case.
-
The |ClassElementName| for |MethodDefinition|. This includes generators, async methods, async generators and accessors. For |MethodDefinition| where |ClassElementName| is *"constructor"*, the [[Name]] should be the original class name if applicable.
Source map generators may chose to additionally emit a named mapping on the opening parenthesis `(`.
-
Source map generators may emit named mapping for |IdentifierReference| in |Expression|.
Resolving sources
If the sources are not absolute URLs after prepending the sourceRoot, the sources are resolved relative to the source map (like resolving the script `src` attribute in an HTML document).
DecodeSourceMapSources (
_baseURL_: an URL,
_sourceRoot_: a String or *null*,
_sources_: a List of either Strings or *null*,
_sourcesContent_: a List of either Strings or *null*,
_ignoreList_: a List of non-negative integers,
): a List of Decoded Source Record
1. Let _decodedSources_ be a new empty List.
1. Let _sourcesContentCount_ be the number of elements in _sourcesContent_.
1. Let _sourceUrlPrefix_ be the empty String.
1. If _sourceRoot_ ≠ *null*, then
1. If _sourceRoot_ ends with the code point U+002F (SOLIDUS), then
1. Set _sourceUrlPrefix_ to _sourceRoot_.
1. Else,
1. Set _sourceUrlPrefix_ to the string-concatenation of _sourceRoot_ and *"/"*.
1. Let _index_ be 0.
1. Repeat, while _index_ < _sources_' length,
1. Let _source_ be _sources_[_index_].
1. Let _decodedSource_ be the Decoded Source Record { [[URL]]: *null*, [[Content]]: *null*, [[Ignored]]: *false* }.
1. If _source_ ≠ *null*, then
1. Set _source_ to the string-concatenation of _sourceUrlPrefix_ and _source_.
1. Let _sourceURL_ be the result of URL parsing _source_ with _baseURL_.
1. If _sourceURL_ is ~failure~, optionally report an error.
1. Else, set _decodedSource_.[[URL]] to _sourceURL_.
1. If _ignoreList_ contains _index_, set _decodedSource_.[[Ignored]] to *true*.
1. If _sourcesContentCount_ > _index_, set _decodedSource_.[[Content]] to _sourcesContent_[_index_].
1. Append _decodedSource_ to _decodedSources_.
1. Set _index_ to _index_ + 1.
1. Return _decodedSources_.
Implementations that support showing source contents but do not support showing multiple sources with the same URL and different content will arbitrarily choose one of the various contents corresponding to the given URL.
Extensions
Source map consumers shall 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.
Index source map
To support concatenating generated code and other common post-processing, an alternate representation of a source 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.
The sections field is an array of objects with the following fields:
offset field is an object with two fields, `line` and `column`, that represent the offset into generated code that the referenced source map represents.
map field is an embedded complete source map object. An embedded map does not inherit any values from the containing index map.
The sections shall be sorted by starting position and the represented sections shall not overlap.
DecodeIndexSourceMap (
_json_: an Object,
_baseURL_: an URL,
): a Decoded Source Map Record
1. Let _sectionsField_ be JSONObjectGet(_json_, *"sections"*).
1. Assert: _sectionsField_ is not ~missing~.
1. If _sectionsField_ is not a JSON array, throw an error.
1. If JSONObjectGet(_json_, *"version"*) is not *3*𝔽, optionally report an error.
1. Let _fileField_ be GetOptionalString(_json_, *"file"*).
1. Let _sourceMap_ be the Decoded Source Map Record { [[File]]: _fileField_, [[Sources]]: « », [[Mappings]]: « » }.
1. Let _previousOffsetPosition_ be *null*.
1. Let _previousLastMapping_ be *null*.
1. For each JSON value _section_ of JSONArrayIterate(_sectionsField_), do
1. If _section_ is not a JSON object, then
1. Optionally report an error.
1. Else,
1. Let _offset_ be JSONObjectGet(_section_, *"offset"*).
1. If _offset_ is not a JSON object, throw an error.
1. Let _offsetLine_ be JSONObjectGet(_offset_, *"line"*).
1. Let _offsetColumn_ be JSONObjectGet(_offset_, *"column"*).
1. If _offsetLine_ is not an integral Number, then
1. Optionally report an error.
1. Set _offsetLine_ to *+0*𝔽.
1. If _offsetColumn_ is not an integral Number, then
1. Optionally report an error.
1. Set _offsetColumn_ to *+0*𝔽.
1. Let _offsetPosition_ be a new Position Record { [[Line]]: _offsetLine_, [[Column]]: _offsetColumn_ }.
1. If _previousOffsetPosition_ ≠ *null*, then
1. If ComparePositions(_offsetPosition_, _previousOffsetPosition_) is ~lesser~, optionally report an error.
1. If _previousLastMapping_ ≠ *null*, then
1. If ComparePositions(_offsetPosition_, _previousLastMapping_.[[GeneratedPosition]]) is ~lesser~, optionally report an error.
1. NOTE: This part of the decoding algorithm checks that entries of the sections field of index source maps are ordered and do not overlap. While it is expected that generators should not produce index source maps with overlapping sections, source map consumers may, for example, only check the simpler condition that the section offsets are ordered.
1. Let _mapField_ be JSONObjectGet(_section_, *"map"*).
1. If _mapField_ is not a JSON object, throw an error.
1. Let _decodedSectionCompletion_ be Completion(DecodeSourceMap(_json_, _baseURL_)).
1. If _decodedSectionCompletion_ is a throw completion, then
1. Optionally report an error.
1. Else,
1. Let _decodedSection_ be _decodedSectionCompletion_.[[Value]].
1. For each Decoded Source Record _additionalSource_ of _decodedSection_.[[Sources]], do
1. If _sourceMap_.[[Sources]] does not contain _additionalSource_, then
1. Append _additionalSource_ to _sourceMap_.[[Sources]].
1. Let _offsetMappings_ be a new empty List.
1. For each Decoded Mapping Record _mapping_ of _decodedSection_.[[Mappings]], do
1. If _mapping_.[[GeneratedPosition]].[[Line]] = 0, then
1. Set _mapping_.[[GeneratedPosition]].[[Column]] to _mapping_.[[GeneratedPosition]].[[Column]] + _offsetColumn_.
1. Set _mapping_.[[GeneratedPosition]].[[Line]] to _mapping_.[[GeneratedPosition]].[[Line]] + _offsetLine_.
1. Append _mapping_ to _offsetMappings_.
1. Set _sourceMap_.[[Mappings]] to the list-concatenation of _sourceMap_.[[Mappings]] and _offsetMappings_.
1. Set _previousOffsetPosition_ to _offsetPosition_.
1. If _offsetMappings_ is not empty, set _previousLastMapping_ to the last element of _offsetMappings_.
1. Return _sourceMap_.
Implementations may choose to represent index source map sections without appending the mappings together, for example, by storing each section separately and conducting a binary search.
Retrieving source maps
Linking generated code to source maps
While the source map format is intended to be language and platform agnostic, it is useful to define how to reference to them for the expected use-case of web server-hosted JavaScript.
There are two possible 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 WHATWG URL; in particular, characters outside the set permitted to appear in URIs shall 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 map URL the same process is used to resolve it, which is as follows.
When the source map 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:
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 shall 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
// `
Extracting a source map URL from it through parsing gives `foo.js.map`, while without parsing gives *null*.
1. Let _module_ be module_decode(_bytes_).
1. If _module_ is WebAssembly error, return *null*.
1. For each custom section _customSection_ of _module_, do
1. Let _name_ be the `name` of _customSection_.
1. If CodePointsToString(_name_) is *"sourceMappingURL"*, then
1. Let _value_ be the `bytes` of _customSection_.
1. Return CodePointsToString(_value_).
1. 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 as a WebAssembly name, 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.
Fetching source maps
FetchSourceMap (
_url_: an URL,
): a Promise
1. Let _promiseCapability_ be NewPromiseCapability(%Promise%).
1. Let _request_ be a new request whose request URL is _url_.
1. Let _processResponseConsumeBody_ be a new Abstract Closure with parameters (_response_, _bodyBytes_) that captures _promiseCapability_ and _url_, and performs the following steps when called:
1. If _bodyBytes_ is *null* or ~failure~, then
1. Perform Call(_promiseCapability_.[[Reject]], *undefined*, « a new *TypeError* »).
1. Return.
1. If _url_'s scheme is an HTTP(S) scheme and the byte sequence \``)]}'`\` is a byte-sequence-prefix of _bodyBytes_, then
1. Repeat, while _bodyBytes_'s byte-sequence-length ≠ 0 and _bodyBytes_[0] is not an HTTP newline byte,
1. Remove the 0th element from _bodyBytes_.
1. Let _bodyString_ be Completion(UTF-8 decode of _bodyBytes_).
1. IfAbruptRejectPromise(_bodyString_, _promiseCapability_).
1. Let _jsonValue_ be Completion(ParseJSON(_bodyString_)).
1. IfAbruptRejectPromise(_jsonValue_, _promiseCapability_).
1. Perform Call(_promiseCapability_.[[Resolve]], *undefined*, « _jsonValue_ »).
1. Perform fetch _request_ with processResponseConsumeBody set to _processResponseConsumeBody_.
1. Return _promiseCapability_.[[Promise]].
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, ...}
Operations on source map records
After decoding a source map, source map consumers can use the resulting Decoded Source Map Records to look up position information for debugging or other use cases. This section describes the behaviour of typical operations that may be supported by source map consumers.
The GetOriginalPositions operation may be used to query the positions in an original source that correspond to a position in the generated code. For example, this can be used in a debugger to navigate from the generated code to the original source based on a user's mouse click.
GetOriginalPositions (
_sourceMapRecord_: a Decoded Source Map Record,
_generatedPosition_: a Position Record,
): a List of Original Position Records
1. Let _mappings_ be _sourceMapRecord_.[[Mappings]].
1. Let _last_ be *null*.
1. Let _originalPositions_ be a new empty List.
1. For each element _mapping_ of _mappings_, in reverse List order, do
1. If _last_ is *null*, then
1. If the result of performing ComparePositions(_mapping_.[[GeneratedPosition]], _generatedPosition_) is ~lesser~ or ~equal~, then
1. Set _last_ to _mapping_.
1. If _last_ is not *null*, then
1. For each element _mapping_ of _mappings_, do
1. If the result of performing ComparePositions(_last_.[[GeneratedPosition]], _mapping_.[[GeneratedPosition]]) is ~equal~, then
1. Append _mapping_.[[OriginalPosition]] to _originalPositions_.
1. Return _originalPositions_.
Conventions
The following conventions should be followed when working with source maps or when generating them.
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.
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 Give your eval a name with //@ sourceURL.
Notes
Language neutral stack mapping
Stack tracing mapping without knowledge of the source language is not covered by this document.
Multi-level mapping
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.
Terms defined in other specifications
This section lists all terms and algorithms used by this document defined by external specifications other than ECMA-262.
- WebAssembly Core Specification <https://www.w3.org/TR/wasm-core-2/>
-
custom section,
module_decode,
WebAssembly error,
WebAssembly names
- WHATWG Encoding <https://encoding.spec.whatwg.org/>
-
UTF-8 decode
- WHATWG Fetch <https://fetch.spec.whatwg.org/>
-
fetch,
HTTP newline byte,
processResponseConsumeBody,
request,
request URL
- WHATWG Infra <https://infra.spec.whatwg.org/>
-
byte sequence,
byte-sequence-prefix,
byte-sequence-length,
- WHATWG URL <https://url.spec.whatwg.org/>
-
HTTP(S) scheme,
scheme,
URL,
URL parsing
Bibliography
-
IETF RFC 4648, The Base16, Base32, and Base64 Data Encodings, available at <https://datatracker.ietf.org/doc/html/rfc4648>
-
ECMA-262, ECMAScript® Language Specification, available at <https://tc39.es/ecma262/>
-
ECMA-404, The JSON Data Interchange Format, available at <https://www.ecma-international.org/publications-and-standards/standards/ecma-404/>
-
WebAssembly Core Specification, available at <https://www.w3.org/TR/wasm-core-2/>
-
WHATWG Encoding, available at <https://encoding.spec.whatwg.org/>
-
WHATWG Fetch, available at <https://fetch.spec.whatwg.org/>
-
WHATWG Infra, available at <https://infra.spec.whatwg.org/>
-
WHATWG URL, available at <https://url.spec.whatwg.org/>
-
Give your eval a name with //@ sourceURL, Firebug (2009), available at <http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/>
-
Source Map Revision 2 Proposal, John Lenz (2010), available at <https://docs.google.com/document/d/1xi12LrcqjqIHTtZzrzZKmQ3lbTv9mKrN076UB-j3UZQ/>
-
Variable-length quantity, Wikipedia, available at <https://en.wikipedia.org/wiki/Variable-length_quantity>
Colophon
This specification is authored on GitHub in a plaintext source format called Ecmarkup. Ecmarkup is an HTML and Markdown dialect that provides a framework and toolset for authoring ECMAScript specifications in plaintext and processing the specification into a full-featured HTML rendering that follows the editorial conventions for this document. Ecmarkup builds on and integrates a number of other formats and technologies including Grammarkdown for defining syntax and Ecmarkdown for authoring algorithm steps. PDF renderings of this specification are produced by printing the HTML rendering to a PDF.
The first edition of this specification was authored using Bikeshed, a different plaintext source format based on HTML and Markdown.
Pre-standard versions of this document were authored using Google Docs.