Draft ECMA-426 / March 15, 2025

Source map format specification

About this Specification

The document at https://tc39.es/ecma426/ is the most accurate and up-to-date source map specification. It contains the content of the most recently published snapshot plus any modifications that will be included in the next snapshot.

Contributing to this Specification

This specification is developed on GitHub. There are a number of ways to contribute to the development of this specification:

Refer to the colophon for more information on how this document is created.

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:

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

1 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.

2 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.

3 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.

3.1 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/

3.2 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/

4 Notational Conventions

This specification follows the same notational conventions as defined by ECMA-262 (Notational conventions), with the extensions defined in this section.

4.1 Algorithm Conventions

4.1.1 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

4.1.1.1 GetTheAnswer ( input )

The abstract operation GetTheAnswer takes argument input (an integer) and returns an integer.

is equivalent to:

4.1.1.2 GetTheAnswer2 ( input )

The abstract operation GetTheAnswer2 takes argument input (an integer) and returns 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 a ReturnIfAbrupt macro, unless they are explicitly wrapped by an explicit Completion call. For example:

  1. Let result be GetTheAnswer(value).
  2. Let second be Completion(GetTheAnswer(value)).

is equivalent to:

  1. Let result be ReturnIfAbrupt(GetTheAnswer(value)).
  2. Let second be Completion(GetTheAnswer(value)).

4.1.2 Optional Errors

Whenever an algorithm is to optionally report an error, an implementation may choose one of the following behaviors:

  • 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 behaviors for different optional errors.

5 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.

base64 VLQ

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.

Note 1
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.
Note 2
The string "iB" represents a base64 VLQ with two digits. The first digit "i" encodes the bit pattern 0x100010, which has a continuation bit of 1 (the VLQ continues), a sign bit of 0 (non-negative), and the value bits 0x0001. The second digit B encodes the bit pattern 0x000001, which has a continuation bit of 0, no sign bit, and value bits 0x00001. The decoding of this VLQ string is the number 17.
Note 3
The string "V" represents a base64 VLQ with one digit. The digit "V" encodes the bit pattern 0x010101, which has a continuation bit of 0 (no continuation), a sign bit of 1 (negative), and the value bits 0x1010. The decoding of this VLQ string is the number -10.
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 indexes in the binary content (represented as a single line) for WebAssembly source maps.

Note 4
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.

6 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:

A JSON array is a JSON object such that:

6.1 ParseJSON ( string )

The abstract operation ParseJSON takes argument string (a String) and returns a JSON value. It performs the following steps when called:

  1. Let result be Call(%JSON.parse%, null, « string »).
  2. Assert: result is a JSON value.
  3. Return result.
Editor's Note
This abstract operation is in the process of being exposed by ECMA-262 itself, at tc39/ecma262#3540.

6.2 JSONObjectGet ( object, key )

The abstract operation JSONObjectGet takes arguments object (a JSON object) and key (a String) and returns a JSON value or missing. It returns the value associated with the specified key in object. It performs the following steps when called:

  1. If object does not have an own property with key key, return missing.
  2. Let prop be object's own property whose key is key.
  3. Return prop's [[Value]] attribute.

6.3 JSONArrayIterate ( array )

The abstract operation JSONArrayIterate takes argument array (a JSON array) and returns a List of JSON values. It returns a List containing all elements of array, so that it can be iterated by algorithms using "For each". It performs the following steps when called:

  1. Let length be JSONObjectGet(array, "length").
  2. Assert: length is a non-negative integral Number.
  3. Let list be a new empty List.
  4. Let i be 0.
  5. While i < (length), do
    1. Let value be JSONObjectGet(array, ToString(𝔽(i))).
    2. Assert: value is not missing.
    3. Append value to list.
    4. Set i to i + 1.
  6. Return list.

6.4 StringSplit ( string, separators )

