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