• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --enable-source-maps
2'use strict';
3
4const common = require('../common');
5const assert = require('assert');
6const { findSourceMap, SourceMap } = require('module');
7const { readFileSync } = require('fs');
8
9// It should throw with invalid args.
10{
11  [1, true, 'foo'].forEach((invalidArg) =>
12    assert.throws(
13      () => new SourceMap(invalidArg),
14      {
15        code: 'ERR_INVALID_ARG_TYPE',
16        name: 'TypeError',
17        message: 'The "payload" argument must be of type object.' +
18               common.invalidArgTypeHelper(invalidArg)
19      }
20    )
21  );
22}
23
24// `findSourceMap()` should return undefined when no source map is found.
25{
26  const files = [
27    __filename,
28    '',
29    'invalid-file',
30  ];
31  for (const file of files) {
32    const sourceMap = findSourceMap(file);
33    assert.strictEqual(sourceMap, undefined);
34  }
35}
36
37// findSourceMap() can lookup source-maps based on URIs, in the
38// non-exceptional case.
39{
40  require('../fixtures/source-map/disk-relative-path.js');
41  const sourceMap = findSourceMap(
42    require.resolve('../fixtures/source-map/disk-relative-path.js')
43  );
44  const {
45    originalLine,
46    originalColumn,
47    originalSource
48  } = sourceMap.findEntry(0, 29);
49  assert.strictEqual(originalLine, 2);
50  assert.strictEqual(originalColumn, 4);
51  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);
60}
61
62// findSourceMap() can be used in Error.prepareStackTrace() to lookup
63// source-map attached to error.
64{
65  let callSite;
66  let sourceMap;
67  Error.prepareStackTrace = (error, trace) => {
68    const throwingRequireCallSite = trace[0];
69    if (throwingRequireCallSite.getFileName().endsWith('typescript-throw.js')) {
70      sourceMap = findSourceMap(throwingRequireCallSite.getFileName());
71      callSite = throwingRequireCallSite;
72    }
73  };
74  try {
75    // Require a file that throws an exception, and has a source map.
76    require('../fixtures/source-map/typescript-throw.js');
77  } catch (err) {
78    // eslint-disable-next-line no-unused-expressions
79    err.stack; // Force prepareStackTrace() to be called.
80  }
81  assert(callSite);
82  assert(sourceMap);
83  const {
84    generatedLine,
85    generatedColumn,
86    originalLine,
87    originalColumn,
88    originalSource
89  } = sourceMap.findEntry(
90    callSite.getLineNumber() - 1,
91    callSite.getColumnNumber() - 1
92  );
93
94  assert.strictEqual(generatedLine, 19);
95  assert.strictEqual(generatedColumn, 14);
96
97  assert.strictEqual(originalLine, 17);
98  assert.strictEqual(originalColumn, 10);
99  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);
112}
113
114// SourceMap can be instantiated with Source Map V3 object as payload.
115{
116  const payload = JSON.parse(readFileSync(
117    require.resolve('../fixtures/source-map/disk.map'), 'utf8'
118  ));
119  const sourceMap = new SourceMap(payload);
120  const {
121    originalLine,
122    originalColumn,
123    originalSource
124  } = sourceMap.findEntry(0, 29);
125  assert.strictEqual(originalLine, 2);
126  assert.strictEqual(originalColumn, 4);
127  assert(originalSource.endsWith('disk.js'));
128  // The stored payload should be a clone:
129  assert.strictEqual(payload.mappings, sourceMap.payload.mappings);
130  assert.notStrictEqual(payload, sourceMap.payload);
131  assert.strictEqual(payload.sources[0], sourceMap.payload.sources[0]);
132  assert.notStrictEqual(payload.sources, sourceMap.payload.sources);
133}
134
135// findEntry() and findOrigin() must return empty object instead of
136// error when receiving a malformed mappings.
137{
138  const payload = JSON.parse(readFileSync(
139    require.resolve('../fixtures/source-map/disk.map'), 'utf8'
140  ));
141  payload.mappings = ';;;;;;;;;';
142
143  const sourceMap = new SourceMap(payload);
144  const result = sourceMap.findEntry(0, 5);
145  assert.strictEqual(typeof result, 'object');
146  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);
150}
151
152// SourceMap can be instantiated with Index Source Map V3 object as payload.
153{
154  const payload = JSON.parse(readFileSync(
155    require.resolve('../fixtures/source-map/disk-index.map'), 'utf8'
156  ));
157  const sourceMap = new SourceMap(payload);
158  const {
159    originalLine,
160    originalColumn,
161    originalSource
162  } = sourceMap.findEntry(0, 29);
163  assert.strictEqual(originalLine, 2);
164  assert.strictEqual(originalColumn, 4);
165  assert(originalSource.endsWith('section.js'));
166  // The stored payload should be a clone:
167  assert.strictEqual(payload.mappings, sourceMap.payload.mappings);
168  assert.notStrictEqual(payload, sourceMap.payload);
169  assert.strictEqual(payload.sources[0], sourceMap.payload.sources[0]);
170  assert.notStrictEqual(payload.sources, sourceMap.payload.sources);
171}
172
173// Test various known decodings to ensure decodeVLQ works correctly.
174{
175  function makeMinimalMap(column) {
176    return {
177      sources: ['test.js'],
178      // Mapping from the 0th line, 0th column of the output file to the 0th
179      // source file, 0th line, ${column}th column.
180      mappings: `AAA${column}`,
181    };
182  }
183  const knownDecodings = {
184    'A': 0,
185    'B': -2147483648,
186    'C': 1,
187    'D': -1,
188    'E': 2,
189    'F': -2,
190
191    // 2^31 - 1, maximum values
192    '+/////D': 2147483647,
193    '8/////D': 2147483646,
194    '6/////D': 2147483645,
195    '4/////D': 2147483644,
196    '2/////D': 2147483643,
197    '0/////D': 2147483642,
198
199    // -2^31 + 1, minimum values
200    '//////D': -2147483647,
201    '9/////D': -2147483646,
202    '7/////D': -2147483645,
203    '5/////D': -2147483644,
204    '3/////D': -2147483643,
205    '1/////D': -2147483642,
206  };
207
208  for (const column in knownDecodings) {
209    const sourceMap = new SourceMap(makeMinimalMap(column));
210    const { originalColumn } = sourceMap.findEntry(0, 0);
211    assert.strictEqual(originalColumn, knownDecodings[column]);
212  }
213}
214
215// Test that generated columns are sorted when a negative offset is
216// observed, see: https://github.com/mozilla/source-map/pull/92
217{
218  function makeMinimalMap(generatedColumns, originalColumns) {
219    return {
220      sources: ['test.js'],
221      // Mapping from the 0th line, ${g}th column of the output file to the 0th
222      // source file, 0th line, ${column}th column.
223      mappings: generatedColumns.map((g, i) => `${g}AA${originalColumns[i]}`)
224        .join(',')
225    };
226  }
227  // U = 10
228  // F = -2
229  // A = 0
230  // E = 2
231  const sourceMap = new SourceMap(makeMinimalMap(
232    ['U', 'F', 'F'],
233    ['A', 'E', 'E']
234  ));
235  assert.strictEqual(sourceMap.findEntry(0, 6).originalColumn, 4);
236  assert.strictEqual(sourceMap.findEntry(0, 8).originalColumn, 2);
237  assert.strictEqual(sourceMap.findEntry(0, 10).originalColumn, 0);
238}
239