The abstract operation StringSplit takes arguments string (a String) and separators (a List of String) and returns a List of Strings. It splits the string in substrings separated by any of the elements of separators. If multiple separators match, those appearing first in separators have higher priority. It performs the following steps when called:

  1. Let parts be a new empty List.
  2. Let strLen be the length of string.
  3. Let lastStart be 0.
  4. Let i be 0.
  5. While i < strLen, do
    1. Let matched be false.
    2. For each sep of separators, do
      1. Let sepLen be the length of sep.
      2. Let candidate be the substring of string from i to min(i + sepLen, strLen).
      3. If candidate = sep and matched is false, then
        1. Let chunk be the substring of string from lastStart to i.
        2. Append chunk to parts.
        3. Set lastStart to i + sepLen.
        4. Set i to i + sepLen.
        5. Set matched to true.
    3. If matched is false, set i to i + 1.
  6. Let chunk be the substring of string from lastStart to strLen.
  7. Append chunk to parts.
  8. Return parts.

7 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]
}

7.1 Decoding source maps

A Decoded Source Map Record has the following fields:

Table 1: Fields of Decoded Source Map Records
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:

Table 2: Fields of Decoded Source Records
Field Name Value Type
[[URL]] a URL or null
[[Content]] a String or null
[[Ignored]] a Boolean

7.1.1 ParseSourceMap ( string, baseURL )

The abstract operation ParseSourceMap takes arguments string (a String) and baseURL (an URL) and returns a Decoded Source Map Record. It performs the following steps when called:

  1. Let json be ParseJSON(string).
  2. If json is not a JSON object, throw an error.
  3. If JSONObjectGet(json, "sections") is not missing, then
    1. Return DecodeIndexSourceMap(json, baseURL).
  4. Return DecodeSourceMap(json, baseURL).

7.1.2 DecodeSourceMap ( json, baseURL )

The abstract operation DecodeSourceMap takes arguments json (a JSON object) and baseURL (an URL) and returns a Decoded Source Map Record. It performs the following steps when called:

  1. If JSONObjectGet(json, "version") is not 3𝔽, optionally report an error.
  2. Let mappingsField be JSONObjectGet(json, "mappings").
  3. If mappingsField is not a String, throw an error.
  4. If JSONObjectGet(json, "sources") is not a JSON array, throw an error.
  5. Let fileField be GetOptionalString(json, "file").
  6. Let sourceRootField be GetOptionalString(json, "sourceRoot").
  7. Let sourcesField be GetOptionalListOfOptionalStrings(json, "sources").
  8. Let sourcesContentField be GetOptionalListOfOptionalStrings(json, "sourcesContent").
  9. Let ignoreListField be GetOptionalListOfArrayIndexes(json, "ignoreList").
  10. Let sources be DecodeSourceMapSources(baseURL, sourceRootField, sourcesField, sourcesContentField, ignoreListField).
  11. Let namesField be GetOptionalListOfStrings(json, "names").
  12. Let mappings be DecodeMappings(mappingsField, namesField, sources).
  13. Let sourceMap be the Decoded Source Map Record { [[File]]: fileField, [[Sources]]: sources, [[Mappings]]: mappings }.

7.1.2.1 GetOptionalString ( object, key )

The abstract operation GetOptionalString takes arguments object (a JSON object) and key (a String) and returns a String or null. It performs the following steps when called:

  1. Let value be JSONObjectGet(object, key).
  2. If value is a String, return value.
  3. If value is not missing, optionally report an error.
  4. Return null.

7.1.2.2 GetOptionalListOfStrings ( object, key )

The abstract operation GetOptionalListOfStrings takes arguments object (a JSON object) and key (a String) and returns a List of Strings. It performs the following steps when called:

  1. Let list be a new empty List.
  2. Let values be JSONObjectGet(object, key).
  3. If values is missing, return list.
  4. If values is not a JSON array, then
    1. Optionally report an error.
    2. Return list.
  5. For each element item of JSONArrayIterate(values),
    1. If item is a String, append item to list.
    2. Else,
      1. Optionally report an error.
      2. Append "" to list.
  6. Return list.

7.1.2.3 GetOptionalListOfOptionalStrings ( object, key )

The abstract operation GetOptionalListOfOptionalStrings takes arguments object (a JSON object) and key (a String) and returns a List of either Strings or null. It performs the following steps when called:

  1. Let list be a new empty List.
  2. Let values be JSONObjectGet(object, key).
  3. If values is missing, return list.
  4. If values is not a JSON array, then
    1. Optionally report an error.
    2. Return list.
  5. For each element item of JSONArrayIterate(values),
    1. If item is a String, append item to list.
    2. Else,
      1. If itemnull, optionally report an error.
      2. Append null to list.
  6. Return list.

7.1.2.4 GetOptionalListOfArrayIndexes ( object, key )

