| // Flags: --enable-source-maps |
| 'use strict'; |
| |
| const common = require('../common'); |
| const assert = require('assert'); |
| const { findSourceMap, SourceMap } = require('module'); |
| const { readFileSync } = require('fs'); |
| |
| // It should throw with invalid args. |
| { |
| [1, true, 'foo'].forEach((invalidArg) => |
| assert.throws( |
| () => new SourceMap(invalidArg), |
| { |
| code: 'ERR_INVALID_ARG_TYPE', |
| name: 'TypeError', |
| message: 'The "payload" argument must be of type object.' + |
| common.invalidArgTypeHelper(invalidArg) |
| } |
| ) |
| ); |
| } |
| |
| // `findSourceMap()` should return undefined when no source map is found. |
| { |
| const files = [ |
| __filename, |
| '', |
| 'invalid-file', |
| ]; |
| for (const file of files) { |
| const sourceMap = findSourceMap(file); |
| assert.strictEqual(sourceMap, undefined); |
| } |
| } |
| |
| // findSourceMap() can lookup source-maps based on URIs, in the |
| // non-exceptional case. |
| { |
| require('../fixtures/source-map/disk-relative-path.js'); |
| const sourceMap = findSourceMap( |
| require.resolve('../fixtures/source-map/disk-relative-path.js') |
| ); |
| const { |
| originalLine, |
| originalColumn, |
| originalSource |
| } = sourceMap.findEntry(0, 29); |
| assert.strictEqual(originalLine, 2); |
| assert.strictEqual(originalColumn, 4); |
| assert(originalSource.endsWith('disk.js')); |
| const { |
| fileName, |
| lineNumber, |
| columnNumber, |
| } = sourceMap.findOrigin(1, 30); |
| assert.strictEqual(fileName, originalSource); |
| assert.strictEqual(lineNumber, 3); |
| assert.strictEqual(columnNumber, 6); |
| assert(Array.isArray(sourceMap.lineLengths)); |
| assert(!sourceMap.lineLengths.some((len) => (typeof len !== 'number'))); |
| } |
| |
| // findSourceMap() can be used in Error.prepareStackTrace() to lookup |
| // source-map attached to error. |
| { |
| let callSite; |
| let sourceMap; |
| Error.prepareStackTrace = (error, trace) => { |
| const throwingRequireCallSite = trace[0]; |
| if (throwingRequireCallSite.getFileName().endsWith('typescript-throw.js')) { |
| sourceMap = findSourceMap(throwingRequireCallSite.getFileName()); |
| callSite = throwingRequireCallSite; |
| } |
| }; |
| try { |
| // Require a file that throws an exception, and has a source map. |
| require('../fixtures/source-map/typescript-throw.js'); |
| } catch (err) { |
| // eslint-disable-next-line no-unused-expressions |
| err.stack; // Force prepareStackTrace() to be called. |
| } |
| assert(callSite); |
| assert(sourceMap); |
| const { |
| generatedLine, |
| generatedColumn, |
| originalLine, |
| originalColumn, |
| originalSource |
| } = sourceMap.findEntry( |
| callSite.getLineNumber() - 1, |
| callSite.getColumnNumber() - 1 |
| ); |
| |
| assert.strictEqual(generatedLine, 19); |
| assert.strictEqual(generatedColumn, 14); |
| |
| assert.strictEqual(originalLine, 17); |
| assert.strictEqual(originalColumn, 10); |
| assert(originalSource.endsWith('typescript-throw.ts')); |
| |
| const { |
| fileName, |
| lineNumber, |
| columnNumber, |
| } = sourceMap.findOrigin( |
| callSite.getLineNumber(), |
| callSite.getColumnNumber() |
| ); |
| assert.strictEqual(fileName, originalSource); |
| assert.strictEqual(lineNumber, 18); |
| assert.strictEqual(columnNumber, 11); |
| } |
| |
| // SourceMap can be instantiated with Source Map V3 object as payload. |
| { |
| const payload = JSON.parse(readFileSync( |
| require.resolve('../fixtures/source-map/disk.map'), 'utf8' |
| )); |
| const lineLengths = readFileSync( |
| require.resolve('../fixtures/source-map/disk.map'), 'utf8' |
| ).replace(/\n$/, '').split('\n').map((l) => l.length); |
| const sourceMap = new SourceMap(payload, { lineLengths }); |
| const { |
| originalLine, |
| originalColumn, |
| originalSource |
| } = sourceMap.findEntry(0, 29); |
| assert.strictEqual(originalLine, 2); |
| assert.strictEqual(originalColumn, 4); |
| assert(originalSource.endsWith('disk.js')); |
| const sourceMapLineLengths = sourceMap.lineLengths; |
| for (let i = 0; i < sourceMapLineLengths.length; i++) { |
| assert.strictEqual(sourceMapLineLengths[i], lineLengths[i]); |
| } |
| assert.strictEqual(sourceMapLineLengths.length, lineLengths.length); |
| // The stored payload should be a clone: |
| assert.strictEqual(payload.mappings, sourceMap.payload.mappings); |
| assert.notStrictEqual(payload, sourceMap.payload); |
| assert.strictEqual(payload.sources[0], sourceMap.payload.sources[0]); |
| assert.notStrictEqual(payload.sources, sourceMap.payload.sources); |
| } |
| |
| // findEntry() and findOrigin() must return empty object instead of |
| // error when receiving a malformed mappings. |
| { |
| const payload = JSON.parse(readFileSync( |
| require.resolve('../fixtures/source-map/disk.map'), 'utf8' |
| )); |
| payload.mappings = ';;;;;;;;;'; |
| |
| const sourceMap = new SourceMap(payload); |
| const result = sourceMap.findEntry(0, 5); |
| assert.strictEqual(typeof result, 'object'); |
| assert.strictEqual(Object.keys(result).length, 0); |
| const origin = sourceMap.findOrigin(0, 5); |
| assert.strictEqual(typeof origin, 'object'); |
| assert.strictEqual(Object.keys(origin).length, 0); |
| } |
| |
| // SourceMap can be instantiated with Index Source Map V3 object as payload. |
| { |
| const payload = JSON.parse(readFileSync( |
| require.resolve('../fixtures/source-map/disk-index.map'), 'utf8' |
| )); |
| const sourceMap = new SourceMap(payload); |
| const { |
| originalLine, |
| originalColumn, |
| originalSource |
| } = sourceMap.findEntry(0, 29); |
| assert.strictEqual(originalLine, 2); |
| assert.strictEqual(originalColumn, 4); |
| assert(originalSource.endsWith('section.js')); |
| // The stored payload should be a clone: |
| assert.strictEqual(payload.mappings, sourceMap.payload.mappings); |
| assert.notStrictEqual(payload, sourceMap.payload); |
| assert.strictEqual(payload.sources[0], sourceMap.payload.sources[0]); |
| assert.notStrictEqual(payload.sources, sourceMap.payload.sources); |
| } |
| |
| // Test various known decodings to ensure decodeVLQ works correctly. |
| { |
| function makeMinimalMap(column) { |
| return { |
| sources: ['test.js'], |
| // Mapping from the 0th line, 0th column of the output file to the 0th |
| // source file, 0th line, ${column}th column. |
| mappings: `AAA${column}`, |
| }; |
| } |
| const knownDecodings = { |
| 'A': 0, |
| 'B': -2147483648, |
| 'C': 1, |
| 'D': -1, |
| 'E': 2, |
| 'F': -2, |
| |
| // 2^31 - 1, maximum values |
| '+/////D': 2147483647, |
| '8/////D': 2147483646, |
| '6/////D': 2147483645, |
| '4/////D': 2147483644, |
| '2/////D': 2147483643, |
| '0/////D': 2147483642, |
| |
| // -2^31 + 1, minimum values |
| '//////D': -2147483647, |
| '9/////D': -2147483646, |
| '7/////D': -2147483645, |
| '5/////D': -2147483644, |
| '3/////D': -2147483643, |
| '1/////D': -2147483642, |
| }; |
| |
| for (const column in knownDecodings) { |
| const sourceMap = new SourceMap(makeMinimalMap(column)); |
| const { originalColumn } = sourceMap.findEntry(0, 0); |
| assert.strictEqual(originalColumn, knownDecodings[column]); |
| } |
| } |
| |
| // Test that generated columns are sorted when a negative offset is |
| // observed, see: https://github.com/mozilla/source-map/pull/92 |
| { |
| function makeMinimalMap(generatedColumns, originalColumns) { |
| return { |
| sources: ['test.js'], |
| // Mapping from the 0th line, ${g}th column of the output file to the 0th |
| // source file, 0th line, ${column}th column. |
| mappings: generatedColumns.map((g, i) => `${g}AA${originalColumns[i]}`) |
| .join(',') |
| }; |
| } |
| // U = 10 |
| // F = -2 |
| // A = 0 |
| // E = 2 |
| const sourceMap = new SourceMap(makeMinimalMap( |
| ['U', 'F', 'F'], |
| ['A', 'E', 'E'] |
| )); |
| assert.strictEqual(sourceMap.findEntry(0, 6).originalColumn, 4); |
| assert.strictEqual(sourceMap.findEntry(0, 8).originalColumn, 2); |
| assert.strictEqual(sourceMap.findEntry(0, 10).originalColumn, 0); |
| } |