@@ -1162,25 +1162,58 @@ function getFunctionBase(value, constructor, tag) {
1162
1162
return base ;
1163
1163
}
1164
1164
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
+ }
1169
1184
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' ) ) ;
1178
1206
}
1179
1207
}
1180
1208
}
1209
+ return frames ;
1210
+ }
1181
1211
1212
+ function improveStack ( stack , constructor , name , tag ) {
1182
1213
// A stack trace may contain arbitrary data. Only manipulate the output
1183
1214
// for "regular errors" (errors that "look normal") for now.
1215
+ let len = name . length ;
1216
+
1184
1217
if ( constructor === null ||
1185
1218
( name . endsWith ( 'Error' ) &&
1186
1219
stack . startsWith ( name ) &&
@@ -1206,6 +1239,33 @@ function formatError(err, constructor, tag, ctx, keys) {
1206
1239
}
1207
1240
}
1208
1241
}
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
+
1209
1269
// Ignore the error message if it's contained in the stack.
1210
1270
let pos = ( err . message && stack . indexOf ( err . message ) ) || - 1 ;
1211
1271
if ( pos !== - 1 )
@@ -1217,7 +1277,7 @@ function formatError(err, constructor, tag, ctx, keys) {
1217
1277
} else if ( ctx . colors ) {
1218
1278
// Highlight userland code and node modules.
1219
1279
let newStack = stack . slice ( 0 , stackStart ) ;
1220
- const lines = stack . slice ( stackStart + 1 ) . split ( '\n' ) ;
1280
+ const lines = getStackFrames ( ctx , err , stack . slice ( stackStart + 1 ) ) ;
1221
1281
for ( const line of lines ) {
1222
1282
const core = line . match ( coreModuleRegExp ) ;
1223
1283
if ( core !== null && NativeModule . exists ( core [ 1 ] ) ) {
0 commit comments