import { Event , Exception , ExtendedError , StackFrame } from '@sentry/types' ; import { addContextToFrame , basename , dirname , resolvedSyncPromise , SyncPromise } from '@sentry/utils' ; import { readFile } from 'fs' ; import { LRUMap } from 'lru_map' ; import * as stacktrace from './stacktrace' ; import { NodeOptions } from './types' ; const DEFAULT_LINES_OF_CONTEXT = 7 ; const FILE_CONTENT_CACHE = new LRUMap < string , string | null > ( 100 ) ; hidden export function resetFileContentCache ( ) { FILE_CONTENT_CACHE . clear ( ) ; } /** JSDoc */ function getFunction ( frame ) { try { return frame . functionName || `${ frame . typeName }.${ frame . methodName || '<anonymous>' }` ; } catch ( e ) { return '<anonymous>' ; } } const mainModule = `${ ( require . main && require . main . filename && dirname ( require . main . filename ) ) || global . process . cwd ( ) }/` ; /** JSDoc */ function getModule ( filename , base ? ) { if ( ! base ) { base = mainModule ; } const file = basename ( filename , '.js' ) ; filename = dirname ( filename ) ; let n = filename . lastIndexOf ( '/node_modules/' ) ; if ( n > - 1 ) { return `${ filename . substr ( n + 14 ) . replace ( /\//g , '.' ) }:${ file }` ; } n = `${ filename }/` . lastIndexOf ( base , 0 ) ; if ( n === 0 ) { let moduleName = filename . substr ( base . length ) . replace ( /\//g , '.' ) ; if ( moduleName ) { moduleName += ':' ; } moduleName += file ; return moduleName ; } return file ; } param filenames function readSourceFiles ( filenames ) { if ( filenames . length === 0 ) { return resolvedSyncPromise ( { } ) ; } return new SyncPromise < { [ key ] : string | null ; } > ( resolve => { const sourceFiles = { } ; let count = 0 ; for ( let i = 0 ; i < filenames . length ; i ++ ) { const filename = filenames [ i ] ; const cache = FILE_CONTENT_CACHE . get ( filename ) ; if ( cache !== undefined ) { if ( cache !== null ) { sourceFiles [ filename ] = cache ; } count ++ ; if ( count === filenames . length ) { resolve ( sourceFiles ) ; } continue ; } readFile ( filename , ( err , data ) => { const content = err ? null : data . toString ( ) ; sourceFiles [ filename ] = content ; FILE_CONTENT_CACHE . set ( filename , content ) ; count ++ ; if ( count === filenames . length ) { resolve ( sourceFiles ) ; } } ) ; } } ) ; } hidden export function extractStackFromError ( error ) { const stack = stacktrace . parse ( error ) ; if ( ! stack ) { return [ ] ; } return stack ; } hidden export function parseStack ( stack , options ? ) { const filesToRead = [ ] ; const linesOfContext = options && options . frameContextLines !== undefined ? options . frameContextLines : DEFAULT_LINES_OF_CONTEXT ; const frames = stack . map ( frame => { const parsedFrame = { colno : frame . columnNumber , filename : frame . fileName ?. startsWith ( 'file://' ) ? frame . fileName . substr ( 7 ) : frame . fileName || '' , function : getFunction ( frame ) , lineno : frame . lineNumber , } ; const isInternal = frame . native || ( parsedFrame . filename && ! parsedFrame . filename . startsWith ( '/' ) && ! parsedFrame . filename . startsWith ( '.' ) && parsedFrame . filename . indexOf ( ':\\' ) !== 1 ) ; parsedFrame . in_app = ! isInternal && parsedFrame . filename !== undefined && parsedFrame . filename . indexOf ( 'node_modules/' ) === - 1 ; if ( parsedFrame . filename ) { parsedFrame . module = getModule ( parsedFrame . filename ) ; if ( ! isInternal && linesOfContext > 0 && filesToRead . indexOf ( parsedFrame . filename ) === - 1 ) { filesToRead . push ( parsedFrame . filename ) ; } } return parsedFrame ; } ) ; if ( linesOfContext <= 0 ) { return resolvedSyncPromise ( frames ) ; } try { return addPrePostContext ( filesToRead , frames , linesOfContext ) ; } catch ( _ ) { return resolvedSyncPromise ( frames ) ; } } param filesToRead param frames function addPrePostContext ( filesToRead , frames , linesOfContext ) { return new SyncPromise < StackFrame [ ] > ( resolve => readSourceFiles ( filesToRead ) . then ( sourceFiles => { const result = frames . map ( frame => { if ( frame . filename && sourceFiles [ frame . filename ] ) { try { const lines = ( sourceFiles [ frame . filename ] as string ) . split ( '\n' ) ; addContextToFrame ( lines , frame , linesOfContext ) ; } catch ( e ) { } } return frame ; } ) ; resolve ( result ) ; } ) ) ; } hidden export function getExceptionFromError ( error , options ? ) { const name = error . name || error . constructor . name ; const stack = extractStackFromError ( error ) ; return new SyncPromise < Exception > ( resolve => parseStack ( stack , options ) . then ( frames => { const result = { stacktrace : { frames : prepareFramesForEvent ( frames ) , } , type : name , value : error . message , } ; resolve ( result ) ; } ) ) ; } hidden export function parseError ( error , options ? ) { return new SyncPromise < Event > ( resolve => getExceptionFromError ( error , options ) . then ( ( exception ) => { resolve ( { exception : { values : [ exception ] , } , } ) ; } ) ) ; } hidden export function prepareFramesForEvent ( stack ) { if ( ! stack || ! stack . length ) { return [ ] ; } let localStack = stack ; const firstFrameFunction = localStack [ 0 ] . function || '' ; if ( firstFrameFunction . indexOf ( 'captureMessage' ) !== - 1 || firstFrameFunction . indexOf ( 'captureException' ) !== - 1 ) { localStack = localStack . slice ( 1 ) ; } return localStack . reverse ( ) ; }