| 'use strict'; |
| |
| if (!process.features.inspector) return; |
| |
| const common = require('../common'); |
| const assert = require('assert'); |
| const { dirname } = require('path'); |
| const fs = require('fs'); |
| const path = require('path'); |
| const { spawnSync } = require('child_process'); |
| const { pathToFileURL } = require('url'); |
| |
| const tmpdir = require('../common/tmpdir'); |
| tmpdir.refresh(); |
| |
| let dirc = 0; |
| function nextdir() { |
| return process.env.NODE_V8_COVERAGE || |
| path.join(tmpdir.path, `source_map_${++dirc}`); |
| } |
| |
| // Outputs source maps when event loop is drained, with no async logic. |
| { |
| const coverageDirectory = nextdir(); |
| const output = spawnSync(process.execPath, [ |
| require.resolve('../fixtures/source-map/basic'), |
| ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); |
| if (output.status !== 0) { |
| console.log(output.stderr.toString()); |
| } |
| assert.strictEqual(output.status, 0); |
| assert.strictEqual(output.stderr.toString(), ''); |
| const sourceMap = getSourceMapFromCache('basic.js', coverageDirectory); |
| assert.strictEqual(sourceMap.url, 'https://ci.nodejs.org/418'); |
| } |
| |
| // Outputs source maps when process.kill(process.pid, "SIGINT"); exits process. |
| { |
| const coverageDirectory = nextdir(); |
| const output = spawnSync(process.execPath, [ |
| require.resolve('../fixtures/source-map/sigint'), |
| ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); |
| if (!common.isWindows) { |
| if (output.signal !== 'SIGINT') { |
| console.log(output.stderr.toString()); |
| } |
| assert.strictEqual(output.signal, 'SIGINT'); |
| } |
| assert.strictEqual(output.stderr.toString(), ''); |
| const sourceMap = getSourceMapFromCache('sigint.js', coverageDirectory); |
| assert.strictEqual(sourceMap.url, 'https://ci.nodejs.org/402'); |
| } |
| |
| // Outputs source maps when source-file calls process.exit(1). |
| { |
| const coverageDirectory = nextdir(); |
| const output = spawnSync(process.execPath, [ |
| require.resolve('../fixtures/source-map/exit-1'), |
| ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); |
| assert.strictEqual(output.stderr.toString(), ''); |
| const sourceMap = getSourceMapFromCache('exit-1.js', coverageDirectory); |
| assert.strictEqual(sourceMap.url, 'https://ci.nodejs.org/404'); |
| } |
| |
| // Outputs source-maps for esm module. |
| { |
| const coverageDirectory = nextdir(); |
| const output = spawnSync(process.execPath, [ |
| '--no-warnings', |
| require.resolve('../fixtures/source-map/esm-basic.mjs'), |
| ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); |
| assert.strictEqual(output.stderr.toString(), ''); |
| const sourceMap = getSourceMapFromCache('esm-basic.mjs', coverageDirectory); |
| assert.strictEqual(sourceMap.url, 'https://ci.nodejs.org/405'); |
| } |
| |
| // Loads source-maps with relative path from .map file on disk. |
| { |
| const coverageDirectory = nextdir(); |
| const output = spawnSync(process.execPath, [ |
| require.resolve('../fixtures/source-map/disk-relative-path'), |
| ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); |
| assert.strictEqual(output.status, 0); |
| assert.strictEqual(output.stderr.toString(), ''); |
| const sourceMap = getSourceMapFromCache( |
| 'disk-relative-path.js', |
| coverageDirectory |
| ); |
| // Source-map should have been loaded from disk and sources should have been |
| // rewritten, such that they're absolute paths. |
| assert.strictEqual( |
| dirname(pathToFileURL( |
| require.resolve('../fixtures/source-map/disk-relative-path')).href), |
| dirname(sourceMap.data.sources[0]) |
| ); |
| } |
| |
| // Loads source-maps from inline data URL. |
| { |
| const coverageDirectory = nextdir(); |
| const output = spawnSync(process.execPath, [ |
| require.resolve('../fixtures/source-map/inline-base64.js'), |
| ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); |
| assert.strictEqual(output.status, 0); |
| assert.strictEqual(output.stderr.toString(), ''); |
| const sourceMap = getSourceMapFromCache( |
| 'inline-base64.js', |
| coverageDirectory |
| ); |
| // base64 JSON should have been decoded, and paths to sources should have |
| // been rewritten such that they're absolute: |
| assert.strictEqual( |
| dirname(pathToFileURL( |
| require.resolve('../fixtures/source-map/inline-base64')).href), |
| dirname(sourceMap.data.sources[0]) |
| ); |
| } |
| |
| // base64 encoding error does not crash application. |
| { |
| const coverageDirectory = nextdir(); |
| const output = spawnSync(process.execPath, [ |
| require.resolve('../fixtures/source-map/inline-base64-type-error.js'), |
| ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); |
| assert.strictEqual(output.status, 0); |
| assert.strictEqual(output.stderr.toString(), ''); |
| const sourceMap = getSourceMapFromCache( |
| 'inline-base64-type-error.js', |
| coverageDirectory |
| ); |
| |
| assert.strictEqual(sourceMap.data, null); |
| } |
| |
| // JSON error does not crash application. |
| { |
| const coverageDirectory = nextdir(); |
| const output = spawnSync(process.execPath, [ |
| require.resolve('../fixtures/source-map/inline-base64-json-error.js'), |
| ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); |
| assert.strictEqual(output.status, 0); |
| assert.strictEqual(output.stderr.toString(), ''); |
| const sourceMap = getSourceMapFromCache( |
| 'inline-base64-json-error.js', |
| coverageDirectory |
| ); |
| |
| assert.strictEqual(sourceMap.data, null); |
| } |
| |
| // Does not apply source-map to stack trace if --experimental-modules |
| // is not set. |
| { |
| const output = spawnSync(process.execPath, [ |
| require.resolve('../fixtures/source-map/uglify-throw.js'), |
| ]); |
| assert.strictEqual( |
| output.stderr.toString().match(/.*uglify-throw-original\.js:5:9/), |
| null |
| ); |
| assert.strictEqual( |
| output.stderr.toString().match(/.*uglify-throw-original\.js:9:3/), |
| null |
| ); |
| } |
| |
| // Applies source-maps generated by uglifyjs to stack trace. |
| { |
| const output = spawnSync(process.execPath, [ |
| '--enable-source-maps', |
| require.resolve('../fixtures/source-map/uglify-throw.js'), |
| ]); |
| assert.match( |
| output.stderr.toString(), |
| /.*uglify-throw-original\.js:5:9/ |
| ); |
| assert.match( |
| output.stderr.toString(), |
| /.*uglify-throw-original\.js:9:3/ |
| ); |
| assert.match(output.stderr.toString(), /at Hello/); |
| } |
| |
| // Applies source-maps generated by tsc to stack trace. |
| { |
| const output = spawnSync(process.execPath, [ |
| '--enable-source-maps', |
| require.resolve('../fixtures/source-map/typescript-throw.js'), |
| ]); |
| assert.ok(output.stderr.toString().match(/.*typescript-throw\.ts:18:11/)); |
| assert.ok(output.stderr.toString().match(/.*typescript-throw\.ts:24:1/)); |
| } |
| |
| // Applies source-maps generated by babel to stack trace. |
| { |
| const output = spawnSync(process.execPath, [ |
| '--enable-source-maps', |
| require.resolve('../fixtures/source-map/babel-throw.js'), |
| ]); |
| assert.ok( |
| output.stderr.toString().match(/.*babel-throw-original\.js:18:31/) |
| ); |
| } |
| |
| // Applies source-maps generated by nyc to stack trace. |
| { |
| const output = spawnSync(process.execPath, [ |
| '--enable-source-maps', |
| require.resolve('../fixtures/source-map/istanbul-throw.js'), |
| ]); |
| assert.ok( |
| output.stderr.toString().match(/.*istanbul-throw-original\.js:5:9/) |
| ); |
| assert.ok( |
| output.stderr.toString().match(/.*istanbul-throw-original\.js:9:3/) |
| ); |
| } |
| |
| // Applies source-maps in esm modules to stack trace. |
| { |
| const output = spawnSync(process.execPath, [ |
| '--enable-source-maps', |
| require.resolve('../fixtures/source-map/babel-esm.mjs'), |
| ]); |
| assert.ok( |
| output.stderr.toString().match(/.*babel-esm-original\.mjs:9:29/) |
| ); |
| } |
| |
| // Does not persist url parameter if source-map has been parsed. |
| { |
| const coverageDirectory = nextdir(); |
| spawnSync(process.execPath, [ |
| require.resolve('../fixtures/source-map/inline-base64.js'), |
| ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); |
| const sourceMap = getSourceMapFromCache( |
| 'inline-base64.js', |
| coverageDirectory |
| ); |
| assert.strictEqual(sourceMap.url, null); |
| } |
| |
| // Persists line lengths for in-memory representation of source file. |
| { |
| const coverageDirectory = nextdir(); |
| spawnSync(process.execPath, [ |
| require.resolve('../fixtures/source-map/istanbul-throw.js'), |
| ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); |
| const sourceMap = getSourceMapFromCache( |
| 'istanbul-throw.js', |
| coverageDirectory |
| ); |
| if (common.checkoutEOL === '\r\n') { |
| assert.deepStrictEqual(sourceMap.lineLengths, [1086, 31, 185, 649, 0]); |
| } else { |
| assert.deepStrictEqual(sourceMap.lineLengths, [1085, 30, 184, 648, 0]); |
| } |
| } |
| |
| // trace.length === 0 . |
| { |
| const output = spawnSync(process.execPath, [ |
| '--enable-source-maps', |
| require.resolve('../fixtures/source-map/emptyStackError.js'), |
| ]); |
| |
| assert.ok( |
| output.stderr.toString().match('emptyStackError') |
| ); |
| } |
| |
| // Does not attempt to apply path resolution logic to absolute URLs |
| // with schemes. |
| // Refs: https://github.com/webpack/webpack/issues/9601 |
| // Refs: https://sourcemaps.info/spec.html#h.75yo6yoyk7x5 |
| { |
| const output = spawnSync(process.execPath, [ |
| '--enable-source-maps', |
| require.resolve('../fixtures/source-map/webpack.js'), |
| ]); |
| // Error in original context of source content: |
| assert.match( |
| output.stderr.toString(), |
| /throw new Error\('oh no!'\)\r?\n.*\^/ |
| ); |
| // Rewritten stack trace: |
| assert.match(output.stderr.toString(), /webpack:\/\/\/webpack\.js:14:9/); |
| assert.match(output.stderr.toString(), /at functionD.*14:9/); |
| assert.match(output.stderr.toString(), /at functionC.*10:3/); |
| } |
| |
| // Stores and applies source map associated with file that throws while |
| // being required. |
| { |
| const coverageDirectory = nextdir(); |
| const output = spawnSync(process.execPath, [ |
| '--enable-source-maps', |
| require.resolve('../fixtures/source-map/throw-on-require-entry.js'), |
| ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); |
| const sourceMap = getSourceMapFromCache( |
| 'throw-on-require.js', |
| coverageDirectory |
| ); |
| // Rewritten stack trace. |
| assert.match(output.stderr.toString(), /throw-on-require\.ts:9:9/); |
| // Source map should have been serialized. |
| assert.ok(sourceMap); |
| } |
| |
| // Does not throw TypeError when primitive value is thrown. |
| { |
| const coverageDirectory = nextdir(); |
| const output = spawnSync(process.execPath, [ |
| '--enable-source-maps', |
| require.resolve('../fixtures/source-map/throw-string.js'), |
| ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); |
| const sourceMap = getSourceMapFromCache( |
| 'throw-string.js', |
| coverageDirectory |
| ); |
| // Original stack trace. |
| assert.match(output.stderr.toString(), /goodbye/); |
| // Source map should have been serialized. |
| assert.ok(sourceMap); |
| } |
| |
| // Does not throw TypeError when exception occurs as result of missing named |
| // export. |
| { |
| const coverageDirectory = nextdir(); |
| const output = spawnSync(process.execPath, [ |
| '--enable-source-maps', |
| require.resolve('../fixtures/source-map/esm-export-missing.mjs'), |
| ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); |
| const sourceMap = getSourceMapFromCache( |
| 'esm-export-missing.mjs', |
| coverageDirectory |
| ); |
| // Module loader error displayed. |
| assert.match(output.stderr.toString(), |
| /does not provide an export named 'Something'/); |
| // Source map should have been serialized. |
| assert.ok(sourceMap); |
| } |
| |
| // Does not include null for async/await with esm |
| // Refs: https://github.com/nodejs/node/issues/42417 |
| { |
| const output = spawnSync(process.execPath, [ |
| '--enable-source-maps', |
| require.resolve('../fixtures/source-map/throw-async.mjs'), |
| ]); |
| // Error in original context of source content: |
| assert.match( |
| output.stderr.toString(), |
| /throw new Error\(message\)\r?\n.*\^/ |
| ); |
| // Rewritten stack trace: |
| assert.match(output.stderr.toString(), /at Throw \([^)]+throw-async\.ts:4:9\)/); |
| } |
| |
| function getSourceMapFromCache(fixtureFile, coverageDirectory) { |
| const jsonFiles = fs.readdirSync(coverageDirectory); |
| for (const jsonFile of jsonFiles) { |
| let maybeSourceMapCache; |
| try { |
| maybeSourceMapCache = require( |
| path.join(coverageDirectory, jsonFile) |
| )['source-map-cache'] || {}; |
| } catch (err) { |
| console.warn(err); |
| maybeSourceMapCache = {}; |
| } |
| const keys = Object.keys(maybeSourceMapCache); |
| for (const key of keys) { |
| if (key.includes(fixtureFile)) { |
| return maybeSourceMapCache[key]; |
| } |
| } |
| } |
| } |