The abstract operation GetOptionalListOfArrayIndexes takes arguments object (an Object) and key (a String) and returns a List of non-negative integers. It performs the following steps when called:

  1. Let list be a new empty List.
  2. Let values be JSONObjectGet(object, key).
  3. If values is missing, return list.
  4. If values is not a JSON array, then
    1. Optionally report an error.
    2. Return list.
  5. For each element item of JSONArrayIterate(values),
    1. If item is an integral Number, item0𝔽, and item0𝔽, then
      1. Append (item) to list.
    2. Else,
      1. If itemnull, optionally report an error.
      2. Append null to list.
  6. Return list.

7.2 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:

  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 from the subsequent fields below because the previous value is reset after every generated line.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
Note 1
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.
Note 2
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 3
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:

Table 3: Fields of Decoded Mapping Records
Field Name Value Type
[[GeneratedLine]] a non-negative integer
[[GeneratedColumn]] a non-negative integer
[[OriginalSource]] a Decoded Source Record or null
[[OriginalLine]] a non-negative integer or null
[[OriginalColumn]] a non-negative integer or null
[[Name]] a String or null

7.2.1 DecodeMappings ( mappings, names, sources )

The abstract operation DecodeMappings takes arguments mappings (a String), names (a List of Strings), and sources (a List of Decoded Source Records) and returns a List of Decoded Mapping Record. It performs the following steps when called:

  1. Perform ValidateBase64VLQGroupings(mappings).
  2. Let decodedMappings be a new empty List.
  3. Let groups be StringSplit(mappings, « ";" »).
  4. Let generatedLine be 0.
  5. Let originalLine be 0.
  6. Let originalColumn be 0.
  7. Let sourceIndex be 0.
  8. Let nameIndex be 0.
  9. Repeat, while generatedLine is less than the number of elements of groups,
    1. If groups[generatedLine] ≠ "", then
      1. Let segments be StringSplit(groups[generatedLine], « "," »).
      2. Let generatedColumn be 0.
      3. For each segment of segments, do
        1. Let position be the Record { [[Value]]: 0 }.
        2. Let relativeGeneratedColumn be DecodeBase64VLQ(segment, position).
        3. If relativeGeneratedColumn = null, optionally report an error.
        4. Else,
          1. Set generatedColumn to generatedColumn + relativeGeneratedColumn.
          2. If generatedColumn < 0, optionally report an error.
          3. Else,
            1. Let decodedMapping be the Decoded Mapping Record { [[GeneratedLine]]: generatedLine, [[GeneratedColumn]]: generatedColumn, [[OriginalSource]]: null, [[OriginalLine]]: null, [[OriginalColumn]]: null, [[Name]]: null }.
            2. Append decodedMapping to decodedMappings.
            3. Let relativeSourceIndex be DecodeBase64VLQ(segment, position).
            4. Let relativeOriginalLine be DecodeBase64VLQ(segment, position).
            5. Let relativeOriginalColumn be DecodeBase64VLQ(segment, position).
            6. If relativeOriginalColumn = null and relativeSourceIndexnull, optionally report an error.
            7. Else if relativeOriginalColumnnull,
              1. Set sourceIndex to sourceIndex + relativeSourceIndex.
              2. Set originalLine to originalLine + relativeOriginalLine.
              3. Set originalColumn to originalColumn + relativeOriginalColumn.
              4. If sourceIndex < 0, originalLine < 0, originalColumn < 0, or sourceIndex ≥ the number of elements of source, then
                1. Optionally report an error.
              5. Else,
                1. Set decodedMapping.[[OriginalSource]] to sources[sourceIndex].
                2. Set decodedMapping.[[OriginalLine]] to originalLine.
                3. Set decodedMapping.[[OriginalColumn]] to originalColumn.
              6. Let relativeNameIndex be DecodeBase64VLQ(segment, position).
              7. If relativeNameIndexnull, then
                1. Set nameIndex to nameIndex + relativeNameIndex.
                2. If nameIndex < 0 or nameIndex ≥ the number of elements of names, then
                  1. Optionally report an error.
                3. Else,
                  1. Set decodedMapping.[[Name]] to names[nameIndex].
              8. If position.[[Value]] ≠ length of segment, optionally report an error.
    2. Set generatedLine to generatedLine + 1.
  10. Return decodedMappings.

