Skip to main content

Source Map Format

Metro produces standard source maps along with its JavaScript bundle output. In addition to the standard information, Metro encodes extra information in vendor-specific fields within the source map. This page serves as a specification for this encoding.

note

The content on this page assumes familiarity with the source map specification. Check out Anatomy of source maps for a general introduction to source maps and how they work.

x_facebook_sources​

The x_facebook_sources field encodes metadata about source files in a source map. Each piece of metadata represents some attribute intrinsic to the source code of that particular file - for example, the result of running some analysis over the AST. This allows tools such as debuggers and JS engines to access such analyses efficiently, without needing to parse or even have access to the source code.

In the same way that the standard sources field is a list of source URLs and sourcesContent is a list of (optional) source code strings, x_facebook_sources is a list of optional metadata tuples. The i-th metadata tuple (x_facebook_sources[i]) corresponds to the source file whose URL is sources[i].

In nested (indexed) source maps, x_facebook_sources may appear as part of any nested source map in sections that itself has a sources field.

If present, x_facebook_sources may be a different length than sources (but usually shouldn't be). In particular, if it's shorter than sources, x_facebook_sources interpreted as if it were padded with null values to match the length of sources.

info

If you are writing a tool that processes source maps generated by Metro, and want to generate a new source map containing a valid x_facebook_sources field, you'll mainly need to ensure that x_facebook_sources[i] still corresponds to sources[i] in the output - even if your tool reorders, adds or deletes elements in sources. Notably, this can be done without parsing/decoding the metadata tuples: unless your tool actively needs to access the information within them, you can treat them as opaque blobs of JSON.

If a tool cannot guarantee that the sources and x_facebook_sources arrays will stay in sync, it should delete the x_facebook_sources field from its output.

Metadata tuple​

Each metadata tuple is encoded as an array of zero or more entries. Each entry may be null to signify that it's missing. A run of trailing nulls may be truncated from the end of the tuple with no change in meaning. The metadata tuple itself may also be null to signify that the source file has no associated metadata.

The indices in each metadata tuple are assigned as follows:

Function map​

A function map is encoded as an object with the following two fields:

  • names: An array of strings.
  • mappings: A string following the encoding described below.

When decoded, mappings represents a list of 3-tuples of integers: (column, nameIndex, line), (column, nameIndex, line), .... The list is ordered by line and then column.

The presence of a 3-tuple (column, nameIndex, line) means that the local function name in the code region beginning at line and column (in the source file described by the current metadata tuple) is names[nameIndex].

Function map mappings field encoding​

The value of the mappings field is described by the Mappings production of the grammar detailed below.

  1. Mappings = [ ";" ] LineMappings { ";" { ";" } LineMappings }
  2. LineMappings = FirstColumnMapping "," ColumnMapping
  3. FirstColumnMapping = VLQ VLQ VLQ
  4. ColumnMapping = VLQ VLQ [ VLQ ]
  5. VLQ = A single Base64-encoded variable-length quantity, as defined in the source map specification.
note

The above grammar uses the following BNF-like notation:

NotationMeaning
[ X ]X appears zero or 1 times.
{ X }X appears 0 or more times.
"foo"The literal characters foo.

The three VLQs in FirstColumnMapping or ColumnMapping represent, in this order:

  1. Column delta:
    • In FirstColumnMapping: The column offset from the beginning of the line. (0 = first column)
    • In ColumnMapping: The column offset from the last-encountered FirstColumnMapping or ColumnMapping.
  2. Name delta: The name index offset from the last-encountered FirstColumnMapping or ColumnMapping. This is not reset between lines.
  3. Line delta: The line offset from the last-encountered FirstColumnMapping or ColumnMapping. This is not reset between lines.
    • This MUST be 0 (Base64 VLQ: A) if it is part of a ColumnMapping.
    • Implementations SHOULD omit this field from the encoded form of ColumnMapping.

Example​

Given a single source file called file.js, a complete source map might look like this:

note

Comments are for illustrative purposes - the source map format does not allow comments.

{
"version": 3,
"sources": ["file.js"],
"sourcesContent": ["function a(){} function b(){}"],
"mappings": "AAAA", // NOTE: Simplified
"x_facebook_sources": [
// Metadata tuple for source #0 (file.js)
[
// Metadata item #0.0 = function map for source #0 (file.js)
{
// a from 1:0
// <global> from 1:14
// b from 1:15
// (See detailed decoding procedure below.)
"mappings": "AAA,cC,CC",
"names": [
"a",
"<global>",
"b",
]
}
]
]
}

The decoding procedure for the function map in the above example is illustrated by the following code:

const decoded = [];
const names = ['a', '<global>', 'b']; // From the function map

let column = 0, nameIndex = 0, line = 1;
column += 0 /* A */; nameIndex += 0 /* A */; line += 0 /* A */;
decoded.push({column, name: names[nameIndex] /* 'a' */, line});

column += 14 /* c */; nameIndex += 1 /* C */; // no line delta
decoded.push({column, name: names[nameIndex] /* '<global>' */, line});

column += 1 /* C */; nameIndex += 1 /* C */; // no line delta
decoded.push({column, name: names[nameIndex] /* 'b' */, line});

/*
decoded = [
{column: 0, name: 'a', line: 1},
{column: 14, name: '<global>', line: 1},
{column: 15, name: 'b', line: 1},
]
*/

x_google_ignoreList​

Metro's source maps include the x_google_ignoreList field by default. The serializer.isThirdPartyModule option can be used to control which modules are ignore-listed.