[DevTools] Only call originalPositionFor once (#22181)
Juan committed
Sep 7, 2021 at 10:39 UTC
abbc79d6fdaf4802951f52270b757c068cebe826
1 file changed
+66
-48
packages/react-devtools-extensions/src/parseHookNames/parseSourceAndMetadata.js
+66
-48
@@ -47,6 +47,12 @@ type HookParsedMetadata = {|
47
// Original source URL if there is a source map, or the same as runtimeSourceURL.
48
originalSourceURL: string | null,
49
50
+ // Line number in original source code.
51
+ originalSourceLineNumber: number | null,
52
+
53
+ // Column number in original source code.
54
+ originalSourceColumnNumber: number | null,
55
+
56
// APIs from source-map for parsing source maps (if detected).
57
sourceConsumer: SourceConsumer | null,
58
|};
@@ -151,47 +157,18 @@ function findHookNames(
157
return null; // Should not be reachable.
158
}
159
154
- const {originalSourceURL, sourceConsumer} = hookParsedMetadata;
155
-
156
- let originalSourceColumnNumber;
157
- let originalSourceLineNumber;
158
- if (areSourceMapsAppliedToErrors() || !sourceConsumer) {
159
- // Either the current environment automatically applies source maps to errors,
160
- // or the current code had no source map to begin with.
161
- // Either way, we don't need to convert the Error stack frame locations.
162
- originalSourceColumnNumber = columnNumber;
163
- originalSourceLineNumber = lineNumber;
164
- } else {
165
- // TODO (named hooks) Refactor this read, github.com/facebook/react/pull/22181
166
- const position = withSyncPerformanceMark(
167
- 'sourceConsumer.originalPositionFor()',
168
- () =>
169
- sourceConsumer.originalPositionFor({
170
- line: lineNumber,
171
-
172
- // Column numbers are represented differently between tools/engines.
173
- // Error.prototype.stack columns are 1-based (like most IDEs) but ASTs are 0-based.
174
- // For more info see https://github.com/facebook/react/issues/21792#issuecomment-873171991
175
- column: columnNumber - 1,
176
- }),
177
- );
178
-
179
- originalSourceColumnNumber = position.column;
180
- originalSourceLineNumber = position.line;
181
- }
182
-
183
- if (__DEBUG__) {
184
- console.log(
185
- `findHookNames() mapped line ${lineNumber}->${originalSourceLineNumber} and column ${columnNumber}->${originalSourceColumnNumber}`,
186
- );
187
- }
160
+ const {
161
+ originalSourceURL,
162
+ originalSourceColumnNumber,
163
+ originalSourceLineNumber,
164
+ } = hookParsedMetadata;
165
166
if (
167
originalSourceLineNumber == null ||
168
originalSourceColumnNumber == null ||
169
originalSourceURL == null
170
) {
194
- return null;
171
+ return null; // Should not be reachable.
172
}
173
174
let name;
@@ -241,6 +218,8 @@ function initializeHookParsedMetadata(
218
originalSourceAST: null,
219
originalSourceCode: null,
220
originalSourceURL: null,
221
+ originalSourceLineNumber: null,
222
+ originalSourceColumnNumber: null,
223
sourceConsumer: null,
224
};
225
@@ -286,21 +265,45 @@ function parseSourceAST(
265
return;
266
}
267
268
+ if (
269
+ hookParsedMetadata.originalSourceURL != null &&
270
+ hookParsedMetadata.originalSourceCode != null &&
271
+ hookParsedMetadata.originalSourceColumnNumber != null &&
272
+ hookParsedMetadata.originalSourceLineNumber != null
273
+ ) {
274
+ // Use cached metadata.
275
+ return;
276
+ }
277
+
278
+ const {lineNumber, columnNumber} = hookSourceAndMetadata.hookSource;
279
+ if (lineNumber == null || columnNumber == null) {
280
+ throw Error('Hook source code location not found.');
281
+ }
282
+
283
const {metadataConsumer, sourceConsumer} = hookParsedMetadata;
284
const runtimeSourceCode = ((hookSourceAndMetadata.runtimeSourceCode: any): string);
285
let hasHookMap = false;
286
let originalSourceURL;
287
let originalSourceCode;
294
- if (sourceConsumer !== null) {
288
+ let originalSourceColumnNumber;
289
+ let originalSourceLineNumber;
290
+ if (areSourceMapsAppliedToErrors() || sourceConsumer == null) {
291
+ // Either the current environment automatically applies source maps to errors,
292
+ // or the current code had no source map to begin with.
293
+ // Either way, we don't need to convert the Error stack frame locations.
294
+ originalSourceColumnNumber = columnNumber;
295
+ originalSourceLineNumber = lineNumber;
296
+ // There's no source map to parse here so we can just parse the original source itself.
297
+ originalSourceCode = runtimeSourceCode;
298
+ // TODO (named hooks) This mixes runtimeSourceURLs with source mapped URLs in the same cache key space.
299
+ // Namespace them?
300
+ originalSourceURL = hookSourceAndMetadata.runtimeSourceURL;
301
+ } else {
302
// Parse and extract the AST from the source map.
296
- const {lineNumber, columnNumber} = hookSourceAndMetadata.hookSource;
297
- if (lineNumber == null || columnNumber == null) {
298
- throw Error('Hook source code location not found.');
299
- }
303
// Now that the source map has been loaded,
304
// extract the original source for later.
305
// TODO (named hooks) Refactor this read, github.com/facebook/react/pull/22181
303
- const {source} = withSyncPerformanceMark(
306
+ const {column, line, source} = withSyncPerformanceMark(
307
'sourceConsumer.originalPositionFor()',
308
() =>
309
sourceConsumer.originalPositionFor({
@@ -320,6 +323,8 @@ function parseSourceAST(
323
);
324
}
325
326
+ originalSourceColumnNumber = column;
327
+ originalSourceLineNumber = line;
328
// TODO (named hooks) maybe canonicalize this URL somehow?
329
// It can be relative if the source map specifies it that way,
330
// but we use it as a cache key across different source maps and there can be collisions.
@@ -331,7 +336,7 @@ function parseSourceAST(
336
337
if (__DEBUG__) {
338
console.groupCollapsed(
334
- 'parseSourceAST() Extracted source code from source map',
339
+ `parseSourceAST() Extracted source code from source map for "${originalSourceURL}"`,
340
);
341
console.log(originalSourceCode);
342
console.groupEnd();
@@ -343,24 +348,37 @@ function parseSourceAST(
348
) {
349
hasHookMap = true;
350
}
346
- } else {
347
- // There's no source map to parse here so we can just parse the original source itself.
348
- originalSourceCode = runtimeSourceCode;
349
- // TODO (named hooks) This mixes runtimeSourceURLs with source mapped URLs in the same cache key space.
350
- // Namespace them?
351
- originalSourceURL = hookSourceAndMetadata.runtimeSourceURL;
351
+ }
352
+
353
+ if (__DEBUG__) {
354
+ console.log(
355
+ `parseSourceAST() mapped line ${lineNumber}->${originalSourceLineNumber} and column ${columnNumber}->${originalSourceColumnNumber}`,
356
+ );
357
}
358
359
hookParsedMetadata.originalSourceCode = originalSourceCode;
360
hookParsedMetadata.originalSourceURL = originalSourceURL;
361
+ hookParsedMetadata.originalSourceLineNumber = originalSourceLineNumber;
362
+ hookParsedMetadata.originalSourceColumnNumber = originalSourceColumnNumber;
363
364
if (hasHookMap) {
365
+ if (__DEBUG__) {
366
+ console.log(
367
+ `parseSourceAST() Found hookMap and skipping parsing for "${originalSourceURL}"`,
368
+ );
369
+ }
370
// If there's a hook map present from an extended sourcemap then
371
// we don't need to parse the source files and instead can use the
372
// hook map to extract hook names.
373
return;
374
}
375
376
+ if (__DEBUG__) {
377
+ console.log(
378
+ `parseSourceAST() Did not find hook map for "${originalSourceURL}"`,
379
+ );
380
+ }
381
+
382
// The cache also serves to deduplicate parsing by URL in our loop over location keys.
383
// This may need to change if we switch to async parsing.
384
const sourceMetadata = originalURLToMetadataCache.get(originalSourceURL);