7.2.1.1 ValidateBase64VLQGroupings ( groupings )

The abstract operation ValidateBase64VLQGroupings takes argument groupings (a String) and returns unused. It performs the following steps when called:

  1. If groupings contains any code unit other than:
    • U+002B (+), U+002C (,), U+002F (/), or U+003B (;);
    • U+0030 (0) to U+0039 (9);
    • U+0041 (A) to U+005A (Z);
    • U+0061 (a) to U+007A (z)
    throw an error.
  2. Return unused.
Note
These are the valid base64 characters (excluding the padding character =), together with , and ;.

7.2.1.2 DecodeBase64VLQ ( segment, position )

The abstract operation DecodeBase64VLQ takes arguments segment (a String) and position (a Record with a non-negative integer [[Value]] field) and returns an integer or null. It performs the following steps when called:

  1. Let segmentLen be the length of segment.
  2. If position.[[Value]] = segmentLen, return null.
  3. Let first be ConsumeBase64ValueAt(segment, position).
  4. Assert: first < 64.
  5. If first modulo 2 is 0, let sign be 1.
  6. Else, let sign be -1.
  7. Let value be floor((first modulo 25) / 2).
  8. Let nextShift be 16.
  9. Let currentByte be first.
  10. Repeat, while floor(currentByte / 25) = 1,
    1. If position.[[Value]] = segmentLen, throw an error.
    2. Set currentByte to ConsumeBase64ValueAt(segment, position).
    3. Let chunk be currentByte modulo 25.
    4. Set value to value + chunk × nextShift.
    5. If value ≥ 231, throw an error.
    6. Set nextShift to nextShift × 25.
  11. If value is 0 and sign is -1, return -231.
  12. Return sign × value.

7.2.1.2.1 ConsumeBase64ValueAt ( string, position )

The abstract operation ConsumeBase64ValueAt takes arguments string (a String) and position (a Record with a non-negative integer [[Value]] field) and returns a non-negative integer. It performs the following steps when called:

  1. Assert: position.[[Value]] is a non-negative integer smaller than the length of string.
  2. Let char be the substring of string from position to position + 1.
  3. Assert: char is a valid base64 character as defined by IETF RFC 4648.
  4. Set position.[[Value]] to position.[[Value]] + 1.
  5. Return the integer corresponding to char, according to the base64 encoding as defined by IETF RFC 4648.
Note
In addition to returning the decoded value, these algorithms update the position passed in by the caller.

7.2.2 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:

7.2.3 Names for generated JavaScript code

Source map generators should create a mapping entry with a [[Name]] field for a JavaScript token, if:

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.

Note 1
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 7.2.2.

The enumeration should be understood as the "minimum". In general, source map generators are free to emit any additional named mappings.

Note 2
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).

7.3 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).

7.3.1 DecodeSourceMapSources ( baseURL, sourceRoot, sources, sourcesContent, ignoreList )

The abstract operation DecodeSourceMapSources takes arguments baseURL (an URL), sourceRoot (a String or null), sources (a List of either Strings or null), sourcesContent (a List of either Strings or null), and ignoreList (a List of non-negative integers) and returns a Decoded Source Record. It performs the following steps when called:

  1. Let decodedSources be a new empty List.
  2. Let sourcesContentCount be the the number of elements in sourcesContent.
  3. Let sourceUrlPrefix be "".
  4. If sourceRootnull, then
    1. If sourceRoot contains the code point U+002F (SOLIDUS), then
      1. Let idx be the index of the last occurrence of U+002F (SOLIDUS) in sourceRoot.
      2. Set sourceUrlPrefix to the substring of sourceRoot from 0 to idx + 1.
    2. Else, set sourceUrlPrefix to the string-concatenation of sourceRoot and "/".
  5. For each source in sources with index index, do
    1. Let decodedSource be the Decoded Source Record { [[URL]]: null, [[Content]]: null, [[Ignored]]: false }.
    2. If sourcenull, then
      1. Set source to the string-concatenation of sourceUrlPrefix and source.
      2. Let sourceURL be the result of URL parsing source with baseURL.
      3. If sourceURL is failure, optionally report an error.
      4. Else, set decodedSource.[[URL]] to sourceURL.
    3. If ignoredSources contains index, set decodedSource.[[Ignored]] to true.
    4. If sourcesContentCount > index, set decodedSource.[[Content]] to sourcesContent[index].
    5. Append decodedSource to decodedSources.
  6. Return decodedSources.
