metro-source-map 0.54.1 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ * @format
9
+ */
10
+ 'use strict';
11
+
12
+ // eslint-disable-next-line lint/sort-requires
13
+ const Consumer = require('./Consumer');
14
+ const {SourceMapGenerator} = require('source-map');
15
+
16
+ import type {IConsumer, MixedSourceMap} from './source-map';
17
+ import type {Number0, Number1} from 'ob1';
18
+
19
+ // Originally based on https://github.com/jakobwesthoff/source-map-merger
20
+ function composeSourceMaps(
21
+ maps: $ReadOnlyArray<MixedSourceMap>,
22
+ ): MixedSourceMap {
23
+ // NOTE: require() here to break dependency cycle
24
+ const SourceMetadataMapConsumer = require('metro-symbolicate/src/SourceMetadataMapConsumer');
25
+ if (maps.length < 1) {
26
+ throw new Error('composeSourceMaps: Expected at least one map');
27
+ }
28
+ const firstMap = maps[0];
29
+
30
+ const consumers = maps
31
+ .map(function(map) {
32
+ return new Consumer(map);
33
+ })
34
+ .reverse();
35
+
36
+ const generator = new SourceMapGenerator({
37
+ file: consumers[0].file,
38
+ });
39
+
40
+ consumers[0].eachMapping(mapping => {
41
+ const original = findOriginalPosition(
42
+ consumers,
43
+ mapping.generatedLine,
44
+ mapping.generatedColumn,
45
+ );
46
+ generator.addMapping({
47
+ generated: {
48
+ line: mapping.generatedLine,
49
+ column: mapping.generatedColumn,
50
+ },
51
+ original:
52
+ original.line != null
53
+ ? {
54
+ line: original.line,
55
+ column: original.column,
56
+ }
57
+ : null,
58
+ source: original.source,
59
+ name: original.name,
60
+ });
61
+ });
62
+
63
+ const composedMap = generator.toJSON();
64
+
65
+ const metadataConsumer = new SourceMetadataMapConsumer(firstMap);
66
+ composedMap.x_facebook_sources = metadataConsumer.toArray(
67
+ composedMap.sources,
68
+ );
69
+ return composedMap;
70
+ }
71
+
72
+ function findOriginalPosition(
73
+ consumers: $ReadOnlyArray<IConsumer>,
74
+ generatedLine: Number1,
75
+ generatedColumn: Number0,
76
+ ): {
77
+ line: ?number,
78
+ column: ?number,
79
+ source: ?string,
80
+ name: ?string,
81
+ } {
82
+ let currentLine = generatedLine;
83
+ let currentColumn = generatedColumn;
84
+ let original = {
85
+ line: null,
86
+ column: null,
87
+ source: null,
88
+ name: null,
89
+ };
90
+
91
+ for (const consumer of consumers) {
92
+ if (currentLine == null || currentColumn == null) {
93
+ return {line: null, column: null, source: null, name: null};
94
+ }
95
+ original = consumer.originalPositionFor({
96
+ line: currentLine,
97
+ column: currentColumn,
98
+ });
99
+
100
+ currentLine = original.line;
101
+ currentColumn = original.column;
102
+
103
+ if (currentLine == null) {
104
+ return {
105
+ line: null,
106
+ column: null,
107
+ source: null,
108
+ name: null,
109
+ };
110
+ }
111
+ }
112
+ return original;
113
+ }
114
+
115
+ module.exports = composeSourceMaps;
package/src/source-map.js CHANGED
@@ -9,10 +9,49 @@
9
9
  */
10
10
  "use strict";
11
11
 
