From c6aa0320f440c064467fb588a772fe129679934a Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Thu, 21 Apr 2022 14:07:26 -0400 Subject: [PATCH 01/24] switch to @jridgewell/trace-mapping --- package-lock.json | 48 ++++++++++++++++++++++++++++++++--------- package.json | 2 +- source-map-support.d.ts | 10 ++++++++- source-map-support.js | 10 +++++---- 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 71c317c..c4ea1c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.7.0", "license": "MIT", "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" + "@jridgewell/trace-mapping": "0.3.9" }, "devDependencies": { "browserify": "^4.2.3", @@ -23,12 +23,26 @@ "node": ">=12" } }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", "engines": { - "node": ">= 12" + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, "node_modules/acorn": { @@ -5252,10 +5266,24 @@ } }, "dependencies": { - "@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==" + "@jridgewell/resolve-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==" + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } }, "acorn": { "version": "4.0.13", diff --git a/package.json b/package.json index 6bcbd26..a14687b 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "/source-map-support.js" ], "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" + "@jridgewell/trace-mapping": "0.3.9" }, "devDependencies": { "browserify": "^4.2.3", diff --git a/source-map-support.d.ts b/source-map-support.d.ts index 4c112db..d8cb9d8 100755 --- a/source-map-support.d.ts +++ b/source-map-support.d.ts @@ -6,7 +6,15 @@ // Griffin Yourick // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -import { RawSourceMap } from '@cspotcode/source-map-consumer'; +export interface RawSourceMap { + version: 3; + sources: string[]; + names: string[]; + sourceRoot?: string; + sourcesContent?: string[]; + mappings: string; + file: string; +} /** * Output of retrieveSourceMap(). diff --git a/source-map-support.js b/source-map-support.js index 4cbbc0d..3c544d5 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -1,4 +1,4 @@ -var SourceMapConsumer = require('@cspotcode/source-map-consumer').SourceMapConsumer; +const { TraceMap, originalPositionFor, AnyMap } = require('@jridgewell/trace-mapping'); var path = require('path'); var util = require('util'); @@ -95,6 +95,7 @@ var sharedData = initializeSharedData({ fileContentsCache: {}, // Maps a file path to a source map for that file + /** @type {Record import('./source-map-support').UrlAndMap | null} */ var retrieveSourceMap = handlerExec(sharedData.retrieveMapHandlers, sharedData.internalRetrieveMapHandlers); sharedData.internalRetrieveMapHandlers.push(function(source) { var sourceMappingURL = retrieveSourceMapURL(source); @@ -270,7 +272,7 @@ function mapSourcePosition(position) { if (urlAndMap) { sourceMap = sharedData.sourceMapCache[position.source] = { url: urlAndMap.url, - map: new SourceMapConsumer(urlAndMap.map) + map: new AnyMap(urlAndMap.map, urlAndMap.url) }; // Load all sources stored inline with the source map into the file cache @@ -293,8 +295,8 @@ function mapSourcePosition(position) { } // Resolve the source URL relative to the URL of the source map - if (sourceMap && sourceMap.map && typeof sourceMap.map.originalPositionFor === 'function') { - var originalPosition = sourceMap.map.originalPositionFor(position); + if (sourceMap && sourceMap.map) { + var originalPosition = originalPositionFor(sourceMap.map, position); // Only return the original position if a matching line was found. If no // matching line is found then we return position instead, which will cause From 12a2b6f10580d564e1c04ece1934ab4675675509 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Sat, 23 Apr 2022 00:26:02 +0000 Subject: [PATCH 02/24] Fix? --- .github/workflows/continuous-integration.yml | 2 ++ source-map-support.js | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 92cc7bc..5274c81 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -18,6 +18,8 @@ jobs: matrix: os: [ubuntu, windows] node: + - 18 + - 17 - 16 - 14 - 12 diff --git a/source-map-support.js b/source-map-support.js index 3c544d5..16388c1 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -1,5 +1,6 @@ const { TraceMap, originalPositionFor, AnyMap } = require('@jridgewell/trace-mapping'); var path = require('path'); +const { fileURLToPath } = require('url'); var util = require('util'); var fs; @@ -187,6 +188,12 @@ sharedData.internalRetrieveFileHandlers.push(function(path) { // in case we are in the browser (i.e. directories may start with "http://" or "file:///") function supportRelativeURL(file, url) { if (!file) return url; + let targetPath = url; + try { + const urlParsed = new URL(url); + if(urlParsed.protocol !== 'file:') return url; + targetPath = fileURLToPath(urlParsed); + } catch(e) {} var dir = path.dirname(file); var match = /^\w+:\/\/[^\/]*/.exec(dir); var protocol = match ? match[0] : ''; @@ -194,9 +201,9 @@ function supportRelativeURL(file, url) { if (protocol && /^\/\w\:/.test(startPath)) { // handle file:///C:/ paths protocol += '/'; - return protocol + path.resolve(dir.slice(protocol.length), url).replace(/\\/g, '/'); + return protocol + path.resolve(dir.slice(protocol.length), targetPath).replace(/\\/g, '/'); } - return protocol + path.resolve(dir.slice(protocol.length), url); + return protocol + path.resolve(dir.slice(protocol.length), targetPath); } function retrieveSourceMapURL(source) { From 62cb778b77aac601153353ca68457d045e2bd3e9 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Sat, 23 Apr 2022 13:23:48 -0400 Subject: [PATCH 03/24] refactor tests to include ESM and other variants of sourcemap --- test.js | 209 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 141 insertions(+), 68 deletions(-) diff --git a/test.js b/test.js index f57cf89..767bc05 100644 --- a/test.js +++ b/test.js @@ -1,3 +1,4 @@ +// @ts-check // Note: some tests rely on side-effects from prior tests. // You may not get meaningful results running a subset of tests. @@ -25,6 +26,15 @@ function compareLines(actual, expected) { } } +function getSourceMapCreators() { + return { + createEmptySourceMap, + createSourceMapWithGap, + createSingleLineSourceMap, + createSecondLineSourceMap, + createMultiLineSourceMap, + createMultiLineSourceMapWithSourcesContent + }; function createEmptySourceMap() { return new SourceMapGenerator({ file: '.generated.js', @@ -88,23 +98,25 @@ function createMultiLineSourceMapWithSourcesContent() { sourceMap.setSourceContent('original.js', original); return sourceMap; } +} -function compareStackTrace(sourceMap, source, expected) { +async function compareStackTrace(id, extension, sourceMap, source, expected) { // Check once with a separate source map - fs.writeFileSync('.generated.js.map', sourceMap.toString()); - fs.writeFileSync('.generated.js', 'exports.test = function() {' + - source.join('\n') + '};//@ sourceMappingURL=.generated.js.map'); + fs.writeFileSync(`.generated.${id}.${extension}.map`, sourceMap.toString()); + fs.writeFileSync(`.generated.${id}.${extension}`, 'exports.test = function() {' + + source.join('\n') + `};//@ sourceMappingURL=.generated.${id}.${extension}.map`); try { - delete require.cache[require.resolve('./.generated')]; - require('./.generated').test(); + // delete require.cache[require.resolve('./.generated')]; + await import(`./.generated.${id}.${extension}`).test(); } catch (e) { + console.log(e); compareLines(e.stack.split(/\r\n|\n/), expected); } - fs.unlinkSync('.generated.js'); - fs.unlinkSync('.generated.js.map'); + fs.unlinkSync('.generated.${extension}'); + fs.unlinkSync('.generated.${extension}.map'); // Check again with an inline source map (in a data URL) - fs.writeFileSync('.generated.js', 'exports.test = function() {' + + fs.writeFileSync('.generated.${extension}', 'exports.test = function() {' + source.join('\n') + '};//@ sourceMappingURL=data:application/json;base64,' + bufferFrom(sourceMap.toString()).toString('base64')); try { @@ -113,13 +125,13 @@ function compareStackTrace(sourceMap, source, expected) { } catch (e) { compareLines(e.stack.split(/\r\n|\n/), expected); } - fs.unlinkSync('.generated.js'); + fs.unlinkSync('.generated.${extension}'); } -function compareStdout(done, sourceMap, source, expected) { - fs.writeFileSync('.original.js', 'this is the original code'); - fs.writeFileSync('.generated.js.map', sourceMap.toString()); - fs.writeFileSync('.generated.js', source.join('\n') + +function compareStdout(done, id, extension, sourceMap, source, expected) { + fs.writeFileSync(`.original.${id}.${extension}`, 'this is the original code'); + fs.writeFileSync(`.generated.${id}.${extension}.map`, sourceMap.toString()); + fs.writeFileSync(`.generated.${id}.${extension}`, source.join('\n') + '//@ sourceMappingURL=.generated.js.map'); child_process.exec('node ./.generated', function(error, stdout, stderr) { try { @@ -133,48 +145,101 @@ function compareStdout(done, sourceMap, source, expected) { } catch (e) { return done(e); } - fs.unlinkSync('.generated.js'); - fs.unlinkSync('.generated.js.map'); - fs.unlinkSync('.original.js'); + fs.unlinkSync(`.generated.${id}.js`); + fs.unlinkSync(`.generated.${id}.js.map`); + fs.unlinkSync(`.original.${id}.js`); done(); }); } -it('normal throw without source-map-support installed', normalThrowWithoutSourceMapSupportInstalled); - -it('normal throw', function() { - installSms(); - normalThrow(); -}); - function installSms() { underTest.install({ emptyCacheBetweenOperations: true // Needed to be able to test for failure }); } -function normalThrow() { - compareStackTrace(createMultiLineSourceMap(), [ +function getTestMacros(sourceMapConstructors) { + return {normalThrow, normalThrowWithoutSourceMapSupportInstalled}; +async function normalThrow() { + await compareStackTrace(sourceMapConstructors.createMultiLineSourceMap(), [ 'throw new Error("test");' ], [ 'Error: test', /^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/ ]); } -function normalThrowWithoutSourceMapSupportInstalled() { - compareStackTrace(createMultiLineSourceMap(), [ +async function normalThrowWithoutSourceMapSupportInstalled() { + await compareStackTrace(sourceMapConstructors.createMultiLineSourceMap(), [ 'throw new Error("test");' ], [ 'Error: test', /^ at Object\.exports\.test \((?:.*[/\\])?\.generated\.js:1:34\)$/ ]); } +} + +describe('Without source-map-support installed', function() { + const sourceMapConstructors = getSourceMapCreators(); + const macros = getTestMacros(sourceMapConstructors); + + it('normal throw without source-map-support installed', macros.normalThrowWithoutSourceMapSupportInstalled); +}); + +function identity(v) {return v} +function addRelativePrefixToSourceMapPaths(sourceMap) { + addPrefixToSourceMapPaths(sourceMap, './'); + return sourceMap; +} +function addAbsolutePrefixToSourceMapPaths(sourceMap) { + addPrefixToSourceMapPaths(sourceMap, '/root/project/'); + return sourceMap; +} +function addFileUrlAbsolutePrefixToSourceMapPaths(sourceMap) { + addPrefixToSourceMapPaths(sourceMap, 'file:///root/project/'); + return sourceMap; +} +function addPrefixToSourceMapPaths(sourceMap, prefix) { + function addPrefix(path) {return `${prefix}${path}`} + sourceMap.file = addPrefix(sourceMap.file); + if(sourceMap.sources) sourceMap.sources = sourceMap.sources.map(addPrefix); + return sourceMap; +} + +// describe('sourcemap style: relative paths sans ./ prefix, e.g. "original.js"', () => { +// declareTests(identity); +// }); +// describe('sourcemap style: relative paths with ./ prefix, e.g. "./original.js"', () => { +// declareTests(addRelativePrefixToSourceMapPaths); +// }); +describe('sourcemap style: absolute paths and sourceRoot removed, e.g. "/abs/path/original.js"', () => { + describe('cjs', () => { + declareTests(addAbsolutePrefixToSourceMapPaths, 'cjs'); + }); + describe('mjs', () => { + declareTests(addAbsolutePrefixToSourceMapPaths, 'mjs'); + }); +}); +// describe('sourcemap style: file urls with absolute paths and sourceRoot removed, e.g. "file:///abs/path/original.js"', () => { +// declareTests(addFileUrlAbsolutePrefixToSourceMapPaths); +// }); + +function declareTests(sourceMapPostprocessor, fileExtension) { + const sourceMapConstructors = getSourceMapCreators(); + for(const [key, value] of Object.entries(sourceMapConstructors)) { + sourceMapConstructors[key] = (...args) => sourceMapPostprocessor(value(...args)); + } + const {createEmptySourceMap, createMultiLineSourceMap, createMultiLineSourceMapWithSourcesContent, createSecondLineSourceMap, createSingleLineSourceMap, createSourceMapWithGap} = sourceMapConstructors; + const {normalThrow} = getTestMacros(sourceMapConstructors); +it('normal throw', async function() { + installSms(); + normalThrow(); +}); /* The following test duplicates some of the code in * `normal throw` but triggers file read failure. */ -it('fs.readFileSync failure', function() { - compareStackTrace(createMultiLineSourceMap(), [ +it('fs.readFileSync failure', async function() { + await compareStackTrace(createMultiLineSourceMap(), [ 'var fs = require("fs");', 'var rfs = fs.readFileSync;', 'fs.readFileSync = function() {', @@ -192,8 +257,8 @@ it('fs.readFileSync failure', function() { }); -it('throw inside function', function() { - compareStackTrace(createMultiLineSourceMap(), [ +it('throw inside function', async function() { + await compareStackTrace(createMultiLineSourceMap(), [ 'function foo() {', ' throw new Error("test");', '}', @@ -205,8 +270,8 @@ it('throw inside function', function() { ]); }); -it('throw inside function inside function', function() { - compareStackTrace(createMultiLineSourceMap(), [ +it('throw inside function inside function', async function() { + await compareStackTrace(createMultiLineSourceMap(), [ 'function foo() {', ' function bar() {', ' throw new Error("test");', @@ -222,8 +287,8 @@ it('throw inside function inside function', function() { ]); }); -it('eval', function() { - compareStackTrace(createMultiLineSourceMap(), [ +it('eval', async function() { + await compareStackTrace(createMultiLineSourceMap(), [ 'eval("throw new Error(\'test\')");' ], [ 'Error: test', @@ -235,8 +300,8 @@ it('eval', function() { ]); }); -it('eval inside eval', function() { - compareStackTrace(createMultiLineSourceMap(), [ +it('eval inside eval', async function() { + await compareStackTrace(createMultiLineSourceMap(), [ 'eval("eval(\'throw new Error(\\"test\\")\')");' ], [ 'Error: test', @@ -246,8 +311,8 @@ it('eval inside eval', function() { ]); }); -it('eval inside function', function() { - compareStackTrace(createMultiLineSourceMap(), [ +it('eval inside function', async function() { + await compareStackTrace(createMultiLineSourceMap(), [ 'function foo() {', ' eval("throw new Error(\'test\')");', '}', @@ -260,8 +325,8 @@ it('eval inside function', function() { ]); }); -it('eval with sourceURL', function() { - compareStackTrace(createMultiLineSourceMap(), [ +it('eval with sourceURL', async function() { + await compareStackTrace(createMultiLineSourceMap(), [ 'eval("throw new Error(\'test\')//@ sourceURL=sourceURL.js");' ], [ 'Error: test', @@ -270,8 +335,8 @@ it('eval with sourceURL', function() { ]); }); -it('eval with sourceURL inside eval', function() { - compareStackTrace(createMultiLineSourceMap(), [ +it('eval with sourceURL inside eval', async function() { + await compareStackTrace(createMultiLineSourceMap(), [ 'eval("eval(\'throw new Error(\\"test\\")//@ sourceURL=sourceURL.js\')");' ], [ 'Error: test', @@ -281,8 +346,8 @@ it('eval with sourceURL inside eval', function() { ]); }); -it('native function', function() { - compareStackTrace(createSingleLineSourceMap(), [ +it('native function', async function() { + await compareStackTrace(createSingleLineSourceMap(), [ '[1].map(function(x) { throw new Error(x); });' ], [ 'Error: 1', @@ -291,16 +356,16 @@ it('native function', function() { ]); }); -it('function constructor', function() { - compareStackTrace(createMultiLineSourceMap(), [ +it('function constructor', async function() { + await compareStackTrace(createMultiLineSourceMap(), [ 'throw new Function(")");' ], [ /SyntaxError: Unexpected token '?\)'?/, ]); }); -it('throw with empty source map', function() { - compareStackTrace(createEmptySourceMap(), [ +it('throw with empty source map', async function() { + await compareStackTrace(createEmptySourceMap(), [ 'throw new Error("test");' ], [ 'Error: test', @@ -323,8 +388,8 @@ it('throw in Timeout with empty source map', function(done) { ]); }); -it('throw with source map with gap', function() { - compareStackTrace(createSourceMapWithGap(), [ +it('throw with source map with gap', async function() { + await compareStackTrace(createSourceMapWithGap(), [ 'throw new Error("test");' ], [ 'Error: test', @@ -332,8 +397,8 @@ it('throw with source map with gap', function() { ]); }); -it('sourcesContent with data URL', function() { - compareStackTrace(createMultiLineSourceMapWithSourcesContent(), [ +it('sourcesContent with data URL', async function() { + await compareStackTrace(createMultiLineSourceMapWithSourcesContent(), [ 'throw new Error("test");' ], [ 'Error: test', @@ -341,8 +406,8 @@ it('sourcesContent with data URL', function() { ]); }); -it('finds the last sourceMappingURL', function() { - compareStackTrace(createMultiLineSourceMapWithSourcesContent(), [ +it('finds the last sourceMappingURL', async function() { + await compareStackTrace(createMultiLineSourceMapWithSourcesContent(), [ '//# sourceMappingURL=missing.map.js', // NB: compareStackTrace adds another source mapping. 'throw new Error("test");' ], [ @@ -351,7 +416,7 @@ it('finds the last sourceMappingURL', function() { ]); }); -it('maps original name from source', function() { +it('maps original name from source', async function() { var sourceMap = createEmptySourceMap(); sourceMap.addMapping({ generated: { line: 2, column: 8 }, @@ -364,7 +429,7 @@ it('maps original name from source', function() { source: ".original.js", name: "myOriginalName" }); - compareStackTrace(sourceMap, [ + await compareStackTrace(sourceMap, [ 'function foo() {', ' throw new Error("test");', '}', @@ -582,12 +647,17 @@ it('should allow for runtime inline source maps', function(done) { '0', // The retrieval should only be attempted once ]); }); +} + +describe('Other', function() { + // Wrapped in a suite to preserve test execution order + const {createEmptySourceMap, createSingleLineSourceMap, createMultiLineSourceMap} = getSourceMapCreators(); /* The following test duplicates some of the code in * `compareStackTrace` but appends a charset to the * source mapping url. */ -it('finds source maps with charset specified', function() { +it('finds source maps with charset specified', async function() { var sourceMap = createMultiLineSourceMap() var source = [ 'throw new Error("test");' ]; var expected = [ @@ -611,7 +681,7 @@ it('finds source maps with charset specified', function() { * `compareStackTrace` but appends some code and a * comment to the source mapping url. */ -it('allows code/comments after sourceMappingURL', function() { +it('allows code/comments after sourceMappingURL', async function() { var sourceMap = createMultiLineSourceMap() var source = [ 'throw new Error("test");' ]; var expected = [ @@ -705,15 +775,16 @@ it('supports multiple instances', function(done) { /^ at foo \((?:.*[/\\])?.original2\.js:1:1\)$/ ]); }); +}); describe('redirects require() of "source-map-support" to this module', function() { - it('redirects', function() { + it('redirects', async function() { assert.strictEqual(require.resolve('source-map-support'), require.resolve('.')); assert.strictEqual(require.resolve('source-map-support/register'), require.resolve('./register')); assert.strictEqual(require('source-map-support'), require('.')); }); - it('emits notifications', function() { + it('emits notifications', async function() { let onConflictingLibraryRedirectCalls = []; let onConflictingLibraryRedirectCalls2 = []; underTest.install({ @@ -741,6 +812,8 @@ describe('redirects require() of "source-map-support" to this module', function( }); describe('uninstall', function() { + const sourceMapConstructors = getSourceMapCreators(); + const {normalThrow, normalThrowWithoutSourceMapSupportInstalled} = getTestMacros(sourceMapConstructors); this.beforeEach(function() { underTest.uninstall(); process.emit = priorProcessEmit; @@ -748,19 +821,19 @@ describe('uninstall', function() { Module._resolveFilename = priorResolveFilename; }); - it('uninstall removes hooks and source-mapping behavior', function() { + it('uninstall removes hooks and source-mapping behavior', async function() { assert.strictEqual(Error.prepareStackTrace, priorErrorPrepareStackTrace); assert.strictEqual(process.emit, priorProcessEmit); assert.strictEqual(Module._resolveFilename, priorResolveFilename); normalThrowWithoutSourceMapSupportInstalled(); }); - it('install re-adds hooks', function() { + it('install re-adds hooks', async function() { installSms(); normalThrow(); }); - it('uninstall removes prepareStackTrace even in presence of third-party hooks if none were installed before us', function() { + it('uninstall removes prepareStackTrace even in presence of third-party hooks if none were installed before us', async function() { installSms(); const wrappedPrepareStackTrace = Error.prepareStackTrace; let pstInvocations = 0; @@ -774,7 +847,7 @@ describe('uninstall', function() { assert(pstInvocations === 0); }); - it('uninstall preserves third-party prepareStackTrace hooks if one was installed before us', function() { + it('uninstall preserves third-party prepareStackTrace hooks if one was installed before us', async function() { let beforeInvocations = 0; function thirdPartyPrepareStackTraceHookInstalledBefore() { beforeInvocations++; @@ -796,7 +869,7 @@ describe('uninstall', function() { assert.strictEqual(afterInvocations, 1); }); - it('uninstall preserves third-party process.emit hooks installed after us', function() { + it('uninstall preserves third-party process.emit hooks installed after us', async function() { installSms(); const wrappedProcessEmit = process.emit; let peInvocations = 0; @@ -812,7 +885,7 @@ describe('uninstall', function() { assert(peInvocations >= 1); }); - it('uninstall preserves third-party module._resolveFilename hooks installed after us', function() { + it('uninstall preserves third-party module._resolveFilename hooks installed after us', async function() { installSms(); const wrappedResolveFilename = Module._resolveFilename; let peInvocations = 0; From ce5e122f6251adf20e86b54be6679f60f3a1e46c Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Sat, 23 Apr 2022 20:14:21 +0000 Subject: [PATCH 04/24] use resolve-uri for handling relative paths --- package-lock.json | 13 +++++++------ package.json | 1 + source-map-support.js | 19 ++----------------- 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index c4ea1c4..717506a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.7.0", "license": "MIT", "dependencies": { + "@jridgewell/resolve-uri": "^3.0.6", "@jridgewell/trace-mapping": "0.3.9" }, "devDependencies": { @@ -24,9 +25,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", + "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==", "engines": { "node": ">=6.0.0" } @@ -5267,9 +5268,9 @@ }, "dependencies": { "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==" + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", + "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==" }, "@jridgewell/sourcemap-codec": { "version": "1.4.11", diff --git a/package.json b/package.json index a14687b..4e6d3af 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "/source-map-support.js" ], "dependencies": { + "@jridgewell/resolve-uri": "^3.0.6", "@jridgewell/trace-mapping": "0.3.9" }, "devDependencies": { diff --git a/source-map-support.js b/source-map-support.js index 16388c1..1b8c2d4 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -1,4 +1,5 @@ const { TraceMap, originalPositionFor, AnyMap } = require('@jridgewell/trace-mapping'); +const resolveUri = require('@jridgewell/resolve-uri'); var path = require('path'); const { fileURLToPath } = require('url'); var util = require('util'); @@ -187,23 +188,7 @@ sharedData.internalRetrieveFileHandlers.push(function(path) { // Support URLs relative to a directory, but be careful about a protocol prefix // in case we are in the browser (i.e. directories may start with "http://" or "file:///") function supportRelativeURL(file, url) { - if (!file) return url; - let targetPath = url; - try { - const urlParsed = new URL(url); - if(urlParsed.protocol !== 'file:') return url; - targetPath = fileURLToPath(urlParsed); - } catch(e) {} - var dir = path.dirname(file); - var match = /^\w+:\/\/[^\/]*/.exec(dir); - var protocol = match ? match[0] : ''; - var startPath = dir.slice(protocol.length); - if (protocol && /^\/\w\:/.test(startPath)) { - // handle file:///C:/ paths - protocol += '/'; - return protocol + path.resolve(dir.slice(protocol.length), targetPath).replace(/\\/g, '/'); - } - return protocol + path.resolve(dir.slice(protocol.length), targetPath); + return resolveUri(url, file); } function retrieveSourceMapURL(source) { From 84fb85a7e4aeb2340c2bb146525af26fb6378943 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Tue, 26 Apr 2022 10:08:02 +0000 Subject: [PATCH 05/24] WIP --- .vscode/launch.json | 14 ++ test.js | 391 +++++++++++++++++++++++++------------------- 2 files changed, 234 insertions(+), 171 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..93ae232 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "configurations": [ + { + "name": "Debug Mocha tests", + "type": "node", + "request": "launch", + "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/_mocha", + "outputCapture": "std", + "skipFiles": [ + "/**/*.js" + ], + } + ], +} diff --git a/test.js b/test.js index 767bc05..d99979e 100644 --- a/test.js +++ b/test.js @@ -14,6 +14,36 @@ var fs = require('fs'); var util = require('util'); var bufferFrom = Buffer.from; +function re(...args) { + return new RegExp(String.raw(...args)); +} + +function exportDecl(extension) { + return extension === 'mjs' ? 'export const test' : 'exports.test'; +} + +// Assign each test a unique ID, to be used in filenames. +// Eliminates need for cache invalidation, because node ESM has no way to +// invalidate cache. +let id = 0; +beforeEach(function() { + id++; +}); +// Consolidate cleanup into a hook so that failed assertions do not leave files +// on disk. +afterEach(function() { + for(const name of [`generated`, `original`]) { + for(const suffix of [``, `-separate`, `-inline`]) { + for(const ext of [`js`, `cjs`, `mjs`]) { + for(const ext2 of [``, `.map`]) { + const file = `.${name}-${id}${suffix}.${ext}${ext2}`; + fs.existsSync(file) && fs.unlinkSync(file); + } + } + } + } +}); + function compareLines(actual, expected) { assert(actual.length >= expected.length, 'got ' + actual.length + ' lines but expected at least ' + expected.length + ' lines\n' + util.inspect({actual, expected})); for (var i = 0; i < expected.length; i++) { @@ -35,45 +65,45 @@ function getSourceMapCreators() { createMultiLineSourceMap, createMultiLineSourceMapWithSourcesContent }; -function createEmptySourceMap() { +function createEmptySourceMap(id, extension) { return new SourceMapGenerator({ - file: '.generated.js', + file: `.generated-${id}.${extension}`, sourceRoot: '.' }); } -function createSourceMapWithGap() { - var sourceMap = createEmptySourceMap(); +function createSourceMapWithGap(id, extension) { + var sourceMap = createEmptySourceMap(id, extension); sourceMap.addMapping({ generated: { line: 100, column: 0 }, original: { line: 100, column: 0 }, - source: '.original.js' + source: `.original-${id}.js` }); return sourceMap; } -function createSingleLineSourceMap() { - var sourceMap = createEmptySourceMap(); +function createSingleLineSourceMap(id, extension) { + var sourceMap = createEmptySourceMap(id, extension); sourceMap.addMapping({ generated: { line: 1, column: 0 }, original: { line: 1, column: 0 }, - source: '.original.js' + source: `.original-${id}.js` }); return sourceMap; } -function createSecondLineSourceMap() { - var sourceMap = createEmptySourceMap(); +function createSecondLineSourceMap(id, extension) { + var sourceMap = createEmptySourceMap(id, extension); sourceMap.addMapping({ generated: { line: 2, column: 0 }, original: { line: 1, column: 0 }, - source: '.original.js' + source: `.original-${id}.js` }); return sourceMap; } -function createMultiLineSourceMap() { - var sourceMap = createEmptySourceMap(); +function createMultiLineSourceMap(id, extension) { + var sourceMap = createEmptySourceMap(id, extension); for (var i = 1; i <= 100; i++) { sourceMap.addMapping({ generated: { line: i, column: 0 }, @@ -84,56 +114,66 @@ function createMultiLineSourceMap() { return sourceMap; } -function createMultiLineSourceMapWithSourcesContent() { - var sourceMap = createEmptySourceMap(); +function createMultiLineSourceMapWithSourcesContent(id, extension) { + var sourceMap = createEmptySourceMap(id, extension); var original = new Array(1001).join('\n'); for (var i = 1; i <= 100; i++) { sourceMap.addMapping({ generated: { line: i, column: 0 }, original: { line: 1000 + i, column: 4 }, - source: 'original.js' + source: `original-${id}.js` }); original += ' line ' + i + '\n'; } - sourceMap.setSourceContent('original.js', original); + sourceMap.setSourceContent(`original-${id}.js`, original); return sourceMap; } } +function rewriteExpectation(expected, generatedFilenameIn, generatedFilenameOut) { + return expected.map(v => { + if(v instanceof RegExp) return new RegExp(v.source.replace(generatedFilenameIn, generatedFilenameOut)); + return v.replace(generatedFilenameIn, generatedFilenameOut); + }); +} +function getSrcPrefix(extension) { + return extension === 'mjs' ? `import {createRequire} from 'module';const require = createRequire(import.meta.url);` : ''; +} async function compareStackTrace(id, extension, sourceMap, source, expected) { + const srcPrefix = getSrcPrefix(extension); // Check once with a separate source map - fs.writeFileSync(`.generated.${id}.${extension}.map`, sourceMap.toString()); - fs.writeFileSync(`.generated.${id}.${extension}`, 'exports.test = function() {' + - source.join('\n') + `};//@ sourceMappingURL=.generated.${id}.${extension}.map`); + fs.writeFileSync(`.generated-${id}-separate.${extension}.map`, sourceMap.toString()); + fs.writeFileSync(`.generated-${id}-separate.${extension}`, `${srcPrefix}${exportDecl(extension)} = function() {` + + source.join('\n') + `};//@ sourceMappingURL=.generated-${id}-separate.${extension}.map`); try { - // delete require.cache[require.resolve('./.generated')]; - await import(`./.generated.${id}.${extension}`).test(); + // delete require.cache[require.resolve(`./.generated-${id}`)]; + (await import(`./.generated-${id}-separate.${extension}`)).test(); } catch (e) { console.log(e); - compareLines(e.stack.split(/\r\n|\n/), expected); + compareLines(e.stack.split(/\r\n|\n/), rewriteExpectation(expected, `.generated-${id}`, `.generated-${id}-separate`)); } - fs.unlinkSync('.generated.${extension}'); - fs.unlinkSync('.generated.${extension}.map'); + fs.unlinkSync(`.generated-${id}-separate.${extension}`); + fs.unlinkSync(`.generated-${id}-separate.${extension}.map`); // Check again with an inline source map (in a data URL) - fs.writeFileSync('.generated.${extension}', 'exports.test = function() {' + + fs.writeFileSync(`.generated-${id}-inline.${extension}`, `${srcPrefix}${exportDecl(extension)} = function() {` + source.join('\n') + '};//@ sourceMappingURL=data:application/json;base64,' + bufferFrom(sourceMap.toString()).toString('base64')); try { - delete require.cache[require.resolve('./.generated')]; - require('./.generated').test(); + (await import (`./.generated-${id}-inline.${extension}`)).test(); } catch (e) { - compareLines(e.stack.split(/\r\n|\n/), expected); + compareLines(e.stack.split(/\r\n|\n/), rewriteExpectation(expected, `.generated-${id}`, `.generated-${id}-inline`)); } - fs.unlinkSync('.generated.${extension}'); + fs.unlinkSync(`.generated-${id}-inline.${extension}`); } function compareStdout(done, id, extension, sourceMap, source, expected) { - fs.writeFileSync(`.original.${id}.${extension}`, 'this is the original code'); - fs.writeFileSync(`.generated.${id}.${extension}.map`, sourceMap.toString()); - fs.writeFileSync(`.generated.${id}.${extension}`, source.join('\n') + - '//@ sourceMappingURL=.generated.js.map'); - child_process.exec('node ./.generated', function(error, stdout, stderr) { + let srcPrefix = getSrcPrefix(extension); + fs.writeFileSync(`.original-${id}.js`, 'this is the original code'); + fs.writeFileSync(`.generated-${id}.${extension}.map`, sourceMap.toString()); + fs.writeFileSync(`.generated-${id}.${extension}`, srcPrefix + source.join('\n') + + `//@ sourceMappingURL=.generated-${id}.${extension}.map`); + child_process.exec(`node ./.generated-${id}.${extension}`, function(error, stdout, stderr) { try { compareLines( (stdout + stderr) @@ -145,9 +185,9 @@ function compareStdout(done, id, extension, sourceMap, source, expected) { } catch (e) { return done(e); } - fs.unlinkSync(`.generated.${id}.js`); - fs.unlinkSync(`.generated.${id}.js.map`); - fs.unlinkSync(`.original.${id}.js`); + fs.unlinkSync(`.generated-${id}.${extension}`); + fs.unlinkSync(`.generated-${id}.${extension}.map`); + fs.unlinkSync(`.original-${id}.js`); done(); }); } @@ -160,20 +200,20 @@ function installSms() { function getTestMacros(sourceMapConstructors) { return {normalThrow, normalThrowWithoutSourceMapSupportInstalled}; -async function normalThrow() { - await compareStackTrace(sourceMapConstructors.createMultiLineSourceMap(), [ +async function normalThrow(id, extension = 'js') { + await compareStackTrace(id, extension, sourceMapConstructors.createMultiLineSourceMap(id, extension), [ 'throw new Error("test");' ], [ 'Error: test', - /^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` ]); } -async function normalThrowWithoutSourceMapSupportInstalled() { - await compareStackTrace(sourceMapConstructors.createMultiLineSourceMap(), [ +async function normalThrowWithoutSourceMapSupportInstalled(id, extension = 'cjs') { + await compareStackTrace(id, extension, sourceMapConstructors.createMultiLineSourceMap(id, extension), [ 'throw new Error("test");' ], [ 'Error: test', - /^ at Object\.exports\.test \((?:.*[/\\])?\.generated\.js:1:34\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.generated-${id}\.${extension}:1:34\)$` ]); } } @@ -181,8 +221,11 @@ async function normalThrowWithoutSourceMapSupportInstalled() { describe('Without source-map-support installed', function() { const sourceMapConstructors = getSourceMapCreators(); const macros = getTestMacros(sourceMapConstructors); + const {normalThrowWithoutSourceMapSupportInstalled} = macros; - it('normal throw without source-map-support installed', macros.normalThrowWithoutSourceMapSupportInstalled); + it('normal throw without source-map-support installed', async function () { + await normalThrowWithoutSourceMapSupportInstalled(id); + }); }); function identity(v) {return v} @@ -205,25 +248,29 @@ function addPrefixToSourceMapPaths(sourceMap, prefix) { return sourceMap; } -// describe('sourcemap style: relative paths sans ./ prefix, e.g. "original.js"', () => { -// declareTests(identity); -// }); -// describe('sourcemap style: relative paths with ./ prefix, e.g. "./original.js"', () => { -// declareTests(addRelativePrefixToSourceMapPaths); -// }); -describe('sourcemap style: absolute paths and sourceRoot removed, e.g. "/abs/path/original.js"', () => { - describe('cjs', () => { - declareTests(addAbsolutePrefixToSourceMapPaths, 'cjs'); +describe('sourcemap style: relative paths sans ./ prefix, e.g. "original-1.js" >', () => { + moduleTypeSuites(identity); +}); +describe('sourcemap style: relative paths with ./ prefix, e.g. "./original-1.js" >', () => { + moduleTypeSuites(addRelativePrefixToSourceMapPaths); +}); +describe('sourcemap style: absolute paths and sourceRoot removed, e.g. "/abs/path/original-1.js" >', () => { + moduleTypeSuites(addAbsolutePrefixToSourceMapPaths); +}); +describe('sourcemap style: file urls with absolute paths and sourceRoot removed, e.g. "file:///abs/path/original-1.js" >', () => { + moduleTypeSuites(addFileUrlAbsolutePrefixToSourceMapPaths); +}); + +function moduleTypeSuites(sourceMapPostprocessor) { + describe('cjs >', () => { + tests(sourceMapPostprocessor, 'cjs'); }); - describe('mjs', () => { - declareTests(addAbsolutePrefixToSourceMapPaths, 'mjs'); + describe('mjs >', () => { + tests(sourceMapPostprocessor, 'mjs'); }); -}); -// describe('sourcemap style: file urls with absolute paths and sourceRoot removed, e.g. "file:///abs/path/original.js"', () => { -// declareTests(addFileUrlAbsolutePrefixToSourceMapPaths); -// }); +} -function declareTests(sourceMapPostprocessor, fileExtension) { +function tests(sourceMapPostprocessor, extension) { const sourceMapConstructors = getSourceMapCreators(); for(const [key, value] of Object.entries(sourceMapConstructors)) { sourceMapConstructors[key] = (...args) => sourceMapPostprocessor(value(...args)); @@ -232,14 +279,14 @@ function declareTests(sourceMapPostprocessor, fileExtension) { const {normalThrow} = getTestMacros(sourceMapConstructors); it('normal throw', async function() { installSms(); - normalThrow(); + await normalThrow(id, extension); }); /* The following test duplicates some of the code in * `normal throw` but triggers file read failure. */ it('fs.readFileSync failure', async function() { - await compareStackTrace(createMultiLineSourceMap(), [ + await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ 'var fs = require("fs");', 'var rfs = fs.readFileSync;', 'fs.readFileSync = function() {', @@ -252,13 +299,13 @@ it('fs.readFileSync failure', async function() { '}' ], [ 'Error: test', - /^ at Object\.exports\.test \((?:.*[/\\])?line7\.js:1007:107\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line7\.js:1007:107\)$` ]); }); it('throw inside function', async function() { - await compareStackTrace(createMultiLineSourceMap(), [ + await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ 'function foo() {', ' throw new Error("test");', '}', @@ -266,12 +313,12 @@ it('throw inside function', async function() { ], [ 'Error: test', /^ at foo \((?:.*[/\\])?line2\.js:1002:102\)$/, - /^ at Object\.exports\.test \((?:.*[/\\])?line4\.js:1004:104\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line4\.js:1004:104\)$` ]); }); it('throw inside function inside function', async function() { - await compareStackTrace(createMultiLineSourceMap(), [ + await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ 'function foo() {', ' function bar() {', ' throw new Error("test");', @@ -283,36 +330,36 @@ it('throw inside function inside function', async function() { 'Error: test', /^ at bar \((?:.*[/\\])?line3\.js:1003:103\)$/, /^ at foo \((?:.*[/\\])?line5\.js:1005:105\)$/, - /^ at Object\.exports\.test \((?:.*[/\\])?line7\.js:1007:107\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line7\.js:1007:107\)$` ]); }); it('eval', async function() { - await compareStackTrace(createMultiLineSourceMap(), [ + await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ 'eval("throw new Error(\'test\')");' ], [ 'Error: test', // Before Node 4, `Object.eval`, after just `eval`. - /^ at (?:Object\.)?eval \(eval at (|exports.test) \((?:.*[/\\])?line1\.js:1001:101\)/, + /^ at (?:Object\.)?eval \(eval at (|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, - /^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` ]); }); it('eval inside eval', async function() { - await compareStackTrace(createMultiLineSourceMap(), [ + await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ 'eval("eval(\'throw new Error(\\"test\\")\')");' ], [ 'Error: test', /^ at (?:Object\.)?eval \(eval at (|exports.test) \(eval at (|exports.test) \((?:.*[/\\])?line1\.js:1001:101\)/, /^ at (?:Object\.)?eval \(eval at (|exports.test) \((?:.*[/\\])?line1\.js:1001:101\)/, - /^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` ]); }); it('eval inside function', async function() { - await compareStackTrace(createMultiLineSourceMap(), [ + await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ 'function foo() {', ' eval("throw new Error(\'test\')");', '}', @@ -321,43 +368,43 @@ it('eval inside function', async function() { 'Error: test', /^ at eval \(eval at foo \((?:.*[/\\])?line2\.js:1002:102\)/, /^ at foo \((?:.*[/\\])?line2\.js:1002:102\)/, - /^ at Object\.exports\.test \((?:.*[/\\])?line4\.js:1004:104\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line4\.js:1004:104\)$` ]); }); it('eval with sourceURL', async function() { - await compareStackTrace(createMultiLineSourceMap(), [ + await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ 'eval("throw new Error(\'test\')//@ sourceURL=sourceURL.js");' ], [ 'Error: test', /^ at (?:Object\.)?eval \(sourceURL\.js:1:7\)$/, - /^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` ]); }); it('eval with sourceURL inside eval', async function() { - await compareStackTrace(createMultiLineSourceMap(), [ + await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ 'eval("eval(\'throw new Error(\\"test\\")//@ sourceURL=sourceURL.js\')");' ], [ 'Error: test', /^ at (?:Object\.)?eval \(sourceURL\.js:1:7\)$/, /^ at (?:Object\.)?eval \(eval at (|exports.test) \((?:.*[/\\])?line1\.js:1001:101\)/, - /^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` ]); }); it('native function', async function() { - await compareStackTrace(createSingleLineSourceMap(), [ + await compareStackTrace(id, extension, createSingleLineSourceMap(id, extension), [ '[1].map(function(x) { throw new Error(x); });' ], [ 'Error: 1', - /[/\\].original\.js/, + re`[/\\].original-${id}.js`, /at Array\.map \((native|)\)/ ]); }); it('function constructor', async function() { - await compareStackTrace(createMultiLineSourceMap(), [ + await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ 'throw new Function(")");' ], [ /SyntaxError: Unexpected token '?\)'?/, @@ -365,121 +412,121 @@ it('function constructor', async function() { }); it('throw with empty source map', async function() { - await compareStackTrace(createEmptySourceMap(), [ + await compareStackTrace(id, extension, createEmptySourceMap(id, extension), [ 'throw new Error("test");' ], [ 'Error: test', - /^ at Object\.exports\.test \((?:.*[/\\])?\.generated.js:1:34\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.generated-${id}.${extension}:1:34\)$` ]); }); it('throw in Timeout with empty source map', function(done) { - compareStdout(done, createEmptySourceMap(), [ + compareStdout(done, id, extension, createEmptySourceMap(id, extension), [ 'require("./source-map-support").install();', 'setTimeout(function () {', ' throw new Error("this is the error")', '})' ], [ - /[/\\].generated.js:3$/, + re`[/\\].generated-${id}.${extension}:3$`, ' throw new Error("this is the error")', /^ \^$/, 'Error: this is the error', - /^ at ((null)|(Timeout))\._onTimeout \((?:.*[/\\])?.generated\.js:3:11\)$/ + re`^ at ((null)|(Timeout))\._onTimeout \((?:.*[/\\])?.generated-${id}\.${extension}:3:11\)$` ]); }); it('throw with source map with gap', async function() { - await compareStackTrace(createSourceMapWithGap(), [ + await compareStackTrace(id, extension, createSourceMapWithGap(id, extension), [ 'throw new Error("test");' ], [ 'Error: test', - /^ at Object\.exports\.test \((?:.*[/\\])?\.generated\.js:1:34\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.generated-${id}\.${extension}:1:34\)$` ]); }); it('sourcesContent with data URL', async function() { - await compareStackTrace(createMultiLineSourceMapWithSourcesContent(), [ + await compareStackTrace(id, extension, createMultiLineSourceMapWithSourcesContent(id, extension), [ 'throw new Error("test");' ], [ 'Error: test', - /^ at Object\.exports\.test \((?:.*[/\\])?original\.js:1001:5\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?original-${id}\.js:1001:5\)$` ]); }); it('finds the last sourceMappingURL', async function() { - await compareStackTrace(createMultiLineSourceMapWithSourcesContent(), [ + await compareStackTrace(id, extension, createMultiLineSourceMapWithSourcesContent(id, extension), [ '//# sourceMappingURL=missing.map.js', // NB: compareStackTrace adds another source mapping. 'throw new Error("test");' ], [ 'Error: test', - /^ at Object\.exports\.test \((?:.*[/\\])?original\.js:1002:5\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?original-${id}\.js:1002:5\)$` ]); }); it('maps original name from source', async function() { - var sourceMap = createEmptySourceMap(); + var sourceMap = createEmptySourceMap(id, extension); sourceMap.addMapping({ generated: { line: 2, column: 8 }, original: { line: 1000, column: 10 }, - source: '.original.js', + source: `.original-${id}.js`, }); sourceMap.addMapping({ generated: { line: 4, column: 0 }, original: { line: 1002, column: 1 }, - source: ".original.js", + source: `.original-${id}.js`, name: "myOriginalName" }); - await compareStackTrace(sourceMap, [ + await compareStackTrace(id, extension, sourceMap, [ 'function foo() {', ' throw new Error("test");', '}', 'foo();' ], [ 'Error: test', - /^ at myOriginalName \((?:.*[/\\])?\.original.js:1000:11\)$/, - /^ at Object\.exports\.test \((?:.*[/\\])?\.original.js:1002:2\)$/ + re`^ at myOriginalName \((?:.*[/\\])?\.original-${id}.js:1000:11\)$`, + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.original-${id}.js:1002:2\)$` ]); }); it('default options', function(done) { - compareStdout(done, createSecondLineSourceMap(), [ + compareStdout(done, id, extension, createSecondLineSourceMap(id, extension), [ '', 'function foo() { throw new Error("this is the error"); }', 'require("./source-map-support").install();', 'process.nextTick(foo);', 'process.nextTick(function() { process.exit(1); });' ], [ - /[/\\].original\.js:1$/, + re`[/\\].original-${id}\.js:1$`, 'this is the original code', '^', 'Error: this is the error', - /^ at foo \((?:.*[/\\])?\.original\.js:1:1\)$/ + re`^ at foo \((?:.*[/\\])?\.original-${id}\.js:1:1\)$` ]); }); it('handleUncaughtExceptions is true', function(done) { - compareStdout(done, createSecondLineSourceMap(), [ + compareStdout(done, id, extension, createSecondLineSourceMap(id, extension), [ '', 'function foo() { throw new Error("this is the error"); }', 'require("./source-map-support").install({ handleUncaughtExceptions: true });', 'process.nextTick(foo);' ], [ - /[/\\].original\.js:1$/, + re`[/\\].original-${id}\.js:1$`, 'this is the original code', '^', 'Error: this is the error', - /^ at foo \((?:.*[/\\])?\.original\.js:1:1\)$/ + re`^ at foo \((?:.*[/\\])?\.original-${id}\.js:1:1\)$` ]); }); it('handleUncaughtExceptions is false', function(done) { - compareStdout(done, createSecondLineSourceMap(), [ + compareStdout(done, id, extension, createSecondLineSourceMap(id, extension), [ '', 'function foo() { throw new Error("this is the error"); }', 'require("./source-map-support").install({ handleUncaughtExceptions: false });', 'process.nextTick(foo);' ], [ - /[/\\].generated.js:2$/, + re`[/\\].generated-${id}.${extension}:2$`, 'function foo() { throw new Error("this is the error"); }', // Before Node 4, the arrow points on the `new`, after on the @@ -487,42 +534,42 @@ it('handleUncaughtExceptions is false', function(done) { /^ (?: )?\^$/, 'Error: this is the error', - /^ at foo \((?:.*[/\\])?.original\.js:1:1\)$/ + re`^ at foo \((?:.*[/\\])?.original-${id}\.js:1:1\)$` ]); }); it('default options with empty source map', function(done) { - compareStdout(done, createEmptySourceMap(), [ + compareStdout(done, id, extension, createEmptySourceMap(id, extension), [ '', 'function foo() { throw new Error("this is the error"); }', 'require("./source-map-support").install();', 'process.nextTick(foo);' ], [ - /[/\\].generated.js:2$/, + re`[/\\].generated-${id}.${extension}:2$`, 'function foo() { throw new Error("this is the error"); }', /^ (?: )?\^$/, 'Error: this is the error', - /^ at foo \((?:.*[/\\])?.generated.js:2:24\)$/ + re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:2:24\)$` ]); }); it('default options with source map with gap', function(done) { - compareStdout(done, createSourceMapWithGap(), [ + compareStdout(done, id, extension, createSourceMapWithGap(id, extension), [ '', 'function foo() { throw new Error("this is the error"); }', 'require("./source-map-support").install();', 'process.nextTick(foo);' ], [ - /[/\\].generated.js:2$/, + re`[/\\].generated-${id}.${extension}:2$`, 'function foo() { throw new Error("this is the error"); }', /^ (?: )?\^$/, 'Error: this is the error', - /^ at foo \((?:.*[/\\])?.generated.js:2:24\)$/ + re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:2:24\)$` ]); }); it('specifically requested error source', function(done) { - compareStdout(done, createSecondLineSourceMap(), [ + compareStdout(done, id, extension, createSecondLineSourceMap(id, extension), [ '', 'function foo() { throw new Error("this is the error"); }', 'var sms = require("./source-map-support");', @@ -530,30 +577,30 @@ it('specifically requested error source', function(done) { 'process.on("uncaughtException", function (e) { console.log("SRC:" + sms.getErrorSource(e)); });', 'process.nextTick(foo);' ], [ - /^SRC:.*[/\\]\.original\.js:1$/, + re`^SRC:.*[/\\]\.original-${id}\.js:1$`, 'this is the original code', '^' ]); }); it('sourcesContent', function(done) { - compareStdout(done, createMultiLineSourceMapWithSourcesContent(), [ + compareStdout(done, id, extension, createMultiLineSourceMapWithSourcesContent(id, extension), [ '', 'function foo() { throw new Error("this is the error"); }', 'require("./source-map-support").install();', 'process.nextTick(foo);', 'process.nextTick(function() { process.exit(1); });' ], [ - /[/\\]original\.js:1002$/, + re`[/\\]original-${id}\.js:1002$`, ' line 2', ' ^', 'Error: this is the error', - /^ at foo \((?:.*[/\\])?original\.js:1002:5\)$/ + re`^ at foo \((?:.*[/\\])?original-${id}\.js:1002:5\)$` ]); }); it('missing source maps should also be cached', function(done) { - compareStdout(done, createSingleLineSourceMap(), [ + compareStdout(done, id, extension, createSingleLineSourceMap(id, extension), [ '', 'var count = 0;', 'function foo() {', @@ -562,7 +609,7 @@ it('missing source maps should also be cached', function(done) { 'require("./source-map-support").install({', ' overrideRetrieveSourceMap: true,', ' retrieveSourceMap: function(name) {', - ' if (/\\.generated.js$/.test(name)) count++;', + ' if (/\\.generated-\\d+\\.(js|cjs|mjs)$/.test(name)) count++;', ' return null;', ' }', '});', @@ -571,15 +618,15 @@ it('missing source maps should also be cached', function(done) { 'process.nextTick(function() { console.log(count); });', ], [ 'Error: this is the error', - /^ at foo \((?:.*[/\\])?.generated.js:4:15\)$/, + re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:4:15\)$`, 'Error: this is the error', - /^ at foo \((?:.*[/\\])?.generated.js:4:15\)$/, + re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:4:15\)$`, '1', // The retrieval should only be attempted once ]); }); it('should consult all retrieve source map providers', function(done) { - compareStdout(done, createSingleLineSourceMap(), [ + compareStdout(done, id, extension, createSingleLineSourceMap(id, extension), [ '', 'var count = 0;', 'function foo() {', @@ -587,15 +634,15 @@ it('should consult all retrieve source map providers', function(done) { '}', 'require("./source-map-support").install({', ' retrieveSourceMap: function(name) {', - ' if (/\\.generated.js$/.test(name)) count++;', + ` if (/\\.generated-${id}\\.${extension}$/.test(name)) count++;`, ' return undefined;', ' }', '});', 'require("./source-map-support").install({', ' retrieveSourceMap: function(name) {', - ' if (/\\.generated.js$/.test(name)) {', + ` if (/\\.generated-${id}\\.${extension}$/.test(name)) {`, ' count++;', - ' return ' + JSON.stringify({url: '.original.js', map: createMultiLineSourceMapWithSourcesContent().toJSON()}) + ';', + ' return ' + JSON.stringify({url: `.original-${id}.js`, map: createMultiLineSourceMapWithSourcesContent(id, extension).toJSON()}) + ';', ' }', ' }', '});', @@ -604,22 +651,22 @@ it('should consult all retrieve source map providers', function(done) { 'process.nextTick(function() { console.log(count); });', ], [ 'Error: this is the error', - /^ at foo \((?:.*[/\\])?original\.js:1004:5\)$/, + re`^ at foo \((?:.*[/\\])?original-${id}\.js:1004:5\)$`, 'Error: this is the error', - /^ at foo \((?:.*[/\\])?original\.js:1004:5\)$/, + re`^ at foo \((?:.*[/\\])?original-${id}\.js:1004:5\)$`, '1', // The retrieval should only be attempted once ]); }); it('should allow for runtime inline source maps', function(done) { - var sourceMap = createMultiLineSourceMapWithSourcesContent(); + var sourceMap = createMultiLineSourceMapWithSourcesContent(id, extension); fs.writeFileSync('.generated.jss', 'foo'); compareStdout(function(err) { fs.unlinkSync('.generated.jss'); done(err); - }, createSingleLineSourceMap(), [ + }, id, extension, createSingleLineSourceMap(id, extension), [ 'require("./source-map-support").install({', ' hookRequire: true', '});', @@ -641,40 +688,41 @@ it('should allow for runtime inline source maps', function(done) { 'require("./.generated.jss");', ], [ 'Error: this is the error', - /^ at foo \(.*[/\\]original\.js:1004:5\)$/, + re`^ at foo \(.*[/\\]original-${id}\.js:1004:5\)$`, 'Error: this is the error', - /^ at foo \(.*[/\\]original\.js:1004:5\)$/, + re`^ at foo \(.*[/\\]original-${id}\.js:1004:5\)$`, '0', // The retrieval should only be attempted once ]); }); } +// TODO should this suite also be inside the matrix? describe('Other', function() { // Wrapped in a suite to preserve test execution order const {createEmptySourceMap, createSingleLineSourceMap, createMultiLineSourceMap} = getSourceMapCreators(); + const extension = 'cjs'; /* The following test duplicates some of the code in * `compareStackTrace` but appends a charset to the * source mapping url. */ it('finds source maps with charset specified', async function() { - var sourceMap = createMultiLineSourceMap() + var sourceMap = createMultiLineSourceMap(id, extension) var source = [ 'throw new Error("test");' ]; var expected = [ 'Error: test', - /^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` ]; - fs.writeFileSync('.generated.js', 'exports.test = function() {' + + fs.writeFileSync(`.generated-${id}.${extension}`, `${exportDecl(extension)} = function() {` + source.join('\n') + '};//@ sourceMappingURL=data:application/json;charset=utf8;base64,' + bufferFrom(sourceMap.toString()).toString('base64')); try { - delete require.cache[require.resolve('./.generated')]; - require('./.generated').test(); + (await import(`./.generated-${id}.${extension}`)).test(); } catch (e) { compareLines(e.stack.split(/\r\n|\n/), expected); } - fs.unlinkSync('.generated.js'); + fs.unlinkSync(`.generated-${id}.${extension}`); }); /* The following test duplicates some of the code in @@ -682,24 +730,24 @@ it('finds source maps with charset specified', async function() { * comment to the source mapping url. */ it('allows code/comments after sourceMappingURL', async function() { - var sourceMap = createMultiLineSourceMap() + var sourceMap = createMultiLineSourceMap(id, extension) var source = [ 'throw new Error("test");' ]; var expected = [ 'Error: test', - /^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/ + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` ]; - fs.writeFileSync('.generated.js', 'exports.test = function() {' + + fs.writeFileSync(`.generated-${id}.${extension}`, `${exportDecl(extension)} = function() {` + source.join('\n') + '};//# sourceMappingURL=data:application/json;base64,' + bufferFrom(sourceMap.toString()).toString('base64') + '\n// Some comment below the sourceMappingURL\nvar foo = 0;'); try { - delete require.cache[require.resolve('./.generated')]; - require('./.generated').test(); + // delete require.cache[require.resolve(`./.generated-${id}`)]; + (await import(`./.generated-${id}.${extension}`)).test(); } catch (e) { compareLines(e.stack.split(/\r\n|\n/), expected); } - fs.unlinkSync('.generated.js'); + fs.unlinkSync(`.generated-${id}.${extension}`); }); it('handleUncaughtExceptions is true with existing listener', function(done) { @@ -708,24 +756,24 @@ it('handleUncaughtExceptions is true with existing listener', function(done) { 'function foo() { throw new Error("this is the error"); }', 'require("./source-map-support").install();', 'process.nextTick(foo);', - '//@ sourceMappingURL=.generated.js.map' + `//@ sourceMappingURL=.generated-${id}.${extension}.map` ]; - fs.writeFileSync('.original.js', 'this is the original code'); - fs.writeFileSync('.generated.js.map', createSingleLineSourceMap().toString()); - fs.writeFileSync('.generated.js', source.join('\n')); + fs.writeFileSync(`.original-${id}.js`, 'this is the original code'); + fs.writeFileSync(`.generated-${id}.${extension}.map`, createSingleLineSourceMap(id, extension).toString()); + fs.writeFileSync(`.generated-${id}.${extension}`, source.join('\n')); - child_process.exec('node ./.generated', function(error, stdout, stderr) { - fs.unlinkSync('.generated.js'); - fs.unlinkSync('.generated.js.map'); - fs.unlinkSync('.original.js'); + child_process.exec(`node ./.generated-${id}.${extension}`, function(error, stdout, stderr) { + fs.unlinkSync(`.generated-${id}.${extension}`); + fs.unlinkSync(`.generated-${id}.${extension}.map`); + fs.unlinkSync(`.original-${id}.js`); assert.equal((stdout + stderr).trim(), ''); done(); }); }); it('normal console.trace', function(done) { - compareStdout(done, createMultiLineSourceMap(), [ + compareStdout(done, id, extension, createMultiLineSourceMap(id, extension), [ 'require("./source-map-support").install();', 'console.trace("test");' ], [ @@ -736,24 +784,24 @@ it('normal console.trace', function(done) { it('supports multiple instances', function(done) { function finish(err) { - fs.unlinkSync('.original2.js'); - fs.unlinkSync('.generated2.js'); - fs.unlinkSync('.generated2.js.map.extra') + fs.unlinkSync(`.original-${id}.js`); + fs.unlinkSync(`.generated-${id}.${extension}`); + fs.unlinkSync(`.generated-${id}.${extension}.map.extra`) done(err); } - var sourceMap = createEmptySourceMap(); + var sourceMap = createEmptySourceMap(id, extension); sourceMap.addMapping({ generated: { line: 1, column: 0 }, original: { line: 1, column: 0 }, - source: '.original2.js' + source: `.original-${id}.js` }); - fs.writeFileSync('.generated2.js.map.extra', sourceMap.toString()); - fs.writeFileSync('.generated2.js', [ + fs.writeFileSync(`.generated-${id}.${extension}.map.extra`, sourceMap.toString()); + fs.writeFileSync(`.generated-${id}.${extension}`, [ 'module.exports = function foo() { throw new Error("this is the error"); }', - '//@ sourceMappingURL=.generated2.js.map' + `//@ sourceMappingURL=.generated-${id}.${extension}.map` ].join('\n')); - fs.writeFileSync('.original2.js', 'this is some other original code'); - compareStdout(finish, createEmptySourceMap(), [ + fs.writeFileSync(`.original-${id}.js`, 'this is some other original code'); + compareStdout(finish, id, extension, createEmptySourceMap(id, extension), [ 'require("./source-map-support").install({', ' retrieveFile: function(path) {', ' var fs = require("fs");', @@ -762,17 +810,17 @@ it('supports multiple instances', function(done) { ' }', ' }', '});', - 'var foo = require("./.generated2.js");', + `var foo = require("./.generated-${id}.${extension}");`, 'delete require.cache[require.resolve("./source-map-support")];', 'require("./source-map-support").install();', 'process.nextTick(foo);', 'process.nextTick(function() { process.exit(1); });' ], [ - /[/\\].original2\.js:1$/, + re`[/\\].original-${id}\.js:1$`, 'this is some other original code', '^', 'Error: this is the error', - /^ at foo \((?:.*[/\\])?.original2\.js:1:1\)$/ + re`^ at foo \((?:.*[/\\])?.original-${id}\.js:1:1\)$` ]); }); }); @@ -814,6 +862,7 @@ describe('redirects require() of "source-map-support" to this module', function( describe('uninstall', function() { const sourceMapConstructors = getSourceMapCreators(); const {normalThrow, normalThrowWithoutSourceMapSupportInstalled} = getTestMacros(sourceMapConstructors); + this.beforeEach(function() { underTest.uninstall(); process.emit = priorProcessEmit; @@ -825,12 +874,12 @@ describe('uninstall', function() { assert.strictEqual(Error.prepareStackTrace, priorErrorPrepareStackTrace); assert.strictEqual(process.emit, priorProcessEmit); assert.strictEqual(Module._resolveFilename, priorResolveFilename); - normalThrowWithoutSourceMapSupportInstalled(); + await normalThrowWithoutSourceMapSupportInstalled(id); }); it('install re-adds hooks', async function() { installSms(); - normalThrow(); + await normalThrow(id); }); it('uninstall removes prepareStackTrace even in presence of third-party hooks if none were installed before us', async function() { @@ -880,7 +929,7 @@ describe('uninstall', function() { process.emit = thirdPartyProcessEmit; underTest.uninstall(); assert.strictEqual(process.emit, thirdPartyProcessEmit); - normalThrowWithoutSourceMapSupportInstalled(); + await normalThrowWithoutSourceMapSupportInstalled(id); process.emit('foo'); assert(peInvocations >= 1); }); @@ -896,7 +945,7 @@ describe('uninstall', function() { Module._resolveFilename = thirdPartyModuleResolveFilename; underTest.uninstall(); assert.strictEqual(Module._resolveFilename, thirdPartyModuleResolveFilename); - normalThrowWithoutSourceMapSupportInstalled(); + await normalThrowWithoutSourceMapSupportInstalled(id); Module._resolveFilename('repl'); assert(peInvocations >= 1); }); From d9cc1137ad4b6042d6ecc79f529f769fd027684e Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Wed, 27 Apr 2022 11:12:16 +0000 Subject: [PATCH 06/24] WIP --- source-map-support.js | 3 +- test.js | 215 +++++++++++++++++++++--------------------- 2 files changed, 109 insertions(+), 109 deletions(-) diff --git a/source-map-support.js b/source-map-support.js index 1b8c2d4..e74d253 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -542,7 +542,8 @@ function createPrepareStackTrace(hookState) { // Generate position and snippet of original source with pointer function getErrorSource(error) { - var match = /\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(error.stack); + // TODO this is not robust enough + var match = /\n at [^(]+ \((?:file:\/{0,2})?(.*):(\d+):(\d+)\)/.exec(error.stack); if (match) { var source = match[1]; var line = +match[2]; diff --git a/test.js b/test.js index d99979e..4184f6a 100644 --- a/test.js +++ b/test.js @@ -14,28 +14,35 @@ var fs = require('fs'); var util = require('util'); var bufferFrom = Buffer.from; +// Helper to create regular expressions from string templates, to use interpolation function re(...args) { return new RegExp(String.raw(...args)); } -function exportDecl(extension) { - return extension === 'mjs' ? 'export const test' : 'exports.test'; +function exportDecl() { + // same length so that offsets are the same either way + return extension === 'mjs' + ? 'export const test' + : 'exports.test '; } // Assign each test a unique ID, to be used in filenames. // Eliminates need for cache invalidation, because node ESM has no way to // invalidate cache. let id = 0; +let extension; beforeEach(function() { id++; + extension = 'js'; }); + // Consolidate cleanup into a hook so that failed assertions do not leave files // on disk. afterEach(function() { for(const name of [`generated`, `original`]) { for(const suffix of [``, `-separate`, `-inline`]) { for(const ext of [`js`, `cjs`, `mjs`]) { - for(const ext2 of [``, `.map`]) { + for(const ext2 of [``, `.map`, `.map.extra`]) { const file = `.${name}-${id}${suffix}.${ext}${ext2}`; fs.existsSync(file) && fs.unlinkSync(file); } @@ -56,7 +63,7 @@ function compareLines(actual, expected) { } } -function getSourceMapCreators() { +function sourceMapCreators() { return { createEmptySourceMap, createSourceMapWithGap, @@ -65,15 +72,15 @@ function getSourceMapCreators() { createMultiLineSourceMap, createMultiLineSourceMapWithSourcesContent }; -function createEmptySourceMap(id, extension) { +function createEmptySourceMap() { return new SourceMapGenerator({ file: `.generated-${id}.${extension}`, sourceRoot: '.' }); } -function createSourceMapWithGap(id, extension) { - var sourceMap = createEmptySourceMap(id, extension); +function createSourceMapWithGap() { + var sourceMap = createEmptySourceMap(); sourceMap.addMapping({ generated: { line: 100, column: 0 }, original: { line: 100, column: 0 }, @@ -82,8 +89,8 @@ function createSourceMapWithGap(id, extension) { return sourceMap; } -function createSingleLineSourceMap(id, extension) { - var sourceMap = createEmptySourceMap(id, extension); +function createSingleLineSourceMap() { + var sourceMap = createEmptySourceMap(); sourceMap.addMapping({ generated: { line: 1, column: 0 }, original: { line: 1, column: 0 }, @@ -92,8 +99,8 @@ function createSingleLineSourceMap(id, extension) { return sourceMap; } -function createSecondLineSourceMap(id, extension) { - var sourceMap = createEmptySourceMap(id, extension); +function createSecondLineSourceMap() { + var sourceMap = createEmptySourceMap(); sourceMap.addMapping({ generated: { line: 2, column: 0 }, original: { line: 1, column: 0 }, @@ -102,8 +109,8 @@ function createSecondLineSourceMap(id, extension) { return sourceMap; } -function createMultiLineSourceMap(id, extension) { - var sourceMap = createEmptySourceMap(id, extension); +function createMultiLineSourceMap() { + var sourceMap = createEmptySourceMap(); for (var i = 1; i <= 100; i++) { sourceMap.addMapping({ generated: { line: i, column: 0 }, @@ -114,8 +121,8 @@ function createMultiLineSourceMap(id, extension) { return sourceMap; } -function createMultiLineSourceMapWithSourcesContent(id, extension) { - var sourceMap = createEmptySourceMap(id, extension); +function createMultiLineSourceMapWithSourcesContent() { + var sourceMap = createEmptySourceMap(); var original = new Array(1001).join('\n'); for (var i = 1; i <= 100; i++) { sourceMap.addMapping({ @@ -136,27 +143,29 @@ function rewriteExpectation(expected, generatedFilenameIn, generatedFilenameOut) return v.replace(generatedFilenameIn, generatedFilenameOut); }); } -function getSrcPrefix(extension) { - return extension === 'mjs' ? `import {createRequire} from 'module';const require = createRequire(import.meta.url);` : ''; +// Tests were initially written as CJS with require() calls. +// We can support require() calls in MJS tests, too, as long as we create a require() function. +// Keep both prefixes the same length so that offsets are the same +function getSrcPrefix() { + return extension === 'mjs' + ? `import {createRequire} from 'module';const require = createRequire(import.meta.url);` + : ` `; } -async function compareStackTrace(id, extension, sourceMap, source, expected) { - const srcPrefix = getSrcPrefix(extension); +async function compareStackTrace(sourceMap, source, expected) { + const srcPrefix = getSrcPrefix(); // Check once with a separate source map + // fs.writeFileSync(`.generated-${id}-separate.${extension}.map`, JSON.stringify({...JSON.parse(sourceMap.toString()), file: `.generated-${id}-separate.${extension}`})); fs.writeFileSync(`.generated-${id}-separate.${extension}.map`, sourceMap.toString()); - fs.writeFileSync(`.generated-${id}-separate.${extension}`, `${srcPrefix}${exportDecl(extension)} = function() {` + + fs.writeFileSync(`.generated-${id}-separate.${extension}`, `${srcPrefix}${exportDecl()} = function() {` + source.join('\n') + `};//@ sourceMappingURL=.generated-${id}-separate.${extension}.map`); try { - // delete require.cache[require.resolve(`./.generated-${id}`)]; (await import(`./.generated-${id}-separate.${extension}`)).test(); } catch (e) { - console.log(e); compareLines(e.stack.split(/\r\n|\n/), rewriteExpectation(expected, `.generated-${id}`, `.generated-${id}-separate`)); } - fs.unlinkSync(`.generated-${id}-separate.${extension}`); - fs.unlinkSync(`.generated-${id}-separate.${extension}.map`); // Check again with an inline source map (in a data URL) - fs.writeFileSync(`.generated-${id}-inline.${extension}`, `${srcPrefix}${exportDecl(extension)} = function() {` + + fs.writeFileSync(`.generated-${id}-inline.${extension}`, `${srcPrefix}${exportDecl()} = function() {` + source.join('\n') + '};//@ sourceMappingURL=data:application/json;base64,' + bufferFrom(sourceMap.toString()).toString('base64')); try { @@ -164,11 +173,10 @@ async function compareStackTrace(id, extension, sourceMap, source, expected) { } catch (e) { compareLines(e.stack.split(/\r\n|\n/), rewriteExpectation(expected, `.generated-${id}`, `.generated-${id}-inline`)); } - fs.unlinkSync(`.generated-${id}-inline.${extension}`); } -function compareStdout(done, id, extension, sourceMap, source, expected) { - let srcPrefix = getSrcPrefix(extension); +function compareStdout(done, sourceMap, source, expected) { + let srcPrefix = getSrcPrefix(); fs.writeFileSync(`.original-${id}.js`, 'this is the original code'); fs.writeFileSync(`.generated-${id}.${extension}.map`, sourceMap.toString()); fs.writeFileSync(`.generated-${id}.${extension}`, srcPrefix + source.join('\n') + @@ -185,9 +193,6 @@ function compareStdout(done, id, extension, sourceMap, source, expected) { } catch (e) { return done(e); } - fs.unlinkSync(`.generated-${id}.${extension}`); - fs.unlinkSync(`.generated-${id}.${extension}.map`); - fs.unlinkSync(`.original-${id}.js`); done(); }); } @@ -200,31 +205,31 @@ function installSms() { function getTestMacros(sourceMapConstructors) { return {normalThrow, normalThrowWithoutSourceMapSupportInstalled}; -async function normalThrow(id, extension = 'js') { - await compareStackTrace(id, extension, sourceMapConstructors.createMultiLineSourceMap(id, extension), [ +async function normalThrow() { + await compareStackTrace(sourceMapConstructors.createMultiLineSourceMap(), [ 'throw new Error("test");' ], [ 'Error: test', re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` ]); } -async function normalThrowWithoutSourceMapSupportInstalled(id, extension = 'cjs') { - await compareStackTrace(id, extension, sourceMapConstructors.createMultiLineSourceMap(id, extension), [ +async function normalThrowWithoutSourceMapSupportInstalled() { + await compareStackTrace(sourceMapConstructors.createMultiLineSourceMap(), [ 'throw new Error("test");' ], [ 'Error: test', - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.generated-${id}\.${extension}:1:34\)$` + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$` ]); } } describe('Without source-map-support installed', function() { - const sourceMapConstructors = getSourceMapCreators(); + const sourceMapConstructors = sourceMapCreators(); const macros = getTestMacros(sourceMapConstructors); const {normalThrowWithoutSourceMapSupportInstalled} = macros; it('normal throw without source-map-support installed', async function () { - await normalThrowWithoutSourceMapSupportInstalled(id); + await normalThrowWithoutSourceMapSupportInstalled(); }); }); @@ -270,23 +275,29 @@ function moduleTypeSuites(sourceMapPostprocessor) { }); } -function tests(sourceMapPostprocessor, extension) { - const sourceMapConstructors = getSourceMapCreators(); +function tests(sourceMapPostprocessor, _extension) { + // let createEmptySourceMap, createMultiLineSourceMap, createMultiLineSourceMapWithSourcesContent, createSecondLineSourceMap, createSingleLineSourceMap, createSourceMapWithGap}) + const sourceMapConstructors = sourceMapCreators(); for(const [key, value] of Object.entries(sourceMapConstructors)) { sourceMapConstructors[key] = (...args) => sourceMapPostprocessor(value(...args)); } const {createEmptySourceMap, createMultiLineSourceMap, createMultiLineSourceMapWithSourcesContent, createSecondLineSourceMap, createSingleLineSourceMap, createSourceMapWithGap} = sourceMapConstructors; const {normalThrow} = getTestMacros(sourceMapConstructors); + + beforeEach(function() { + extension = _extension; + }); + it('normal throw', async function() { installSms(); - await normalThrow(id, extension); + await normalThrow(); }); /* The following test duplicates some of the code in * `normal throw` but triggers file read failure. */ it('fs.readFileSync failure', async function() { - await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ + await compareStackTrace(createMultiLineSourceMap(), [ 'var fs = require("fs");', 'var rfs = fs.readFileSync;', 'fs.readFileSync = function() {', @@ -305,7 +316,7 @@ it('fs.readFileSync failure', async function() { it('throw inside function', async function() { - await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ + await compareStackTrace(createMultiLineSourceMap(), [ 'function foo() {', ' throw new Error("test");', '}', @@ -318,7 +329,7 @@ it('throw inside function', async function() { }); it('throw inside function inside function', async function() { - await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ + await compareStackTrace(createMultiLineSourceMap(), [ 'function foo() {', ' function bar() {', ' throw new Error("test");', @@ -335,7 +346,7 @@ it('throw inside function inside function', async function() { }); it('eval', async function() { - await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ + await compareStackTrace(createMultiLineSourceMap(), [ 'eval("throw new Error(\'test\')");' ], [ 'Error: test', @@ -348,18 +359,18 @@ it('eval', async function() { }); it('eval inside eval', async function() { - await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ + await compareStackTrace(createMultiLineSourceMap(), [ 'eval("eval(\'throw new Error(\\"test\\")\')");' ], [ 'Error: test', - /^ at (?:Object\.)?eval \(eval at (|exports.test) \(eval at (|exports.test) \((?:.*[/\\])?line1\.js:1001:101\)/, - /^ at (?:Object\.)?eval \(eval at (|exports.test) \((?:.*[/\\])?line1\.js:1001:101\)/, + /^ at (?:Object\.)?eval \(eval at (|exports\.test|test) \(eval at (|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, + /^ at (?:Object\.)?eval \(eval at (|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` ]); }); it('eval inside function', async function() { - await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ + await compareStackTrace(createMultiLineSourceMap(), [ 'function foo() {', ' eval("throw new Error(\'test\')");', '}', @@ -373,7 +384,7 @@ it('eval inside function', async function() { }); it('eval with sourceURL', async function() { - await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ + await compareStackTrace(createMultiLineSourceMap(), [ 'eval("throw new Error(\'test\')//@ sourceURL=sourceURL.js");' ], [ 'Error: test', @@ -383,18 +394,18 @@ it('eval with sourceURL', async function() { }); it('eval with sourceURL inside eval', async function() { - await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ + await compareStackTrace(createMultiLineSourceMap(), [ 'eval("eval(\'throw new Error(\\"test\\")//@ sourceURL=sourceURL.js\')");' ], [ 'Error: test', /^ at (?:Object\.)?eval \(sourceURL\.js:1:7\)$/, - /^ at (?:Object\.)?eval \(eval at (|exports.test) \((?:.*[/\\])?line1\.js:1001:101\)/, + /^ at (?:Object\.)?eval \(eval at (|exports.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` ]); }); it('native function', async function() { - await compareStackTrace(id, extension, createSingleLineSourceMap(id, extension), [ + await compareStackTrace(createSingleLineSourceMap(), [ '[1].map(function(x) { throw new Error(x); });' ], [ 'Error: 1', @@ -404,7 +415,7 @@ it('native function', async function() { }); it('function constructor', async function() { - await compareStackTrace(id, extension, createMultiLineSourceMap(id, extension), [ + await compareStackTrace(createMultiLineSourceMap(), [ 'throw new Function(")");' ], [ /SyntaxError: Unexpected token '?\)'?/, @@ -412,16 +423,16 @@ it('function constructor', async function() { }); it('throw with empty source map', async function() { - await compareStackTrace(id, extension, createEmptySourceMap(id, extension), [ + await compareStackTrace(createEmptySourceMap(), [ 'throw new Error("test");' ], [ 'Error: test', - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.generated-${id}.${extension}:1:34\)$` + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.generated-${id}.${extension}:1:123\)$` ]); }); it('throw in Timeout with empty source map', function(done) { - compareStdout(done, id, extension, createEmptySourceMap(id, extension), [ + compareStdout(done, createEmptySourceMap(), [ 'require("./source-map-support").install();', 'setTimeout(function () {', ' throw new Error("this is the error")', @@ -436,16 +447,16 @@ it('throw in Timeout with empty source map', function(done) { }); it('throw with source map with gap', async function() { - await compareStackTrace(id, extension, createSourceMapWithGap(id, extension), [ + await compareStackTrace(createSourceMapWithGap(), [ 'throw new Error("test");' ], [ 'Error: test', - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.generated-${id}\.${extension}:1:34\)$` + re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$` ]); }); it('sourcesContent with data URL', async function() { - await compareStackTrace(id, extension, createMultiLineSourceMapWithSourcesContent(id, extension), [ + await compareStackTrace(createMultiLineSourceMapWithSourcesContent(), [ 'throw new Error("test");' ], [ 'Error: test', @@ -454,7 +465,7 @@ it('sourcesContent with data URL', async function() { }); it('finds the last sourceMappingURL', async function() { - await compareStackTrace(id, extension, createMultiLineSourceMapWithSourcesContent(id, extension), [ + await compareStackTrace(createMultiLineSourceMapWithSourcesContent(), [ '//# sourceMappingURL=missing.map.js', // NB: compareStackTrace adds another source mapping. 'throw new Error("test");' ], [ @@ -464,7 +475,7 @@ it('finds the last sourceMappingURL', async function() { }); it('maps original name from source', async function() { - var sourceMap = createEmptySourceMap(id, extension); + var sourceMap = createEmptySourceMap(); sourceMap.addMapping({ generated: { line: 2, column: 8 }, original: { line: 1000, column: 10 }, @@ -476,7 +487,7 @@ it('maps original name from source', async function() { source: `.original-${id}.js`, name: "myOriginalName" }); - await compareStackTrace(id, extension, sourceMap, [ + await compareStackTrace(sourceMap, [ 'function foo() {', ' throw new Error("test");', '}', @@ -489,7 +500,7 @@ it('maps original name from source', async function() { }); it('default options', function(done) { - compareStdout(done, id, extension, createSecondLineSourceMap(id, extension), [ + compareStdout(done, createSecondLineSourceMap(), [ '', 'function foo() { throw new Error("this is the error"); }', 'require("./source-map-support").install();', @@ -505,7 +516,7 @@ it('default options', function(done) { }); it('handleUncaughtExceptions is true', function(done) { - compareStdout(done, id, extension, createSecondLineSourceMap(id, extension), [ + compareStdout(done, createSecondLineSourceMap(), [ '', 'function foo() { throw new Error("this is the error"); }', 'require("./source-map-support").install({ handleUncaughtExceptions: true });', @@ -520,7 +531,7 @@ it('handleUncaughtExceptions is true', function(done) { }); it('handleUncaughtExceptions is false', function(done) { - compareStdout(done, id, extension, createSecondLineSourceMap(id, extension), [ + compareStdout(done, createSecondLineSourceMap(), [ '', 'function foo() { throw new Error("this is the error"); }', 'require("./source-map-support").install({ handleUncaughtExceptions: false });', @@ -539,7 +550,7 @@ it('handleUncaughtExceptions is false', function(done) { }); it('default options with empty source map', function(done) { - compareStdout(done, id, extension, createEmptySourceMap(id, extension), [ + compareStdout(done, createEmptySourceMap(), [ '', 'function foo() { throw new Error("this is the error"); }', 'require("./source-map-support").install();', @@ -554,7 +565,7 @@ it('default options with empty source map', function(done) { }); it('default options with source map with gap', function(done) { - compareStdout(done, id, extension, createSourceMapWithGap(id, extension), [ + compareStdout(done, createSourceMapWithGap(), [ '', 'function foo() { throw new Error("this is the error"); }', 'require("./source-map-support").install();', @@ -569,7 +580,7 @@ it('default options with source map with gap', function(done) { }); it('specifically requested error source', function(done) { - compareStdout(done, id, extension, createSecondLineSourceMap(id, extension), [ + compareStdout(done, createSecondLineSourceMap(), [ '', 'function foo() { throw new Error("this is the error"); }', 'var sms = require("./source-map-support");', @@ -583,8 +594,8 @@ it('specifically requested error source', function(done) { ]); }); -it('sourcesContent', function(done) { - compareStdout(done, id, extension, createMultiLineSourceMapWithSourcesContent(id, extension), [ +it.only('sourcesContent', function(done) { + compareStdout(done, createMultiLineSourceMapWithSourcesContent(), [ '', 'function foo() { throw new Error("this is the error"); }', 'require("./source-map-support").install();', @@ -600,7 +611,7 @@ it('sourcesContent', function(done) { }); it('missing source maps should also be cached', function(done) { - compareStdout(done, id, extension, createSingleLineSourceMap(id, extension), [ + compareStdout(done, createSingleLineSourceMap(), [ '', 'var count = 0;', 'function foo() {', @@ -626,7 +637,7 @@ it('missing source maps should also be cached', function(done) { }); it('should consult all retrieve source map providers', function(done) { - compareStdout(done, id, extension, createSingleLineSourceMap(id, extension), [ + compareStdout(done, createSingleLineSourceMap(), [ '', 'var count = 0;', 'function foo() {', @@ -642,7 +653,7 @@ it('should consult all retrieve source map providers', function(done) { ' retrieveSourceMap: function(name) {', ` if (/\\.generated-${id}\\.${extension}$/.test(name)) {`, ' count++;', - ' return ' + JSON.stringify({url: `.original-${id}.js`, map: createMultiLineSourceMapWithSourcesContent(id, extension).toJSON()}) + ';', + ' return ' + JSON.stringify({url: `.original-${id}.js`, map: createMultiLineSourceMapWithSourcesContent().toJSON()}) + ';', ' }', ' }', '});', @@ -659,14 +670,14 @@ it('should consult all retrieve source map providers', function(done) { }); it('should allow for runtime inline source maps', function(done) { - var sourceMap = createMultiLineSourceMapWithSourcesContent(id, extension); + var sourceMap = createMultiLineSourceMapWithSourcesContent(); fs.writeFileSync('.generated.jss', 'foo'); compareStdout(function(err) { fs.unlinkSync('.generated.jss'); done(err); - }, id, extension, createSingleLineSourceMap(id, extension), [ + }, createSingleLineSourceMap(), [ 'require("./source-map-support").install({', ' hookRequire: true', '});', @@ -699,7 +710,7 @@ it('should allow for runtime inline source maps', function(done) { // TODO should this suite also be inside the matrix? describe('Other', function() { // Wrapped in a suite to preserve test execution order - const {createEmptySourceMap, createSingleLineSourceMap, createMultiLineSourceMap} = getSourceMapCreators(); + const {createEmptySourceMap, createSingleLineSourceMap, createMultiLineSourceMap} = sourceMapCreators(); const extension = 'cjs'; /* The following test duplicates some of the code in @@ -707,7 +718,7 @@ describe('Other', function() { * source mapping url. */ it('finds source maps with charset specified', async function() { - var sourceMap = createMultiLineSourceMap(id, extension) + var sourceMap = createMultiLineSourceMap() var source = [ 'throw new Error("test");' ]; var expected = [ 'Error: test', @@ -722,7 +733,6 @@ it('finds source maps with charset specified', async function() { } catch (e) { compareLines(e.stack.split(/\r\n|\n/), expected); } - fs.unlinkSync(`.generated-${id}.${extension}`); }); /* The following test duplicates some of the code in @@ -730,7 +740,7 @@ it('finds source maps with charset specified', async function() { * comment to the source mapping url. */ it('allows code/comments after sourceMappingURL', async function() { - var sourceMap = createMultiLineSourceMap(id, extension) + var sourceMap = createMultiLineSourceMap() var source = [ 'throw new Error("test");' ]; var expected = [ 'Error: test', @@ -742,12 +752,10 @@ it('allows code/comments after sourceMappingURL', async function() { bufferFrom(sourceMap.toString()).toString('base64') + '\n// Some comment below the sourceMappingURL\nvar foo = 0;'); try { - // delete require.cache[require.resolve(`./.generated-${id}`)]; (await import(`./.generated-${id}.${extension}`)).test(); } catch (e) { compareLines(e.stack.split(/\r\n|\n/), expected); } - fs.unlinkSync(`.generated-${id}.${extension}`); }); it('handleUncaughtExceptions is true with existing listener', function(done) { @@ -760,20 +768,17 @@ it('handleUncaughtExceptions is true with existing listener', function(done) { ]; fs.writeFileSync(`.original-${id}.js`, 'this is the original code'); - fs.writeFileSync(`.generated-${id}.${extension}.map`, createSingleLineSourceMap(id, extension).toString()); + fs.writeFileSync(`.generated-${id}.${extension}.map`, createSingleLineSourceMap().toString()); fs.writeFileSync(`.generated-${id}.${extension}`, source.join('\n')); child_process.exec(`node ./.generated-${id}.${extension}`, function(error, stdout, stderr) { - fs.unlinkSync(`.generated-${id}.${extension}`); - fs.unlinkSync(`.generated-${id}.${extension}.map`); - fs.unlinkSync(`.original-${id}.js`); assert.equal((stdout + stderr).trim(), ''); done(); }); }); it('normal console.trace', function(done) { - compareStdout(done, id, extension, createMultiLineSourceMap(id, extension), [ + compareStdout(done, createMultiLineSourceMap(), [ 'require("./source-map-support").install();', 'console.trace("test");' ], [ @@ -783,25 +788,19 @@ it('normal console.trace', function(done) { }); it('supports multiple instances', function(done) { - function finish(err) { - fs.unlinkSync(`.original-${id}.js`); - fs.unlinkSync(`.generated-${id}.${extension}`); - fs.unlinkSync(`.generated-${id}.${extension}.map.extra`) - done(err); - } - var sourceMap = createEmptySourceMap(id, extension); + var sourceMap = createEmptySourceMap(); sourceMap.addMapping({ generated: { line: 1, column: 0 }, original: { line: 1, column: 0 }, - source: `.original-${id}.js` + source: `.original2-${id}.js` }); - fs.writeFileSync(`.generated-${id}.${extension}.map.extra`, sourceMap.toString()); - fs.writeFileSync(`.generated-${id}.${extension}`, [ + fs.writeFileSync(`.generated2-${id}.${extension}.map.extra`, sourceMap.toString()); + fs.writeFileSync(`.generated2-${id}.${extension}`, [ 'module.exports = function foo() { throw new Error("this is the error"); }', - `//@ sourceMappingURL=.generated-${id}.${extension}.map` + `//@ sourceMappingURL=.generated2-${id}.${extension}.map` ].join('\n')); - fs.writeFileSync(`.original-${id}.js`, 'this is some other original code'); - compareStdout(finish, id, extension, createEmptySourceMap(id, extension), [ + fs.writeFileSync(`.original2-${id}.js`, 'this is some other original code'); + compareStdout(done, createEmptySourceMap(), [ 'require("./source-map-support").install({', ' retrieveFile: function(path) {', ' var fs = require("fs");', @@ -810,17 +809,17 @@ it('supports multiple instances', function(done) { ' }', ' }', '});', - `var foo = require("./.generated-${id}.${extension}");`, + `var foo = require("./.generated2-${id}.${extension}");`, 'delete require.cache[require.resolve("./source-map-support")];', 'require("./source-map-support").install();', 'process.nextTick(foo);', 'process.nextTick(function() { process.exit(1); });' ], [ - re`[/\\].original-${id}\.js:1$`, + re`[/\\].original2-${id}\.js:1$`, 'this is some other original code', '^', 'Error: this is the error', - re`^ at foo \((?:.*[/\\])?.original-${id}\.js:1:1\)$` + re`^ at foo \((?:.*[/\\])?.original2-${id}\.js:1:1\)$` ]); }); }); @@ -860,7 +859,7 @@ describe('redirects require() of "source-map-support" to this module', function( }); describe('uninstall', function() { - const sourceMapConstructors = getSourceMapCreators(); + const sourceMapConstructors = sourceMapCreators(); const {normalThrow, normalThrowWithoutSourceMapSupportInstalled} = getTestMacros(sourceMapConstructors); this.beforeEach(function() { @@ -874,12 +873,12 @@ describe('uninstall', function() { assert.strictEqual(Error.prepareStackTrace, priorErrorPrepareStackTrace); assert.strictEqual(process.emit, priorProcessEmit); assert.strictEqual(Module._resolveFilename, priorResolveFilename); - await normalThrowWithoutSourceMapSupportInstalled(id); + await normalThrowWithoutSourceMapSupportInstalled(); }); it('install re-adds hooks', async function() { installSms(); - await normalThrow(id); + await normalThrow(); }); it('uninstall removes prepareStackTrace even in presence of third-party hooks if none were installed before us', async function() { @@ -929,7 +928,7 @@ describe('uninstall', function() { process.emit = thirdPartyProcessEmit; underTest.uninstall(); assert.strictEqual(process.emit, thirdPartyProcessEmit); - await normalThrowWithoutSourceMapSupportInstalled(id); + await normalThrowWithoutSourceMapSupportInstalled(); process.emit('foo'); assert(peInvocations >= 1); }); @@ -945,7 +944,7 @@ describe('uninstall', function() { Module._resolveFilename = thirdPartyModuleResolveFilename; underTest.uninstall(); assert.strictEqual(Module._resolveFilename, thirdPartyModuleResolveFilename); - await normalThrowWithoutSourceMapSupportInstalled(id); + await normalThrowWithoutSourceMapSupportInstalled(); Module._resolveFilename('repl'); assert(peInvocations >= 1); }); From c0f5b1e5687dc2809c49fe59a7d82fcc82b1ccaa Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Thu, 28 Apr 2022 16:35:48 +0000 Subject: [PATCH 07/24] fix esm/file URI support --- source-map-support.js | 72 +++++++++++++++++++++++++++++++++---------- test.js | 2 +- 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/source-map-support.js b/source-map-support.js index e74d253..644b0b1 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -1,7 +1,7 @@ const { TraceMap, originalPositionFor, AnyMap } = require('@jridgewell/trace-mapping'); const resolveUri = require('@jridgewell/resolve-uri'); var path = require('path'); -const { fileURLToPath } = require('url'); +const { fileURLToPath, pathToFileURL } = require('url'); var util = require('util'); var fs; @@ -94,11 +94,11 @@ var sharedData = initializeSharedData({ emptyCacheBetweenOperations: false, // Maps a file path to a string containing the file contents - fileContentsCache: {}, + fileContentsCache: Object.create(null), // Maps a file path to a source map for that file /** @type {Record /root-dir/file }); } - if (path in sharedData.fileContentsCache) { - return sharedData.fileContentsCache[path]; + const key = getCacheKey(path); + if(hasFileContentsCacheFromKey(key)) { + return getFileContentsCacheFromKey(key); } var contents = ''; @@ -182,7 +221,7 @@ sharedData.internalRetrieveFileHandlers.push(function(path) { /* ignore any errors */ } - return sharedData.fileContentsCache[path] = contents; + return setFileContentsCache(path, contents); }); // Support URLs relative to a directory, but be careful about a protocol prefix @@ -257,15 +296,15 @@ sharedData.internalRetrieveMapHandlers.push(function(source) { }); function mapSourcePosition(position) { - var sourceMap = sharedData.sourceMapCache[position.source]; + var sourceMap = getSourceMapCache(position.source); if (!sourceMap) { // Call the (overrideable) retrieveSourceMap function to get the source map. var urlAndMap = retrieveSourceMap(position.source); if (urlAndMap) { - sourceMap = sharedData.sourceMapCache[position.source] = { + sourceMap = setSourceMapCache(position.source, { url: urlAndMap.url, map: new AnyMap(urlAndMap.map, urlAndMap.url) - }; + }); // Load all sources stored inline with the source map into the file cache // to pretend like they are already loaded. They may not exist on disk. @@ -274,15 +313,15 @@ function mapSourcePosition(position) { var contents = sourceMap.map.sourcesContent[i]; if (contents) { var url = supportRelativeURL(sourceMap.url, source); - sharedData.fileContentsCache[url] = contents; + setFileContentsCache(url, contents); } }); } } else { - sourceMap = sharedData.sourceMapCache[position.source] = { + sourceMap = setSourceMapCache(position.source, { url: null, map: null - }; + }); } } @@ -509,8 +548,7 @@ function createPrepareStackTrace(hookState) { if(!hookState.enabled) return hookState.originalValue.apply(this, arguments); if (sharedData.emptyCacheBetweenOperations) { - sharedData.fileContentsCache = {}; - sharedData.sourceMapCache = {}; + clearCaches(); } // node gives its own errors special treatment. Mimic that behavior @@ -550,7 +588,7 @@ function getErrorSource(error) { var column = +match[3]; // Support the inline sourceContents inside the source map - var contents = sharedData.fileContentsCache[source]; + var contents = getFileContentsCache(source); // Support files on disk if (!contents && fs && fs.existsSync(source)) { @@ -705,8 +743,8 @@ exports.install = function(options) { if (!$compile.__sourceMapSupport) { Module.prototype._compile = function(content, filename) { - sharedData.fileContentsCache[filename] = content; - sharedData.sourceMapCache[filename] = undefined; + setFileContentsCache(filename, content); + setSourceMapCache(filename, undefined); return $compile.call(this, content, filename); }; diff --git a/test.js b/test.js index 4184f6a..958bbe6 100644 --- a/test.js +++ b/test.js @@ -594,7 +594,7 @@ it('specifically requested error source', function(done) { ]); }); -it.only('sourcesContent', function(done) { +it('sourcesContent', function(done) { compareStdout(done, createMultiLineSourceMapWithSourcesContent(), [ '', 'function foo() { throw new Error("this is the error"); }', From 978471720ee257752bb87c5b22bd3595b84996d3 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Thu, 28 Apr 2022 22:11:49 +0000 Subject: [PATCH 08/24] fix --- package.json | 1 + test.js | 313 ++++++++++++++++++++++++++++----------------------- 2 files changed, 171 insertions(+), 143 deletions(-) diff --git a/package.json b/package.json index 4e6d3af..6484c3b 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "coffeescript": "^1.12.7", "http-server": "^0.11.1", "mocha": "^3.5.3", + "semver": "^7.3.7", "source-map": "0.6.1", "webpack": "^1.15.0" }, diff --git a/test.js b/test.js index 958bbe6..9e7420f 100644 --- a/test.js +++ b/test.js @@ -13,18 +13,50 @@ var assert = require('assert'); var fs = require('fs'); var util = require('util'); var bufferFrom = Buffer.from; +const semver = require('semver'); // Helper to create regular expressions from string templates, to use interpolation function re(...args) { return new RegExp(String.raw(...args)); } -function exportDecl() { +//#region module format differences +function namedExportDeclaration() { // same length so that offsets are the same either way return extension === 'mjs' ? 'export const test' : 'exports.test '; } +/** + * How stack frame will describe invocations of the `test` function, when it is imported and invoked by a different file. + * This varies across CJS / ESM and node versions. + * Example: ` at Module.exports.test (`... + */ +function stackFrameAtTest(sourceMapSupportInstalled = true) { + console.log(extension); + if(semver.gte(process.versions.node, '18.0.0')) { + return extension === 'mjs' ? 'Module\\.test' : sourceMapSupportInstalled ? 'Module\\.exports\\.test' : 'exports\\.test'; + } else { + return extension === 'mjs' ? 'Module\\.test' : 'Module\\.exports\\.test'; + } +} +/** + * Describes the first stack frame for `console.trace` which is slightly different in ESM and CJS + */ +function stackFrameAtTrace(fileRe) { + return extension === 'mjs' ? `${fileRe}` : `Object\\. \\(${fileRe}\\)`; +} +/** + * Tests were initially written as CJS with require() calls. + * We can support require() calls in MJS tests, too, as long as we create a require() function. + * Keep both prefixes the same length so that offsets are the same + */ +function srcPrefix() { + return extension === 'mjs' + ? `import {createRequire} from 'module';const require = createRequire(import.meta.url);` + : ` `; +} +//#endregion // Assign each test a unique ID, to be used in filenames. // Eliminates need for cache invalidation, because node ESM has no way to @@ -39,7 +71,7 @@ beforeEach(function() { // Consolidate cleanup into a hook so that failed assertions do not leave files // on disk. afterEach(function() { - for(const name of [`generated`, `original`]) { + for(const name of [`generated`, `generated2`, `original`, `original2`]) { for(const suffix of [``, `-separate`, `-inline`]) { for(const ext of [`js`, `cjs`, `mjs`]) { for(const ext2 of [``, `.map`, `.map.extra`]) { @@ -72,69 +104,69 @@ function sourceMapCreators() { createMultiLineSourceMap, createMultiLineSourceMapWithSourcesContent }; -function createEmptySourceMap() { - return new SourceMapGenerator({ - file: `.generated-${id}.${extension}`, - sourceRoot: '.' - }); -} - -function createSourceMapWithGap() { - var sourceMap = createEmptySourceMap(); - sourceMap.addMapping({ - generated: { line: 100, column: 0 }, - original: { line: 100, column: 0 }, - source: `.original-${id}.js` - }); - return sourceMap; -} - -function createSingleLineSourceMap() { - var sourceMap = createEmptySourceMap(); - sourceMap.addMapping({ - generated: { line: 1, column: 0 }, - original: { line: 1, column: 0 }, - source: `.original-${id}.js` - }); - return sourceMap; -} + function createEmptySourceMap() { + return new SourceMapGenerator({ + file: `.generated-${id}.${extension}`, + sourceRoot: '.' + }); + } -function createSecondLineSourceMap() { - var sourceMap = createEmptySourceMap(); - sourceMap.addMapping({ - generated: { line: 2, column: 0 }, - original: { line: 1, column: 0 }, - source: `.original-${id}.js` - }); - return sourceMap; -} + function createSourceMapWithGap() { + var sourceMap = createEmptySourceMap(); + sourceMap.addMapping({ + generated: { line: 100, column: 0 }, + original: { line: 100, column: 0 }, + source: `.original-${id}.js` + }); + return sourceMap; + } -function createMultiLineSourceMap() { - var sourceMap = createEmptySourceMap(); - for (var i = 1; i <= 100; i++) { + function createSingleLineSourceMap() { + var sourceMap = createEmptySourceMap(); sourceMap.addMapping({ - generated: { line: i, column: 0 }, - original: { line: 1000 + i, column: 99 + i }, - source: 'line' + i + '.js' + generated: { line: 1, column: 0 }, + original: { line: 1, column: 0 }, + source: `.original-${id}.js` }); + return sourceMap; } - return sourceMap; -} -function createMultiLineSourceMapWithSourcesContent() { - var sourceMap = createEmptySourceMap(); - var original = new Array(1001).join('\n'); - for (var i = 1; i <= 100; i++) { + function createSecondLineSourceMap() { + var sourceMap = createEmptySourceMap(); sourceMap.addMapping({ - generated: { line: i, column: 0 }, - original: { line: 1000 + i, column: 4 }, - source: `original-${id}.js` + generated: { line: 2, column: 0 }, + original: { line: 1, column: 0 }, + source: `.original-${id}.js` }); - original += ' line ' + i + '\n'; + return sourceMap; + } + + function createMultiLineSourceMap() { + var sourceMap = createEmptySourceMap(); + for (var i = 1; i <= 100; i++) { + sourceMap.addMapping({ + generated: { line: i, column: 0 }, + original: { line: 1000 + i, column: 99 + i }, + source: 'line' + i + '.js' + }); + } + return sourceMap; + } + + function createMultiLineSourceMapWithSourcesContent() { + var sourceMap = createEmptySourceMap(); + var original = new Array(1001).join('\n'); + for (var i = 1; i <= 100; i++) { + sourceMap.addMapping({ + generated: { line: i, column: 0 }, + original: { line: 1000 + i, column: 4 }, + source: `original-${id}.js` + }); + original += ' line ' + i + '\n'; + } + sourceMap.setSourceContent(`original-${id}.js`, original); + return sourceMap; } - sourceMap.setSourceContent(`original-${id}.js`, original); - return sourceMap; -} } function rewriteExpectation(expected, generatedFilenameIn, generatedFilenameOut) { @@ -143,20 +175,12 @@ function rewriteExpectation(expected, generatedFilenameIn, generatedFilenameOut) return v.replace(generatedFilenameIn, generatedFilenameOut); }); } -// Tests were initially written as CJS with require() calls. -// We can support require() calls in MJS tests, too, as long as we create a require() function. -// Keep both prefixes the same length so that offsets are the same -function getSrcPrefix() { - return extension === 'mjs' - ? `import {createRequire} from 'module';const require = createRequire(import.meta.url);` - : ` `; -} async function compareStackTrace(sourceMap, source, expected) { - const srcPrefix = getSrcPrefix(); + const header = srcPrefix(); // Check once with a separate source map // fs.writeFileSync(`.generated-${id}-separate.${extension}.map`, JSON.stringify({...JSON.parse(sourceMap.toString()), file: `.generated-${id}-separate.${extension}`})); fs.writeFileSync(`.generated-${id}-separate.${extension}.map`, sourceMap.toString()); - fs.writeFileSync(`.generated-${id}-separate.${extension}`, `${srcPrefix}${exportDecl()} = function() {` + + fs.writeFileSync(`.generated-${id}-separate.${extension}`, `${header}${namedExportDeclaration()} = function() {` + source.join('\n') + `};//@ sourceMappingURL=.generated-${id}-separate.${extension}.map`); try { (await import(`./.generated-${id}-separate.${extension}`)).test(); @@ -165,7 +189,7 @@ async function compareStackTrace(sourceMap, source, expected) { } // Check again with an inline source map (in a data URL) - fs.writeFileSync(`.generated-${id}-inline.${extension}`, `${srcPrefix}${exportDecl()} = function() {` + + fs.writeFileSync(`.generated-${id}-inline.${extension}`, `${header}${namedExportDeclaration()} = function() {` + source.join('\n') + '};//@ sourceMappingURL=data:application/json;base64,' + bufferFrom(sourceMap.toString()).toString('base64')); try { @@ -176,10 +200,10 @@ async function compareStackTrace(sourceMap, source, expected) { } function compareStdout(done, sourceMap, source, expected) { - let srcPrefix = getSrcPrefix(); + let header = srcPrefix(); fs.writeFileSync(`.original-${id}.js`, 'this is the original code'); fs.writeFileSync(`.generated-${id}.${extension}.map`, sourceMap.toString()); - fs.writeFileSync(`.generated-${id}.${extension}`, srcPrefix + source.join('\n') + + fs.writeFileSync(`.generated-${id}.${extension}`, header + source.join('\n') + `//@ sourceMappingURL=.generated-${id}.${extension}.map`); child_process.exec(`node ./.generated-${id}.${extension}`, function(error, stdout, stderr) { try { @@ -210,7 +234,7 @@ async function normalThrow() { 'throw new Error("test");' ], [ 'Error: test', - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` ]); } async function normalThrowWithoutSourceMapSupportInstalled() { @@ -218,18 +242,31 @@ async function normalThrowWithoutSourceMapSupportInstalled() { 'throw new Error("test");' ], [ 'Error: test', - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$` + re`^ at ${stackFrameAtTest(false)} \((?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$` ]); } } +function describePerModuleType(fn, ...args) { + for(const ext of ['cjs', 'mjs']) { + describe(`${ext} >`, () => { + beforeEach(function() { + extension = ext; + }); + fn(...args); + }); + } +} + describe('Without source-map-support installed', function() { - const sourceMapConstructors = sourceMapCreators(); - const macros = getTestMacros(sourceMapConstructors); - const {normalThrowWithoutSourceMapSupportInstalled} = macros; + describePerModuleType(() => { + const sourceMapConstructors = sourceMapCreators(); + const macros = getTestMacros(sourceMapConstructors); + const {normalThrowWithoutSourceMapSupportInstalled} = macros; - it('normal throw without source-map-support installed', async function () { - await normalThrowWithoutSourceMapSupportInstalled(); + it('normal throw without source-map-support installed', async function () { + await normalThrowWithoutSourceMapSupportInstalled(); + }); }); }); @@ -254,28 +291,19 @@ function addPrefixToSourceMapPaths(sourceMap, prefix) { } describe('sourcemap style: relative paths sans ./ prefix, e.g. "original-1.js" >', () => { - moduleTypeSuites(identity); + describePerModuleType(tests, identity); }); describe('sourcemap style: relative paths with ./ prefix, e.g. "./original-1.js" >', () => { - moduleTypeSuites(addRelativePrefixToSourceMapPaths); + describePerModuleType(tests, addRelativePrefixToSourceMapPaths); }); describe('sourcemap style: absolute paths and sourceRoot removed, e.g. "/abs/path/original-1.js" >', () => { - moduleTypeSuites(addAbsolutePrefixToSourceMapPaths); + describePerModuleType(tests, addAbsolutePrefixToSourceMapPaths); }); describe('sourcemap style: file urls with absolute paths and sourceRoot removed, e.g. "file:///abs/path/original-1.js" >', () => { - moduleTypeSuites(addFileUrlAbsolutePrefixToSourceMapPaths); + describePerModuleType(tests, addFileUrlAbsolutePrefixToSourceMapPaths); }); -function moduleTypeSuites(sourceMapPostprocessor) { - describe('cjs >', () => { - tests(sourceMapPostprocessor, 'cjs'); - }); - describe('mjs >', () => { - tests(sourceMapPostprocessor, 'mjs'); - }); -} - -function tests(sourceMapPostprocessor, _extension) { +function tests(sourceMapPostprocessor) { // let createEmptySourceMap, createMultiLineSourceMap, createMultiLineSourceMapWithSourcesContent, createSecondLineSourceMap, createSingleLineSourceMap, createSourceMapWithGap}) const sourceMapConstructors = sourceMapCreators(); for(const [key, value] of Object.entries(sourceMapConstructors)) { @@ -284,10 +312,6 @@ function tests(sourceMapPostprocessor, _extension) { const {createEmptySourceMap, createMultiLineSourceMap, createMultiLineSourceMapWithSourcesContent, createSecondLineSourceMap, createSingleLineSourceMap, createSourceMapWithGap} = sourceMapConstructors; const {normalThrow} = getTestMacros(sourceMapConstructors); - beforeEach(function() { - extension = _extension; - }); - it('normal throw', async function() { installSms(); await normalThrow(); @@ -310,7 +334,7 @@ it('fs.readFileSync failure', async function() { '}' ], [ 'Error: test', - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line7\.js:1007:107\)$` + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line7\.js:1007:107\)$` ]); }); @@ -324,7 +348,7 @@ it('throw inside function', async function() { ], [ 'Error: test', /^ at foo \((?:.*[/\\])?line2\.js:1002:102\)$/, - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line4\.js:1004:104\)$` + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line4\.js:1004:104\)$` ]); }); @@ -341,7 +365,7 @@ it('throw inside function inside function', async function() { 'Error: test', /^ at bar \((?:.*[/\\])?line3\.js:1003:103\)$/, /^ at foo \((?:.*[/\\])?line5\.js:1005:105\)$/, - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line7\.js:1007:107\)$` + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line7\.js:1007:107\)$` ]); }); @@ -351,10 +375,10 @@ it('eval', async function() { ], [ 'Error: test', - // Before Node 4, `Object.eval`, after just `eval`. - /^ at (?:Object\.)?eval \(eval at (|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, + // TODO + /^ at eval \(eval at (|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` ]); }); @@ -363,9 +387,9 @@ it('eval inside eval', async function() { 'eval("eval(\'throw new Error(\\"test\\")\')");' ], [ 'Error: test', - /^ at (?:Object\.)?eval \(eval at (|exports\.test|test) \(eval at (|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, - /^ at (?:Object\.)?eval \(eval at (|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` + /^ at eval \(eval at (|exports\.test|test) \(eval at (|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, + /^ at eval \(eval at (|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` ]); }); @@ -379,7 +403,7 @@ it('eval inside function', async function() { 'Error: test', /^ at eval \(eval at foo \((?:.*[/\\])?line2\.js:1002:102\)/, /^ at foo \((?:.*[/\\])?line2\.js:1002:102\)/, - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line4\.js:1004:104\)$` + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line4\.js:1004:104\)$` ]); }); @@ -388,8 +412,8 @@ it('eval with sourceURL', async function() { 'eval("throw new Error(\'test\')//@ sourceURL=sourceURL.js");' ], [ 'Error: test', - /^ at (?:Object\.)?eval \(sourceURL\.js:1:7\)$/, - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` + /^ at eval \(sourceURL\.js:1:7\)$/, + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` ]); }); @@ -398,9 +422,9 @@ it('eval with sourceURL inside eval', async function() { 'eval("eval(\'throw new Error(\\"test\\")//@ sourceURL=sourceURL.js\')");' ], [ 'Error: test', - /^ at (?:Object\.)?eval \(sourceURL\.js:1:7\)$/, - /^ at (?:Object\.)?eval \(eval at (|exports.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` + /^ at eval \(sourceURL\.js:1:7\)$/, + /^ at eval \(eval at (|exports.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` ]); }); @@ -427,7 +451,7 @@ it('throw with empty source map', async function() { 'throw new Error("test");' ], [ 'Error: test', - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.generated-${id}.${extension}:1:123\)$` + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?\.generated-${id}.${extension}:1:123\)$` ]); }); @@ -451,7 +475,7 @@ it('throw with source map with gap', async function() { 'throw new Error("test");' ], [ 'Error: test', - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$` + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$` ]); }); @@ -460,7 +484,7 @@ it('sourcesContent with data URL', async function() { 'throw new Error("test");' ], [ 'Error: test', - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?original-${id}\.js:1001:5\)$` + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?original-${id}\.js:1001:5\)$` ]); }); @@ -470,7 +494,7 @@ it('finds the last sourceMappingURL', async function() { 'throw new Error("test");' ], [ 'Error: test', - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?original-${id}\.js:1002:5\)$` + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?original-${id}\.js:1002:5\)$` ]); }); @@ -495,7 +519,7 @@ it('maps original name from source', async function() { ], [ 'Error: test', re`^ at myOriginalName \((?:.*[/\\])?\.original-${id}.js:1000:11\)$`, - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?\.original-${id}.js:1002:2\)$` + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?\.original-${id}.js:1002:2\)$` ]); }); @@ -540,9 +564,7 @@ it('handleUncaughtExceptions is false', function(done) { re`[/\\].generated-${id}.${extension}:2$`, 'function foo() { throw new Error("this is the error"); }', - // Before Node 4, the arrow points on the `new`, after on the - // `throw`. - /^ (?: )?\^$/, + ' ^', 'Error: this is the error', re`^ at foo \((?:.*[/\\])?.original-${id}\.js:1:1\)$` @@ -558,7 +580,7 @@ it('default options with empty source map', function(done) { ], [ re`[/\\].generated-${id}.${extension}:2$`, 'function foo() { throw new Error("this is the error"); }', - /^ (?: )?\^$/, + ' ^', 'Error: this is the error', re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:2:24\)$` ]); @@ -573,7 +595,7 @@ it('default options with source map with gap', function(done) { ], [ re`[/\\].generated-${id}.${extension}:2$`, 'function foo() { throw new Error("this is the error"); }', - /^ (?: )?\^$/, + ' ^', 'Error: this is the error', re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:2:24\)$` ]); @@ -705,13 +727,13 @@ it('should allow for runtime inline source maps', function(done) { '0', // The retrieval should only be attempted once ]); }); -} +// } // TODO should this suite also be inside the matrix? -describe('Other', function() { +// describe('Other', function() { // Wrapped in a suite to preserve test execution order - const {createEmptySourceMap, createSingleLineSourceMap, createMultiLineSourceMap} = sourceMapCreators(); - const extension = 'cjs'; + // const {createEmptySourceMap, createSingleLineSourceMap, createMultiLineSourceMap} = sourceMapCreators(); + // const extension = 'cjs'; /* The following test duplicates some of the code in * `compareStackTrace` but appends a charset to the @@ -722,10 +744,10 @@ it('finds source maps with charset specified', async function() { var source = [ 'throw new Error("test");' ]; var expected = [ 'Error: test', - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` ]; - fs.writeFileSync(`.generated-${id}.${extension}`, `${exportDecl(extension)} = function() {` + + fs.writeFileSync(`.generated-${id}.${extension}`, `${namedExportDeclaration()} = function() {` + source.join('\n') + '};//@ sourceMappingURL=data:application/json;charset=utf8;base64,' + bufferFrom(sourceMap.toString()).toString('base64')); try { @@ -744,10 +766,10 @@ it('allows code/comments after sourceMappingURL', async function() { var source = [ 'throw new Error("test");' ]; var expected = [ 'Error: test', - re`^ at (Module|Object)(\.exports)?\.test \((?:.*[/\\])?line1\.js:1001:101\)$` + re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` ]; - fs.writeFileSync(`.generated-${id}.${extension}`, `${exportDecl(extension)} = function() {` + + fs.writeFileSync(`.generated-${id}.${extension}`, `${namedExportDeclaration()} = function() {` + source.join('\n') + '};//# sourceMappingURL=data:application/json;base64,' + bufferFrom(sourceMap.toString()).toString('base64') + '\n// Some comment below the sourceMappingURL\nvar foo = 0;'); @@ -783,7 +805,7 @@ it('normal console.trace', function(done) { 'console.trace("test");' ], [ 'Trace: test', - /^ at Object\. \((?:.*[/\\])?line2\.js:1002:102\)$/ + re`^ at ${stackFrameAtTrace(String.raw`(?:.*[/\\])?line2\.js:1002:102`)}$` ]); }); @@ -796,33 +818,38 @@ it('supports multiple instances', function(done) { }); fs.writeFileSync(`.generated2-${id}.${extension}.map.extra`, sourceMap.toString()); fs.writeFileSync(`.generated2-${id}.${extension}`, [ - 'module.exports = function foo() { throw new Error("this is the error"); }', + `${namedExportDeclaration()} = function test() { throw new Error("this is the error"); }`, `//@ sourceMappingURL=.generated2-${id}.${extension}.map` ].join('\n')); fs.writeFileSync(`.original2-${id}.js`, 'this is some other original code'); compareStdout(done, createEmptySourceMap(), [ - 'require("./source-map-support").install({', - ' retrieveFile: function(path) {', - ' var fs = require("fs");', - ' if (fs.existsSync(path + ".extra")) {', - ' return fs.readFileSync(path + ".extra", "utf8");', + '(async function() {', + ' require("./source-map-support").install({', + ' retrieveFile: function(path) {', + ' var fs = require("fs");', + ' var url = require("url");', + ' try { path = url.fileURLToPath(path) } catch {}', + ' if (fs.existsSync(path + ".extra")) {', + ' return fs.readFileSync(path + ".extra", "utf8");', + ' }', ' }', - ' }', - '});', - `var foo = require("./.generated2-${id}.${extension}");`, - 'delete require.cache[require.resolve("./source-map-support")];', - 'require("./source-map-support").install();', - 'process.nextTick(foo);', - 'process.nextTick(function() { process.exit(1); });' + ' });', + ` var {test} = await import("./.generated2-${id}.${extension}");`, + ' delete require.cache[require.resolve("./source-map-support")];', + ' require("./source-map-support").install();', + ' process.nextTick(test);', + ' process.nextTick(function() { process.exit(1); });', + '})();' ], [ re`[/\\].original2-${id}\.js:1$`, 'this is some other original code', '^', 'Error: this is the error', - re`^ at foo \((?:.*[/\\])?.original2-${id}\.js:1:1\)$` + re`^ at test \((?:.*[/\\])?.original2-${id}\.js:1:1\)$` ]); }); -}); +// }); +} describe('redirects require() of "source-map-support" to this module', function() { it('redirects', async function() { From 0677a3c9a9293c6786e88b8cf53a11ee2bd5445d Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 29 Apr 2022 03:14:56 +0000 Subject: [PATCH 09/24] fix CI --- package-lock.json | 60 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/package-lock.json b/package-lock.json index 717506a..610a4b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "coffeescript": "^1.12.7", "http-server": "^0.11.1", "mocha": "^3.5.3", + "semver": "^7.3.7", "source-map": "0.6.1", "webpack": "^1.15.0" }, @@ -4307,6 +4308,33 @@ "ret": "~0.1.10" } }, + "node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -5244,6 +5272,12 @@ "node": ">=0.4" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/yargs": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz", @@ -8830,6 +8864,26 @@ "ret": "~0.1.10" } }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -9637,6 +9691,12 @@ "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", "dev": true }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "yargs": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz", From 5b79f3833f80b07ea4660118398034899b79368e Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 29 Apr 2022 02:30:26 -0400 Subject: [PATCH 10/24] fix --- .vscode/launch.json | 1 + source-map-support.js | 36 +++++++++++++++++++++++++++++++++--- test.js | 7 ++++--- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 93ae232..2a96219 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,6 +5,7 @@ "type": "node", "request": "launch", "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/_mocha", + "runtimeArgs": ["--timeout", "999999999"], "outputCapture": "std", "skipFiles": [ "/**/*.js" diff --git a/source-map-support.js b/source-map-support.js index 644b0b1..ecadd39 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -129,9 +129,20 @@ function hasGlobalProcessEventEmitter() { return ((typeof process === 'object') && (process !== null) && (typeof process.on === 'function')); } +function tryFileURLToPath(v) { + // TODO technically, file URL can omit /s. + // Copy the isFileURL util from resolve-uri? + if(v.startsWith('file:/')) { + return fileURLToPath(v); + } + return v; +} + // #region Caches /** @param {string} pathOrFileUrl */ function getCacheKey(pathOrFileUrl) { + if(pathOrFileUrl.startsWith('node:')) return pathOrFileUrl; + // TODO unify with isFileURL checks elsewhere? as helper fn? if(pathOrFileUrl.startsWith('file:/')) { // Must normalize spaces to %20, stuff like that return new URL(pathOrFileUrl).toString(); @@ -224,9 +235,26 @@ sharedData.internalRetrieveFileHandlers.push(function(path) { return setFileContentsCache(path, contents); }); +// TODO un-copy these from resolve-uri; see if they can be exported from that lib +function isAbsoluteUrl(input) { + return schemeRegex.test(input); +} +// Matches the scheme of a URL, eg "http://" +const schemeRegex = /^[\w+.-]+:\/\//; +function isSchemeRelativeUrl(input) { + return input.startsWith('//'); +} + // Support URLs relative to a directory, but be careful about a protocol prefix // in case we are in the browser (i.e. directories may start with "http://" or "file:///") function supportRelativeURL(file, url) { + // We want to preserve path style. + // resolveUri cannot handle windows paths. + // Therefore, special-case when output will be a windows path + if(process.platform === 'win32' && path.isAbsolute(file) && !isAbsoluteUrl(url) && !isSchemeRelativeUrl(url)) { + const dir = path.dirname(file); + return path.resolve(dir, url); + } return resolveUri(url, file); } @@ -251,7 +279,7 @@ function retrieveSourceMapURL(source) { } // Get the URL of the source map - fileData = retrieveFile(source); + fileData = retrieveFile(tryFileURLToPath(source)); var re = /(?:\/\/[@#][\s]*sourceMappingURL=([^\s'"]+)[\s]*$)|(?:\/\*[@#][\s]*sourceMappingURL=([^\s*'"]+)[\s]*(?:\*\/)[\s]*$)/mg; // Keep executing the search to find the *last* sourceMappingURL to avoid // picking up sourceMappingURLs from comments, strings, etc. @@ -282,7 +310,7 @@ sharedData.internalRetrieveMapHandlers.push(function(source) { } else { // Support source map URLs relative to the source URL sourceMappingURL = supportRelativeURL(source, sourceMappingURL); - sourceMapData = retrieveFile(sourceMappingURL); + sourceMapData = retrieveFile(tryFileURLToPath(sourceMappingURL)); } if (!sourceMapData) { @@ -581,7 +609,7 @@ function createPrepareStackTrace(hookState) { // Generate position and snippet of original source with pointer function getErrorSource(error) { // TODO this is not robust enough - var match = /\n at [^(]+ \((?:file:\/{0,2})?(.*):(\d+):(\d+)\)/.exec(error.stack); + var match = /\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(error.stack); if (match) { var source = match[1]; var line = +match[2]; @@ -590,6 +618,8 @@ function getErrorSource(error) { // Support the inline sourceContents inside the source map var contents = getFileContentsCache(source); + source = tryFileURLToPath(source); + // Support files on disk if (!contents && fs && fs.existsSync(source)) { try { diff --git a/test.js b/test.js index 9e7420f..f835d1c 100644 --- a/test.js +++ b/test.js @@ -33,7 +33,6 @@ function namedExportDeclaration() { * Example: ` at Module.exports.test (`... */ function stackFrameAtTest(sourceMapSupportInstalled = true) { - console.log(extension); if(semver.gte(process.versions.node, '18.0.0')) { return extension === 'mjs' ? 'Module\\.test' : sourceMapSupportInstalled ? 'Module\\.exports\\.test' : 'exports\\.test'; } else { @@ -88,7 +87,7 @@ function compareLines(actual, expected) { for (var i = 0; i < expected.length; i++) { // Some tests are regular expressions because the output format changed slightly between node v0.9.2 and v0.9.3 if (expected[i] instanceof RegExp) { - assert(expected[i].test(actual[i]), JSON.stringify(actual[i]) + ' does not match ' + expected[i]); + assert(expected[i].test(actual[i]), JSON.stringify(actual[i]) + ' does not match ' + expected[i] + '\n' + JSON.stringify({actual, expected: expected.map(v => typeof v === 'string' ? v : v.toString())}, null, 2)); } else { assert.equal(actual[i], expected[i]); } @@ -211,7 +210,9 @@ function compareStdout(done, sourceMap, source, expected) { (stdout + stderr) .trim() .split(/\r\n|\n/) - .filter(function (line) { return line !== '' }), // Empty lines are not relevant. + // Empty lines are not relevant. + // Running in a debugger causes additional output. + .filter(function (line) { return line !== '' && line !== 'Debugger attached.' }), expected ); } catch (e) { From 2b315b8ddad246617f4e643ba876df14738e779d Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 29 Apr 2022 14:55:27 +0000 Subject: [PATCH 11/24] Test that paths in mapped stack frames match expected format: either file:/ or native path --- source-map-support.js | 1 - test.js | 106 +++++++++++++++++++++++------------------- 2 files changed, 57 insertions(+), 50 deletions(-) diff --git a/source-map-support.js b/source-map-support.js index ecadd39..3d3dc03 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -608,7 +608,6 @@ function createPrepareStackTrace(hookState) { // Generate position and snippet of original source with pointer function getErrorSource(error) { - // TODO this is not robust enough var match = /\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(error.stack); if (match) { var source = match[1]; diff --git a/test.js b/test.js index f835d1c..279f662 100644 --- a/test.js +++ b/test.js @@ -12,6 +12,8 @@ var child_process = require('child_process'); var assert = require('assert'); var fs = require('fs'); var util = require('util'); +var path = require('path'); +const { pathToFileURL } = require('url'); var bufferFrom = Buffer.from; const semver = require('semver'); @@ -45,6 +47,17 @@ function stackFrameAtTest(sourceMapSupportInstalled = true) { function stackFrameAtTrace(fileRe) { return extension === 'mjs' ? `${fileRe}` : `Object\\. \\(${fileRe}\\)`; } +/** + * Describe how the source path in a stack frame is expected to start. + * If generated module is ESM, node uses file:// URL, so we expect mapped original path to also be file:// to match + * On windows, when not doing file:// URIs, expect windows-style paths + */ +function stackFramePathStartsWith() { + if(extension === 'mjs') return 'file:/'; + // Escape backslashes since we are returning regexp syntax + return path.parse(process.cwd()).root.replace(/\\/g, '\\\\'); + // this re \((?:.*[/\\])? +} /** * Tests were initially written as CJS with require() calls. * We can support require() calls in MJS tests, too, as long as we create a require() function. @@ -177,7 +190,6 @@ function rewriteExpectation(expected, generatedFilenameIn, generatedFilenameOut) async function compareStackTrace(sourceMap, source, expected) { const header = srcPrefix(); // Check once with a separate source map - // fs.writeFileSync(`.generated-${id}-separate.${extension}.map`, JSON.stringify({...JSON.parse(sourceMap.toString()), file: `.generated-${id}-separate.${extension}`})); fs.writeFileSync(`.generated-${id}-separate.${extension}.map`, sourceMap.toString()); fs.writeFileSync(`.generated-${id}-separate.${extension}`, `${header}${namedExportDeclaration()} = function() {` + source.join('\n') + `};//@ sourceMappingURL=.generated-${id}-separate.${extension}.map`); @@ -235,7 +247,7 @@ async function normalThrow() { 'throw new Error("test");' ], [ 'Error: test', - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$` ]); } async function normalThrowWithoutSourceMapSupportInstalled() { @@ -243,7 +255,7 @@ async function normalThrowWithoutSourceMapSupportInstalled() { 'throw new Error("test");' ], [ 'Error: test', - re`^ at ${stackFrameAtTest(false)} \((?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$` + re`^ at ${stackFrameAtTest(false)} \(${stackFramePathStartsWith()}(?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$` ]); } } @@ -335,7 +347,7 @@ it('fs.readFileSync failure', async function() { '}' ], [ 'Error: test', - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line7\.js:1007:107\)$` + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line7\.js:1007:107\)$` ]); }); @@ -348,8 +360,8 @@ it('throw inside function', async function() { 'foo();' ], [ 'Error: test', - /^ at foo \((?:.*[/\\])?line2\.js:1002:102\)$/, - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line4\.js:1004:104\)$` + re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?line2\.js:1002:102\)$`, + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line4\.js:1004:104\)$` ]); }); @@ -364,9 +376,9 @@ it('throw inside function inside function', async function() { 'foo();' ], [ 'Error: test', - /^ at bar \((?:.*[/\\])?line3\.js:1003:103\)$/, - /^ at foo \((?:.*[/\\])?line5\.js:1005:105\)$/, - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line7\.js:1007:107\)$` + re`^ at bar \(${stackFramePathStartsWith()}(?:.*[/\\])?line3\.js:1003:103\)$`, + re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?line5\.js:1005:105\)$`, + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line7\.js:1007:107\)$` ]); }); @@ -377,9 +389,9 @@ it('eval', async function() { 'Error: test', // TODO - /^ at eval \(eval at (|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, + re`^ at eval \(eval at (|exports\.test|test) \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)`, - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$` ]); }); @@ -388,9 +400,9 @@ it('eval inside eval', async function() { 'eval("eval(\'throw new Error(\\"test\\")\')");' ], [ 'Error: test', - /^ at eval \(eval at (|exports\.test|test) \(eval at (|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, - /^ at eval \(eval at (|exports\.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` + re`^ at eval \(eval at (|exports\.test|test) \(eval at (|exports\.test|test) \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)`, + re`^ at eval \(eval at (|exports\.test|test) \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)`, + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$` ]); }); @@ -402,9 +414,9 @@ it('eval inside function', async function() { 'foo();' ], [ 'Error: test', - /^ at eval \(eval at foo \((?:.*[/\\])?line2\.js:1002:102\)/, - /^ at foo \((?:.*[/\\])?line2\.js:1002:102\)/, - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line4\.js:1004:104\)$` + re`^ at eval \(eval at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?line2\.js:1002:102\)`, + re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?line2\.js:1002:102\)`, + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line4\.js:1004:104\)$` ]); }); @@ -414,7 +426,7 @@ it('eval with sourceURL', async function() { ], [ 'Error: test', /^ at eval \(sourceURL\.js:1:7\)$/, - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$` ]); }); @@ -424,8 +436,8 @@ it('eval with sourceURL inside eval', async function() { ], [ 'Error: test', /^ at eval \(sourceURL\.js:1:7\)$/, - /^ at eval \(eval at (|exports.test|test) \((?:.*[/\\])?line1\.js:1001:101\)/, - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` + re`^ at eval \(eval at (|exports.test|test) \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)`, + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$` ]); }); @@ -452,7 +464,7 @@ it('throw with empty source map', async function() { 'throw new Error("test");' ], [ 'Error: test', - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?\.generated-${id}.${extension}:1:123\)$` + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?\.generated-${id}.${extension}:1:123\)$` ]); }); @@ -467,7 +479,7 @@ it('throw in Timeout with empty source map', function(done) { ' throw new Error("this is the error")', /^ \^$/, 'Error: this is the error', - re`^ at ((null)|(Timeout))\._onTimeout \((?:.*[/\\])?.generated-${id}\.${extension}:3:11\)$` + re`^ at ((null)|(Timeout))\._onTimeout \(${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}\.${extension}:3:11\)$` ]); }); @@ -476,7 +488,7 @@ it('throw with source map with gap', async function() { 'throw new Error("test");' ], [ 'Error: test', - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$` + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$` ]); }); @@ -485,7 +497,7 @@ it('sourcesContent with data URL', async function() { 'throw new Error("test");' ], [ 'Error: test', - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?original-${id}\.js:1001:5\)$` + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?original-${id}\.js:1001:5\)$` ]); }); @@ -495,7 +507,7 @@ it('finds the last sourceMappingURL', async function() { 'throw new Error("test");' ], [ 'Error: test', - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?original-${id}\.js:1002:5\)$` + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?original-${id}\.js:1002:5\)$` ]); }); @@ -519,8 +531,8 @@ it('maps original name from source', async function() { 'foo();' ], [ 'Error: test', - re`^ at myOriginalName \((?:.*[/\\])?\.original-${id}.js:1000:11\)$`, - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?\.original-${id}.js:1002:2\)$` + re`^ at myOriginalName \(${stackFramePathStartsWith()}(?:.*[/\\])?\.original-${id}.js:1000:11\)$`, + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?\.original-${id}.js:1002:2\)$` ]); }); @@ -536,7 +548,7 @@ it('default options', function(done) { 'this is the original code', '^', 'Error: this is the error', - re`^ at foo \((?:.*[/\\])?\.original-${id}\.js:1:1\)$` + re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?\.original-${id}\.js:1:1\)$` ]); }); @@ -551,7 +563,7 @@ it('handleUncaughtExceptions is true', function(done) { 'this is the original code', '^', 'Error: this is the error', - re`^ at foo \((?:.*[/\\])?\.original-${id}\.js:1:1\)$` + re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?\.original-${id}\.js:1:1\)$` ]); }); @@ -568,7 +580,7 @@ it('handleUncaughtExceptions is false', function(done) { ' ^', 'Error: this is the error', - re`^ at foo \((?:.*[/\\])?.original-${id}\.js:1:1\)$` + re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?.original-${id}\.js:1:1\)$` ]); }); @@ -583,7 +595,7 @@ it('default options with empty source map', function(done) { 'function foo() { throw new Error("this is the error"); }', ' ^', 'Error: this is the error', - re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:2:24\)$` + re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}.${extension}:2:24\)$` ]); }); @@ -598,7 +610,7 @@ it('default options with source map with gap', function(done) { 'function foo() { throw new Error("this is the error"); }', ' ^', 'Error: this is the error', - re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:2:24\)$` + re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}.${extension}:2:24\)$` ]); }); @@ -629,7 +641,7 @@ it('sourcesContent', function(done) { ' line 2', ' ^', 'Error: this is the error', - re`^ at foo \((?:.*[/\\])?original-${id}\.js:1002:5\)$` + re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?original-${id}\.js:1002:5\)$` ]); }); @@ -652,14 +664,18 @@ it('missing source maps should also be cached', function(done) { 'process.nextTick(function() { console.log(count); });', ], [ 'Error: this is the error', - re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:4:15\)$`, + re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}.${extension}:4:15\)$`, 'Error: this is the error', - re`^ at foo \((?:.*[/\\])?.generated-${id}.${extension}:4:15\)$`, + re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}.${extension}:4:15\)$`, '1', // The retrieval should only be attempted once ]); }); it('should consult all retrieve source map providers', function(done) { + // TODO are we supposed to be resolving this URL to absolute? Or should we test that non-absolute is supported? + // Test in vanilla source-map-support + let originalPath = path.resolve(`.original-${id}.js`); + if(extension === 'mjs') originalPath = pathToFileURL(originalPath).toString(); compareStdout(done, createSingleLineSourceMap(), [ '', 'var count = 0;', @@ -676,7 +692,7 @@ it('should consult all retrieve source map providers', function(done) { ' retrieveSourceMap: function(name) {', ` if (/\\.generated-${id}\\.${extension}$/.test(name)) {`, ' count++;', - ' return ' + JSON.stringify({url: `.original-${id}.js`, map: createMultiLineSourceMapWithSourcesContent().toJSON()}) + ';', + ' return ' + JSON.stringify({url: originalPath, map: createMultiLineSourceMapWithSourcesContent().toJSON()}) + ';', ' }', ' }', '});', @@ -685,9 +701,9 @@ it('should consult all retrieve source map providers', function(done) { 'process.nextTick(function() { console.log(count); });', ], [ 'Error: this is the error', - re`^ at foo \((?:.*[/\\])?original-${id}\.js:1004:5\)$`, + re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?original-${id}\.js:1004:5\)$`, 'Error: this is the error', - re`^ at foo \((?:.*[/\\])?original-${id}\.js:1004:5\)$`, + re`^ at foo \(${stackFramePathStartsWith()}(?:.*[/\\])?original-${id}\.js:1004:5\)$`, '1', // The retrieval should only be attempted once ]); }); @@ -728,13 +744,6 @@ it('should allow for runtime inline source maps', function(done) { '0', // The retrieval should only be attempted once ]); }); -// } - -// TODO should this suite also be inside the matrix? -// describe('Other', function() { - // Wrapped in a suite to preserve test execution order - // const {createEmptySourceMap, createSingleLineSourceMap, createMultiLineSourceMap} = sourceMapCreators(); - // const extension = 'cjs'; /* The following test duplicates some of the code in * `compareStackTrace` but appends a charset to the @@ -745,7 +754,7 @@ it('finds source maps with charset specified', async function() { var source = [ 'throw new Error("test");' ]; var expected = [ 'Error: test', - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$` ]; fs.writeFileSync(`.generated-${id}.${extension}`, `${namedExportDeclaration()} = function() {` + @@ -767,7 +776,7 @@ it('allows code/comments after sourceMappingURL', async function() { var source = [ 'throw new Error("test");' ]; var expected = [ 'Error: test', - re`^ at ${stackFrameAtTest()} \((?:.*[/\\])?line1\.js:1001:101\)$` + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$` ]; fs.writeFileSync(`.generated-${id}.${extension}`, `${namedExportDeclaration()} = function() {` + @@ -846,10 +855,9 @@ it('supports multiple instances', function(done) { 'this is some other original code', '^', 'Error: this is the error', - re`^ at test \((?:.*[/\\])?.original2-${id}\.js:1:1\)$` + re`^ at test \(${stackFramePathStartsWith()}(?:.*[/\\])?.original2-${id}\.js:1:1\)$` ]); }); -// }); } describe('redirects require() of "source-map-support" to this module', function() { From 69012b90a99292b91ce1ff60251cd53751572d52 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 29 Apr 2022 18:04:15 +0000 Subject: [PATCH 12/24] Ensure source snippets also output URLs/paths in the correct format, matching stack frame --- source-map-support.js | 6 +++--- test.js | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/source-map-support.js b/source-map-support.js index 3d3dc03..5ee9249 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -617,12 +617,12 @@ function getErrorSource(error) { // Support the inline sourceContents inside the source map var contents = getFileContentsCache(source); - source = tryFileURLToPath(source); + const sourceAsPath = tryFileURLToPath(source); // Support files on disk - if (!contents && fs && fs.existsSync(source)) { + if (!contents && fs && fs.existsSync(sourceAsPath)) { try { - contents = fs.readFileSync(source, 'utf8'); + contents = fs.readFileSync(sourceAsPath, 'utf8'); } catch (er) { contents = ''; } diff --git a/test.js b/test.js index 279f662..0e1f41f 100644 --- a/test.js +++ b/test.js @@ -446,7 +446,7 @@ it('native function', async function() { '[1].map(function(x) { throw new Error(x); });' ], [ 'Error: 1', - re`[/\\].original-${id}.js`, + re`${stackFramePathStartsWith()}(?:.*[/\\])?.original-${id}.js`, /at Array\.map \((native|)\)/ ]); }); @@ -475,7 +475,7 @@ it('throw in Timeout with empty source map', function(done) { ' throw new Error("this is the error")', '})' ], [ - re`[/\\].generated-${id}.${extension}:3$`, + re`${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}.${extension}:3$`, ' throw new Error("this is the error")', /^ \^$/, 'Error: this is the error', @@ -544,7 +544,7 @@ it('default options', function(done) { 'process.nextTick(foo);', 'process.nextTick(function() { process.exit(1); });' ], [ - re`[/\\].original-${id}\.js:1$`, + re`${stackFramePathStartsWith()}(?:.*[/\\])?.original-${id}\.js:1$`, 'this is the original code', '^', 'Error: this is the error', @@ -559,7 +559,7 @@ it('handleUncaughtExceptions is true', function(done) { 'require("./source-map-support").install({ handleUncaughtExceptions: true });', 'process.nextTick(foo);' ], [ - re`[/\\].original-${id}\.js:1$`, + re`${stackFramePathStartsWith()}(?:.*[/\\])?.original-${id}\.js:1$`, 'this is the original code', '^', 'Error: this is the error', @@ -574,7 +574,7 @@ it('handleUncaughtExceptions is false', function(done) { 'require("./source-map-support").install({ handleUncaughtExceptions: false });', 'process.nextTick(foo);' ], [ - re`[/\\].generated-${id}.${extension}:2$`, + re`${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}.${extension}:2$`, 'function foo() { throw new Error("this is the error"); }', ' ^', @@ -591,7 +591,7 @@ it('default options with empty source map', function(done) { 'require("./source-map-support").install();', 'process.nextTick(foo);' ], [ - re`[/\\].generated-${id}.${extension}:2$`, + re`${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}.${extension}:2$`, 'function foo() { throw new Error("this is the error"); }', ' ^', 'Error: this is the error', @@ -606,7 +606,7 @@ it('default options with source map with gap', function(done) { 'require("./source-map-support").install();', 'process.nextTick(foo);' ], [ - re`[/\\].generated-${id}.${extension}:2$`, + re`${stackFramePathStartsWith()}(?:.*[/\\])?.generated-${id}.${extension}:2$`, 'function foo() { throw new Error("this is the error"); }', ' ^', 'Error: this is the error', @@ -637,7 +637,7 @@ it('sourcesContent', function(done) { 'process.nextTick(foo);', 'process.nextTick(function() { process.exit(1); });' ], [ - re`[/\\]original-${id}\.js:1002$`, + re`${stackFramePathStartsWith()}(?:.*[/\\])?original-${id}\.js:1002$`, ' line 2', ' ^', 'Error: this is the error', @@ -851,7 +851,7 @@ it('supports multiple instances', function(done) { ' process.nextTick(function() { process.exit(1); });', '})();' ], [ - re`[/\\].original2-${id}\.js:1$`, + re`${stackFramePathStartsWith()}(?:.*[/\\])?.original2-${id}\.js:1$`, 'this is some other original code', '^', 'Error: this is the error', From 0582d538600d6f9fd84e9a671765545f74388bc1 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 29 Apr 2022 16:41:34 -0400 Subject: [PATCH 13/24] fix --- package-lock.json | 26 ++++++++++++++++++++++++++ package.json | 2 ++ source-map-support.js | 8 ++++++-- test.js | 36 ++++++++++++++++++++++++++---------- 4 files changed, 60 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 610a4b5..029491c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,9 +13,11 @@ "@jridgewell/trace-mapping": "0.3.9" }, "devDependencies": { + "@types/lodash": "^4.14.182", "browserify": "^4.2.3", "coffeescript": "^1.12.7", "http-server": "^0.11.1", + "lodash": "^4.17.21", "mocha": "^3.5.3", "semver": "^7.3.7", "source-map": "0.6.1", @@ -47,6 +49,12 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@types/lodash": { + "version": "4.14.182", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", + "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==", + "dev": true + }, "node_modules/acorn": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", @@ -2711,6 +2719,12 @@ "object-assign": "^4.0.1" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "node_modules/lodash._baseassign": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", @@ -5320,6 +5334,12 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@types/lodash": { + "version": "4.14.182", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", + "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==", + "dev": true + }, "acorn": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", @@ -7479,6 +7499,12 @@ "object-assign": "^4.0.1" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "lodash._baseassign": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", diff --git a/package.json b/package.json index 6484c3b..8287437 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,11 @@ "@jridgewell/trace-mapping": "0.3.9" }, "devDependencies": { + "@types/lodash": "^4.14.182", "browserify": "^4.2.3", "coffeescript": "^1.12.7", "http-server": "^0.11.1", + "lodash": "^4.17.21", "mocha": "^3.5.3", "semver": "^7.3.7", "source-map": "0.6.1", diff --git a/source-map-support.js b/source-map-support.js index 5ee9249..b4280f8 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -398,8 +398,12 @@ function mapEvalOrigin(origin) { } // This is copied almost verbatim from the V8 source code at -// https://code.google.com/p/v8/source/browse/trunk/src/messages.js. The -// implementation of wrapCallSite() used to just forward to the actual source +// https://code.google.com/p/v8/source/browse/trunk/src/messages.js +// Update 2022-04-29: +// https://github.com/v8/v8/blob/98f6f100c5ab8e390e51422747c4ef644d5ac6f2/src/builtins/builtins-callsite.cc#L175-L179 +// https://github.com/v8/v8/blob/98f6f100c5ab8e390e51422747c4ef644d5ac6f2/src/objects/call-site-info.cc#L795-L804 +// https://github.com/v8/v8/blob/98f6f100c5ab8e390e51422747c4ef644d5ac6f2/src/objects/call-site-info.cc#L717-L750 +// The implementation of wrapCallSite() used to just forward to the actual source // code of CallSite.prototype.toString but unfortunately a new release of V8 // did something to the prototype chain and broke the shim. The only fix I // could find was copy/paste. diff --git a/test.js b/test.js index 0e1f41f..8fabc4f 100644 --- a/test.js +++ b/test.js @@ -16,6 +16,7 @@ var path = require('path'); const { pathToFileURL } = require('url'); var bufferFrom = Buffer.from; const semver = require('semver'); +const { once, mapValues, flow } = require('lodash'); // Helper to create regular expressions from string templates, to use interpolation function re(...args) { @@ -34,9 +35,9 @@ function namedExportDeclaration() { * This varies across CJS / ESM and node versions. * Example: ` at Module.exports.test (`... */ -function stackFrameAtTest(sourceMapSupportInstalled = true) { +function stackFrameAtTest() { if(semver.gte(process.versions.node, '18.0.0')) { - return extension === 'mjs' ? 'Module\\.test' : sourceMapSupportInstalled ? 'Module\\.exports\\.test' : 'exports\\.test'; + return extension === 'mjs' ? 'Module\\.test' : '(?:Module\\.)?exports\\.test'; } else { return extension === 'mjs' ? 'Module\\.test' : 'Module\\.exports\\.test'; } @@ -194,7 +195,7 @@ async function compareStackTrace(sourceMap, source, expected) { fs.writeFileSync(`.generated-${id}-separate.${extension}`, `${header}${namedExportDeclaration()} = function() {` + source.join('\n') + `};//@ sourceMappingURL=.generated-${id}-separate.${extension}.map`); try { - (await import(`./.generated-${id}-separate.${extension}`)).test(); + await (await import(`./.generated-${id}-separate.${extension}`)).test(); } catch (e) { compareLines(e.stack.split(/\r\n|\n/), rewriteExpectation(expected, `.generated-${id}`, `.generated-${id}-separate`)); } @@ -204,7 +205,7 @@ async function compareStackTrace(sourceMap, source, expected) { source.join('\n') + '};//@ sourceMappingURL=data:application/json;base64,' + bufferFrom(sourceMap.toString()).toString('base64')); try { - (await import (`./.generated-${id}-inline.${extension}`)).test(); + await (await import (`./.generated-${id}-inline.${extension}`)).test(); } catch (e) { compareLines(e.stack.split(/\r\n|\n/), rewriteExpectation(expected, `.generated-${id}`, `.generated-${id}-inline`)); } @@ -239,6 +240,8 @@ function installSms() { emptyCacheBetweenOperations: true // Needed to be able to test for failure }); } +const installSmsOnce = once(installSms); + function getTestMacros(sourceMapConstructors) { return {normalThrow, normalThrowWithoutSourceMapSupportInstalled}; @@ -255,7 +258,7 @@ async function normalThrowWithoutSourceMapSupportInstalled() { 'throw new Error("test");' ], [ 'Error: test', - re`^ at ${stackFrameAtTest(false)} \(${stackFramePathStartsWith()}(?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$` + re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?\.generated-${id}\.${extension}:1:123\)$` ]); } } @@ -318,15 +321,14 @@ describe('sourcemap style: file urls with absolute paths and sourceRoot removed, function tests(sourceMapPostprocessor) { // let createEmptySourceMap, createMultiLineSourceMap, createMultiLineSourceMapWithSourcesContent, createSecondLineSourceMap, createSingleLineSourceMap, createSourceMapWithGap}) - const sourceMapConstructors = sourceMapCreators(); - for(const [key, value] of Object.entries(sourceMapConstructors)) { - sourceMapConstructors[key] = (...args) => sourceMapPostprocessor(value(...args)); - } + const sourceMapConstructors = mapValues(sourceMapCreators(), v => flow(v, sourceMapPostprocessor)); const {createEmptySourceMap, createMultiLineSourceMap, createMultiLineSourceMapWithSourcesContent, createSecondLineSourceMap, createSingleLineSourceMap, createSourceMapWithGap} = sourceMapConstructors; const {normalThrow} = getTestMacros(sourceMapConstructors); + // Run as a hook to ensure it runs even when we execute a subset of tests + before(installSmsOnce); + it('normal throw', async function() { - installSms(); await normalThrow(); }); @@ -459,6 +461,18 @@ it('function constructor', async function() { ]); }); +it('async stack frames', async function() { + await compareStackTrace(createMultiLineSourceMap(), [ + 'async function foo() { await bar(); }', + 'async function bar() { await null; throw new Error("test"); }', + 'return foo()' + ], [ + 'Error: test', + re`^ at bar \(${stackFramePathStartsWith()}(?:.*[/\\])?line2.js:1002:102\)$`, + re`^ at async foo \(${stackFramePathStartsWith()}(?:.*[/\\])?line1.js:1001:101\)$` + ]); +}); + it('throw with empty source map', async function() { await compareStackTrace(createEmptySourceMap(), [ 'throw new Error("test");' @@ -861,6 +875,7 @@ it('supports multiple instances', function(done) { } describe('redirects require() of "source-map-support" to this module', function() { + before(installSmsOnce); it('redirects', async function() { assert.strictEqual(require.resolve('source-map-support'), require.resolve('.')); assert.strictEqual(require.resolve('source-map-support/register'), require.resolve('./register')); @@ -898,6 +913,7 @@ describe('uninstall', function() { const sourceMapConstructors = sourceMapCreators(); const {normalThrow, normalThrowWithoutSourceMapSupportInstalled} = getTestMacros(sourceMapConstructors); + before(installSmsOnce); this.beforeEach(function() { underTest.uninstall(); process.emit = priorProcessEmit; From fc3f549213ea8fde85d3a47a8b8204e7e1f705b4 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 29 Apr 2022 18:08:39 -0400 Subject: [PATCH 14/24] WIP --- source-map-support.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/source-map-support.js b/source-map-support.js index b4280f8..a1d89b7 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -138,6 +138,17 @@ function tryFileURLToPath(v) { return v; } +// TODO un-copy these from resolve-uri; see if they can be exported from that lib +function isAbsoluteUrl(input) { + return schemeRegex.test(input); +} +// Matches the scheme of a URL, eg "http://" +const schemeRegex = /^[\w+.-]+:\/\//; +function isSchemeRelativeUrl(input) { + return input.startsWith('//'); +} + + // #region Caches /** @param {string} pathOrFileUrl */ function getCacheKey(pathOrFileUrl) { @@ -235,16 +246,6 @@ sharedData.internalRetrieveFileHandlers.push(function(path) { return setFileContentsCache(path, contents); }); -// TODO un-copy these from resolve-uri; see if they can be exported from that lib -function isAbsoluteUrl(input) { - return schemeRegex.test(input); -} -// Matches the scheme of a URL, eg "http://" -const schemeRegex = /^[\w+.-]+:\/\//; -function isSchemeRelativeUrl(input) { - return input.startsWith('//'); -} - // Support URLs relative to a directory, but be careful about a protocol prefix // in case we are in the browser (i.e. directories may start with "http://" or "file:///") function supportRelativeURL(file, url) { From 6d3de7e665067a143d928bb0bba14b53c6b03a83 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 29 Apr 2022 18:13:29 -0400 Subject: [PATCH 15/24] WIP --- source-map-support.js | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/source-map-support.js b/source-map-support.js index a1d89b7..a7e666b 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -132,13 +132,16 @@ function hasGlobalProcessEventEmitter() { function tryFileURLToPath(v) { // TODO technically, file URL can omit /s. // Copy the isFileURL util from resolve-uri? - if(v.startsWith('file:/')) { + if(isFileUrl(v)) { return fileURLToPath(v); } return v; } // TODO un-copy these from resolve-uri; see if they can be exported from that lib +function isFileUrl(input) { + return input.startsWith('file:'); +} function isAbsoluteUrl(input) { return schemeRegex.test(input); } @@ -148,7 +151,6 @@ function isSchemeRelativeUrl(input) { return input.startsWith('//'); } - // #region Caches /** @param {string} pathOrFileUrl */ function getCacheKey(pathOrFileUrl) { @@ -252,9 +254,14 @@ function supportRelativeURL(file, url) { // We want to preserve path style. // resolveUri cannot handle windows paths. // Therefore, special-case when output will be a windows path - if(process.platform === 'win32' && path.isAbsolute(file) && !isAbsoluteUrl(url) && !isSchemeRelativeUrl(url)) { - const dir = path.dirname(file); - return path.resolve(dir, url); + if(process.platform === 'win32') { + if(path.isAbsolute(file) && !isAbsoluteUrl(url) && !isSchemeRelativeUrl(url)) { + const dir = path.dirname(file); + return path.resolve(dir, url); + } + // if(isFileUrl(file) && path.isAbsolute(url)) { + // url = pathToFileURL(url).toString(); + // } } return resolveUri(url, file); } @@ -335,14 +342,17 @@ function mapSourcePosition(position) { map: new AnyMap(urlAndMap.map, urlAndMap.url) }); + // Overwrite trace-mapping's resolutions, because they do not handle + // Windows paths the way we want. + sourceMap.map.resolvedSources = sourceMap.map.sources.map(s => supportRelativeURL(sourceMap.url, s)); + // Load all sources stored inline with the source map into the file cache // to pretend like they are already loaded. They may not exist on disk. if (sourceMap.map.sourcesContent) { - sourceMap.map.sources.forEach(function(source, i) { + sourceMap.map.resolvedSources.forEach(function(resolvedSource, i) { var contents = sourceMap.map.sourcesContent[i]; if (contents) { - var url = supportRelativeURL(sourceMap.url, source); - setFileContentsCache(url, contents); + setFileContentsCache(resolvedSource, contents); } }); } From e9adffcd1f0f697382e853d84bf5a8f40f30dfd1 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Sat, 30 Apr 2022 18:18:22 -0400 Subject: [PATCH 16/24] add tests for async stack frames: async, Promise.all, Promise.any --- test.js | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/test.js b/test.js index 8fabc4f..b22144c 100644 --- a/test.js +++ b/test.js @@ -461,14 +461,48 @@ it('function constructor', async function() { ]); }); -it('async stack frames', async function() { +it('Verify node does not support Promise.allSettled stack frames. When this test starts breaking, we need to start testing for Promise.allSettled.', async () => { + // results1/driver1 is not strictly necessary to test allSettled; it is here to remind myself how to correctly get Promise.* methods to appear in a stack trace. + let result1; + await driver1().catch(e => result1 = e); + assert.match(result1.stack, /\bPromise\.all\b/); + + // Copied from V8 tests: https://github.com/v8/v8/commit/89ed081c176e286f9d65f3821d43f568cd56a035#diff-1a0a032688d7546dcfe5730eaab2854fb1b8dab656d8bb940dffc71b7b975aae + async function fine() { } + async function thrower() { + await fine(); + throw new Error(); + } + async function driver1() { + return await Promise.all([fine(), fine(), thrower(), thrower()]); + } + async function driver2() { + return await Promise.allSettled([fine(), fine(), thrower(), thrower()]); + } + + const results2 = await driver2(); + assert.equal(results2[2].status, 'rejected'); + assert.doesNotMatch(results2[2].reason.stack, /\bPromise\.allSettled\b/); +}); + +it('async stack frames: async, Promise.all, Promise.any'/*Promise.allSettled*/, async function() { await compareStackTrace(createMultiLineSourceMap(), [ - 'async function foo() { await bar(); }', - 'async function bar() { await null; throw new Error("test"); }', - 'return foo()' + // Add once node upgrades to v8 10.2, where this was added: https://github.com/v8/v8/commit/89ed081c176e286f9d65f3821d43f568cd56a035 + // 'async function foo() { return await bar(); }', + // 'async function bar() { return await Promise.allSettled([baz()]) }', + + 'async function foo() { return await baz(); }', + 'async function baz() { await Promise.all([biff()]) }', + 'async function biff() { await Promise.any([larry()]); }', + 'async function larry() { await null; throw new Error("test"); }', + 'return foo().catch(e => { throw e.errors[0] })' ], [ 'Error: test', - re`^ at bar \(${stackFramePathStartsWith()}(?:.*[/\\])?line2.js:1002:102\)$`, + re`^ at larry \(${stackFramePathStartsWith()}(?:.*[/\\])?line4.js:1004:104\)$`, + re`^ at async Promise\.any \(index 0\)$`, + re`^ at async biff \(${stackFramePathStartsWith()}(?:.*[/\\])?line3.js:1003:103\)$`, + re`^ at async Promise\.all \(index 0\)$`, + re`^ at async baz \(${stackFramePathStartsWith()}(?:.*[/\\])?line2.js:1002:102\)$`, re`^ at async foo \(${stackFramePathStartsWith()}(?:.*[/\\])?line1.js:1001:101\)$` ]); }); From 727cbe63cd213423213beca951d69c7c476b908f Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Sat, 30 Apr 2022 18:30:13 -0400 Subject: [PATCH 17/24] fix test --- test.js | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/test.js b/test.js index b22144c..c8b75c7 100644 --- a/test.js +++ b/test.js @@ -485,24 +485,43 @@ it('Verify node does not support Promise.allSettled stack frames. When this test assert.doesNotMatch(results2[2].reason.stack, /\bPromise\.allSettled\b/); }); -it('async stack frames: async, Promise.all, Promise.any'/*Promise.allSettled*/, async function() { +it('async stack frames: async, Promise.all'/*Promise.allSettled*/, async function() { await compareStackTrace(createMultiLineSourceMap(), [ // Add once node upgrades to v8 10.2, where this was added: https://github.com/v8/v8/commit/89ed081c176e286f9d65f3821d43f568cd56a035 // 'async function foo() { return await bar(); }', // 'async function bar() { return await Promise.allSettled([baz()]) }', - 'async function foo() { return await baz(); }', - 'async function baz() { await Promise.all([biff()]) }', - 'async function biff() { await Promise.any([larry()]); }', - 'async function larry() { await null; throw new Error("test"); }', + 'async function foo() { return await bar(); }', + 'async function bar() { await Promise.all([baz()]) }', + 'async function baz() { await null; throw new Error("test"); }', + 'return foo();' + ], [ + 'Error: test', + re`^ at baz \(${stackFramePathStartsWith()}(?:.*[/\\])?line3.js:1003:103\)$`, + re`^ at async Promise\.all \(index 0\)$`, + re`^ at async bar \(${stackFramePathStartsWith()}(?:.*[/\\])?line2.js:1002:102\)$`, + re`^ at async foo \(${stackFramePathStartsWith()}(?:.*[/\\])?line1.js:1001:101\)$` + ]); +}); + +it('async stack frames: Promise.any', async function() { + // node 14 and older does not have Promise.any + if(semver.lt(process.versions.node, '16.0.0')) return this.skip(); + + await compareStackTrace(createMultiLineSourceMap(), [ + // Add once node upgrades to v8 10.2, where this was added: https://github.com/v8/v8/commit/89ed081c176e286f9d65f3821d43f568cd56a035 + // 'async function foo() { return await bar(); }', + // 'async function bar() { return await Promise.allSettled([baz()]) }', + + 'async function foo() { return await bar(); }', + 'async function bar() { await Promise.any([baz()]); }', + 'async function baz() { await null; throw new Error("test"); }', 'return foo().catch(e => { throw e.errors[0] })' ], [ 'Error: test', - re`^ at larry \(${stackFramePathStartsWith()}(?:.*[/\\])?line4.js:1004:104\)$`, + re`^ at baz \(${stackFramePathStartsWith()}(?:.*[/\\])?line3.js:1003:103\)$`, re`^ at async Promise\.any \(index 0\)$`, - re`^ at async biff \(${stackFramePathStartsWith()}(?:.*[/\\])?line3.js:1003:103\)$`, - re`^ at async Promise\.all \(index 0\)$`, - re`^ at async baz \(${stackFramePathStartsWith()}(?:.*[/\\])?line2.js:1002:102\)$`, + re`^ at async bar \(${stackFramePathStartsWith()}(?:.*[/\\])?line2.js:1002:102\)$`, re`^ at async foo \(${stackFramePathStartsWith()}(?:.*[/\\])?line1.js:1001:101\)$` ]); }); From da3e63f2da0cb28ca77a992a19a81455078c4893 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Sun, 1 May 2022 00:31:29 -0400 Subject: [PATCH 18/24] add tests and support for wasm stack frames --- source-map-support.js | 6 ++++++ test-fixtures/wasm/README.md | 12 ++++++++++++ test-fixtures/wasm/wasm.js | 14 ++++++++++++++ test-fixtures/wasm/wasm.wasm | Bin 0 -> 89 bytes test-fixtures/wasm/wasm.wat | 6 ++++++ test.js | 11 +++++++++++ 6 files changed, 49 insertions(+) create mode 100644 test-fixtures/wasm/README.md create mode 100644 test-fixtures/wasm/wasm.js create mode 100644 test-fixtures/wasm/wasm.wasm create mode 100644 test-fixtures/wasm/wasm.wat diff --git a/source-map-support.js b/source-map-support.js index a7e666b..f9de253 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -520,6 +520,12 @@ function wrapCallSite(frame, state) { // from getScriptNameOrSourceURL() instead var source = frame.getFileName() || frame.getScriptNameOrSourceURL(); if (source) { + // v8 does not expose its internal isWasm, etc methods, so we do this instead. + if(source.startsWith('wasm://')) { + state.curPosition = null; + return frame; + } + var line = frame.getLineNumber(); var column = frame.getColumnNumber() - 1; diff --git a/test-fixtures/wasm/README.md b/test-fixtures/wasm/README.md new file mode 100644 index 0000000..4d6e5cf --- /dev/null +++ b/test-fixtures/wasm/README.md @@ -0,0 +1,12 @@ +To test support for WASM stack traces, we have a tiny WASM module that we call +into. + +It imports a JS function and exports a WASM function +that, when called, will call the JS function. + +When we call the wasm function, it calls back into JS. We can throw an error +and know that one of the stack frames will be wasm. + +The module is described in both text and binary formats. Compilation from text +to binary format was done using an online tool. I didn't bother to set up a +build script, opting instead ot store the binary in version control. diff --git a/test-fixtures/wasm/wasm.js b/test-fixtures/wasm/wasm.js new file mode 100644 index 0000000..602230c --- /dev/null +++ b/test-fixtures/wasm/wasm.js @@ -0,0 +1,14 @@ +const fs = require('fs'); +const path = require('path'); + +exports.call_js_function = async function(fn) { + const mod = await WebAssembly.instantiate( + fs.readFileSync(path.resolve(__dirname, 'wasm.wasm')), + { + jsapi: { + fn + } + } + ); + mod.instance.exports.call_js_function(); +} diff --git a/test-fixtures/wasm/wasm.wasm b/test-fixtures/wasm/wasm.wasm new file mode 100644 index 0000000000000000000000000000000000000000..f43ee87642d1c2500de8b5038ef533681b2feb82 GIT binary patch literal 89 zcmW;E%ML&=5CzbCM?Hc?7dF1GCXFUUhot;J@d4*ROC|t1E`W@z_oCM($$(0TcA}4Z g9APgZJr?inG_Y^z;4xr!rg`N+biWJ9q?WJ43*`zB(EtDd literal 0 HcmV?d00001 diff --git a/test-fixtures/wasm/wasm.wat b/test-fixtures/wasm/wasm.wat new file mode 100644 index 0000000..55e12ea --- /dev/null +++ b/test-fixtures/wasm/wasm.wat @@ -0,0 +1,6 @@ +(module + (import "jsapi" "fn" (func $jsapi_fn)) + (func (export "call_js_function") + call $jspapi_fn + ) +) diff --git a/test.js b/test.js index c8b75c7..2151df2 100644 --- a/test.js +++ b/test.js @@ -526,6 +526,17 @@ it('async stack frames: Promise.any', async function() { ]); }); +it('wasm stack frames', async function() { + await compareStackTrace(createMultiLineSourceMap(), [ + 'return require("./test-fixtures/wasm/wasm.js").call_js_function(() => { throw new Error("test"); });' + ], [ + 'Error: test', + re`^ at ${stackFramePathStartsWith()}(?:.*[/\\])?line1.js:1001:101$`, + re`^ at wasm:\/\/wasm\/c2de0ab2:wasm-function\[1\]:0x3b$`, + re`^ at Object\.exports\.call_js_function \(.*[/\\]wasm\.js:13:24\)$`, + ]); +}); + it('throw with empty source map', async function() { await compareStackTrace(createEmptySourceMap(), [ 'throw new Error("test");' From 3a68f099f3f51f22915e5c54f266626090a7396c Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Sun, 1 May 2022 00:43:42 -0400 Subject: [PATCH 19/24] fix tests on node 12 and 14 --- test.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test.js b/test.js index 2151df2..2571a53 100644 --- a/test.js +++ b/test.js @@ -527,12 +527,19 @@ it('async stack frames: Promise.any', async function() { }); it('wasm stack frames', async function() { + const wasmFrame = semver.gte(process.versions.node, '16.0.0') + ? String.raw`wasm:\/\/wasm\/c2de0ab2:wasm-function\[1\]:0x3b` + : semver.gte(process.versions.node, '14.0.0') + ? String.raw`call_js_function \(:wasm-function\[1\]:0x3b\)` + // Node 12 + : String.raw`wasm-function\[1\]:0x3b`; + await compareStackTrace(createMultiLineSourceMap(), [ 'return require("./test-fixtures/wasm/wasm.js").call_js_function(() => { throw new Error("test"); });' ], [ 'Error: test', re`^ at ${stackFramePathStartsWith()}(?:.*[/\\])?line1.js:1001:101$`, - re`^ at wasm:\/\/wasm\/c2de0ab2:wasm-function\[1\]:0x3b$`, + re`^ at ${wasmFrame}$`, re`^ at Object\.exports\.call_js_function \(.*[/\\]wasm\.js:13:24\)$`, ]); }); From 69893db3b2d686dad66d0c906dce635134232b26 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Sun, 1 May 2022 01:14:27 -0400 Subject: [PATCH 20/24] fix? --- source-map-support.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source-map-support.js b/source-map-support.js index f9de253..7cbec40 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -259,9 +259,9 @@ function supportRelativeURL(file, url) { const dir = path.dirname(file); return path.resolve(dir, url); } - // if(isFileUrl(file) && path.isAbsolute(url)) { - // url = pathToFileURL(url).toString(); - // } + if(isFileUrl(file) && path.isAbsolute(url)) { + url = pathToFileURL(url).toString(); + } } return resolveUri(url, file); } From e15066193d12993c1d7bfd78edb53650c2f496e3 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Sun, 1 May 2022 13:22:55 -0400 Subject: [PATCH 21/24] fix --- package-lock.json | 1 - package.json | 1 - source-map-support.js | 53 ++++++++++++++++++++++++++++++++++--------- test.js | 3 +++ 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 029491c..df9d050 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "0.7.0", "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.6", "@jridgewell/trace-mapping": "0.3.9" }, "devDependencies": { diff --git a/package.json b/package.json index 8287437..6e5dfa3 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "/source-map-support.js" ], "dependencies": { - "@jridgewell/resolve-uri": "^3.0.6", "@jridgewell/trace-mapping": "0.3.9" }, "devDependencies": { diff --git a/source-map-support.js b/source-map-support.js index 7cbec40..fbefe6a 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -1,5 +1,4 @@ const { TraceMap, originalPositionFor, AnyMap } = require('@jridgewell/trace-mapping'); -const resolveUri = require('@jridgewell/resolve-uri'); var path = require('path'); const { fileURLToPath, pathToFileURL } = require('url'); var util = require('util'); @@ -251,19 +250,51 @@ sharedData.internalRetrieveFileHandlers.push(function(path) { // Support URLs relative to a directory, but be careful about a protocol prefix // in case we are in the browser (i.e. directories may start with "http://" or "file:///") function supportRelativeURL(file, url) { - // We want to preserve path style. - // resolveUri cannot handle windows paths. - // Therefore, special-case when output will be a windows path - if(process.platform === 'win32') { - if(path.isAbsolute(file) && !isAbsoluteUrl(url) && !isSchemeRelativeUrl(url)) { - const dir = path.dirname(file); - return path.resolve(dir, url); + if(!file) return url; + // given that this happens within error formatting codepath, probably best to + // fallback instead of throwing if anything goes wrong + try { + // if should output a URL + if(isAbsoluteUrl(file) || isSchemeRelativeUrl(file)) { + if(isAbsoluteUrl(url) || isSchemeRelativeUrl(url)) { + return new URL(url, file).toString(); + } + if(path.isAbsolute(url)) { + return new URL(pathToFileURL(url), file).toString(); + } + // url is relative path or URL + return new URL(url.replace(/\\/g, '/'), file).toString(); + } + // if should output a path (unless URL is something like https://) + if(path.isAbsolute(file)) { + if(isFileUrl(url)) { + return fileURLToPath(url); + } + if(isSchemeRelativeUrl(url)) { + return fileURLToPath(new URL(url, 'file://')); + } + if(isAbsoluteUrl(url)) { + // url is a non-file URL + // Go with the URL + return url; + } + if(path.isAbsolute(url)) { + return url; + // Normalize at all? decodeURI or normalize slashes? + } + // url is relative path or URL + return path.join(file, '..', decodeURI(url)); } - if(isFileUrl(file) && path.isAbsolute(url)) { - url = pathToFileURL(url).toString(); + // If we get here, file is relative. + // Shouldn't happen since node identifies modules with absolute paths or URLs. + // But we can take a stab at returning something meaningful anyway. + if(isAbsoluteUrl(url) || isSchemeRelativeUrl(url)) { + return url; } + return path.join(file, '..', url); + } catch(e) { + return url; } - return resolveUri(url, file); } function retrieveSourceMapURL(source) { diff --git a/test.js b/test.js index 2571a53..aa9bc97 100644 --- a/test.js +++ b/test.js @@ -1072,3 +1072,6 @@ describe('uninstall', function() { assert(peInvocations >= 1); }); }); +// Without this, the code under test sees stuff in the test cases above and tries to load source-maps +// This causes confusing red herrings while debugging. +//#sourceMappingURL=test.js.map-intentionally-does-not-exist \ No newline at end of file From 944a35d7c67df45afa3cf3076148017528bf69ba Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Sun, 1 May 2022 13:53:13 -0400 Subject: [PATCH 22/24] fix --- source-map-support.js | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/source-map-support.js b/source-map-support.js index fbefe6a..902994a 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -279,8 +279,8 @@ function supportRelativeURL(file, url) { return url; } if(path.isAbsolute(url)) { - return url; // Normalize at all? decodeURI or normalize slashes? + return path.normalize(url); } // url is relative path or URL return path.join(file, '..', decodeURI(url)); @@ -297,6 +297,23 @@ function supportRelativeURL(file, url) { } } +// Return pathOrUrl in the same style as matchStyleOf: either a file URL or a native path +function matchStyleOfPathOrUrl(matchStyleOf, pathOrUrl) { + try { + if(isAbsoluteUrl(matchStyleOf) || isSchemeRelativeUrl(matchStyleOf)) { + if(isAbsoluteUrl(pathOrUrl) || isSchemeRelativeUrl(pathOrUrl)) return pathOrUrl; + if(path.isAbsolute(pathOrUrl)) return pathToFileURL(pathOrUrl).toString(); + } else if(path.isAbsolute(matchStyleOf)) { + if(isAbsoluteUrl(pathOrUrl) || isSchemeRelativeUrl(pathOrUrl)) { + return fileURLToPath(new URL(pathOrUrl, 'file://')); + } + } + return pathOrUrl; + } catch(e) { + return pathOrUrl; + } +} + function retrieveSourceMapURL(source) { var fileData; @@ -375,6 +392,7 @@ function mapSourcePosition(position) { // Overwrite trace-mapping's resolutions, because they do not handle // Windows paths the way we want. + // TODO Remove now that windows path support was added to resolve-uri and thus trace-mapping? sourceMap.map.resolvedSources = sourceMap.map.sources.map(s => supportRelativeURL(sourceMap.url, s)); // Load all sources stored inline with the source map into the file cache @@ -405,8 +423,11 @@ function mapSourcePosition(position) { // better to give a precise location in the compiled file than a vague // location in the original file. if (originalPosition.source !== null) { - originalPosition.source = supportRelativeURL( - sourceMap.url, originalPosition.source); + // originalPosition.source has *already* been resolved against sourceMap.url + // so is *already* as absolute as possible. + // However, we want to ensure we output in same format as input: URL or native path + originalPosition.source = matchStyleOfPathOrUrl( + position.source, originalPosition.source); return originalPosition; } } From 50c71ddf1ff64d41e1f6bd6faca3757f6156e980 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Sun, 1 May 2022 14:30:37 -0400 Subject: [PATCH 23/24] re-add browser-source-map-support so that legacy stuff that tries to get it from us will work --- browser-source-map-support.js | 208 +++++++++++++++++----------------- package.json | 3 +- 2 files changed, 106 insertions(+), 105 deletions(-) diff --git a/browser-source-map-support.js b/browser-source-map-support.js index 37ac188..782da50 100644 --- a/browser-source-map-support.js +++ b/browser-source-map-support.js @@ -8,107 +8,107 @@ @author Feross Aboukhadijeh license MIT */ -(this.define||function(G,J){this.sourceMapSupport=J()})("browser-source-map-support",function(G){(function b(n,x,m){function e(d,a){if(!x[d]){if(!n[d]){var l="function"==typeof require&&require;if(!a&&l)return l(d,!0);if(g)return g(d,!0);throw Error("Cannot find module '"+d+"'");}l=x[d]={exports:{}};n[d][0].call(l.exports,function(a){var b=n[d][1][a];return e(b?b:a)},l,l.exports,b,n,x,m)}return x[d].exports}for(var g="function"==typeof require&&require,h=0;hb)return-1;if(58>b)return b-48+52;if(91>b)return b-65;if(123>b)return b-97+26}var g="undefined"!==typeof Uint8Array?Uint8Array:Array;b.toByteArray=function(b){function d(a){r[v++]=a}if(0>16);d((h&65280)>>8);d(h&255)}2===l?(h=e(b.charAt(a))<<2|e(b.charAt(a+1))>>4,d(h&255)):1===l&&(h=e(b.charAt(a))<<10|e(b.charAt(a+1))<<4|e(b.charAt(a+2))>>2,d(h>>8&255),d(h&255));return r};b.fromByteArray=function(b){var d=b.length%3,a="",l;var e=0;for(l=b.length-d;e> -18&63)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(g>>12&63)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(g>>6&63)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(g&63);a+=g}switch(d){case 1:g=b[b.length-1];a+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(g>>2);a+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(g<<4&63);a+="==";break;case 2:g=(b[b.length-2]<<8)+ -b[b.length-1],a+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(g>>10),a+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(g>>4&63),a+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(g<<2&63),a+="="}return a}})("undefined"===typeof m?this.base64js={}:m)},{}],3:[function(n,x,m){},{}],4:[function(n,x,m){(function(b){var e=Object.prototype.toString,g="function"===typeof b.alloc&&"function"===typeof b.allocUnsafe&&"function"=== -typeof b.from;x.exports=function(h,d,a){if("number"===typeof h)throw new TypeError('"value" argument must not be a number');if("ArrayBuffer"===e.call(h).slice(8,-1)){d>>>=0;var l=h.byteLength-d;if(0>l)throw new RangeError("'offset' is out of bounds");if(void 0===a)a=l;else if(a>>>=0,a>l)throw new RangeError("'length' is out of bounds");return g?b.from(h.slice(d,d+a)):new b(new Uint8Array(h.slice(d,d+a)))}if("string"===typeof h){a=d;if("string"!==typeof a||""===a)a="utf8";if(!b.isEncoding(a))throw new TypeError('"encoding" must be a valid string encoding'); -return g?b.from(h,a):new b(h,a)}return g?b.from(h):new b(h)}}).call(this,n("buffer").Buffer)},{buffer:5}],5:[function(n,x,m){function b(f,p,a){if(!(this instanceof b))return new b(f,p,a);var c=typeof f;if("number"===c)var d=0>>0:0;else if("string"===c){if("base64"===p)for(f=(f.trim?f.trim():f.replace(/^\s+|\s+$/g,"")).replace(H,"");0!==f.length%4;)f+="=";d=b.byteLength(f,p)}else if("object"===c&&null!==f)"Buffer"===f.type&&F(f.data)&&(f=f.data),d=0<+f.length?Math.floor(+f.length):0;else throw new TypeError("must start with number, buffer, array or string"); -if(this.length>D)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+D.toString(16)+" bytes");if(b.TYPED_ARRAY_SUPPORT)var k=b._augment(new Uint8Array(d));else k=this,k.length=d,k._isBuffer=!0;if(b.TYPED_ARRAY_SUPPORT&&"number"===typeof f.byteLength)k._set(f);else{var C=f;if(F(C)||b.isBuffer(C)||C&&"object"===typeof C&&"number"===typeof C.length)if(b.isBuffer(f))for(p=0;pf)throw new RangeError("offset is not uint");if(f+p>b)throw new RangeError("Trying to access beyond buffer length");}function h(f,p,a,c,d,k){if(!b.isBuffer(f))throw new TypeError("buffer must be a Buffer instance");if(p>d||pf.length)throw new TypeError("index out of range"); -}function d(f,p,b,a){0>p&&(p=65535+p+1);for(var c=0,d=Math.min(f.length-b,2);c>>8*(a?c:1-c)}function a(f,p,b,a){0>p&&(p=4294967295+p+1);for(var c=0,d=Math.min(f.length-b,4);c>>8*(a?c:3-c)&255}function l(f,p,b,a,c,d){if(p>c||pf.length)throw new TypeError("index out of range");}function r(f,p,b,a,c){c||l(f,p,b,4,3.4028234663852886E38,-3.4028234663852886E38);y.write(f,p,b,a,23,4);return b+4}function q(f, -p,b,a,c){c||l(f,p,b,8,1.7976931348623157E308,-1.7976931348623157E308);y.write(f,p,b,a,52,8);return b+8}function v(f){for(var p=[],b=0;b=a)p.push(a);else{var c=b;55296<=a&&57343>=a&&b++;a=encodeURIComponent(f.slice(c,b+1)).substr(1).split("%");for(c=0;c=b.length||d>=f.length);d++)b[d+ -a]=f[d];return d}function k(f){try{return decodeURIComponent(f)}catch(p){return String.fromCharCode(65533)}}var w=n("base64-js"),y=n("ieee754"),F=n("is-array");m.Buffer=b;m.SlowBuffer=b;m.INSPECT_MAX_BYTES=50;b.poolSize=8192;var D=1073741823;b.TYPED_ARRAY_SUPPORT=function(){try{var f=new ArrayBuffer(0),b=new Uint8Array(f);b.foo=function(){return 42};return 42===b.foo()&&"function"===typeof b.subarray&&0===(new Uint8Array(1)).subarray(1,1).byteLength}catch(C){return!1}}();b.isBuffer=function(f){return!(null== -f||!f._isBuffer)};b.compare=function(f,a){if(!b.isBuffer(f)||!b.isBuffer(a))throw new TypeError("Arguments must be Buffers");for(var c=f.length,p=a.length,d=0,k=Math.min(c,p);d>>1;break;case "utf8":case "utf-8":b=v(f).length;break;case "base64":b=w.toByteArray(f).length; -break;default:b=f.length}return b};b.prototype.length=void 0;b.prototype.parent=void 0;b.prototype.toString=function(f,b,a){var c=!1;b>>>=0;a=void 0===a||Infinity===a?this.length:a>>>0;f||(f="utf8");0>b&&(b=0);a>this.length&&(a=this.length);if(a<=b)return"";for(;;)switch(f){case "hex":f=b;b=a;a=this.length;if(!f||0>f)f=0;if(!b||0>b||b>a)b=a;c="";for(a=f;ac?"0"+c.toString(16):c.toString(16),c=f+c;return c;case "utf8":case "utf-8":c=f="";for(a=Math.min(this.length,a);b= -this[b]?(f+=k(c)+String.fromCharCode(this[b]),c=""):c+="%"+this[b].toString(16);return f+k(c);case "ascii":return e(this,b,a);case "binary":return e(this,b,a);case "base64":return b=0===b&&a===this.length?w.fromByteArray(this):w.fromByteArray(this.slice(b,a)),b;case "ucs2":case "ucs-2":case "utf16le":case "utf-16le":b=this.slice(b,a);a="";for(f=0;fb&&(f+=" ... "));return""};b.prototype.compare=function(f){if(!b.isBuffer(f))throw new TypeError("Argument must be a Buffer");return b.compare(this,f)};b.prototype.get=function(f){console.log(".get() is deprecated. Access using array indexes instead."); -return this.readUInt8(f)};b.prototype.set=function(f,b){console.log(".set() is deprecated. Access using array indexes instead.");return this.writeUInt8(f,b)};b.prototype.write=function(f,b,a,d){if(isFinite(b))isFinite(a)||(d=a,a=void 0);else{var p=d;d=b;b=a;a=p}b=Number(b)||0;p=this.length-b;a?(a=Number(a),a>p&&(a=p)):a=p;d=String(d||"utf8").toLowerCase();switch(d){case "hex":b=Number(b)||0;d=this.length-b;a?(a=Number(a),a>d&&(a=d)):a=d;d=f.length;if(0!==d%2)throw Error("Invalid hex string");a>d/ -2&&(a=d/2);for(d=0;d>8;l%=256;p.push(l);p.push(d)}f=c(p,this,b,a,2);break;default:throw new TypeError("Unknown encoding: "+ -d);}return f};b.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};b.prototype.slice=function(f,a){var c=this.length;f=~~f;a=void 0===a?c:~~a;0>f?(f+=c,0>f&&(f=0)):f>c&&(f=c);0>a?(a+=c,0>a&&(a=0)):a>c&&(a=c);a>>=0;d||h(this,a,c,1,255,0);b.TYPED_ARRAY_SUPPORT||(a=Math.floor(a));this[c]=a;return c+1};b.prototype.writeUInt16LE=function(a, -c,k){a=+a;c>>>=0;k||h(this,a,c,2,65535,0);b.TYPED_ARRAY_SUPPORT?(this[c]=a,this[c+1]=a>>>8):d(this,a,c,!0);return c+2};b.prototype.writeUInt16BE=function(a,c,k){a=+a;c>>>=0;k||h(this,a,c,2,65535,0);b.TYPED_ARRAY_SUPPORT?(this[c]=a>>>8,this[c+1]=a):d(this,a,c,!1);return c+2};b.prototype.writeUInt32LE=function(f,c,d){f=+f;c>>>=0;d||h(this,f,c,4,4294967295,0);b.TYPED_ARRAY_SUPPORT?(this[c+3]=f>>>24,this[c+2]=f>>>16,this[c+1]=f>>>8,this[c]=f):a(this,f,c,!0);return c+4};b.prototype.writeUInt32BE=function(f, -c,d){f=+f;c>>>=0;d||h(this,f,c,4,4294967295,0);b.TYPED_ARRAY_SUPPORT?(this[c]=f>>>24,this[c+1]=f>>>16,this[c+2]=f>>>8,this[c+3]=f):a(this,f,c,!1);return c+4};b.prototype.writeInt8=function(a,c,d){a=+a;c>>>=0;d||h(this,a,c,1,127,-128);b.TYPED_ARRAY_SUPPORT||(a=Math.floor(a));0>a&&(a=255+a+1);this[c]=a;return c+1};b.prototype.writeInt16LE=function(a,c,k){a=+a;c>>>=0;k||h(this,a,c,2,32767,-32768);b.TYPED_ARRAY_SUPPORT?(this[c]=a,this[c+1]=a>>>8):d(this,a,c,!0);return c+2};b.prototype.writeInt16BE=function(a, -c,k){a=+a;c>>>=0;k||h(this,a,c,2,32767,-32768);b.TYPED_ARRAY_SUPPORT?(this[c]=a>>>8,this[c+1]=a):d(this,a,c,!1);return c+2};b.prototype.writeInt32LE=function(c,d,k){c=+c;d>>>=0;k||h(this,c,d,4,2147483647,-2147483648);b.TYPED_ARRAY_SUPPORT?(this[d]=c,this[d+1]=c>>>8,this[d+2]=c>>>16,this[d+3]=c>>>24):a(this,c,d,!0);return d+4};b.prototype.writeInt32BE=function(c,d,k){c=+c;d>>>=0;k||h(this,c,d,4,2147483647,-2147483648);0>c&&(c=4294967295+c+1);b.TYPED_ARRAY_SUPPORT?(this[d]=c>>>24,this[d+1]=c>>>16,this[d+ -2]=c>>>8,this[d+3]=c):a(this,c,d,!1);return d+4};b.prototype.writeFloatLE=function(a,c,b){return r(this,a,c,!0,b)};b.prototype.writeFloatBE=function(a,c,b){return r(this,a,c,!1,b)};b.prototype.writeDoubleLE=function(a,c,b){return q(this,a,c,!0,b)};b.prototype.writeDoubleBE=function(a,c,b){return q(this,a,c,!1,b)};b.prototype.copy=function(a,c,d,k){d||(d=0);k||0===k||(k=this.length);c||(c=0);if(k!==d&&0!==a.length&&0!==this.length){if(kc||c>=a.length)throw new TypeError("targetStart out of bounds"); -if(0>d||d>=this.length)throw new TypeError("sourceStart out of bounds");if(0>k||k>this.length)throw new TypeError("sourceEnd out of bounds");k>this.length&&(k=this.length);a.length-ck||!b.TYPED_ARRAY_SUPPORT)for(var f=0;fc||c>=this.length)throw new TypeError("start out of bounds"); -if(0>b||b>this.length)throw new TypeError("end out of bounds");if("number"===typeof a)for(;c>1,q=-7;d=g?d-1:0;var v=g?-1:1,u=b[e+d];d+=v;g=u&(1<<-q)-1;u>>=-q;for(q+=a;0>=-q;for(q+=h;0>1,u=23===d?Math.pow(2,-24)-Math.pow(2,-77):0;a=h?0:a-1;var c=h?1:-1,k=0>e||0===e&&0>1/e?1:0;e=Math.abs(e);isNaN(e)||Infinity===e?(e=isNaN(e)?1:0,h=q):(h=Math.floor(Math.log(e)/Math.LN2),1>e*(l=Math.pow(2,-h))&&(h--,l*=2),e=1<=h+v?e+u/l:e+u*Math.pow(2,1-v),2<=e*l&&(h++,l/=2),h+v>=q?(e=0,h=q):1<=h+v?(e=(e*l-1)*Math.pow(2,d),h+=v):(e=e*Math.pow(2,v-1)*Math.pow(2,d),h=0));for(;8<=d;b[g+a]=e&255,a+= -c,e/=256,d-=8);h=h<b?[]:a.slice(c,b-c+1)}a=m.resolve(a).substr(1);b=m.resolve(b).substr(1); -for(var l=d(a.split("/")),v=d(b.split("/")),e=Math.min(l.length,v.length),c=e,k=0;kb&&(b=a.length+b);return a.substr(b,d)}}).call(this,n("g5I+bs"))},{"g5I+bs":9}],9:[function(n,x,m){function b(){}n=x.exports={};n.nextTick=function(){if("undefined"!==typeof window&&window.setImmediate)return function(b){return window.setImmediate(b)};if("undefined"!==typeof window&&window.postMessage&&window.addEventListener){var b=[];window.addEventListener("message",function(e){var g=e.source;g!==window&&null!== -g||"process-tick"!==e.data||(e.stopPropagation(),0e?(-e<<1)+1:e<<1;do e=h&31,h>>>=5,0=d)throw Error("Expected more digits in base 64 VLQ value.");var r=b.decode(e.charCodeAt(g++));if(-1===r)throw Error("Invalid base64 digit: "+e.charAt(g-1));var q=!!(r&32);r&=31;a+=r<>1;h.value=1===(a&1)?-e:e;h.rest=g}},{"./base64":12}],12:[function(n, -x,m){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");m.encode=function(e){if(0<=e&&e=b?b-65:97<=b&&122>=b?b-97+26:48<=b&&57>=b?b-48+52:43==b?62:47==b?63:-1}},{}],13:[function(n,x,m){function b(e,g,h,d,a,l){var r=Math.floor((g-e)/2)+e,q=a(h,d[r],!0);return 0===q?r:0e?-1:e}m.GREATEST_LOWER_BOUND=1;m.LEAST_UPPER_BOUND=2;m.search=function(e,g,h,d){if(0===g.length)return-1;e=b(-1,g.length,e,g,h,d||m.GREATEST_LOWER_BOUND);if(0>e)return-1;for(;0<=e-1&&0===h(g[e],g[e-1],!0);)--e;return e}},{}],14:[function(n,x,m){function b(){this._array=[];this._sorted=!0;this._last={generatedLine:-1,generatedColumn:0}}var e=n("./util");b.prototype.unsortedForEach=function(b,e){this._array.forEach(b,e)};b.prototype.add=function(b){var g=this._last,d=g.generatedLine, -a=b.generatedLine,l=g.generatedColumn,r=b.generatedColumn;a>d||a==d&&r>=l||0>=e.compareByGeneratedPositionsInflated(g,b)?this._last=b:this._sorted=!1;this._array.push(b)};b.prototype.toArray=function(){this._sorted||(this._array.sort(e.compareByGeneratedPositionsInflated),this._sorted=!0);return this._array};m.MappingList=b},{"./util":19}],15:[function(n,x,m){function b(b,e,d){var a=b[e];b[e]=b[d];b[d]=a}function e(g,h,d,a){if(d=h(g[q],r)&&(l+=1,b(g,l,q));b(g,l+1,q);l+=1;e(g,h,d,l-1);e(g,h,l+1,a)}}m.quickSort=function(b,h){e(b,h,0,b.length-1)}},{}],16:[function(n,x,m){function b(a,b){var c=a;"string"===typeof a&&(c=d.parseSourceMapInput(a));return null!=c.sections?new h(c,b):new e(c,b)}function e(a,b){var c=a;"string"===typeof a&&(c=d.parseSourceMapInput(a));var k=d.getArg(c,"version"),e=d.getArg(c,"sources"),v=d.getArg(c,"names",[]),g=d.getArg(c,"sourceRoot",null),h=d.getArg(c,"sourcesContent",null),q=d.getArg(c, -"mappings");c=d.getArg(c,"file",null);if(k!=this._version)throw Error("Unsupported version: "+k);g&&(g=d.normalize(g));e=e.map(String).map(d.normalize).map(function(a){return g&&d.isAbsolute(g)&&d.isAbsolute(a)?d.relative(g,a):a});this._names=l.fromArray(v.map(String),!0);this._sources=l.fromArray(e,!0);this.sourceRoot=g;this.sourcesContent=h;this._mappings=q;this._sourceMapURL=b;this.file=c}function g(){this.generatedColumn=this.generatedLine=0;this.name=this.originalColumn=this.originalLine=this.source= -null}function h(a,e){var c=a;"string"===typeof a&&(c=d.parseSourceMapInput(a));var k=d.getArg(c,"version");c=d.getArg(c,"sections");if(k!=this._version)throw Error("Unsupported version: "+k);this._sources=new l;this._names=new l;var v={line:-1,column:0};this._sections=c.map(function(a){if(a.url)throw Error("Support for url field in sections not implemented.");var c=d.getArg(a,"offset"),k=d.getArg(c,"line"),g=d.getArg(c,"column");if(k=b[c])throw new TypeError("Line must be greater than or equal to 1, got "+ -b[c]);if(0>b[k])throw new TypeError("Column must be greater than or equal to 0, got "+b[k]);return a.search(b,d,e,g)};e.prototype.computeColumnSpans=function(){for(var a=0;a=this._sources.size()&&!this.sourcesContent.some(function(a){return null==a}):!1};e.prototype.sourceContentFor=function(a,b){if(!this.sourcesContent)return null;var c=a;null!=this.sourceRoot&&(c=d.relative(this.sourceRoot,c));if(this._sources.has(c))return this.sourcesContent[this._sources.indexOf(c)]; -var k=this.sources,e;for(e=0;e -b||95!==a.charCodeAt(b-1)||95!==a.charCodeAt(b-2)||111!==a.charCodeAt(b-3)||116!==a.charCodeAt(b-4)||111!==a.charCodeAt(b-5)||114!==a.charCodeAt(b-6)||112!==a.charCodeAt(b-7)||95!==a.charCodeAt(b-8)||95!==a.charCodeAt(b-9))return!1;for(b-=10;0<=b;b--)if(36!==a.charCodeAt(b))return!1;return!0}function q(a,b){return a===b?0:null===a?1:null===b?-1:a>b?1:-1}m.getArg=function(a,b,d){if(b in a)return a[b];if(3===arguments.length)return d;throw Error('"'+b+'" is a required argument.');};var v=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/, -u=/^data:.+,.+$/;m.urlParse=b;m.urlGenerate=e;m.normalize=g;m.join=h;m.isAbsolute=function(a){return"/"===a.charAt(0)||v.test(a)};m.relative=function(a,b){""===a&&(a=".");a=a.replace(/\/$/,"");for(var c=0;0!==b.indexOf(a+"/");){var d=a.lastIndexOf("/");if(0>d)return b;a=a.slice(0,d);if(a.match(/^([^\/]+:\/)?\/*$/))return b;++c}return Array(c+1).join("../")+b.substr(a.length+1)};n=!("__proto__"in Object.create(null));m.toSetString=n?d:a;m.fromSetString=n?d:l;m.compareByOriginalPositions=function(a, -b,d){var c=q(a.source,b.source);if(0!==c)return c;c=a.originalLine-b.originalLine;if(0!==c)return c;c=a.originalColumn-b.originalColumn;if(0!==c||d)return c;c=a.generatedColumn-b.generatedColumn;if(0!==c)return c;c=a.generatedLine-b.generatedLine;return 0!==c?c:q(a.name,b.name)};m.compareByGeneratedPositionsDeflated=function(a,b,d){var c=a.generatedLine-b.generatedLine;if(0!==c)return c;c=a.generatedColumn-b.generatedColumn;if(0!==c||d)return c;c=q(a.source,b.source);if(0!==c)return c;c=a.originalLine- -b.originalLine;if(0!==c)return c;c=a.originalColumn-b.originalColumn;return 0!==c?c:q(a.name,b.name)};m.compareByGeneratedPositionsInflated=function(a,b){var c=a.generatedLine-b.generatedLine;if(0!==c)return c;c=a.generatedColumn-b.generatedColumn;if(0!==c)return c;c=q(a.source,b.source);if(0!==c)return c;c=a.originalLine-b.originalLine;if(0!==c)return c;c=a.originalColumn-b.originalColumn;return 0!==c?c:q(a.name,b.name)};m.parseSourceMapInput=function(a){return JSON.parse(a.replace(/^\)]}'[^\n]*\n/, -""))};m.computeSourceURL=function(a,d,l){d=d||"";a&&("/"!==a[a.length-1]&&"/"!==d[0]&&(a+="/"),d=a+d);if(l){a=b(l);if(!a)throw Error("sourceMapURL could not be parsed");a.path&&(l=a.path.lastIndexOf("/"),0<=l&&(a.path=a.path.substring(0,l+1)));d=h(e(a),d)}return g(d)}},{}],20:[function(n,x,m){m.SourceMapGenerator=n("./lib/source-map-generator").SourceMapGenerator;m.SourceMapConsumer=n("./lib/source-map-consumer").SourceMapConsumer;m.SourceNode=n("./lib/source-node").SourceNode},{"./lib/source-map-consumer":16, -"./lib/source-map-generator":17,"./lib/source-node":18}],21:[function(n,x,m){(function(b){function e(){return"browser"===f?!0:"node"===f?!1:"undefined"!==typeof window&&"function"===typeof XMLHttpRequest&&!(window.require&&window.module&&window.process&&"renderer"===window.process.type)}function g(a){return function(b){for(var c=0;c";b=this.getLineNumber();null!=b&&(a+=":"+b,(b= -this.getColumnNumber())&&(a+=":"+b))}b="";var c=this.getFunctionName(),d=!0,e=this.isConstructor();if(this.isToplevel()||e)e?b+="new "+(c||""):c?b+=c:(b+=a,d=!1);else{e=this.getTypeName();"[object Object]"===e&&(e="null");var f=this.getMethodName();c?(e&&0!=c.indexOf(e)&&(b+=e+"."),b+=c,f&&c.indexOf("."+f)!=c.length-f.length-1&&(b+=" [as "+f+"]")):b+=e+"."+(f||"")}d&&(b+=" ("+a+")");return b}function r(a){var b={};Object.getOwnPropertyNames(Object.getPrototypeOf(a)).forEach(function(c){b[c]= -/^(?:is|get)/.test(c)?function(){return a[c].call(a)}:a[c]});b.toString=l;return b}function q(c,f){void 0===f&&(f={nextPosition:null,curPosition:null});if(c.isNative())return f.curPosition=null,c;var g=c.getFileName()||c.getScriptNameOrSourceURL();if(g){var h=c.getLineNumber(),k=c.getColumnNumber()-1,l=/^v(10\.1[6-9]|10\.[2-9][0-9]|10\.[0-9]{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/.test(b.version)?0:62;1===h&&k>l&&!e()&&!c.isEval()&&(k-=l);var m=d({source:g,line:h,column:k});f.curPosition=m;c=r(c);var p= -c.getFunctionName;c.getFunctionName=function(){return null==f.nextPosition?p():f.nextPosition.name||p()};c.getFileName=function(){return m.source};c.getLineNumber=function(){return m.line};c.getColumnNumber=function(){return m.column+1};c.getScriptNameOrSourceURL=function(){return m.source};return c}var n=c.isEval()&&c.getEvalOrigin();n&&(n=a(n),c=r(c),c.getEvalOrigin=function(){return n});return c}function v(a,b){H&&(p={},C={});for(var c=(a.name||"Error")+": "+(a.message||""),d={nextPosition:null, -curPosition:null},e=[],f=b.length-1;0<=f;f--)e.push("\n at "+q(b[f],d)),d.nextPosition=d.curPosition;d.curPosition=d.nextPosition=null;return c+e.reverse().join("")}function u(a){var b=/\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(a.stack);if(b){a=b[1];var c=+b[2];b=+b[3];var d=p[a];if(!d&&y&&y.existsSync(a))try{d=y.readFileSync(a,"utf8")}catch(N){d=""}if(d&&(d=d.split(/(?:\r\n|\r|\n)/)[c-1]))return a+":"+c+"\n"+d+"\n"+Array(b).join(" ")+"^"}return null}function c(){var a=b.emit;b.emit=function(c){if("uncaughtException"=== -c){var d=arguments[1]&&arguments[1].stack,e=0m)return-1;if(58>m)return m-48+52;if(91>m)return m-65;if(123>m)return m-97+26}var t="undefined"!==typeof Uint8Array?Uint8Array:Array;e.toByteArray=function(m){function f(d){q[k++]=d}if(0>16);f((u&65280)>>8);f(u&255)}2===l?(u=p(m.charAt(c))<<2|p(m.charAt(c+1))>>4,f(u&255)):1===l&&(u=p(m.charAt(c))<<10|p(m.charAt(c+1))<<4|p(m.charAt(c+2))>>2,f(u>>8&255),f(u&255));return q};e.fromByteArray=function(m){var f=m.length%3,c="",l;var q=0;for(l=m.length-f;q> +18&63)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(r>>12&63)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(r>>6&63)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(r&63);c+=r}switch(f){case 1:r=m[m.length-1];c+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(r>>2);c+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(r<<4&63);c+="==";break;case 2:r=(m[m.length-2]<<8)+ +m[m.length-1],c+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(r>>10),c+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(r>>4&63),c+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(r<<2&63),c+="="}return c}})("undefined"===typeof A?this.base64js={}:A)},{}],3:[function(C,J,A){},{}],4:[function(C,J,A){(function(e){var p=Object.prototype.toString,t="function"===typeof e.alloc&&"function"===typeof e.allocUnsafe&&"function"=== +typeof e.from;J.exports=function(m,f,c){if("number"===typeof m)throw new TypeError('"value" argument must not be a number');if("ArrayBuffer"===p.call(m).slice(8,-1)){f>>>=0;var l=m.byteLength-f;if(0>l)throw new RangeError("'offset' is out of bounds");if(void 0===c)c=l;else if(c>>>=0,c>l)throw new RangeError("'length' is out of bounds");return t?e.from(m.slice(f,f+c)):new e(new Uint8Array(m.slice(f,f+c)))}if("string"===typeof m){c=f;if("string"!==typeof c||""===c)c="utf8";if(!e.isEncoding(c))throw new TypeError('"encoding" must be a valid string encoding'); +return t?e.from(m,c):new e(m,c)}return t?e.from(m):new e(m)}}).call(this,C("buffer").Buffer)},{buffer:5}],5:[function(C,J,A){function e(a,b,h){if(!(this instanceof e))return new e(a,b,h);var w=typeof a;if("number"===w)var y=0>>0:0;else if("string"===w){if("base64"===b)for(a=(a.trim?a.trim():a.replace(/^\s+|\s+$/g,"")).replace(L,"");0!==a.length%4;)a+="=";y=e.byteLength(a,b)}else if("object"===w&&null!==a)"Buffer"===a.type&&z(a.data)&&(a=a.data),y=0<+a.length?Math.floor(+a.length):0;else throw new TypeError("must start with number, buffer, array or string"); +if(this.length>G)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+G.toString(16)+" bytes");if(e.TYPED_ARRAY_SUPPORT)var I=e._augment(new Uint8Array(y));else I=this,I.length=y,I._isBuffer=!0;if(e.TYPED_ARRAY_SUPPORT&&"number"===typeof a.byteLength)I._set(a);else{var K=a;if(z(K)||e.isBuffer(K)||K&&"object"===typeof K&&"number"===typeof K.length)if(e.isBuffer(a))for(b=0;ba)throw new RangeError("offset is not uint");if(a+b>h)throw new RangeError("Trying to access beyond buffer length");}function m(a,b,h,w,y,I){if(!e.isBuffer(a))throw new TypeError("buffer must be a Buffer instance");if(b>y||ba.length)throw new TypeError("index out of range"); +}function f(a,b,h,w){0>b&&(b=65535+b+1);for(var y=0,I=Math.min(a.length-h,2);y>>8*(w?y:1-y)}function c(a,b,h,w){0>b&&(b=4294967295+b+1);for(var y=0,I=Math.min(a.length-h,4);y>>8*(w?y:3-y)&255}function l(a,b,h,w,y,I){if(b>y||ba.length)throw new TypeError("index out of range");}function q(a,b,h,w,y){y||l(a,b,h,4,3.4028234663852886E38,-3.4028234663852886E38);v.write(a,b,h,w,23,4);return h+4}function r(a, +b,h,w,y){y||l(a,b,h,8,1.7976931348623157E308,-1.7976931348623157E308);v.write(a,b,h,w,52,8);return h+8}function k(a){for(var b=[],h=0;h=w)b.push(w);else{var y=h;55296<=w&&57343>=w&&h++;w=encodeURIComponent(a.slice(y,h+1)).substr(1).split("%");for(y=0;y=b.length||y>=a.length);y++)b[y+ +h]=a[y];return y}function g(a){try{return decodeURIComponent(a)}catch(b){return String.fromCharCode(65533)}}var n=C("base64-js"),v=C("ieee754"),z=C("is-array");A.Buffer=e;A.SlowBuffer=e;A.INSPECT_MAX_BYTES=50;e.poolSize=8192;var G=1073741823;e.TYPED_ARRAY_SUPPORT=function(){try{var a=new ArrayBuffer(0),b=new Uint8Array(a);b.foo=function(){return 42};return 42===b.foo()&&"function"===typeof b.subarray&&0===(new Uint8Array(1)).subarray(1,1).byteLength}catch(h){return!1}}();e.isBuffer=function(a){return!(null== +a||!a._isBuffer)};e.compare=function(a,b){if(!e.isBuffer(a)||!e.isBuffer(b))throw new TypeError("Arguments must be Buffers");for(var h=a.length,w=b.length,y=0,I=Math.min(h,w);y>>1;break;case "utf8":case "utf-8":h=k(a).length;break;case "base64":h=n.toByteArray(a).length; +break;default:h=a.length}return h};e.prototype.length=void 0;e.prototype.parent=void 0;e.prototype.toString=function(a,b,h){var w=!1;b>>>=0;h=void 0===h||Infinity===h?this.length:h>>>0;a||(a="utf8");0>b&&(b=0);h>this.length&&(h=this.length);if(h<=b)return"";for(;;)switch(a){case "hex":a=b;b=h;h=this.length;if(!a||0>a)a=0;if(!b||0>b||b>h)b=h;w="";for(h=a;hw?"0"+w.toString(16):w.toString(16),w=a+w;return w;case "utf8":case "utf-8":w=a="";for(h=Math.min(this.length,h);b= +this[b]?(a+=g(w)+String.fromCharCode(this[b]),w=""):w+="%"+this[b].toString(16);return a+g(w);case "ascii":return p(this,b,h);case "binary":return p(this,b,h);case "base64":return b=0===b&&h===this.length?n.fromByteArray(this):n.fromByteArray(this.slice(b,h)),b;case "ucs2":case "ucs-2":case "utf16le":case "utf-16le":b=this.slice(b,h);h="";for(a=0;ab&&(a+=" ... "));return""};e.prototype.compare=function(a){if(!e.isBuffer(a))throw new TypeError("Argument must be a Buffer");return e.compare(this,a)};e.prototype.get=function(a){console.log(".get() is deprecated. Access using array indexes instead."); +return this.readUInt8(a)};e.prototype.set=function(a,b){console.log(".set() is deprecated. Access using array indexes instead.");return this.writeUInt8(a,b)};e.prototype.write=function(a,b,h,w){if(isFinite(b))isFinite(h)||(w=h,h=void 0);else{var y=w;w=b;b=h;h=y}b=Number(b)||0;y=this.length-b;h?(h=Number(h),h>y&&(h=y)):h=y;w=String(w||"utf8").toLowerCase();switch(w){case "hex":b=Number(b)||0;w=this.length-b;h?(h=Number(h),h>w&&(h=w)):h=w;w=a.length;if(0!==w%2)throw Error("Invalid hex string");h>w/ +2&&(h=w/2);for(w=0;w>8;K%=256;y.push(K);y.push(w)}a=d(y,this,b,h,2);break;default:throw new TypeError("Unknown encoding: "+ +w);}return a};e.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};e.prototype.slice=function(a,b){var h=this.length;a=~~a;b=void 0===b?h:~~b;0>a?(a+=h,0>a&&(a=0)):a>h&&(a=h);0>b?(b+=h,0>b&&(b=0)):b>h&&(b=h);b>>=0;h||m(this,a,b,1,255,0);e.TYPED_ARRAY_SUPPORT||(a=Math.floor(a));this[b]=a;return b+1};e.prototype.writeUInt16LE=function(a, +b,h){a=+a;b>>>=0;h||m(this,a,b,2,65535,0);e.TYPED_ARRAY_SUPPORT?(this[b]=a,this[b+1]=a>>>8):f(this,a,b,!0);return b+2};e.prototype.writeUInt16BE=function(a,b,h){a=+a;b>>>=0;h||m(this,a,b,2,65535,0);e.TYPED_ARRAY_SUPPORT?(this[b]=a>>>8,this[b+1]=a):f(this,a,b,!1);return b+2};e.prototype.writeUInt32LE=function(a,b,h){a=+a;b>>>=0;h||m(this,a,b,4,4294967295,0);e.TYPED_ARRAY_SUPPORT?(this[b+3]=a>>>24,this[b+2]=a>>>16,this[b+1]=a>>>8,this[b]=a):c(this,a,b,!0);return b+4};e.prototype.writeUInt32BE=function(a, +b,h){a=+a;b>>>=0;h||m(this,a,b,4,4294967295,0);e.TYPED_ARRAY_SUPPORT?(this[b]=a>>>24,this[b+1]=a>>>16,this[b+2]=a>>>8,this[b+3]=a):c(this,a,b,!1);return b+4};e.prototype.writeInt8=function(a,b,h){a=+a;b>>>=0;h||m(this,a,b,1,127,-128);e.TYPED_ARRAY_SUPPORT||(a=Math.floor(a));0>a&&(a=255+a+1);this[b]=a;return b+1};e.prototype.writeInt16LE=function(a,b,h){a=+a;b>>>=0;h||m(this,a,b,2,32767,-32768);e.TYPED_ARRAY_SUPPORT?(this[b]=a,this[b+1]=a>>>8):f(this,a,b,!0);return b+2};e.prototype.writeInt16BE=function(a, +b,h){a=+a;b>>>=0;h||m(this,a,b,2,32767,-32768);e.TYPED_ARRAY_SUPPORT?(this[b]=a>>>8,this[b+1]=a):f(this,a,b,!1);return b+2};e.prototype.writeInt32LE=function(a,b,h){a=+a;b>>>=0;h||m(this,a,b,4,2147483647,-2147483648);e.TYPED_ARRAY_SUPPORT?(this[b]=a,this[b+1]=a>>>8,this[b+2]=a>>>16,this[b+3]=a>>>24):c(this,a,b,!0);return b+4};e.prototype.writeInt32BE=function(a,b,h){a=+a;b>>>=0;h||m(this,a,b,4,2147483647,-2147483648);0>a&&(a=4294967295+a+1);e.TYPED_ARRAY_SUPPORT?(this[b]=a>>>24,this[b+1]=a>>>16,this[b+ +2]=a>>>8,this[b+3]=a):c(this,a,b,!1);return b+4};e.prototype.writeFloatLE=function(a,b,h){return q(this,a,b,!0,h)};e.prototype.writeFloatBE=function(a,b,h){return q(this,a,b,!1,h)};e.prototype.writeDoubleLE=function(a,b,h){return r(this,a,b,!0,h)};e.prototype.writeDoubleBE=function(a,b,h){return r(this,a,b,!1,h)};e.prototype.copy=function(a,b,h,w){h||(h=0);w||0===w||(w=this.length);b||(b=0);if(w!==h&&0!==a.length&&0!==this.length){if(wb||b>=a.length)throw new TypeError("targetStart out of bounds"); +if(0>h||h>=this.length)throw new TypeError("sourceStart out of bounds");if(0>w||w>this.length)throw new TypeError("sourceEnd out of bounds");w>this.length&&(w=this.length);a.length-bw||!e.TYPED_ARRAY_SUPPORT)for(var y=0;yb||b>=this.length)throw new TypeError("start out of bounds"); +if(0>h||h>this.length)throw new TypeError("end out of bounds");if("number"===typeof a)for(;b>1,r=-7;f=t?f-1:0;var k=t?-1:1,u=e[p+f];f+=k;t=u&(1<<-r)-1;u>>=-r;for(r+=c;0>=-r;for(r+=m;0>1,u=23===f?Math.pow(2,-24)-Math.pow(2,-77):0;c=m?0:c-1;var d=m?1:-1,g=0>p||0===p&&0>1/p?1:0;p=Math.abs(p);isNaN(p)||Infinity===p?(p=isNaN(p)?1:0,m=r):(m=Math.floor(Math.log(p)/Math.LN2),1>p*(l=Math.pow(2,-m))&&(m--,l*=2),p=1<=m+k?p+u/l:p+u*Math.pow(2,1-k),2<=p*l&&(m++,l/=2),m+k>=r?(p=0,m=r):1<=m+k?(p=(p*l-1)*Math.pow(2,f),m+=k):(p=p*Math.pow(2,k-1)*Math.pow(2,f),m=0));for(;8<=f;e[t+c]=p&255,c+= +d,p/=256,f-=8);m=m<z?[]:n.slice(v,z-v+1)}c=A.resolve(c).substr(1);l=A.resolve(l).substr(1); +for(var r=q(c.split("/")),k=q(l.split("/")),u=Math.min(r.length,k.length),d=u,g=0;gl&&(l=c.length+l);return c.substr(l,q)}}).call(this,C("g5I+bs"))},{"g5I+bs":9}],9:[function(C,J,A){function e(){}C=J.exports={};C.nextTick=function(){if("undefined"!==typeof window&&window.setImmediate)return function(t){return window.setImmediate(t)};if("undefined"!==typeof window&&window.postMessage&&window.addEventListener){var p=[];window.addEventListener("message",function(t){var m=t.source;m!==window&&null!== +m||"process-tick"!==t.data||(t.stopPropagation(),0p?(-p<<1)+1:p<<1;do p=m&31,m>>>=5,0=f)throw Error("Expected more digits in base 64 VLQ value.");var q=e.decode(p.charCodeAt(t++));if(-1===q)throw Error("Invalid base64 digit: "+p.charAt(t-1));var r=!!(q&32);q&=31;c+=q<>1;m.value=1===(c&1)?-p:p;m.rest=t}},{"./base64":12}],12:[function(C, +J,A){var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");A.encode=function(p){if(0<=p&&p=p?p-65:97<=p&&122>=p?p-97+26:48<=p&&57>=p?p-48+52:43==p?62:47==p?63:-1}},{}],13:[function(C,J,A){function e(p,t,m,f,c,l){var q=Math.floor((t-p)/2)+p,r=c(m,f[q],!0);return 0===r?q:0p?-1:p}A.GREATEST_LOWER_BOUND=1;A.LEAST_UPPER_BOUND=2;A.search=function(p,t,m,f){if(0===t.length)return-1;p=e(-1,t.length,p,t,m,f||A.GREATEST_LOWER_BOUND);if(0>p)return-1;for(;0<=p-1&&0===m(t[p],t[p-1],!0);)--p;return p}},{}],14:[function(C,J,A){function e(){this._array=[];this._sorted=!0;this._last={generatedLine:-1,generatedColumn:0}}var p=C("./util");e.prototype.unsortedForEach=function(t,m){this._array.forEach(t,m)};e.prototype.add=function(t){var m=this._last,f=m.generatedLine, +c=t.generatedLine,l=m.generatedColumn,q=t.generatedColumn;c>f||c==f&&q>=l||0>=p.compareByGeneratedPositionsInflated(m,t)?this._last=t:this._sorted=!1;this._array.push(t)};e.prototype.toArray=function(){this._sorted||(this._array.sort(p.compareByGeneratedPositionsInflated),this._sorted=!0);return this._array};A.MappingList=e},{"./util":19}],15:[function(C,J,A){function e(t,m,f){var c=t[m];t[m]=t[f];t[f]=c}function p(t,m,f,c){if(f=m(t[r],q)&&(l+=1,e(t,l,r));e(t,l+1,r);l+=1;p(t,m,f,l-1);p(t,m,l+1,c)}}A.quickSort=function(t,m){p(t,m,0,t.length-1)}},{}],16:[function(C,J,A){function e(k,u){var d=k;"string"===typeof k&&(d=f.parseSourceMapInput(k));return null!=d.sections?new m(d,u):new p(d,u)}function p(k,u){var d=k;"string"===typeof k&&(d=f.parseSourceMapInput(k));var g=f.getArg(d,"version"),n=f.getArg(d,"sources"),v=f.getArg(d,"names",[]),z=f.getArg(d,"sourceRoot",null),G=f.getArg(d,"sourcesContent",null),D=f.getArg(d, +"mappings");d=f.getArg(d,"file",null);if(g!=this._version)throw Error("Unsupported version: "+g);z&&(z=f.normalize(z));n=n.map(String).map(f.normalize).map(function(L){return z&&f.isAbsolute(z)&&f.isAbsolute(L)?f.relative(z,L):L});this._names=l.fromArray(v.map(String),!0);this._sources=l.fromArray(n,!0);this.sourceRoot=z;this.sourcesContent=G;this._mappings=D;this._sourceMapURL=u;this.file=d}function t(){this.generatedColumn=this.generatedLine=0;this.name=this.originalColumn=this.originalLine=this.source= +null}function m(k,u){var d=k;"string"===typeof k&&(d=f.parseSourceMapInput(k));var g=f.getArg(d,"version");d=f.getArg(d,"sections");if(g!=this._version)throw Error("Unsupported version: "+g);this._sources=new l;this._names=new l;var n={line:-1,column:0};this._sections=d.map(function(v){if(v.url)throw Error("Support for url field in sections not implemented.");var z=f.getArg(v,"offset"),G=f.getArg(z,"line"),D=f.getArg(z,"column");if(G=k[d])throw new TypeError("Line must be greater than or equal to 1, got "+ +k[d]);if(0>k[g])throw new TypeError("Column must be greater than or equal to 0, got "+k[g]);return c.search(k,u,n,v)};p.prototype.computeColumnSpans=function(){for(var k=0;k=this._sources.size()&&!this.sourcesContent.some(function(k){return null==k}):!1};p.prototype.sourceContentFor=function(k,u){if(!this.sourcesContent)return null;var d=k;null!=this.sourceRoot&&(d=f.relative(this.sourceRoot,d));if(this._sources.has(d))return this.sourcesContent[this._sources.indexOf(d)]; +var g=this.sources,n;for(n=0;n +g||95!==d.charCodeAt(g-1)||95!==d.charCodeAt(g-2)||111!==d.charCodeAt(g-3)||116!==d.charCodeAt(g-4)||111!==d.charCodeAt(g-5)||114!==d.charCodeAt(g-6)||112!==d.charCodeAt(g-7)||95!==d.charCodeAt(g-8)||95!==d.charCodeAt(g-9))return!1;for(g-=10;0<=g;g--)if(36!==d.charCodeAt(g))return!1;return!0}function r(d,g){return d===g?0:null===d?1:null===g?-1:d>g?1:-1}A.getArg=function(d,g,n){if(g in d)return d[g];if(3===arguments.length)return n;throw Error('"'+g+'" is a required argument.');};var k=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/, +u=/^data:.+,.+$/;A.urlParse=e;A.urlGenerate=p;A.normalize=t;A.join=m;A.isAbsolute=function(d){return"/"===d.charAt(0)||k.test(d)};A.relative=function(d,g){""===d&&(d=".");d=d.replace(/\/$/,"");for(var n=0;0!==g.indexOf(d+"/");){var v=d.lastIndexOf("/");if(0>v)return g;d=d.slice(0,v);if(d.match(/^([^\/]+:\/)?\/*$/))return g;++n}return Array(n+1).join("../")+g.substr(d.length+1)};C=!("__proto__"in Object.create(null));A.toSetString=C?f:c;A.fromSetString=C?f:l;A.compareByOriginalPositions=function(d, +g,n){var v=r(d.source,g.source);if(0!==v)return v;v=d.originalLine-g.originalLine;if(0!==v)return v;v=d.originalColumn-g.originalColumn;if(0!==v||n)return v;v=d.generatedColumn-g.generatedColumn;if(0!==v)return v;v=d.generatedLine-g.generatedLine;return 0!==v?v:r(d.name,g.name)};A.compareByGeneratedPositionsDeflated=function(d,g,n){var v=d.generatedLine-g.generatedLine;if(0!==v)return v;v=d.generatedColumn-g.generatedColumn;if(0!==v||n)return v;v=r(d.source,g.source);if(0!==v)return v;v=d.originalLine- +g.originalLine;if(0!==v)return v;v=d.originalColumn-g.originalColumn;return 0!==v?v:r(d.name,g.name)};A.compareByGeneratedPositionsInflated=function(d,g){var n=d.generatedLine-g.generatedLine;if(0!==n)return n;n=d.generatedColumn-g.generatedColumn;if(0!==n)return n;n=r(d.source,g.source);if(0!==n)return n;n=d.originalLine-g.originalLine;if(0!==n)return n;n=d.originalColumn-g.originalColumn;return 0!==n?n:r(d.name,g.name)};A.parseSourceMapInput=function(d){return JSON.parse(d.replace(/^\)]}'[^\n]*\n/, +""))};A.computeSourceURL=function(d,g,n){g=g||"";d&&("/"!==d[d.length-1]&&"/"!==g[0]&&(d+="/"),g=d+g);if(n){d=e(n);if(!d)throw Error("sourceMapURL could not be parsed");d.path&&(n=d.path.lastIndexOf("/"),0<=n&&(d.path=d.path.substring(0,n+1)));g=m(p(d),g)}return t(g)}},{}],20:[function(C,J,A){A.SourceMapGenerator=C("./lib/source-map-generator").SourceMapGenerator;A.SourceMapConsumer=C("./lib/source-map-consumer").SourceMapConsumer;A.SourceNode=C("./lib/source-node").SourceNode},{"./lib/source-map-consumer":16, +"./lib/source-map-generator":17,"./lib/source-node":18}],21:[function(C,J,A){(function(e){function p(){return"browser"===a?!0:"node"===a?!1:"undefined"!==typeof window&&"function"===typeof XMLHttpRequest&&!(window.require&&window.module&&window.process&&"renderer"===window.process.type)}function t(x){return function(B){for(var F=0;F";B=this.getLineNumber();null!=B&&(x+=":"+B,(B= +this.getColumnNumber())&&(x+=":"+B))}B="";var F=this.getFunctionName(),E=!0,H=this.isConstructor();if(this.isToplevel()||H)H?B+="new "+(F||""):F?B+=F:(B+=x,E=!1);else{H=this.getTypeName();"[object Object]"===H&&(H="null");var M=this.getMethodName();F?(H&&0!=F.indexOf(H)&&(B+=H+"."),B+=F,M&&F.indexOf("."+M)!=F.length-M.length-1&&(B+=" [as "+M+"]")):B+=H+"."+(M||"")}E&&(B+=" ("+x+")");return B}function q(x){var B={};Object.getOwnPropertyNames(Object.getPrototypeOf(x)).forEach(function(F){B[F]= +/^(?:is|get)/.test(F)?function(){return x[F].call(x)}:x[F]});B.toString=l;return B}function r(x,B){void 0===B&&(B={nextPosition:null,curPosition:null});if(x.isNative())return B.curPosition=null,x;var F=x.getFileName()||x.getScriptNameOrSourceURL();if(F){var E=x.getLineNumber(),H=x.getColumnNumber()-1,M=/^v(10\.1[6-9]|10\.[2-9][0-9]|10\.[0-9]{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/,S=M.test;var V="object"===typeof e&&null!==e?e.version:"";M=S.call(M,V)?0:62;1===E&&H>M&&!p()&&!x.isEval()&&(H-=M);var O= +f({source:F,line:E,column:H});B.curPosition=O;x=q(x);var T=x.getFunctionName;x.getFunctionName=function(){return null==B.nextPosition?T():B.nextPosition.name||T()};x.getFileName=function(){return O.source};x.getLineNumber=function(){return O.line};x.getColumnNumber=function(){return O.column+1};x.getScriptNameOrSourceURL=function(){return O.source};return x}var Q=x.isEval()&&x.getEvalOrigin();Q&&(Q=c(Q),x=q(x),x.getEvalOrigin=function(){return Q});return x}function k(x,B){L&&(b={},h={});for(var F= +(x.name||"Error")+": "+(x.message||""),E={nextPosition:null,curPosition:null},H=[],M=B.length-1;0<=M;M--)H.push("\n at "+r(B[M],E)),E.nextPosition=E.curPosition;E.curPosition=E.nextPosition=null;return F+H.reverse().join("")}function u(x){var B=/\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(x.stack);if(B){x=B[1];var F=+B[2];B=+B[3];var E=b[x];if(!E&&v&&v.existsSync(x))try{E=v.readFileSync(x,"utf8")}catch(H){E=""}if(E&&(E=E.split(/(?:\r\n|\r|\n)/)[F-1]))return x+":"+F+"\n"+E+"\n"+Array(B).join(" ")+ +"^"}return null}function d(){var x=e.emit;e.emit=function(B){if("uncaughtException"===B){var F=arguments[1]&&arguments[1].stack,E=0 Date: Sun, 1 May 2022 14:53:52 -0400 Subject: [PATCH 24/24] clean up TODOs --- source-map-support.js | 5 +---- test.js | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/source-map-support.js b/source-map-support.js index 902994a..b4995ec 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -129,8 +129,6 @@ function hasGlobalProcessEventEmitter() { } function tryFileURLToPath(v) { - // TODO technically, file URL can omit /s. - // Copy the isFileURL util from resolve-uri? if(isFileUrl(v)) { return fileURLToPath(v); } @@ -154,8 +152,7 @@ function isSchemeRelativeUrl(input) { /** @param {string} pathOrFileUrl */ function getCacheKey(pathOrFileUrl) { if(pathOrFileUrl.startsWith('node:')) return pathOrFileUrl; - // TODO unify with isFileURL checks elsewhere? as helper fn? - if(pathOrFileUrl.startsWith('file:/')) { + if(isFileUrl(pathOrFileUrl)) { // Must normalize spaces to %20, stuff like that return new URL(pathOrFileUrl).toString(); } else { diff --git a/test.js b/test.js index aa9bc97..7dabc6c 100644 --- a/test.js +++ b/test.js @@ -390,7 +390,6 @@ it('eval', async function() { ], [ 'Error: test', - // TODO re`^ at eval \(eval at (|exports\.test|test) \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)`, re`^ at ${stackFrameAtTest()} \(${stackFramePathStartsWith()}(?:.*[/\\])?line1\.js:1001:101\)$`