Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 62bc80c

Browse filesBrowse files
bcoetargos
authored andcommitted
process: add lineLength to source-map-cache
Without the line lengths of in-memory transpiled source, it's not possible to convert from byte ofsets to line/column offsets. PR-URL: #29863 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: David Carlier <devnexen@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 735ec1b commit 62bc80c
Copy full SHA for 62bc80c

File tree

Expand file treeCollapse file tree

6 files changed

+118
-55
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

6 files changed

+118
-55
lines changed
Open diff view settings
Collapse file

‎doc/api/cli.md‎

Copy file name to clipboardExpand all lines: doc/api/cli.md
+10-3Lines changed: 10 additions & 3 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1199,8 +1199,9 @@ If found, Source Map data is appended to the top-level key `source-map-cache`
11991199
on the JSON coverage object.
12001200

12011201
`source-map-cache` is an object with keys representing the files source maps
1202-
were extracted from, and the values include the raw source-map URL
1203-
(in the key `url`) and the parsed Source Map V3 information (in the key `data`).
1202+
were extracted from, and values which include the raw source-map URL
1203+
(in the key `url`), the parsed Source Map V3 information (in the key `data`),
1204+
and the line lengths of the source file (in the key `lineLengths`).
12041205

12051206
```json
12061207
{
@@ -1226,7 +1227,13 @@ were extracted from, and the values include the raw source-map URL
12261227
],
12271228
"mappings": "MAAMA,IACJC,YAAaC",
12281229
"sourceRoot": "./"
1229-
}
1230+
},
1231+
"lineLengths": [
1232+
13,
1233+
62,
1234+
38,
1235+
27
1236+
]
12301237
}
12311238
}
12321239
}
Collapse file

‎lib/internal/bootstrap/pre_execution.js‎

Copy file name to clipboardExpand all lines: lib/internal/bootstrap/pre_execution.js
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function prepareMainThreadExecution(expandArgv1 = false) {
2525
// prepareStackTrace method, replacing the default in errors.js.
2626
if (getOptionValue('--enable-source-maps')) {
2727
const { prepareStackTrace } =
28-
require('internal/source_map/source_map_cache');
28+
require('internal/source_map/prepare_stack_trace');
2929
const { setPrepareStackTraceCallback } = internalBinding('errors');
3030
setPrepareStackTraceCallback(prepareStackTrace);
3131
}
Collapse file
+52Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use strict';
2+
3+
const debug = require('internal/util/debuglog').debuglog('source_map');
4+
const { findSourceMap } = require('internal/source_map/source_map_cache');
5+
const { overrideStackTrace } = require('internal/errors');
6+
7+
// Create a prettified stacktrace, inserting context from source maps
8+
// if possible.
9+
const ErrorToString = Error.prototype.toString; // Capture original toString.
10+
const prepareStackTrace = (globalThis, error, trace) => {
11+
// API for node internals to override error stack formatting
12+
// without interfering with userland code.
13+
// TODO(bcoe): add support for source-maps to repl.
14+
if (overrideStackTrace.has(error)) {
15+
const f = overrideStackTrace.get(error);
16+
overrideStackTrace.delete(error);
17+
return f(error, trace);
18+
}
19+
20+
const { SourceMap } = require('internal/source_map/source_map');
21+
const errorString = ErrorToString.call(error);
22+
23+
if (trace.length === 0) {
24+
return errorString;
25+
}
26+
const preparedTrace = trace.map((t, i) => {
27+
let str = i !== 0 ? '\n at ' : '';
28+
str = `${str}${t}`;
29+
try {
30+
const sourceMap = findSourceMap(t.getFileName(), error);
31+
if (sourceMap && sourceMap.data) {
32+
const sm = new SourceMap(sourceMap.data);
33+
// Source Map V3 lines/columns use zero-based offsets whereas, in
34+
// stack traces, they start at 1/1.
35+
const [, , url, line, col] =
36+
sm.findEntry(t.getLineNumber() - 1, t.getColumnNumber() - 1);
37+
if (url && line !== undefined && col !== undefined) {
38+
str +=
39+
`\n -> ${url.replace('file://', '')}:${line + 1}:${col + 1}`;
40+
}
41+
}
42+
} catch (err) {
43+
debug(err.stack);
44+
}
45+
return str;
46+
});
47+
return `${errorString}\n at ${preparedTrace.join('')}`;
48+
};
49+
50+
module.exports = {
51+
prepareStackTrace,
52+
};
Collapse file

‎lib/internal/source_map/source_map_cache.js‎

Copy file name to clipboardExpand all lines: lib/internal/source_map/source_map_cache.js
+24-51Lines changed: 24 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ const cjsSourceMapCache = new WeakMap();
1717
// on filenames.
1818
const esmSourceMapCache = new Map();
1919
const { fileURLToPath, URL } = require('url');
20-
const { overrideStackTrace } = require('internal/errors');
2120

2221
let experimentalSourceMaps;
2322
function maybeCacheSourceMap(filename, content, cjsModuleInstance) {
@@ -38,18 +37,22 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance) {
3837

3938
const match = content.match(/\/[*/]#\s+sourceMappingURL=(?<sourceMappingURL>[^\s]+)/);
4039
if (match) {
40+
const data = dataFromUrl(basePath, match.groups.sourceMappingURL);
41+
const url = data ? null : match.groups.sourceMappingURL;
4142
if (cjsModuleInstance) {
4243
cjsSourceMapCache.set(cjsModuleInstance, {
4344
filename,
44-
url: match.groups.sourceMappingURL,
45-
data: dataFromUrl(basePath, match.groups.sourceMappingURL)
45+
lineLengths: lineLengths(content),
46+
data,
47+
url
4648
});
4749
} else {
4850
// If there is no cjsModuleInstance assume we are in a
4951
// "modules/esm" context.
5052
esmSourceMapCache.set(filename, {
51-
url: match.groups.sourceMappingURL,
52-
data: dataFromUrl(basePath, match.groups.sourceMappingURL)
53+
lineLengths: lineLengths(content),
54+
data,
55+
url
5356
});
5457
}
5558
}
@@ -73,6 +76,18 @@ function dataFromUrl(basePath, sourceMappingURL) {
7376
}
7477
}
7578

79+
// Cache the length of each line in the file that a source map was extracted
80+
// from. This allows translation from byte offset V8 coverage reports,
81+
// to line/column offset Source Map V3.
82+
function lineLengths(content) {
83+
// We purposefully keep \r as part of the line-length calculation, in
84+
// cases where there is a \r\n separator, so that this can be taken into
85+
// account in coverage calculations.
86+
return content.split(/\n|\u2028|\u2029/).map((line) => {
87+
return line.length;
88+
});
89+
}
90+
7691
function sourceMapFromFile(sourceMapFile) {
7792
try {
7893
const content = fs.readFileSync(sourceMapFile, 'utf8');
@@ -161,56 +176,14 @@ function appendCJSCache(obj) {
161176
const value = cjsSourceMapCache.get(Module._cache[key]);
162177
if (value) {
163178
obj[`file://${key}`] = {
164-
url: value.url,
165-
data: value.data
179+
lineLengths: value.lineLengths,
180+
data: value.data,
181+
url: value.url
166182
};
167183
}
168184
});
169185
}
170186