12
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
13
+ try {
14
+ var info = gen[key](arg);
15
+ var value = info.value;
16
+ } catch (error) {
17
+ reject(error);
18
+ return;
19
+ }
20
+ if (info.done) {
21
+ resolve(value);
22
+ } else {
23
+ Promise.resolve(value).then(_next, _throw);
24
+ }
25
+ }
26
+
27
+ function _asyncToGenerator(fn) {
28
+ return function() {
29
+ var self = this,
30
+ args = arguments;
31
+ return new Promise(function(resolve, reject) {
32
+ var gen = fn.apply(self, args);
33
+ function _next(value) {
34
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
35
+ }
36
+ function _throw(err) {
37
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
38
+ }
39
+ _next(undefined);
40
+ });
41
+ };
42
+ }
43
+
44
+ const Consumer = require("./Consumer");
45
+
12
46
  const Generator = require("./Generator");
13
47
 
14
48
  const SourceMap = require("source-map");
15
49
 
50
+ // We need to export this for `metro-symbolicate`
51
+ const normalizeSourcePath = require("./Consumer/normalizeSourcePath");
52
+
53
+ const composeSourceMaps = require("./composeSourceMaps");
54
+
16
55
  const _require = require("./BundleBuilder"),
17
56
  createIndexMap = _require.createIndexMap,
18
57
  BundleBuilder = _require.BundleBuilder;
@@ -20,42 +59,109 @@ const _require = require("./BundleBuilder"),
20
59
  const _require2 = require("./generateFunctionMap"),
21
60
  generateFunctionMap = _require2.generateFunctionMap;
22
61
 
