diff --git a/source-map-support.d.ts b/source-map-support.d.ts index d8cb9d8..21c1055 100755 --- a/source-map-support.d.ts +++ b/source-map-support.d.ts @@ -50,6 +50,13 @@ export interface Options { * @param options options object internally passed to node's `_resolveFilename` hook */ onConflictingLibraryRedirect?: (request: string, parent: any, isMain: boolean, options: any, redirectedRequest: string) => void; + + /** + * If enabled, will fallback to getting mapped function name from callsite (from the following stack frame) when neither + * enclosing position nor runtime have a function name. + * Default: true + */ + callsiteFallback?: boolean; } export interface Position { diff --git a/source-map-support.js b/source-map-support.js index ad830b6..5c48b36 100644 --- a/source-map-support.js +++ b/source-map-support.js @@ -92,6 +92,10 @@ var sharedData = initializeSharedData({ // If true, the caches are reset before a stack trace formatting operation emptyCacheBetweenOperations: false, + // If true, will fallback to getting mapped function name from callsite (from the following stack frame) when neither + // enclosing position nor runtime have a function name. + callsiteFallback: true, + // Maps a file path to a string containing the file contents fileContentsCache: Object.create(null), @@ -554,6 +558,14 @@ function cloneCallSite(frame) { return object; } +// Fix position in Node where some (internal) code is prepended. +// See https://github.com/evanw/node-source-map-support/issues/36 +// Header removed in node at ^10.16 || >=11.11.0 +// v11 is not an LTS candidate, we can just test the one version with it. +// Test node versions for: 10.16-19, 10.20+, 12-19, 20-99, 100+, or 11.11 +const noHeader = /^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)/; +const headerLength = isInBrowser() ? 0 : (noHeader.test(process.version) ? 0 : 62); + function wrapCallSite(frame, state) { // provides interface backward compatibility if (state === undefined) { @@ -577,31 +589,31 @@ function wrapCallSite(frame, state) { var line = frame.getLineNumber(); var column = frame.getColumnNumber() - 1; - - // Fix position in Node where some (internal) code is prepended. - // See https://github.com/evanw/node-source-map-support/issues/36 - // Header removed in node at ^10.16 || >=11.11.0 - // v11 is not an LTS candidate, we can just test the one version with it. - // Test node versions for: 10.16-19, 10.20+, 12-19, 20-99, 100+, or 11.11 - var noHeader = /^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)/; - var headerLength = noHeader.test(process.version) ? 0 : 62; - if (line === 1 && column > headerLength && !isInBrowser() && !frame.isEval()) { + if (line === 1 && column > headerLength && !frame.isEval()) { column -= headerLength; } - var position = mapSourcePosition({ source: source, line: line, column: column }); + state.curPosition = position; + const nextPosition = state.nextPosition; + frame = cloneCallSite(frame); + + // Check for function name in this order: + // runtime function name, then fallback to callsite (subsequent stack frame's position) var originalFunctionName = frame.getFunctionName; frame.getFunctionName = function() { - if (state.nextPosition == null) { - return originalFunctionName(); + const originalName = originalFunctionName(); + if(originalName != null) return originalName; + if (sharedData.callsiteFallback && nextPosition != null) { + const nextPositionName = nextPosition.name; + if(nextPositionName != null) return nextPositionName; } - return state.nextPosition.name || originalFunctionName(); + return null; }; frame.getFileName = function() { return position.source; }; frame.getLineNumber = function() { return position.line; }; @@ -895,6 +907,10 @@ exports.install = function(options) { shimEmitUncaughtException(); } } + + if (typeof options.callsiteFallback === 'boolean') { + sharedData.callsiteFallback = options.callsiteFallback; + } }; exports.uninstall = function() { diff --git a/test.js b/test.js index 7dabc6c..7c870ae 100644 --- a/test.js +++ b/test.js @@ -598,20 +598,21 @@ it('finds the last sourceMappingURL', async function() { it('maps original name from source', async function() { var sourceMap = createEmptySourceMap(); sourceMap.addMapping({ - generated: { line: 2, column: 8 }, + generated: { line: 3, column: 8 }, original: { line: 1000, column: 10 }, source: `.original-${id}.js`, }); sourceMap.addMapping({ - generated: { line: 4, column: 0 }, + generated: { line: 5, column: 0 }, original: { line: 1002, column: 1 }, source: `.original-${id}.js`, name: "myOriginalName" }); await compareStackTrace(sourceMap, [ - 'function foo() {', + 'function identity(v) { return v }', + 'const foo = identity(function () {', ' throw new Error("test");', - '}', + '})', 'foo();' ], [ 'Error: test',