Note
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.

7.4 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.

8 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:

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

8.1 DecodeIndexSourceMap ( json, baseURL )

The abstract operation DecodeIndexSourceMap takes arguments json (an Object) and baseURL (an URL) and returns a Decoded Source Map Record. It performs the following steps when called:

  1. Let sectionsField be JSONObjectGet(json, "sections").
  2. Assert: sectionsField is not missing.
  3. If sectionsField is not a JSON array, throw an error.
  4. If JSONObjectGet(json, "version") is not 3𝔽, optionally report an error.
  5. Let fileField be GetOptionalString(json, "file").
  6. Let sourceMap be the Decoded Source Map Record { [[File]]: fileField, [[Sources]]: « », [[Mappings]]: « » }.
  7. Let previousOffset be null.
  8. Let previousLastMapping be null.
  9. For each section of JSONArrayIterate(sectionsField), do
    1. If section is not a JSON object, optionally report an error.
    2. Else,
      1. Let offset be JSONObjectGet(section, "offset").
      2. If offset is not a JSON object, throw an error.
      3. Let offsetLine be JSONObjectGet(offset, "line").
      4. Let offsetColumn be JSONObjectGet(offset, "column").
      5. If offsetLine is not an integral Number, then
        1. Optionally report an error.
        2. Set offsetLine to 0𝔽.
      6. If offsetColumn is not an integral Number, then
        1. Optionally report an error.
        2. Set offsetColumn to 0𝔽.
      7. If previousOffsetnull, then
        1. If offsetLine < JSONObjectGet(previousOffset, "line"), optionally report an error.
        2. Else if offsetLine = JSONObjectGet(previousOffset, "line") and offsetColumn < Get(previousOffset, "column"), optionally report an error.
      8. If previousLastMapping is not null, then
        1. If offsetLine < previousLastMapping.[[GeneratedLine]], optionally report an error.
        2. If offsetLine = previousLastMapping.[[GeneratedLine]] and offsetColumn < previousLastMapping.[[GeneratedColumn]], optionally report an error.
        3. 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.
      9. Let mapField be JSONObjectGet(section, "map").
      10. If mapField is not a JSON object, throw an error.
      11. Let decodedSectionCompletion be Completion(DecodeSourceMap(json, baseURL)).
      12. If decodedSectionCompletion is a throw completion, then
        1. Optionally report an error.
      13. Else,
        1. Let decodedSection be decodedSectionCompletion.[[Value]].
        2. 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]].
        3. Let offsetMappings be a new empty List.
        4. For each Decoded Mapping Record mapping of decodedSection.[[Mappings]], do
          1. If mapping.[[GeneratedLine]] = 0, then
            1. Set mapping.[[GeneratedColumn]] to mapping.[[GeneratedColumn]] + offsetColumn.
          2. Set mapping.[[GeneratedLine]] to mapping.[[GeneratedLine]] + offsetLine.
          3. Append mapping to offsetMappings.
        5. Set sourceMap.[[Mappings]] to the list-concatenation of sourceMap.[[Mappings]] and offsetMappings.
        6. Set previousOffset to offset.
        7. Let sortedMappings be a copy of offsetMappings, sorted in ascending order, with a Decoded Mapping Record a being less than a Decoded Mapping Record b if GeneratedPositionLessThan(a, b) is true.
        8. If sortedMappings is not empty, set previousLastMapping to the last element of sortedMappings.
    3. Return sourceMap.
Note
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.

8.1.1 GeneratedPositionLessThan ( a, b )

The abstract operation GeneratedPositionLessThan takes arguments a (a Decoded Mapping Record) and b (a Decoded Mapping Record) and returns a Boolean. It performs the following steps when called:

  1. If a.[[GeneratedLine]] < b.[[GeneratedLine]], return true.
  2. If a.[[GeneratedLine]] = b.[[GeneratedLine]] and a.[[GeneratedColumn]] < b.[[GeneratedColumn]], return true.
  3. Return false.

9 Retrieving source maps

