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 f0709fd

Browse filesBrowse files
isaacsRafaelGSS
authored andcommitted
module: add SourceMap.findOrigin
This adds the `SourceMap.findOrigin(lineNumber, columnNumber)` method, for finding the origin source file and 1-indexed line and column numbers corresponding to the 1-indexed line and column numbers from a call site in generated source code. Fix: #47770 PR-URL: #47790 Fixes: #47770 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
1 parent 112335e commit f0709fd
Copy full SHA for f0709fd

File tree

Expand file treeCollapse file tree

3 files changed

+115
-22
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+115
-22
lines changed
Open diff view settings
Collapse file

‎doc/api/module.md‎

Copy file name to clipboardExpand all lines: doc/api/module.md
+56-12Lines changed: 56 additions & 12 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -198,23 +198,67 @@ Creates a new `sourceMap` instance.
198198
199199
Getter for the payload used to construct the [`SourceMap`][] instance.
200200
201-
#### `sourceMap.findEntry(lineNumber, columnNumber)`
201+
#### `sourceMap.findEntry(lineOffset, columnOffset)`
202202
203-
* `lineNumber` {number}
204-
* `columnNumber` {number}
203+
* `lineOffset` {number} The zero-indexed line number offset in
204+
the generated source
205+
* `columnOffset` {number} The zero-indexed column number offset
206+
in the generated source
205207
* Returns: {Object}
206208
207-
Given a line number and column number in the generated source file, returns
208-
an object representing the position in the original file. The object returned
209-
consists of the following keys:
210-
211-
* generatedLine: {number}
212-
* generatedColumn: {number}
213-
* originalSource: {string}
214-
* originalLine: {number}
215-
* originalColumn: {number}
209+
Given a line offset and column offset in the generated source
210+
file, returns an object representing the SourceMap range in the
211+
original file if found, or an empty object if not.
212+
213+
The object returned contains the following keys:
214+
215+
* generatedLine: {number} The line offset of the start of the
216+
range in the generated source
217+
* generatedColumn: {number} The column offset of start of the
218+
range in the generated source
219+
* originalSource: {string} The file name of the original source,
220+
as reported in the SourceMap
221+
* originalLine: {number} The line offset of the start of the
222+
range in the original source
223+
* originalColumn: {number} The column offset of start of the
224+
range in the original source
216225
* name: {string}
217226
227+
The returned value represents the raw range as it appears in the
228+
SourceMap, based on zero-indexed offsets, _not_ 1-indexed line and
229+
column numbers as they appear in Error messages and CallSite
230+
objects.
231+
232+
To get the corresponding 1-indexed line and column numbers from a
233+
lineNumber and columnNumber as they are reported by Error stacks
234+
and CallSite objects, use `sourceMap.findOrigin(lineNumber,
235+
columnNumber)`
236+
237+
#### `sourceMap.findOrigin(lineNumber, columnNumber)`
238+
239+
* `lineNumber` {number} The 1-indexed line number of the call
240+
site in the generated source
241+
* `columnOffset` {number} The 1-indexed column number
242+
of the call site in the generated source
243+
* Returns: {Object}
244+
245+
Given a 1-indexed lineNumber and columnNumber from a call site in
246+
the generated source, find the corresponding call site location
247+
in the original source.
248+
249+
If the lineNumber and columnNumber provided are not found in any
250+
source map, then an empty object is returned. Otherwise, the
251+
returned object contains the following keys:
252+
253+
* name: {string | undefined} The name of the range in the
254+
source map, if one was provided
255+
* fileName: {string} The file name of the original source, as
256+
reported in the SourceMap
257+
* lineNumber: {number} The 1-indexed lineNumber of the
258+
corresponding call site in the original source
259+
* columnNumber: {number} The 1-indexed columnNumber of the
260+
corresponding call site in the original source
261+
218262
[CommonJS]: modules.md
219263
[ES Modules]: esm.md
220264
[Source map v3 format]: https://sourcemaps.info/spec.html#h.mofvlxcwqzej
Collapse file

‎lib/internal/source_map/source_map.js‎

Copy file name to clipboardExpand all lines: lib/internal/source_map/source_map.js
+34-8Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,28 +169,28 @@ class SourceMap {
169169
};
170170

