@@ -1162,25 +1162,58 @@ function getFunctionBase(value, constructor, tag) {
11621162 return base ;
11631163}
11641164
1165- function formatError ( err , constructor , tag , ctx , keys ) {
1166- const name = err . name != null ? String ( err . name ) : 'Error' ;
1167- let len = name . length ;
1168- let stack = err . stack ? String ( err . stack ) : ErrorPrototypeToString ( err ) ;
1165+ function identicalSequenceRange ( a , b ) {
1166+ for ( let i = 0 ; i < a . length - 3 ; i ++ ) {
1167+ // Find the first entry of b that matches the current entry of a.
1168+ const pos = b . indexOf ( a [ i ] ) ;
1169+ if ( pos !== - 1 ) {
1170+ const rest = b . length - pos ;
1171+ if ( rest > 3 ) {
1172+ let len = 1 ;
1173+ const maxLen = MathMin ( a . length - i , rest ) ;
1174+ // Count the number of consecutive entries.
1175+ while ( maxLen > len && a [ i + len ] === b [ pos + len ] ) {
1176+ len ++ ;
1177+ }
1178+ if ( len > 3 ) {
1179+ return { len, offset : i } ;
1180+ }
1181+ }
1182+ }
1183+ }
11691184
1170- // Do not "duplicate" error properties that are already included in the output
1171- // otherwise.
1172- if ( ! ctx . showHidden && keys . length !== 0 ) {
1173- for ( const name of [ 'name' , 'message' , 'stack' ] ) {
1174- const index = keys . indexOf ( name ) ;
1175- // Only hide the property in case it's part of the original stack
1176- if ( index !== - 1 && stack . includes ( err [ name ] ) ) {
1177- keys . splice ( index , 1 ) ;
1185+ return { len : 0 , offset : 0 } ;
1186+ }
1187+
1188+ function getStackString ( error ) {
1189+ return error . stack ? String ( error . stack ) : ErrorPrototypeToString ( error ) ;
1190+ }
1191+
1192+ function getStackFrames ( ctx , err , stack ) {
1193+ const frames = stack . split ( '\n' ) ;
1194+
1195+ // Remove stack frames identical to frames in cause.
1196+ if ( err . cause ) {
1197+ const causeStack = getStackString ( err . cause ) ;
1198+ const causeStackStart = causeStack . indexOf ( '\n at' ) ;
1199+ if ( causeStackStart !== - 1 ) {
1200+ const causeFrames = causeStack . slice ( causeStackStart + 1 ) . split ( '\n' ) ;
1201+ const { len, offset } = identicalSequenceRange ( frames , causeFrames ) ;
1202+ if ( len > 0 ) {
1203+ const skipped = len - 2 ;
1204+ const msg = ` ... ${ skipped } lines matching cause stack trace ...` ;
1205+ frames . splice ( offset + 1 , skipped , ctx . stylize ( msg , 'undefined' ) ) ;
11781206 }
11791207 }
11801208 }
1209+ return frames ;
1210+ }
11811211
1212+ function improveStack ( stack , constructor , name , tag ) {
11821213 // A stack trace may contain arbitrary data. Only manipulate the output
11831214 // for "regular errors" (errors that "look normal") for now.
1215+ let len = name . length ;
1216+
11841217 if ( constructor === null ||
11851218 ( name . endsWith ( 'Error' ) &&
11861219 stack . startsWith ( name ) &&
@@ -1206,6 +1239,33 @@ function formatError(err, constructor, tag, ctx, keys) {
12061239 }
12071240 }
12081241 }
1242+ return stack ;
1243+ }
1244+
1245+ function removeDuplicateErrorKeys ( ctx , keys , err , stack ) {
1246+ if ( ! ctx . showHidden && keys . length !== 0 ) {
1247+ for ( const name of [ 'name' , 'message' , 'stack' ] ) {
1248+ const index = keys . indexOf ( name ) ;
1249+ // Only hide the property in case it's part of the original stack
1250+ if ( index !== - 1 && stack . includes ( err [ name ] ) ) {
1251+ keys . splice ( index , 1 ) ;
1252+ }
1253+ }
1254+ }
1255+ }
1256+
1257+ function formatError ( err , constructor , tag , ctx , keys ) {
1258+ const name = err . name != null ? String ( err . name ) : 'Error' ;
1259+ let stack = getStackString ( err ) ;
1260+
1261+ removeDuplicateErrorKeys ( ctx , keys , err , stack ) ;
1262+
1263+ if ( err . cause && ( keys . length === 0 || ! keys . includes ( 'cause' ) ) ) {
1264+ keys . push ( 'cause' ) ;
1265+ }
1266+
1267+ stack = improveStack ( stack , constructor , name , tag ) ;
1268+
12091269 // Ignore the error message if it's contained in the stack.
12101270 let pos = ( err . message && stack . indexOf ( err . message ) ) || - 1 ;
12111271 if ( pos !== - 1 )
@@ -1214,27 +1274,31 @@ function formatError(err, constructor, tag, ctx, keys) {
12141274 const stackStart = stack . indexOf ( '\n at' , pos ) ;
12151275 if ( stackStart === - 1 ) {
12161276 stack = `[${ stack } ]` ;
1217- } else if ( ctx . colors ) {
1218- // Highlight userland code and node modules.
1277+ } else {
12191278 let newStack = stack . slice ( 0 , stackStart ) ;
1220- const lines = stack . slice ( stackStart + 1 ) . split ( '\n' ) ;
1221- for ( const line of lines ) {
1222- const core = line . match ( coreModuleRegExp ) ;
1223- if ( core !== null && NativeModule . exists ( core [ 1 ] ) ) {
1224- newStack += `\n${ ctx . stylize ( line , 'undefined' ) } ` ;
1225- } else {
1226- // This adds underscores to all node_modules to quickly identify them.
1227- let nodeModule ;
1228- newStack += '\n' ;
1229- let pos = 0 ;
1230- while ( nodeModule = nodeModulesRegExp . exec ( line ) ) {
1231- // '/node_modules/'.length === 14
1232- newStack += line . slice ( pos , nodeModule . index + 14 ) ;
1233- newStack += ctx . stylize ( nodeModule [ 1 ] , 'module' ) ;
1234- pos = nodeModule . index + nodeModule [ 0 ] . length ;
1279+ const lines = getStackFrames ( ctx , err , stack . slice ( stackStart + 1 ) ) ;
1280+ if ( ctx . colors ) {
1281+ // Highlight userland code and node modules.
1282+ for ( const line of lines ) {
1283+ const core = line . match ( coreModuleRegExp ) ;
1284+ if ( core !== null && NativeModule . exists ( core [ 1 ] ) ) {
1285+ newStack += `\n${ ctx . stylize ( line , 'undefined' ) } ` ;
1286+ } else {
1287+ // This adds underscores to all node_modules to quickly identify them.
1288+ let nodeModule ;
1289+ newStack += '\n' ;
1290+ let pos = 0 ;
1291+ while ( nodeModule = nodeModulesRegExp . exec ( line ) ) {
1292+ // '/node_modules/'.length === 14
1293+ newStack += line . slice ( pos , nodeModule . index + 14 ) ;
1294+ newStack += ctx . stylize ( nodeModule [ 1 ] , 'module' ) ;
1295+ pos = nodeModule . index + nodeModule [ 0 ] . length ;
1296+ }
1297+ newStack += pos === 0 ? line : line . slice ( pos ) ;
12351298 }
1236- newStack += pos === 0 ? line : line . slice ( pos ) ;
12371299 }
1300+ } else {
1301+ newStack += `\n${ lines . join ( '\n' ) } ` ;
12381302 }
12391303 stack = newStack ;
12401304 }
0 commit comments