9.1 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:

  • If the generated source is not associated with a script element that has a src attribute and there exists a //# sourceURL comment in the generated code, that comment should be used to determine the source origin.

    Note
    Previously, this was //@ sourceURL, as with //@ sourceMappingURL, it is reasonable to accept both but //# is preferred.
  • If the generated code is associated with a script element and the script element has a src attribute, the src attribute of the script element will be the source origin.
  • If the generated code is associated with a script element and the script element does not have a src attribute, then the source origin will be the page's origin.
  • If the generated code is being evaluated as a string with the eval() function or via new Function(), then the source origin will be the page's origin.

9.1.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.

9.1.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 shall do so unambiguously.

Note 1

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 null, while without parsing gives foo.js.map.

Note 2

Having multiple ways to extract a source map URL, that can lead to different results, can have negative security and privacy implications. Implementations that need to detect which source maps are potentially going to be loaded are strongly encouraged to always apply both algorithms, rather than just assuming that they will give the same result.

A fix to this problem is being worked on, and is expected to be included in a future version of the standard. It will likely involve early returning from the below algorithms whenever there is a comment (or comment-like) that contains the characters U+0060 (`), U+0022 ("), or U+0027 ('), or the the sequence U+002A U+002F (*/).

9.1.2.1 JavaScriptExtractSourceMapURL ( source )

The abstract operation JavaScriptExtractSourceMapURL takes argument source (a String) and returns a String or null. It extracts a source map URL from a JavaScript source. It has two possible implementations: either through parsing or without parsing.

To extract a source map URL through parsing:

  1. Let tokens be the List of tokens obtained by parsing source according to ECMA-262's lexical grammar.
  2. For each token in tokens, in reverse order, do:
    1. If token is not SingleLineComment or MultiLineComment, return null.
    2. Let comment be the content of token.
    3. Let sourceMapURL be MatchSourceMapURL(comment).
    4. If sourceMapURL is a String, return sourceMapURL.
  3. Return null.

To extract a source map URL without parsing:

  1. Let lines be StringSplit(mappings, « "\u000D\u000A", "\u000A", "\u000D", "\u2028", "\u2029" »).
  2. NOTE: The regular expression above matches the LineTerminatorSequence production.
  3. Let lastURL be null.
  4. For each lineStr in lines, do:
    1. Let line be StringToCodePoints(lineStr).
    2. Let position be 0.
    3. Let lineLength be the length of line.
    4. While position < lineLength,
      1. Let first be line[position].
      2. Set position to position + 1.
      3. If first is U+002F (SOLIDUS) and position < lineLength, then
        1. Let second be line[position].
        2. Set position to position + 1.
        3. If second is U+002F (SOLIDUS), then
          1. Let comment be the substring of lineStr from position to lineLength.
          2. Let sourceMapURL be MatchSourceMapURL(comment).
          3. If sourceMapURL is a String, set lastURL to sourceMapURL.
          4. Set position to lineLength.
        4. Else if second is U+002A (ASTERISK), then
          1. Let commentCp be a new empty List.
          2. While position + 1 < lineLength,
            1. Let c1 be line[position].
            2. Set position to position + 1.
            3. Let c2 be line[position].
            4. If c1 is U+002A (ASTERISK) and c2 is U+002F (SOLIDUS), then
              1. Set position to position + 1.
              2. Let sourceMapURL be MatchSourceMapURL(CodePointsToString(commentCp)).
              3. If sourceMapURL is a String, set lastURL to sourceMapURL.
            5. Append c1 to commentCp.
        5. Else, set lastURL to null.
      4. Else if first is not an ECMAScript WhiteSpace, then
        1. Set lastURL to null.
      5. NOTE: We reset lastURL to null whenever we find a non-comment code character.
  5. Return lastURL.
Note 1
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 2

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;

9.1.2.1.1 MatchSourceMapURL ( comment )

The abstract operation MatchSourceMapURL takes argument comment (a String) and returns either NONE or a String. It performs the following steps when called:

  1. Let pattern be RegExpCreate("^[@#]\s*sourceMappingURL=(\S*?)\s*$", "").
  2. Let match be RegExpExec(pattern, comment).
  3. If match is not null, return Get(match, "1").
  4. Return NONE.
Note
The prefix for this annotation was initially //@, however this conflicts with Internet Explorer's Conditional Compilation and was changed to //#.

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

9.1.2.2 CSSExtractSourceMapURL ( source )

The abstract operation CSSExtractSourceMapURL takes argument source (a String) and returns a String or null. It extracts a source map URL from a CSS source.

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

9.1.2.3 WebAssemblyExtractSourceMapURL ( bytes )

The abstract operation WebAssemblyExtractSourceMapURL takes argument bytes (a Data Block) and returns a String or null. It extracts a source map URL from a WebAssembly binary source.

  1. Let module be module_decode(bytes).
  2. If module is WebAssembly error, return null.
  3. For each custom section customSection of module,
    1. Let name be the name of customSection.
    2. If CodePointsToString(name) is "sourceMappingURL", then
      1. Let value be the bytes of customSection.
      2. Return CodePointsToString(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 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.

9.2 Fetching source maps

9.2.1 FetchSourceMap ( url )

The abstract operation FetchSourceMap takes argument url (an URL) and returns a Promise. It performs the following steps when called:

  1. Let promiseCapability be NewPromiseCapability(%Promise%).
  2. Let request be a new request whose request URL is url.
  3. Let processResponseConsumeBody be a new Abstract Closure with parameters (response, bodyBytes) that performs the following steps when called:
    1. If bodyBytes is null or failure, then
      1. Perform Call(promiseCapability.[[Reject]], undefined, « a new TypeError »).
      2. Return.
    2. If url's scheme is an HTTP(S) scheme and the byte sequence `)]}'` is a byte-sequence-prefix of bodyBytes, then
      1. While bodyBytes's length ≠ 0 and bodyBytes[0] is not an HTTP newline byte, remove the 0th element from bodyBytes.
    3. Let bodyString be Completion(UTF-8 decode of bodyBytes).
    4. IfAbruptRejectPromise(bodyString, promiseCapability).
    5. Let jsonValue be Completion(ParseJSON(bodyString)).
    6. IfAbruptRejectPromise(jsonValue, promiseCapability).
    7. Perform Call(promiseCapability.[[Resolve]], undefined, « jsonValue »).
  4. Perform fetch request with processResponseConsumeBody set to processResponseConsumeBody.
  5. Return promise.[[Promise]].
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, ...}