23
- /**
24
- * Creates a source map from modules with "raw mappings", i.e. an array of
25
- * tuples with either 2, 4, or 5 elements:
26
- * generated line, generated column, source line, source line, symbol name.
27
- * Accepts an `offsetLines` argument in case modules' code is to be offset in
28
- * the resulting bundle, e.g. by some prefix code.
29
- */
30
- function fromRawMappings(modules) {
31
- let offsetLines =
32
- arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
62
+ function fromRawMappingsImpl(isBlocking, onDone, modules, offsetLines) {
63
+ const modulesToProcess = modules.slice();
33
64
  const generator = new Generator();
34
65
  let carryOver = offsetLines;
35
66
 
36
- for (var j = 0, o = modules.length; j < o; ++j) {
37
- var module = modules[j];
38
- var code = module.code,
39
- map = module.map;
67
+ function processNextModule() {
68
+ if (modulesToProcess.length === 0) {
69
+ return true;
70
+ }
71
+
72
+ const mod = modulesToProcess.shift();
73
+ const code = mod.code,
74
+ map = mod.map;
40
75
 
41
76
  if (Array.isArray(map)) {
42
- addMappingsForFile(generator, map, module, carryOver);
77
+ addMappingsForFile(generator, map, mod, carryOver);
43
78
  } else if (map != null) {
44
79
  throw new Error(
45
- `Unexpected module with full source map found: ${module.path}`
80
+ `Unexpected module with full source map found: ${mod.path}`
46
81
  );
47
82
  }
48
83
 
49
84
  carryOver = carryOver + countLines(code);
85
+ return false;
86
+ }
87
+
88
+ function workLoop() {
89
+ const time = process.hrtime();
90
+
91
+ while (true) {
92
+ const isDone = processNextModule();
93
+
94
+ if (isDone) {
95
+ onDone(generator);
96
+ break;
97
+ }
98
+
99
+ if (!isBlocking) {
100
+ // Keep the loop running but try to avoid blocking
101
+ // for too long because this is not in a worker yet.
102
+ const diff = process.hrtime(time);
103
+ const NS_IN_MS = 1000000;
104
+
105
+ if (diff[1] > 50 * NS_IN_MS) {
106
+ // We've blocked for more than 50ms.
107
+ // This code currently runs on the main thread,
108
+ // so let's give Metro an opportunity to handle requests.
109
+ setImmediate(workLoop);
110
+ break;
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ workLoop();
117
+ }
118
+ /**
119
+ * Creates a source map from modules with "raw mappings", i.e. an array of
120
+ * tuples with either 2, 4, or 5 elements:
121
+ * generated line, generated column, source line, source line, symbol name.
122
+ * Accepts an `offsetLines` argument in case modules' code is to be offset in
123
+ * the resulting bundle, e.g. by some prefix code.
124
+ */
125
+
126
+ function fromRawMappings(modules) {
127
+ let offsetLines =
128
+ arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
129
+ let generator;
130
+ fromRawMappingsImpl(
131
+ true,
132
+ g => {
133
+ generator = g;
134
+ },
135
+ modules,
136
+ offsetLines
137
+ );
138
+
139
+ if (generator == null) {
140
+ throw new Error("Expected fromRawMappingsImpl() to finish synchronously.");
50
141
  }
51
142
 
52
143
  return generator;
53
144
  }
145
+
146
+ function fromRawMappingsNonBlocking(_x) {
147
+ return _fromRawMappingsNonBlocking.apply(this, arguments);
148
+ }
54
149
  /**
55
150
  * Transforms a standard source map object into a Raw Mappings object, to be
56
151
  * used across the bundler.
57
152
  */
58
153
 
154
+ function _fromRawMappingsNonBlocking() {
155
+ _fromRawMappingsNonBlocking = _asyncToGenerator(function*(modules) {
156
+ let offsetLines =
157
+ arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
158
+ return new Promise(resolve => {
159
+ fromRawMappingsImpl(false, resolve, modules, offsetLines);
160
+ });
161
+ });
162
+ return _fromRawMappingsNonBlocking.apply(this, arguments);
163
+ }
164
+
59
165
  function toBabelSegments(sourceMap) {
60
166
  const rawMappings = [];
61
167
  new SourceMap.SourceMapConsumer(sourceMap).eachMapping(map => {
@@ -94,7 +200,7 @@ function toSegmentTuple(mapping) {
94
200
  }
95
201
 
96
202
  function addMappingsForFile(generator, mappings, module, carryOver) {
97
- generator.startFile(module.path, module.source);
203
+ generator.startFile(module.path, module.source, module.functionMap);
98
204
 
99
205
  for (let i = 0, n = mappings.length; i < n; ++i) {
100
206
  addMapping(generator, mappings[i], carryOver);
@@ -134,9 +240,13 @@ function countLines(string) {
134
240
 
135
241
  module.exports = {
136
242
  BundleBuilder,
243
+ composeSourceMaps,
244
+ Consumer,
137
245
  createIndexMap,
138
246
  generateFunctionMap,
139
247
  fromRawMappings,
248
+ fromRawMappingsNonBlocking,
249
+ normalizeSourcePath,
140
250
  toBabelSegments,
141
251
  toSegmentTuple
142
252
  };
@@ -10,13 +10,20 @@
10
10
 
11
11
  'use strict';
12
12
 
13
+ const Consumer = require('./Consumer');
13
14
  const Generator = require('./Generator');
14
15
  const SourceMap = require('source-map');
15
16
 
17
+ import type {IConsumer} from './Consumer/types.flow';
18
+ export type {IConsumer};
19
+
20
+ // We need to export this for `metro-symbolicate`
21
+ const normalizeSourcePath = require('./Consumer/normalizeSourcePath');
22
+
23
+ const composeSourceMaps = require('./composeSourceMaps');
16
24
  const {createIndexMap, BundleBuilder} = require('./BundleBuilder');
17
25
  const {generateFunctionMap} = require('./generateFunctionMap');
18
26
 
19
- import type {BabelSourceMap} from '@babel/core';
20
27
  import type {BabelSourceMapSegment} from '@babel/generator';
21
28
 
22
29
  type GeneratedCodeMapping = [number, number];
@@ -28,12 +35,6 @@ export type MetroSourceMapSegmentTuple =
28
35
  | SourceMapping
29
36
  | GeneratedCodeMapping;
30
37
 
31
- type FBExtensions = {
32
- x_facebook_offsets: Array<number>,
33
- x_metro_module_paths: Array<string>,
34
- x_facebook_sources?: FBSourcesArray,
35
- };
36
-
37
38
  export type FBSourcesArray = $ReadOnlyArray<?FBSourceMetadata>;
38
39
  export type FBSourceMetadata = [?FBSourceFunctionMap];
39
40
  export type FBSourceFunctionMap = {|
@@ -41,66 +42,156 @@ export type FBSourceFunctionMap = {|
41
42
  +mappings: string,
42
43
  |};
43
44
 
45
+ export type FBSegmentMap = {
46
+ [id: string]: MixedSourceMap,
47
+ };
48
+
49
+ export type BasicSourceMap = {|
50
+ +file?: string,
51
+ +mappings: string,
52
+ +names: Array<string>,
53
+ +sourceRoot?: string,
54
+ +sources: Array<string>,
55
+ +sourcesContent?: Array<?string>,
56
+ +version: number,
57
+ +x_facebook_offsets?: Array<number>,
58
+ +x_metro_module_paths?: Array<string>,
59
+ +x_facebook_sources?: FBSourcesArray,
60
+ +x_facebook_segments?: FBSegmentMap,
61
+ |};
62
+
44
63
  export type IndexMapSection = {
45
- map: MetroSourceMap,
64
+ map: IndexMap | BasicSourceMap,
46
65
  offset: {line: number, column: number},
47
66
  };
48
67
 
49
- export type IndexMap = {
50
- file?: string,
51
- mappings?: void, // avoids SourceMap being a disjoint union
52
- sections: Array<IndexMapSection>,
53
- version: number,
54
- };
68
+ export type IndexMap = {|
69
+ +file?: string,
70
+ +mappings?: void, // avoids SourceMap being a disjoint union
71
+ +sections: Array<IndexMapSection>,
72
+ +version: number,
73
+ +x_facebook_offsets?: Array<number>,
74
+ +x_metro_module_paths?: Array<string>,
75
+ +x_facebook_sources?: FBSourcesArray,
76
+ +x_facebook_segments?: FBSegmentMap,
77
+ |};
55
78
 
56
- export type FBIndexMap = IndexMap & FBExtensions;
57
- export type MetroSourceMap = IndexMap | BabelSourceMap;
58
- export type FBBasicSourceMap = BabelSourceMap & FBExtensions;
59
- export type FBSourceMap = FBIndexMap | (BabelSourceMap & FBExtensions);
79
+ export type MixedSourceMap = IndexMap | BasicSourceMap;
60
80
 
61
- /**
62
- * Creates a source map from modules with "raw mappings", i.e. an array of
63
- * tuples with either 2, 4, or 5 elements:
64
- * generated line, generated column, source line, source line, symbol name.
65
- * Accepts an `offsetLines` argument in case modules' code is to be offset in
66
- * the resulting bundle, e.g. by some prefix code.
67
- */
68
- function fromRawMappings(
81
+ function fromRawMappingsImpl(
82
+ isBlocking: boolean,
83
+ onDone: Generator => void,
69
84
  modules: $ReadOnlyArray<{
70
85
  +map: ?Array<MetroSourceMapSegmentTuple>,
86
+ +functionMap: ?FBSourceFunctionMap,
71
87
  +path: string,
72
88
  +source: string,
73
89
  +code: string,
74
90
  }>,
75
- offsetLines: number = 0,
76
- ): Generator {
91
+ offsetLines: number,
92
+ ): void {
93
+ const modulesToProcess = modules.slice();
77
94
  const generator = new Generator();
78
95
  let carryOver = offsetLines;
79
96
 
80
- for (var j = 0, o = modules.length; j < o; ++j) {
81
- var module = modules[j];
82
- var {code, map} = module;
97
+ function processNextModule() {
98
+ if (modulesToProcess.length === 0) {
99
+ return true;
100
+ }
83
101
 
102
+ const mod = modulesToProcess.shift();
103
+ const {code, map} = mod;
84
104
  if (Array.isArray(map)) {
85
- addMappingsForFile(generator, map, module, carryOver);
105
+ addMappingsForFile(generator, map, mod, carryOver);
86
106
  } else if (map != null) {
87
107
  throw new Error(
88
- `Unexpected module with full source map found: ${module.path}`,
108
+ `Unexpected module with full source map found: ${mod.path}`,
89
109
  );
90
110
  }
91
-
92
111
  carryOver = carryOver + countLines(code);
112
+ return false;
93
113
  }
94
114
 
115
+ function workLoop() {
116
+ const time = process.hrtime();
117
+ while (true) {
118
+ const isDone = processNextModule();
119
+ if (isDone) {
120
+ onDone(generator);
121
+ break;
122
+ }
123
+ if (!isBlocking) {
124
+ // Keep the loop running but try to avoid blocking
125
+ // for too long because this is not in a worker yet.
126
+ const diff = process.hrtime(time);
127
+ const NS_IN_MS = 1000000;
128
+ if (diff[1] > 50 * NS_IN_MS) {
129
+ // We've blocked for more than 50ms.
130
+ // This code currently runs on the main thread,
131
+ // so let's give Metro an opportunity to handle requests.
132
+ setImmediate(workLoop);
133
+ break;
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ workLoop();
140
+ }
141
+
142
+ /**
143
+ * Creates a source map from modules with "raw mappings", i.e. an array of
144
+ * tuples with either 2, 4, or 5 elements:
145
+ * generated line, generated column, source line, source line, symbol name.
146
+ * Accepts an `offsetLines` argument in case modules' code is to be offset in
147
+ * the resulting bundle, e.g. by some prefix code.
148
+ */
149
+ function fromRawMappings(
150
+ modules: $ReadOnlyArray<{
151
+ +map: ?Array<MetroSourceMapSegmentTuple>,
152
+ +functionMap: ?FBSourceFunctionMap,
153
+ +path: string,
154
+ +source: string,
155
+ +code: string,
156
+ }>,
157
+ offsetLines: number = 0,
158
+ ): Generator {
159
+ let generator: void | Generator;
160
+ fromRawMappingsImpl(
161
+ true,
162
+ g => {
163
+ generator = g;
164
+ },
165
+ modules,
166
+ offsetLines,
167
+ );
168
+ if (generator == null) {
169
+ throw new Error('Expected fromRawMappingsImpl() to finish synchronously.');
170
+ }
95
171
  return generator;
96
172
  }
97
173
 
174
+ async function fromRawMappingsNonBlocking(
175
+ modules: $ReadOnlyArray<{
176
+ +map: ?Array<MetroSourceMapSegmentTuple>,
177
+ +functionMap: ?FBSourceFunctionMap,
178
+ +path: string,
179
+ +source: string,
180
+ +code: string,
181
+ }>,
182
+ offsetLines: number = 0,
183
+ ): Promise<Generator> {
184
+ return new Promise(resolve => {
185
+ fromRawMappingsImpl(false, resolve, modules, offsetLines);
186
+ });
187
+ }
188
+
98
189
  /**
99
190
  * Transforms a standard source map object into a Raw Mappings object, to be
100
191
  * used across the bundler.
101
192
  */
102
193
  function toBabelSegments(
103
- sourceMap: BabelSourceMap,
194
+ sourceMap: BasicSourceMap,
104
195
  ): Array<BabelSourceMapSegment> {
105
196
  const rawMappings = [];
106
197
 
@@ -140,7 +231,7 @@ function toSegmentTuple(
140
231
  }
141
232
 
142
233
  function addMappingsForFile(generator, mappings, module, carryOver) {
143
- generator.startFile(module.path, module.source);
234
+ generator.startFile(module.path, module.source, module.functionMap);
144
235
 
145
236
  for (let i = 0, n = mappings.length; i < n; ++i) {
146
237
  addMapping(generator, mappings[i], carryOver);
@@ -181,9 +272,13 @@ function countLines(string) {
181
272
 
182
273
  module.exports = {
183
274
  BundleBuilder,
275
+ composeSourceMaps,
276
+ Consumer,
184
277
  createIndexMap,
185
278
  generateFunctionMap,
186
279
  fromRawMappings,
280
+ fromRawMappingsNonBlocking,
281
+ normalizeSourcePath,
187
282
  toBabelSegments,
188
283
  toSegmentTuple,
189
284
  };