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 071eaad

Browse filesBrowse files
isaacsruyadorno
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 3363cfa commit 071eaad
Copy full SHA for 071eaad

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
@@ -196,23 +196,67 @@ Creates a new `sourceMap` instance.
196196
197197
Getter for the payload used to construct the [`SourceMap`][] instance.
198198
199-
#### `sourceMap.findEntry(lineNumber, columnNumber)`
199+
#### `sourceMap.findEntry(lineOffset, columnOffset)`
200200
201-
* `lineNumber` {number}
202-
* `columnNumber` {number}
201+
* `lineOffset` {number} The zero-indexed line number offset in
202+
the generated source
203+
* `columnOffset` {number} The zero-indexed column number offset
204+
in the generated source
203205
* Returns: {Object}
204206
205-
Given a line number and column number in the generated source file, returns
206-
an object representing the position in the original file. The object returned
207-
consists of the following keys:
208-
209-
* generatedLine: {number}
210-
* generatedColumn: {number}
211-
* originalSource: {string}
212-
* originalLine: {number}
213-
* originalColumn: {number}
207+
Given a line offset and column offset in the generated source
208+
file, returns an object representing the SourceMap range in the
209+
original file if found, or an empty object if not.
210+
211+
The object returned contains the following keys:
212+
213+
* generatedLine: {number} The line offset of the start of the
214+
range in the generated source
215+
* generatedColumn: {number} The column offset of start of the
216+
range in the generated source
217+
* originalSource: {string} The file name of the original source,
218+
as reported in the SourceMap
219+
* originalLine: {number} The line offset of the start of the
220+
range in the original source
221+
* originalColumn: {number} The column offset of start of the
222+
range in the original source
214223
* name: {string}
215224
225+
The returned value represents the raw range as it appears in the
226+
SourceMap, based on zero-indexed offsets, _not_ 1-indexed line and
227+
column numbers as they appear in Error messages and CallSite
228+
objects.
229+
230+
To get the corresponding 1-indexed line and column numbers from a
231+
lineNumber and columnNumber as they are reported by Error stacks
232+
and CallSite objects, use `sourceMap.findOrigin(lineNumber,
233+
columnNumber)`
234+
235+
#### `sourceMap.findOrigin(lineNumber, columnNumber)`
236+
237+
* `lineNumber` {number} The 1-indexed line number of the call
238+
site in the generated source
239+
* `columnOffset` {number} The 1-indexed column number
240+
of the call site in the generated source
241+
* Returns: {Object}
242+
243+
Given a 1-indexed lineNumber and columnNumber from a call site in
244+
the generated source, find the corresponding call site location
245+
in the original source.
246+
247+
If the lineNumber and columnNumber provided are not found in any
248+
source map, then an empty object is returned. Otherwise, the
249+
returned object contains the following keys:
250+
251+
* name: {string | undefined} The name of the range in the
252+
source map, if one was provided
253+
* fileName: {string} The file name of the original source, as
254+
reported in the SourceMap
255+
* lineNumber: {number} The 1-indexed lineNumber of the
256+
corresponding call site in the original source
257+
* columnNumber: {number} The 1-indexed columnNumber of the
258+
corresponding call site in the original source
259+
216260
[CommonJS]: modules.md
217261
[ES Modules]: esm.md
218262
[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.