171-
// Create a prettified stacktrace, inserting context from source maps
172-
// if possible.
173-
const ErrorToString = Error.prototype.toString; // Capture original toString.
174-
const prepareStackTrace = (globalThis, error, trace) => {
175-
// API for node internals to override error stack formatting
176-
// without interfering with userland code.
177-
// TODO(bcoe): add support for source-maps to repl.
178-
if (overrideStackTrace.has(error)) {
179-
const f = overrideStackTrace.get(error);
180-
overrideStackTrace.delete(error);
181-
return f(error, trace);
182-
}
183-
184-
const { SourceMap } = require('internal/source_map/source_map');
185-
const errorString = ErrorToString.call(error);
186-
187-
if (trace.length === 0) {
188-
return errorString;
189-
}
190-
const preparedTrace = trace.map((t, i) => {
191-
let str = i !== 0 ? '\n at ' : '';
192-
str = `${str}${t}`;
193-
try {
194-
const sourceMap = findSourceMap(t.getFileName(), error);
195-
if (sourceMap && sourceMap.data) {
196-
const sm = new SourceMap(sourceMap.data);
197-
// Source Map V3 lines/columns use zero-based offsets whereas, in
198-
// stack traces, they start at 1/1.
199-
const [, , url, line, col] =
200-
sm.findEntry(t.getLineNumber() - 1, t.getColumnNumber() - 1);
201-
if (url && line !== undefined && col !== undefined) {
202-
str +=
203-
`\n -> ${url.replace('file://', '')}:${line + 1}:${col + 1}`;
204-
}
205-
}
206-
} catch (err) {
207-
debug(err.stack);
208-
}
209-
return str;
210-
});
211-
return `${errorString}\n at ${preparedTrace.join('')}`;
212-
};
213-
214187
// Attempt to lookup a source map, which is either attached to a file URI, or
215188
// keyed on an error instance.
216189
function findSourceMap(uri, error) {
@@ -230,8 +203,8 @@ function findSourceMap(uri, error) {
230203
}
231204

232205
module.exports = {
206+
findSourceMap,
233207
maybeCacheSourceMap,
234-
prepareStackTrace,
235208
rekeySourceMap,
236209
sourceMapCacheToObject,
237210
};
Collapse file

‎node.gyp‎

Copy file name to clipboardExpand all lines: node.gyp
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@
176176
'lib/internal/repl/history.js',
177177
'lib/internal/repl/utils.js',
178178
'lib/internal/socket_list.js',
179+
'lib/internal/source_map/prepare_stack_trace.js',
179180
'lib/internal/source_map/source_map.js',
180181
'lib/internal/source_map/source_map_cache.js',
181182
'lib/internal/test/binding.js',
Collapse file

‎test/parallel/test-source-map.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-source-map.js
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,36 @@ function nextdir() {
193193
);
194194
}
195195

196+
// Does not persist url parameter if source-map has been parsed.
197+
{
198+
const coverageDirectory = nextdir();
199+
spawnSync(process.execPath, [
200+
require.resolve('../fixtures/source-map/inline-base64.js')
201+
], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } });
202+
const sourceMap = getSourceMapFromCache(
203+
'inline-base64.js',
204+
coverageDirectory
205+
);
206+
assert.strictEqual(sourceMap.url, null);
207+
}
208+
209+
// Persists line lengths for in-memory representation of source file.
210+
{
211+
const coverageDirectory = nextdir();
212+
spawnSync(process.execPath, [
213+
require.resolve('../fixtures/source-map/istanbul-throw.js')
214+
], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } });
215+
const sourceMap = getSourceMapFromCache(
216+
'istanbul-throw.js',
217+
coverageDirectory
218+
);
219+
if (common.isWindows) {
220+
assert.deepStrictEqual(sourceMap.lineLengths, [1086, 31, 185, 649, 0]);
221+
} else {
222+
assert.deepStrictEqual(sourceMap.lineLengths, [1085, 30, 184, 648, 0]);
223+
}
224+
}
225+
196226
function getSourceMapFromCache(fixtureFile, coverageDirectory) {
197227
const jsonFiles = fs.readdirSync(coverageDirectory);
198228
for (const jsonFile of jsonFiles) {

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.