• 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() can lookup source-maps based on URIs, in the
25// non-exceptional case.
26{
27  require('../fixtures/source-map/disk-relative-path.js');
28  const sourceMap = findSourceMap(
29    require.resolve('../fixtures/source-map/disk-relative-path.js')
30  );
31  const {
32    originalLine,
33    originalColumn,
34    originalSource
35  } = sourceMap.findEntry(0, 29);
36  assert.strictEqual(originalLine, 2);
37  assert.strictEqual(originalColumn, 4);
38  assert(originalSource.endsWith('disk.js'));
39}
40
41// findSourceMap() can be used in Error.prepareStackTrace() to lookup
42// source-map attached to error.
43{
44  let callSite;
45  let sourceMap;
46  Error.prepareStackTrace = (error, trace) => {
47    const throwingRequireCallSite = trace[0];
48    if (throwingRequireCallSite.getFileName().endsWith('typescript-throw.js')) {
49      sourceMap = findSourceMap(throwingRequireCallSite.getFileName());
50      callSite = throwingRequireCallSite;
51    }
52  };
53  try {
54    // Require a file that throws an exception, and has a source map.
55    require('../fixtures/source-map/typescript-throw.js');
56  } catch (err) {
57    // eslint-disable-next-line no-unused-expressions
58    err.stack; // Force prepareStackTrace() to be called.
59  }
60  assert(callSite);
61  assert(sourceMap);
62  const {
63    generatedLine,
64    generatedColumn,
65    originalLine,
66    originalColumn,
67    originalSource
68  } = sourceMap.findEntry(
69    callSite.getLineNumber() - 1,
70    callSite.getColumnNumber() - 1
71  );
72
73  assert.strictEqual(generatedLine, 19);
74  assert.strictEqual(generatedColumn, 14);
75
76  assert.strictEqual(originalLine, 17);
77  assert.strictEqual(originalColumn, 10);
78  assert(originalSource.endsWith('typescript-throw.ts'));
79}
80
81// SourceMap can be instantiated with Source Map V3 object as payload.
82{
83  const payload = JSON.parse(readFileSync(
84    require.resolve('../fixtures/source-map/disk.map'), 'utf8'
85  ));
86  const sourceMap = new SourceMap(payload);
87  const {
88    originalLine,
89    originalColumn,
90    originalSource
91  } = sourceMap.findEntry(0, 29);
92  assert.strictEqual(originalLine, 2);
93  assert.strictEqual(originalColumn, 4);
94  assert(originalSource.endsWith('disk.js'));
95  // The stored payload should be a clone:
96  assert.strictEqual(payload.mappings, sourceMap.payload.mappings);
97  assert.notStrictEqual(payload, sourceMap.payload);
98  assert.strictEqual(payload.sources[0], sourceMap.payload.sources[0]);
99  assert.notStrictEqual(payload.sources, sourceMap.payload.sources);
100}
101
102// Test various known decodings to ensure decodeVLQ works correctly.
103{
104  function makeMinimalMap(column) {
105    return {
106      sources: ['test.js'],
107      // Mapping from the 0th line, 0th column of the output file to the 0th
108      // source file, 0th line, ${column}th column.
109      mappings: `AAA${column}`,
110    };
111  }
112  const knownDecodings = {
113    'A': 0,
114    'B': -2147483648,
115    'C': 1,
116    'D': -1,
117    'E': 2,
118    'F': -2,
119
120    // 2^31 - 1, maximum values
121    '+/////D': 2147483647,
122    '8/////D': 2147483646,
123    '6/////D': 2147483645,
124    '4/////D': 2147483644,
125    '2/////D': 2147483643,
126    '0/////D': 2147483642,
127
128    // -2^31 + 1, minimum values
129    '//////D': -2147483647,
130    '9/////D': -2147483646,
131    '7/////D': -2147483645,
132    '5/////D': -2147483644,
133    '3/////D': -2147483643,
134    '1/////D': -2147483642,
135  };
136
137  for (const column in knownDecodings) {
138    const sourceMap = new SourceMap(makeMinimalMap(column));
139    const { originalColumn } = sourceMap.findEntry(0, 0);
140    assert.strictEqual(originalColumn, knownDecodings[column]);
141  }
142}
143
144// Test that generated columns are sorted when a negative offset is
145// observed, see: https://github.com/mozilla/source-map/pull/92
146{
147  function makeMinimalMap(generatedColumns, originalColumns) {
148    return {
149      sources: ['test.js'],
150      // Mapping from the 0th line, ${g}th column of the output file to the 0th
151      // source file, 0th line, ${column}th column.
152      mappings: generatedColumns.map((g, i) => `${g}AA${originalColumns[i]}`)
153        .join(',')
154    };
155  }
156  // U = 10
157  // F = -2
158  // A = 0
159  // E = 2
160  const sourceMap = new SourceMap(makeMinimalMap(
161    ['U', 'F', 'F'],
162    ['A', 'E', 'E']
163  ));
164  assert.strictEqual(sourceMap.findEntry(0, 6).originalColumn, 4);
165  assert.strictEqual(sourceMap.findEntry(0, 8).originalColumn, 2);
166  assert.strictEqual(sourceMap.findEntry(0, 10).originalColumn, 0);
167}
168