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.
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
.
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 null
s 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:
- Index 0: Function map or
null
.- In Metro, this is the result of calling
generateFunctionMap
on the source AST.
- In Metro, this is the result of calling
- Index 1-β: Reserved for future use.
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.
Mappings = [ ";" ] LineMappings { ";" { ";" } LineMappings }
LineMappings = FirstColumnMapping "," ColumnMapping
FirstColumnMapping = VLQ VLQ VLQ
ColumnMapping = VLQ VLQ [ VLQ ]
VLQ =
A single Base64-encoded variable-length quantity, as defined in the source map specification.
The above grammar uses the following BNF-like notation:
Notation | Meaning |
---|---|
[ 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:
- 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.
- Name delta: The name index offset from the last-encountered FirstColumnMapping or ColumnMapping. This is not reset between lines.
- 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.
- This MUST be 0 (Base64 VLQ:
Exampleβ
Given a single source file called file.js
, a complete source map might look like this:
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.