@@ -1162,25 +1162,57 @@ 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 = Math . min ( 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
+ frames . splice ( offset + 1 , len - 2 ,
1204
+ ctx . stylize ( ` ... ${ len - 2 } lines matching cause stack trace ...` , 'undefined' ) ) ;
1178
1205
}
1179
1206
}
1180
1207
}
1208
+ return frames ;
1209
+ }
1181
1210
1211
+ function improveStack ( stack , constructor , name , tag ) {
1182
1212
// A stack trace may contain arbitrary data. Only manipulate the output
1183
1213
// for "regular errors" (errors that "look normal") for now.
1214
+ let len = name . length ;
1215
+
1184
1216
if ( constructor === null ||
1185
1217
( name . endsWith ( 'Error' ) &&
1186
1218
stack . startsWith ( name ) &&
@@ -1206,6 +1238,33 @@ function formatError(err, constructor, tag, ctx, keys) {
1206
1238
}
1207
1239
}
1208
1240
}
1241
+ return stack ;
1242
+ }
1243
+
1244
+ function removeDuplicateErrorKeys ( ctx , keys , err , stack ) {
1245
+ if ( ! ctx . showHidden && keys . length !== 0 ) {
1246
+ for ( const name of [ 'name' , 'message' , 'stack' ] ) {
1247
+ const index = keys . indexOf ( name ) ;
1248
+ // Only hide the property in case it's part of the original stack
1249
+ if ( index !== - 1 && stack . includes ( err [ name ] ) ) {
1250
+ keys . splice ( index , 1 ) ;
1251
+ }
1252
+ }
1253
+ }
1254
+ }
1255
+
1256
+ function formatError ( err , constructor , tag , ctx , keys ) {
1257
+ const name = err . name != null ? String ( err . name ) : 'Error' ;
1258
+ let stack = getStackString ( err ) ;
1259
+
1260
+ removeDuplicateErrorKeys ( ctx , keys , err , stack ) ;
1261
+
1262
+ if ( err . cause && ( keys . length === 0 || ! keys . includes ( 'cause' ) ) ) {
1263
+ keys . push ( 'cause' )
1264
+ }
1265
+
1266
+ stack = improveStack ( stack , constructor , name , tag ) ;
1267
+
1209
1268
// Ignore the error message if it's contained in the stack.
1210
1269
let pos = ( err . message && stack . indexOf ( err . message ) ) || - 1 ;
1211
1270
if ( pos !== - 1 )
@@ -1217,7 +1276,7 @@ function formatError(err, constructor, tag, ctx, keys) {
1217
1276
} else if ( ctx . colors ) {
1218
1277
// Highlight userland code and node modules.
1219
1278
let newStack = stack . slice ( 0 , stackStart ) ;
1220
- const lines = stack . slice ( stackStart + 1 ) . split ( '\n' ) ;
1279
+ const lines = getStackFrames ( ctx , err , stack . slice ( stackStart + 1 ) ) ;
1221
1280
for ( const line of lines ) {
1222
1281
const core = line . match ( coreModuleRegExp ) ;
1223
1282
if ( core !== null && NativeModule . exists ( core [ 1 ] ) ) {
0 commit comments