Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/metro-config/src/defaults/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ const getDefaultValues = (projectRoot: ?string): ConfigT => ({
unstable_disableNormalizePseudoGlobals: false,
unstable_renameRequire: true,
unstable_compactOutput: false,
unstable_compactSourceMaps: false,
unstable_memoizeInlineRequires: false,
unstable_workerThreads: false,
},
Expand Down
224 changes: 223 additions & 1 deletion packages/metro-source-map/src/__tests__/source-map-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@
* @oncall react_native
*/

import type {BabelDecodedMap, MetroSourceMapSegmentTuple} from '../source-map';

import Generator from '../Generator';
import {fromRawMappings, toBabelSegments, toSegmentTuple} from '../source-map';
import {
fromRawMappings,
isVlqMap,
toBabelSegments,
toSegmentTuple,
vlqMapFromBabelDecodedMap,
vlqMapFromTuples,
} from '../source-map';

describe('flattening mappings / compacting', () => {
test('flattens simple mappings', () => {
Expand Down Expand Up @@ -167,3 +176,216 @@ describe('build map from raw mappings', () => {
});

const lines = (n: number) => Array(n).join('\n');

function makeVlqMap(
mappings: string,
names: ReadonlyArray<string>,
): {readonly mappings: string, readonly names: ReadonlyArray<string>} {
return {
mappings,
names,
};
}

describe('isVlqMap', () => {
test('returns false for null', () => {
expect(isVlqMap(null)).toBe(false);
});

test('returns false for tuple array', () => {
expect(isVlqMap([[1, 2, 3, 4]])).toBe(false);
});

test('returns true for VlqMap', () => {
expect(isVlqMap(makeVlqMap('AAAA', []))).toBe(true);
});

test('returns false for plain object without string mappings', () => {
// $FlowFixMe[incompatible-type] Testing runtime behavior with invalid type
expect(isVlqMap({mappings: 123, names: []})).toBe(false);
});
});

describe('fromRawMappings with VlqMap', () => {
// Shared tuple definitions. We build two parallel module lists from these —
// one storing decoded tuples, one storing the equivalent VLQ — and assert the
// serialized flat map is byte-identical, i.e. VLQ storage is transparent.
const tuples0: Array<MetroSourceMapSegmentTuple> = [
[1, 2],
[3, 4, 5, 6, 'apples'],
[7, 8, 9, 10],
[11, 12, 13, 14, 'pears'],
];
const tuples1: Array<MetroSourceMapSegmentTuple> = [
[1, 2],
[3, 4, 15, 16, 'bananas'],
];

const tupleModules = [
{
code: lines(11),
functionMap: {names: ['<global>'], mappings: 'AAA'},
map: tuples0,
source: 'code1',
path: 'path1',
isIgnored: false,
},
{
code: lines(3),
functionMap: null,
map: tuples1,
source: 'code2',
path: 'path2',
isIgnored: true,
},
];

const vlqModules = [
{...tupleModules[0], map: vlqMapFromTuples(tuples0)},
{...tupleModules[1], map: vlqMapFromTuples(tuples1)},
];

test('produces a flat (non-indexed) map for VlqMap inputs', () => {
const map = fromRawMappings(vlqModules).toMap();
expect(typeof map.mappings).toBe('string');
expect(map.sources).toEqual(['path1', 'path2']);
expect(map.version).toBe(3);
});

test('VlqMap input serializes byte-identically to tuple input', () => {
expect(fromRawMappings(vlqModules).toString()).toBe(
fromRawMappings(tupleModules).toString(),
);
expect(fromRawMappings(vlqModules).toMap()).toEqual(
fromRawMappings(tupleModules).toMap(),
);
});

test('preserves functionMap and ignoreList from VlqMap modules', () => {
const map = fromRawMappings(vlqModules).toMap();
expect(map.x_facebook_sources).toEqual([
[{names: ['<global>'], mappings: 'AAA'}],
null,
]);
expect(map.x_google_ignoreList).toEqual([1]);
});

test('handles mixed tuple and VlqMap modules identically to all-tuple', () => {
const mixed = [tupleModules[0], vlqModules[1]];
expect(fromRawMappings(mixed).toString()).toBe(
fromRawMappings(tupleModules).toString(),
);
});

test('applies offsetLines identically for VlqMap and tuple inputs', () => {
expect(fromRawMappings(vlqModules, 8).toString()).toBe(
fromRawMappings(tupleModules, 8).toString(),
);
});

test('excludeSource option omits sourcesContent', () => {
const map = fromRawMappings(vlqModules).toMap(undefined, {
excludeSource: true,
});
expect(map.sourcesContent).toBeUndefined();
});
});

describe('vlqMapFromTuples', () => {
// Decode via Metro's existing string->tuples path, the inverse of
// vlqMapFromTuples.
const decode = (vlqMap: {
readonly mappings: string,
readonly names: ReadonlyArray<string>,
}) =>
toBabelSegments({
version: 3,
sources: [''],
names: [...vlqMap.names],
mappings: vlqMap.mappings,
}).map(toSegmentTuple);

test('encodes tuples into a VlqMap', () => {
const vlqMap = vlqMapFromTuples([
[1, 2],
[3, 4, 5, 6, 'apples'],
[7, 8, 9, 10],
[11, 12, 13, 14, 'pears'],
]);
expect(isVlqMap(vlqMap)).toBe(true);
expect(typeof vlqMap.mappings).toBe('string');
expect(vlqMap.names).toEqual(['apples', 'pears']);
});

test('round-trips via toBabelSegments + toSegmentTuple', () => {
const tuples = [
[1, 2],
[3, 4, 5, 6, 'apples'],
[7, 8, 9, 10],
[11, 12, 13, 14, 'pears'],
[11, 20, 30, 40],
];
expect(decode(vlqMapFromTuples(tuples))).toEqual(tuples);
});

test('round-trips multi-line, multi-segment maps', () => {
const tuples = [
[1, 0, 1, 0],
[1, 8, 1, 4, 'foo'],
[2, 0, 2, 0],
[3, 4, 3, 2, 'bar'],
[5, 0],
];
expect(decode(vlqMapFromTuples(tuples))).toEqual(tuples);
});

test('encodes an empty map', () => {
const vlqMap = vlqMapFromTuples([]);
expect(vlqMap.mappings).toBe('');
expect(decode(vlqMap)).toEqual([]);
});
});

describe('vlqMapFromBabelDecodedMap', () => {
test('matches vlqMapFromTuples, appending a terminator when needed', () => {
// Decoded format: grouped by generated line (0-based), source lines 0-based.
const decodedMap: BabelDecodedMap = {
names: ['foo'],
mappings: [
[[0, 0, 0, 0]], // gen 1:0 -> src 1:0
[[2, 0, 0, 4, 0]], // gen 2:2 -> src 1:4 name 'foo'
[[0]], // gen 3:0 generated-only
],
};
// Equivalent Metro tuples (source lines 1-based) + terminator at gen 3:5.
const tuples: Array<MetroSourceMapSegmentTuple> = [
[1, 0, 1, 0],
[2, 2, 1, 4, 'foo'],
[3, 0],
[3, 5],
];
expect(vlqMapFromBabelDecodedMap(decodedMap, [3, 5])).toEqual(
vlqMapFromTuples(tuples),
);
});

test('does not append a terminator already present', () => {
const decodedMap: BabelDecodedMap = {
names: [],
mappings: [[[0], [5]]],
};
expect(vlqMapFromBabelDecodedMap(decodedMap, [1, 5])).toEqual(
vlqMapFromTuples([
[1, 0],
[1, 5],
]),
);
});

test('handles an empty decoded map (terminator only)', () => {
const decodedMap: BabelDecodedMap = {names: [], mappings: []};
expect(vlqMapFromBabelDecodedMap(decodedMap, [1, 0])).toEqual(
vlqMapFromTuples([[1, 0]]),
);
});
});
Loading
Loading