A Conventions

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

A.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.

A.2 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.

B Notes

B.1 Language neutral stack mapping

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

B.2 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.

C 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
WHATWG URL <https://url.spec.whatwg.org/>
HTTP(S) scheme, scheme, URL, URL parsing

D Bibliography

  1. IETF RFC 4648, The Base16, Base32, and Base64 Data Encodings, available at <https://datatracker.ietf.org/doc/html/rfc4648>
  2. ECMA-262, ECMAScript® Language Specification, available at <https://tc39.es/ecma262/>
  3. ECMA-404, The JSON Data Interchange Format, available at <https://www.ecma-international.org/publications-and-standards/standards/ecma-404/>
  4. WebAssembly Core Specification, available at <https://www.w3.org/TR/wasm-core-2/>
  5. WHATWG Encoding, available at <https://encoding.spec.whatwg.org/>
  6. WHATWG Fetch, available at <https://fetch.spec.whatwg.org/>
  7. WHATWG Infra, available at <https://infra.spec.whatwg.org/>
  8. WHATWG URL, available at <https://url.spec.whatwg.org/>
  9. 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/>
  10. Source Map Revision 2 Proposal, John Lenz (2010), available at <https://docs.google.com/document/d/1xi12LrcqjqIHTtZzrzZKmQ3lbTv9mKrN076UB-j3UZQ/>
  11. Variable-length quantity, Wikipedia, available at <https://en.wikipedia.org/wiki/Variable-length_quantity>

E 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.

F Copyright & Software License

Ecma International

Rue du Rhone 114

CH-1204 Geneva

Tel: +41 22 849 6000

Fax: +41 22 849 6001

Web: https://ecma-international.org/

Copyright Notice

© 2025 Ecma International

This draft document may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published, and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this section are included on all such copies and derivative works. However, this document itself may not be modified in any way, including by removing the copyright notice or references to Ecma International, except as needed for the purpose of developing any document or deliverable produced by Ecma International.

This disclaimer is valid only prior to final version of this document. After approval all rights on the standard are reserved by Ecma International.

The limited permissions are granted through the standardization phase and will not be revoked by Ecma International or its successors or assigns during this time.

This document and the information contained herein is provided on an "AS IS" basis and ECMA INTERNATIONAL DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.