|
9 | 9 | RegExpPrototypeSymbolSplit, |
10 | 10 | SafeMap, |
11 | 11 | StringPrototypeCodePointAt, |
| 12 | + StringPrototypeIndexOf, |
12 | 13 | StringPrototypeSplit, |
13 | 14 | StringPrototypeStartsWith, |
14 | 15 | } = primordials; |
@@ -176,16 +177,14 @@ function maybeCacheSourceMap(filename, content, moduleInstance, isGeneratedSourc |
176 | 177 | // Normalize the sourceURL to a file URL if it is a path. |
177 | 178 | sourceURL = normalizeReferrerURL(sourceURL); |
178 | 179 |
|
179 | | - const data = dataFromUrl(filename, sourceMapURL); |
180 | | - // `data` could be null if the source map is invalid. |
181 | | - // In this case, create a cache entry with null data with source url for test coverage. |
182 | | - |
| 180 | + // The payload is resolved on first use (see sourceMapData()), except under |
| 181 | + // coverage, where it is serialized at exit when no more JS may run. |
183 | 182 | const entry = { |
184 | 183 | __proto__: null, |
185 | 184 | lineLengths: lineLengths(content), |
186 | | - data, |
187 | | - // Save the source map url if it is not a data url. |
188 | | - sourceMapURL: data ? null : sourceMapURL, |
| 185 | + data: process.env.NODE_V8_COVERAGE ? dataFromUrl(filename, sourceMapURL) : undefined, |
| 186 | + filename, |
| 187 | + sourceMapURL, |
189 | 188 | sourceURL, |
190 | 189 | }; |
191 | 190 |
|
@@ -254,20 +253,36 @@ function dataFromUrl(sourceURL, sourceMappingURL) { |
254 | 253 | return sourceMapFromFile(mapURL); |
255 | 254 | } |
256 | 255 |
|
| 256 | +const kUnicodeLineTerminators = /[\u2028\u2029]/; |
| 257 | + |
257 | 258 | // Cache the length of each line in the file that a source map was extracted |
258 | 259 | // from. This allows translation from byte offset V8 coverage reports, |
259 | 260 | // to line/column offset Source Map V3. |
260 | 261 | function lineLengths(content) { |
| 262 | + if (RegExpPrototypeExec(kUnicodeLineTerminators, content) !== null) { |
| 263 | + return lineLengthsWithUnicodeTerminators(content); |
| 264 | + } |
| 265 | + // We purposefully keep \r as part of the line-length calculation, in |
| 266 | + // cases where there is a \r\n separator, so that this can be taken into |
| 267 | + // account in coverage calculations. |
| 268 | + const output = []; |
| 269 | + let lineStart = 0; |
| 270 | + let lineEnd; |
| 271 | + while ((lineEnd = StringPrototypeIndexOf(content, '\n', lineStart)) !== -1) { |
| 272 | + ArrayPrototypePush(output, lineEnd - lineStart); |
| 273 | + lineStart = lineEnd + 1; |
| 274 | + } |
| 275 | + ArrayPrototypePush(output, content.length - lineStart); |
| 276 | + return output; |
| 277 | +} |
| 278 | + |
| 279 | +function lineLengthsWithUnicodeTerminators(content) { |
261 | 280 | const contentLength = content.length; |
262 | 281 | const output = []; |
263 | 282 | let lineLength = 0; |
264 | 283 | for (let i = 0; i < contentLength; i++, lineLength++) { |
265 | 284 | const codePoint = StringPrototypeCodePointAt(content, i); |
266 | | - |
267 | | - // We purposefully keep \r as part of the line-length calculation, in |
268 | | - // cases where there is a \r\n separator, so that this can be taken into |
269 | | - // account in coverage calculations. |
270 | | - // codepoints for \n (new line), \u2028 (line separator) and \u2029 (paragraph separator) |
| 285 | + // \n (new line), \u2028 (line separator) and \u2029 (paragraph separator) |
271 | 286 | if (codePoint === 10 || codePoint === 0x2028 || codePoint === 0x2029) { |
272 | 287 | ArrayPrototypePush(output, lineLength); |
273 | 288 | lineLength = -1; // To not count the matched codePoint such as \n character |
@@ -351,16 +366,31 @@ function sourceMapCacheToObject() { |
351 | 366 |
|
352 | 367 | const obj = { __proto__: null }; |
353 | 368 | for (const { 0: k, 1: v } of moduleSourceMapCache) { |
| 369 | + const data = v.data ?? null; |
354 | 370 | obj[k] = { |
355 | 371 | __proto__: null, |
356 | 372 | lineLengths: v.lineLengths, |
357 | | - data: v.data, |
358 | | - url: v.sourceMapURL, |
| 373 | + data, |
| 374 | + // Save the source map url if it is not a data url. |
| 375 | + url: data ? null : v.sourceMapURL, |
359 | 376 | }; |
360 | 377 | } |
361 | 378 | return obj; |
362 | 379 | } |
363 | 380 |
|
| 381 | +/** |
| 382 | + * Resolve and parse the payload of a cache entry the first time it is needed; |
| 383 | + * `null` marks a source map that could not be loaded. |
| 384 | + * @param {object} entry |
| 385 | + * @returns {object|null} |
| 386 | + */ |
| 387 | +function sourceMapData(entry) { |
| 388 | + if (entry.data === undefined) { |
| 389 | + entry.data = dataFromUrl(entry.filename, entry.sourceMapURL); |
| 390 | + } |
| 391 | + return entry.data; |
| 392 | +} |
| 393 | + |
364 | 394 | /** |
365 | 395 | * Find a source map for a given actual source URL or path. |
366 | 396 | * |
@@ -390,7 +420,7 @@ function findSourceMap(sourceURL) { |
390 | 420 | sourceURL = pathToFileURL(sourceURL).href; |
391 | 421 | } |
392 | 422 | const entry = getModuleSourceMapCache().get(sourceURL) ?? generatedSourceMapCache.get(sourceURL); |
393 | | - if (entry?.data == null) { |
| 423 | + if (entry === undefined || sourceMapData(entry) === null) { |
394 | 424 | return undefined; |
395 | 425 | } |
396 | 426 |
|
|
0 commit comments