1'use strict'; 2 3if (!process.features.inspector) return; 4 5const common = require('../common'); 6const assert = require('assert'); 7const { dirname } = require('path'); 8const fs = require('fs'); 9const path = require('path'); 10const { spawnSync } = require('child_process'); 11 12const tmpdir = require('../common/tmpdir'); 13tmpdir.refresh(); 14 15let dirc = 0; 16function nextdir() { 17 return process.env.NODE_V8_COVERAGE || 18 path.join(tmpdir.path, `source_map_${++dirc}`); 19} 20 21// Outputs source maps when event loop is drained, with no async logic. 22{ 23 const coverageDirectory = nextdir(); 24 const output = spawnSync(process.execPath, [ 25 require.resolve('../fixtures/source-map/basic') 26 ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); 27 if (output.status !== 0) { 28 console.log(output.stderr.toString()); 29 } 30 assert.strictEqual(output.status, 0); 31 assert.strictEqual(output.stderr.toString(), ''); 32 const sourceMap = getSourceMapFromCache('basic.js', coverageDirectory); 33 assert.strictEqual(sourceMap.url, 'https://ci.nodejs.org/418'); 34} 35 36// Outputs source maps when process.kill(process.pid, "SIGINT"); exits process. 37{ 38 const coverageDirectory = nextdir(); 39 const output = spawnSync(process.execPath, [ 40 require.resolve('../fixtures/source-map/sigint') 41 ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); 42 if (!common.isWindows) { 43 if (output.signal !== 'SIGINT') { 44 console.log(output.stderr.toString()); 45 } 46 assert.strictEqual(output.signal, 'SIGINT'); 47 } 48 assert.strictEqual(output.stderr.toString(), ''); 49 const sourceMap = getSourceMapFromCache('sigint.js', coverageDirectory); 50 assert.strictEqual(sourceMap.url, 'https://ci.nodejs.org/402'); 51} 52 53// Outputs source maps when source-file calls process.exit(1). 54{ 55 const coverageDirectory = nextdir(); 56 const output = spawnSync(process.execPath, [ 57 require.resolve('../fixtures/source-map/exit-1') 58 ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); 59 assert.strictEqual(output.stderr.toString(), ''); 60 const sourceMap = getSourceMapFromCache('exit-1.js', coverageDirectory); 61 assert.strictEqual(sourceMap.url, 'https://ci.nodejs.org/404'); 62} 63 64// Outputs source-maps for esm module. 65{ 66 const coverageDirectory = nextdir(); 67 const output = spawnSync(process.execPath, [ 68 '--no-warnings', 69 require.resolve('../fixtures/source-map/esm-basic.mjs') 70 ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); 71 assert.strictEqual(output.stderr.toString(), ''); 72 const sourceMap = getSourceMapFromCache('esm-basic.mjs', coverageDirectory); 73 assert.strictEqual(sourceMap.url, 'https://ci.nodejs.org/405'); 74} 75 76// Loads source-maps with relative path from .map file on disk. 77{ 78 const coverageDirectory = nextdir(); 79 const output = spawnSync(process.execPath, [ 80 require.resolve('../fixtures/source-map/disk-relative-path') 81 ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); 82 assert.strictEqual(output.status, 0); 83 assert.strictEqual(output.stderr.toString(), ''); 84 const sourceMap = getSourceMapFromCache( 85 'disk-relative-path.js', 86 coverageDirectory 87 ); 88 // Source-map should have been loaded from disk and sources should have been 89 // rewritten, such that they're absolute paths. 90 assert.strictEqual( 91 dirname( 92 `file://${require.resolve('../fixtures/source-map/disk-relative-path')}`), 93 dirname(sourceMap.data.sources[0]) 94 ); 95} 96 97// Loads source-maps from inline data URL. 98{ 99 const coverageDirectory = nextdir(); 100 const output = spawnSync(process.execPath, [ 101 require.resolve('../fixtures/source-map/inline-base64.js') 102 ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); 103 assert.strictEqual(output.status, 0); 104 assert.strictEqual(output.stderr.toString(), ''); 105 const sourceMap = getSourceMapFromCache( 106 'inline-base64.js', 107 coverageDirectory 108 ); 109 // base64 JSON should have been decoded, and paths to sources should have 110 // been rewritten such that they're absolute: 111 assert.strictEqual( 112 dirname( 113 `file://${require.resolve('../fixtures/source-map/inline-base64')}`), 114 dirname(sourceMap.data.sources[0]) 115 ); 116} 117 118// base64 encoding error does not crash application. 119{ 120 const coverageDirectory = nextdir(); 121 const output = spawnSync(process.execPath, [ 122 require.resolve('../fixtures/source-map/inline-base64-type-error.js') 123 ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); 124 assert.strictEqual(output.status, 0); 125 assert.strictEqual(output.stderr.toString(), ''); 126 const sourceMap = getSourceMapFromCache( 127 'inline-base64-type-error.js', 128 coverageDirectory 129 ); 130 131 assert.strictEqual(sourceMap.data, null); 132} 133 134// JSON error does not crash application. 135{ 136 const coverageDirectory = nextdir(); 137 const output = spawnSync(process.execPath, [ 138 require.resolve('../fixtures/source-map/inline-base64-json-error.js') 139 ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); 140 assert.strictEqual(output.status, 0); 141 assert.strictEqual(output.stderr.toString(), ''); 142 const sourceMap = getSourceMapFromCache( 143 'inline-base64-json-error.js', 144 coverageDirectory 145 ); 146 147 assert.strictEqual(sourceMap.data, null); 148} 149 150// Does not apply source-map to stack trace if --experimental-modules 151// is not set. 152{ 153 const output = spawnSync(process.execPath, [ 154 require.resolve('../fixtures/source-map/uglify-throw.js') 155 ]); 156 assert.strictEqual( 157 output.stderr.toString().match(/->.*uglify-throw-original\.js:5:9/), 158 null 159 ); 160 assert.strictEqual( 161 output.stderr.toString().match(/->.*uglify-throw-original\.js:9:3/), 162 null 163 ); 164} 165 166// Applies source-maps generated by uglifyjs to stack trace. 167{ 168 const output = spawnSync(process.execPath, [ 169 '--enable-source-maps', 170 require.resolve('../fixtures/source-map/uglify-throw.js') 171 ]); 172 assert.ok( 173 output.stderr.toString().match(/->.*uglify-throw-original\.js:5:9/) 174 ); 175 assert.ok( 176 output.stderr.toString().match(/->.*uglify-throw-original\.js:9:3/) 177 ); 178} 179 180// Applies source-maps generated by tsc to stack trace. 181{ 182 const output = spawnSync(process.execPath, [ 183 '--enable-source-maps', 184 require.resolve('../fixtures/source-map/typescript-throw.js') 185 ]); 186 assert.ok(output.stderr.toString().match(/->.*typescript-throw\.ts:18:11/)); 187 assert.ok(output.stderr.toString().match(/->.*typescript-throw\.ts:24:1/)); 188} 189 190// Applies source-maps generated by babel to stack trace. 191{ 192 const output = spawnSync(process.execPath, [ 193 '--enable-source-maps', 194 require.resolve('../fixtures/source-map/babel-throw.js') 195 ]); 196 assert.ok( 197 output.stderr.toString().match(/->.*babel-throw-original\.js:18:31/) 198 ); 199} 200 201// Applies source-maps generated by nyc to stack trace. 202{ 203 const output = spawnSync(process.execPath, [ 204 '--enable-source-maps', 205 require.resolve('../fixtures/source-map/istanbul-throw.js') 206 ]); 207 assert.ok( 208 output.stderr.toString().match(/->.*istanbul-throw-original\.js:5:9/) 209 ); 210 assert.ok( 211 output.stderr.toString().match(/->.*istanbul-throw-original\.js:9:3/) 212 ); 213} 214 215// Applies source-maps in esm modules to stack trace. 216{ 217 const output = spawnSync(process.execPath, [ 218 '--enable-source-maps', 219 require.resolve('../fixtures/source-map/babel-esm.mjs') 220 ]); 221 assert.ok( 222 output.stderr.toString().match(/->.*babel-esm-original\.mjs:9:29/) 223 ); 224} 225 226// Does not persist url parameter if source-map has been parsed. 227{ 228 const coverageDirectory = nextdir(); 229 spawnSync(process.execPath, [ 230 require.resolve('../fixtures/source-map/inline-base64.js') 231 ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); 232 const sourceMap = getSourceMapFromCache( 233 'inline-base64.js', 234 coverageDirectory 235 ); 236 assert.strictEqual(sourceMap.url, null); 237} 238 239// Persists line lengths for in-memory representation of source file. 240{ 241 const coverageDirectory = nextdir(); 242 spawnSync(process.execPath, [ 243 require.resolve('../fixtures/source-map/istanbul-throw.js') 244 ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); 245 const sourceMap = getSourceMapFromCache( 246 'istanbul-throw.js', 247 coverageDirectory 248 ); 249 if (common.isWindows) { 250 assert.deepStrictEqual(sourceMap.lineLengths, [1086, 31, 185, 649, 0]); 251 } else { 252 assert.deepStrictEqual(sourceMap.lineLengths, [1085, 30, 184, 648, 0]); 253 } 254} 255 256// trace.length === 0 . 257{ 258 const output = spawnSync(process.execPath, [ 259 '--enable-source-maps', 260 require.resolve('../fixtures/source-map/emptyStackError.js') 261 ]); 262 263 assert.ok( 264 output.stderr.toString().match('emptyStackError') 265 ); 266} 267 268function getSourceMapFromCache(fixtureFile, coverageDirectory) { 269 const jsonFiles = fs.readdirSync(coverageDirectory); 270 for (const jsonFile of jsonFiles) { 271 let maybeSourceMapCache; 272 try { 273 maybeSourceMapCache = require( 274 path.join(coverageDirectory, jsonFile) 275 )['source-map-cache'] || {}; 276 } catch (err) { 277 console.warn(err); 278 maybeSourceMapCache = {}; 279 } 280 const keys = Object.keys(maybeSourceMapCache); 281 for (const key of keys) { 282 if (key.includes(fixtureFile)) { 283 return maybeSourceMapCache[key]; 284 } 285 } 286 } 287} 288