171171
/**
172-
* @param {number} lineNumber in compiled resource
173-
* @param {number} columnNumber in compiled resource
174-
* @return {?Array}
172+
* @param {number} lineOffset 0-indexed line offset in compiled resource
173+
* @param {number} columnOffset 0-indexed column offset in compiled resource
174+
* @return {object} representing start of range if found, or empty object
175175
*/
176-
findEntry(lineNumber, columnNumber) {
176+
findEntry(lineOffset, columnOffset) {
177177
let first = 0;
178178
let count = this.#mappings.length;
179179
while (count > 1) {
180180
const step = count >> 1;
181181
const middle = first + step;
182182
const mapping = this.#mappings[middle];
183-
if (lineNumber < mapping[0] ||
184-
(lineNumber === mapping[0] && columnNumber < mapping[1])) {
183+
if (lineOffset < mapping[0] ||
184+
(lineOffset === mapping[0] && columnOffset < mapping[1])) {
185185
count = step;
186186
} else {
187187
first = middle;
188188
count -= step;
189189
}
190190
}
191191
const entry = this.#mappings[first];
192-
if (!first && entry && (lineNumber < entry[0] ||
193-
(lineNumber === entry[0] && columnNumber < entry[1]))) {
192+
if (!first && entry && (lineOffset < entry[0] ||
193+
(lineOffset === entry[0] && columnOffset < entry[1]))) {
194194
return {};
195195
} else if (!entry) {
196196
return {};
@@ -205,6 +205,32 @@ class SourceMap {
205205
};
206206
}
207207

208+
/**
209+
* @param {number} lineNumber 1-indexed line number in compiled resource call site
210+
* @param {number} columnNumber 1-indexed column number in compiled resource call site
211+
* @return {object} representing origin call site if found, or empty object
212+
*/
213+
findOrigin(lineNumber, columnNumber) {
214+
const range = this.findEntry(lineNumber - 1, columnNumber - 1);
215+
if (
216+
range.originalSource === undefined ||
217+
range.originalLine === undefined ||
218+
range.originalColumn === undefined ||
219+
range.generatedLine === undefined ||
220+
range.generatedColumn === undefined
221+
) {
222+
return {};
223+
}
224+
const lineOffset = lineNumber - range.generatedLine;
225+
const columnOffset = columnNumber - range.generatedColumn;
226+
return {
227+
name: range.name,
228+
fileName: range.originalSource,
229+
lineNumber: range.originalLine + lineOffset,
230+
columnNumber: range.originalColumn + columnOffset,
231+
};
232+
}
233+
208234
/**
209235
* @override
210236
*/
Collapse file

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

Copy file name to clipboardExpand all lines: test/parallel/test-source-map-api.js
+25-2Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ const { readFileSync } = require('fs');
4949
assert.strictEqual(originalLine, 2);
5050
assert.strictEqual(originalColumn, 4);
5151
assert(originalSource.endsWith('disk.js'));
52+
const {
53+
fileName,
54+
lineNumber,
55+
columnNumber,
56+
} = sourceMap.findOrigin(1, 30);
57+
assert.strictEqual(fileName, originalSource);
58+
assert.strictEqual(lineNumber, 3);
59+
assert.strictEqual(columnNumber, 6);
5260
}
5361

5462
// findSourceMap() can be used in Error.prepareStackTrace() to lookup
@@ -89,6 +97,18 @@ const { readFileSync } = require('fs');
8997
assert.strictEqual(originalLine, 17);
9098
assert.strictEqual(originalColumn, 10);
9199
assert(originalSource.endsWith('typescript-throw.ts'));
100+
101+
const {
102+
fileName,
103+
lineNumber,
104+
columnNumber,
105+
} = sourceMap.findOrigin(
106+
callSite.getLineNumber(),
107+
callSite.getColumnNumber()
108+
);
109+
assert.strictEqual(fileName, originalSource);
110+
assert.strictEqual(lineNumber, 18);
111+
assert.strictEqual(columnNumber, 11);
92112
}
93113

94114
// SourceMap can be instantiated with Source Map V3 object as payload.
@@ -112,8 +132,8 @@ const { readFileSync } = require('fs');
112132
assert.notStrictEqual(payload.sources, sourceMap.payload.sources);
113133
}
114134

115-
// findEntry() must return empty object instead error when
116-
// receive a malformed mappings.
135+
// findEntry() and findOrigin() must return empty object instead of
136+
// error when receiving a malformed mappings.
117137
{
118138
const payload = JSON.parse(readFileSync(
119139
require.resolve('../fixtures/source-map/disk.map'), 'utf8'
@@ -124,6 +144,9 @@ const { readFileSync } = require('fs');
124144
const result = sourceMap.findEntry(0, 5);
125145
assert.strictEqual(typeof result, 'object');
126146
assert.strictEqual(Object.keys(result).length, 0);
147+
const origin = sourceMap.findOrigin(0, 5);
148+
assert.strictEqual(typeof origin, 'object');
149+
assert.strictEqual(Object.keys(origin).length, 0);
127150
}
128151

129152
// SourceMap can be instantiated with Index Source Map V3 object as payload.

0 commit comments

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