diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 0746fb35ac7f..fea6f4473ba8 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -10,6 +10,7 @@ import { extractRequestData as _extractRequestData, getGlobalObject, logger, + nodeStackLineParser, stackParserFromStackParserOptions, } from '@sentry/utils'; import * as cookie from 'cookie'; @@ -19,7 +20,6 @@ import * as url from 'url'; import { NodeClient } from './client'; import { Console, ContextLines, Http, LinkedErrors, OnUncaughtException, OnUnhandledRejection } from './integrations'; import { getModule } from './module'; -import { nodeStackLineParser } from './stack-parser'; import { makeNodeTransport } from './transports'; import { NodeClientOptions, NodeOptions } from './types'; diff --git a/packages/node/src/stack-parser.ts b/packages/node/src/stack-parser.ts deleted file mode 100644 index 7fd8a5ee3abd..000000000000 --- a/packages/node/src/stack-parser.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { StackLineParser, StackLineParserFn } from '@sentry/types'; - -const FILENAME_MATCH = /^\s*[-]{4,}$/; -const FULL_MATCH = /at (?:async )?(?:(.+?)\s+\()?(?:(.+?):(\d+)(?::(\d+))?|([^)]+))\)?/; - -type GetModuleFn = (filename: string | undefined) => string | undefined; - -// eslint-disable-next-line complexity -function node(getModule?: GetModuleFn): StackLineParserFn { - // eslint-disable-next-line complexity - return (line: string) => { - if (line.match(FILENAME_MATCH)) { - return { - filename: line, - }; - } - - const lineMatch = line.match(FULL_MATCH); - if (!lineMatch) { - return undefined; - } - - let object: string | undefined; - let method: string | undefined; - let functionName: string | undefined; - let typeName: string | undefined; - let methodName: string | undefined; - - if (lineMatch[1]) { - functionName = lineMatch[1]; - - let methodStart = functionName.lastIndexOf('.'); - if (functionName[methodStart - 1] === '.') { - // eslint-disable-next-line no-plusplus - methodStart--; - } - - if (methodStart > 0) { - object = functionName.substr(0, methodStart); - method = functionName.substr(methodStart + 1); - const objectEnd = object.indexOf('.Module'); - if (objectEnd > 0) { - functionName = functionName.substr(objectEnd + 1); - object = object.substr(0, objectEnd); - } - } - typeName = undefined; - } - - if (method) { - typeName = object; - methodName = method; - } - - if (method === '') { - methodName = undefined; - functionName = undefined; - } - - if (functionName === undefined) { - methodName = methodName || ''; - functionName = typeName ? `${typeName}.${methodName}` : methodName; - } - - const filename = lineMatch[2]?.startsWith('file://') ? lineMatch[2].substr(7) : lineMatch[2]; - const isNative = lineMatch[5] === 'native'; - const isInternal = - isNative || (filename && !filename.startsWith('/') && !filename.startsWith('.') && filename.indexOf(':\\') !== 1); - - // in_app is all that's not an internal Node function or a module within node_modules - // note that isNative appears to return true even for node core libraries - // see https://github.com/getsentry/raven-node/issues/176 - const in_app = !isInternal && filename !== undefined && !filename.includes('node_modules/'); - - return { - filename, - module: getModule?.(filename), - function: functionName, - lineno: parseInt(lineMatch[3], 10) || undefined, - colno: parseInt(lineMatch[4], 10) || undefined, - in_app, - }; - }; -} - -/** Node.js stack line parser */ -export function nodeStackLineParser(getModule?: GetModuleFn): StackLineParser { - return [90, node(getModule)]; -} diff --git a/packages/utils/src/stacktrace.ts b/packages/utils/src/stacktrace.ts index 0949a6194526..d044e19fa7b7 100644 --- a/packages/utils/src/stacktrace.ts +++ b/packages/utils/src/stacktrace.ts @@ -1,4 +1,4 @@ -import { StackFrame, StackLineParser, StackParser } from '@sentry/types'; +import { StackFrame, StackLineParser, StackLineParserFn, StackParser } from '@sentry/types'; const STACKTRACE_LIMIT = 50; @@ -94,3 +94,96 @@ export function getFunctionName(fn: unknown): string { return defaultFunctionName; } } + +type GetModuleFn = (filename: string | undefined) => string | undefined; + +// eslint-disable-next-line complexity +function node(getModule?: GetModuleFn): StackLineParserFn { + const FILENAME_MATCH = /^\s*[-]{4,}$/; + const FULL_MATCH = /at (?:async )?(?:(.+?)\s+\()?(?:(.+?):(\d+)(?::(\d+))?|([^)]+))\)?/; + + // eslint-disable-next-line complexity + return (line: string) => { + if (line.match(FILENAME_MATCH)) { + return { + filename: line, + }; + } + + const lineMatch = line.match(FULL_MATCH); + if (!lineMatch) { + return undefined; + } + + let object: string | undefined; + let method: string | undefined; + let functionName: string | undefined; + let typeName: string | undefined; + let methodName: string | undefined; + + if (lineMatch[1]) { + functionName = lineMatch[1]; + + let methodStart = functionName.lastIndexOf('.'); + if (functionName[methodStart - 1] === '.') { + // eslint-disable-next-line no-plusplus + methodStart--; + } + + if (methodStart > 0) { + object = functionName.substr(0, methodStart); + method = functionName.substr(methodStart + 1); + const objectEnd = object.indexOf('.Module'); + if (objectEnd > 0) { + functionName = functionName.substr(objectEnd + 1); + object = object.substr(0, objectEnd); + } + } + typeName = undefined; + } + + if (method) { + typeName = object; + methodName = method; + } + + if (method === '') { + methodName = undefined; + functionName = undefined; + } + + if (functionName === undefined) { + methodName = methodName || ''; + functionName = typeName ? `${typeName}.${methodName}` : methodName; + } + + const filename = lineMatch[2]?.startsWith('file://') ? lineMatch[2].substr(7) : lineMatch[2]; + const isNative = lineMatch[5] === 'native'; + const isInternal = + isNative || (filename && !filename.startsWith('/') && !filename.startsWith('.') && filename.indexOf(':\\') !== 1); + + // in_app is all that's not an internal Node function or a module within node_modules + // note that isNative appears to return true even for node core libraries + // see https://github.com/getsentry/raven-node/issues/176 + const in_app = !isInternal && filename !== undefined && !filename.includes('node_modules/'); + + return { + filename, + module: getModule?.(filename), + function: functionName, + lineno: parseInt(lineMatch[3], 10) || undefined, + colno: parseInt(lineMatch[4], 10) || undefined, + in_app, + }; + }; +} + +/** + * Node.js stack line parser + * + * This is in @sentry/utils so it can be used from the Electron SDK in the browser for when `nodeIntegration == true`. + * This allows it to be used without referencing or importing any node specific code which causes bundlers to complain + */ +export function nodeStackLineParser(getModule?: GetModuleFn): StackLineParser { + return [90, node(getModule)]; +}