From 66914bf28c8fb6073cf23d441e2c01d99a562015 Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Thu, 16 Feb 2023 07:05:10 -0800 Subject: [PATCH] Handle "simple" (missing origin) source mappings in minifier output Summary: Fixes https://github.com/facebook/metro/issues/927 Terser, unlike the previous default minifier UglifyES, produces source maps whose entries may contain "simple mappings" - that is entries with a generated line+col but no original location. When Metro merges source maps to serialize minified output, it doesn't correctly handle these entries. Instead it incorrectly detects them as complete mappings and produces a merged source map with `-1` origin line numbers in place of simple mappings. Some tools will reject the whole source map (which is otherwise correct) based on the appearance of a negative line number. The issue comes down to the conversion of raw mappings to `BabelSourceMapSegments` in `toBabelSegments`, which currently generates entries for simple mappings with `null` `origin` *properties*, such as: ``` { generated: { column: 2, line: 1 }, origin: { column: null, line: null }, // ... } ``` According to the Flow type for `BabelSourceMapSegments`, that's incorrect. `origin` itself is optional and should be missing - the properties of `origin` are not nullable. Based on the presence of the `origin` property in that object, `toSegmentTuple` generates a 4-tuple and we go on to add a "source mapping" rather than a "simple mapping". This fixes `toBabelSegments` to omit `origin` unless `line` and `column` are both populated. The final source map output is then correct. Differential Revision: D43351987 fbshipit-source-id: 5ba5e9edc6913d1feaf7a6b1827ad43e579fbada --- .../__snapshots__/source-map-test.js.snap | 12 ------- packages/metro-source-map/src/source-map.js | 35 ++++++++++++------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/packages/metro-source-map/src/__tests__/__snapshots__/source-map-test.js.snap b/packages/metro-source-map/src/__tests__/__snapshots__/source-map-test.js.snap index f7ad4c3cdb..83d248696a 100644 --- a/packages/metro-source-map/src/__tests__/__snapshots__/source-map-test.js.snap +++ b/packages/metro-source-map/src/__tests__/__snapshots__/source-map-test.js.snap @@ -8,10 +8,6 @@ Array [ "line": 1, }, "name": null, - "original": Object { - "column": null, - "line": null, - }, "source": null, }, Object { @@ -56,10 +52,6 @@ Array [ "line": 12, }, "name": null, - "original": Object { - "column": null, - "line": null, - }, "source": null, }, Object { @@ -80,10 +72,6 @@ Array [ "line": 25, }, "name": null, - "original": Object { - "column": null, - "line": null, - }, "source": null, }, Object { diff --git a/packages/metro-source-map/src/source-map.js b/packages/metro-source-map/src/source-map.js index 29935b247e..3c507086ea 100644 --- a/packages/metro-source-map/src/source-map.js +++ b/packages/metro-source-map/src/source-map.js @@ -206,18 +206,29 @@ function toBabelSegments( const rawMappings: Array = []; new SourceMap.SourceMapConsumer(sourceMap).eachMapping(map => { - rawMappings.push({ - generated: { - line: map.generatedLine, - column: map.generatedColumn, - }, - original: { - line: map.originalLine, - column: map.originalColumn, - }, - source: map.source, - name: map.name, - }); + rawMappings.push( + map.originalLine == null || map.originalColumn == null + ? { + generated: { + line: map.generatedLine, + column: map.generatedColumn, + }, + source: map.source, + name: map.name, + } + : { + generated: { + line: map.generatedLine, + column: map.generatedColumn, + }, + original: { + line: map.originalLine, + column: map.originalColumn, + }, + source: map.source, + name: map.name, + }, + ); }); return rawMappings;