@@ -26,6 +26,21 @@ const {
2626const messages = new Map ( ) ;
2727const codes = { } ;
2828
29+ const classRegExp = / ^ ( [ A - Z ] [ a - z 0 - 9 ] * ) + $ / ;
30+ // Sorted by a rough estimate on most frequently used entries.
31+ const kTypes = [
32+ 'string' ,
33+ 'function' ,
34+ 'number' ,
35+ 'object' ,
36+ // Accept 'Function' and 'Object' as alternative to the lower cased version.
37+ 'Function' ,
38+ 'Object' ,
39+ 'boolean' ,
40+ 'bigint' ,
41+ 'symbol'
42+ ] ;
43+
2944const { kMaxLength } = internalBinding ( 'buffer' ) ;
3045
3146const MainContextError = Error ;
@@ -616,26 +631,6 @@ function isStackOverflowError(err) {
616631 err . message === maxStack_ErrorMessage ;
617632}
618633
619- function oneOf ( expected , thing ) {
620- assert ( typeof thing === 'string' , '`thing` has to be of type string' ) ;
621- if ( ArrayIsArray ( expected ) ) {
622- const len = expected . length ;
623- assert ( len > 0 ,
624- 'At least one expected value needs to be specified' ) ;
625- expected = expected . map ( ( i ) => String ( i ) ) ;
626- if ( len > 2 ) {
627- return `one of ${ thing } ${ expected . slice ( 0 , len - 1 ) . join ( ', ' ) } , or ` +
628- expected [ len - 1 ] ;
629- } else if ( len === 2 ) {
630- return `one of ${ thing } ${ expected [ 0 ] } or ${ expected [ 1 ] } ` ;
631- } else {
632- return `of ${ thing } ${ expected [ 0 ] } ` ;
633- }
634- } else {
635- return `of ${ thing } ${ String ( expected ) } ` ;
636- }
637- }
638-
639634// Only use this for integers! Decimal numbers do not work with this function.
640635function addNumericalSeparator ( val ) {
641636 let res = '' ;
@@ -934,27 +929,114 @@ E('ERR_INVALID_ADDRESS_FAMILY', function(addressType, host, port) {
934929E ( 'ERR_INVALID_ARG_TYPE' ,
935930 ( name , expected , actual ) => {
936931 assert ( typeof name === 'string' , "'name' must be a string" ) ;
932+ if ( ! ArrayIsArray ( expected ) ) {
933+ expected = [ expected ] ;
934+ }
935+
936+ let msg = 'The ' ;
937+ if ( name . endsWith ( ' argument' ) ) {
938+ // For cases like 'first argument'
939+ msg += `${ name } ` ;
940+ } else {
941+ const type = name . includes ( '.' ) ? 'property' : 'argument' ;
942+ msg += `"${ name } " ${ type } ` ;
943+ }
937944
938945 // determiner: 'must be' or 'must not be'
939- let determiner ;
940946 if ( typeof expected === 'string' && expected . startsWith ( 'not ' ) ) {
941- determiner = 'must not be' ;
947+ msg + = 'must not be ' ;
942948 expected = expected . replace ( / ^ n o t / , '' ) ;
943949 } else {
944- determiner = 'must be' ;
950+ msg + = 'must be ' ;
945951 }
946952
947- let msg ;
948- if ( name . endsWith ( ' argument' ) ) {
949- // For cases like 'first argument'
950- msg = `The ${ name } ${ determiner } ${ oneOf ( expected , 'type' ) } ` ;
951- } else {
952- const type = name . includes ( '.' ) ? 'property' : 'argument' ;
953- msg = `The "${ name } " ${ type } ${ determiner } ${ oneOf ( expected , 'type' ) } ` ;
953+ const types = [ ] ;
954+ const instances = [ ] ;
955+ const other = [ ] ;
956+
957+ for ( const value of expected ) {
958+ assert ( typeof value === 'string' ,
959+ 'All expected entries have to be of type string' ) ;
960+ if ( kTypes . includes ( value ) ) {
961+ types . push ( value . toLowerCase ( ) ) ;
962+ } else if ( classRegExp . test ( value ) ) {
963+ instances . push ( value ) ;
964+ } else {
965+ assert ( value !== 'object' ,
966+ 'The value "object" should be written as "Object"' ) ;
967+ other . push ( value ) ;
968+ }
954969 }
955970
956- // TODO(BridgeAR): Improve the output by showing `null` and similar.
957- msg += `. Received type ${ typeof actual } ` ;
971+ // Special handle `object` in case other instances are allowed to outline
972+ // the differences between each other.
973+ if ( instances . length > 0 ) {
974+ const pos = types . indexOf ( 'object' ) ;
975+ if ( pos !== - 1 ) {
976+ types . splice ( pos , 1 ) ;
977+ instances . push ( 'Object' ) ;
978+ }
979+ }
980+
981+ if ( types . length > 0 ) {
982+ if ( types . length > 2 ) {
983+ const last = types . pop ( ) ;
984+ msg += `one of type ${ types . join ( ', ' ) } , or ${ last } ` ;
985+ } else if ( types . length === 2 ) {
986+ msg += `one of type ${ types [ 0 ] } or ${ types [ 1 ] } ` ;
987+ } else {
988+ msg += `of type ${ types [ 0 ] } ` ;
989+ }
990+ if ( instances . length > 0 || other . length > 0 )
991+ msg += ' or ' ;
992+ }
993+
994+ if ( instances . length > 0 ) {
995+ if ( instances . length > 2 ) {
996+ const last = instances . pop ( ) ;
997+ msg += `an instance of ${ instances . join ( ', ' ) } , or ${ last } ` ;
998+ } else {
999+ msg += `an instance of ${ instances [ 0 ] } ` ;
1000+ if ( instances . length === 2 ) {
1001+ msg += ` or ${ instances [ 1 ] } ` ;
1002+ }
1003+ }
1004+ if ( other . length > 0 )
1005+ msg += ' or ' ;
1006+ }
1007+
1008+ if ( other . length > 0 ) {
1009+ if ( other . length > 2 ) {
1010+ const last = other . pop ( ) ;
1011+ msg += `one of ${ other . join ( ', ' ) } , or ${ last } ` ;
1012+ } else if ( other . length === 2 ) {
1013+ msg += `one of ${ other [ 0 ] } or ${ other [ 1 ] } ` ;
1014+ } else {
1015+ if ( other [ 0 ] . toLowerCase ( ) !== other [ 0 ] )
1016+ msg += 'an ' ;
1017+ msg += `${ other [ 0 ] } ` ;
1018+ }
1019+ }
1020+
1021+ if ( actual == null ) {
1022+ msg += `. Received ${ actual } ` ;
1023+ } else if ( typeof actual === 'function' && actual . name ) {
1024+ msg += `. Received function ${ actual . name } ` ;
1025+ } else if ( typeof actual === 'object' ) {
1026+ if ( actual . constructor && actual . constructor . name ) {
1027+ msg += `. Received an instance of ${ actual . constructor . name } ` ;
1028+ } else {
1029+ const inspected = lazyInternalUtilInspect ( )
1030+ . inspect ( actual , { depth : - 1 } ) ;
1031+ msg += `. Received ${ inspected } ` ;
1032+ }
1033+ } else {
1034+ let inspected = lazyInternalUtilInspect ( )
1035+ . inspect ( actual , { colors : false } ) ;
1036+ if ( inspected . length > 25 )
1037+ inspected = `${ inspected . slice ( 0 , 25 ) } ...` ;
1038+ msg += `. Received type ${ typeof actual } (${ inspected } )` ;
1039+ }
9581040 return msg ;
9591041 } , TypeError ) ;
9601042E ( 'ERR_INVALID_ARG_VALUE' , ( name , value , reason = 'is invalid' ) => {
@@ -1042,7 +1124,15 @@ E('ERR_INVALID_URL', function(input) {
10421124 return `Invalid URL: ${ input } ` ;
10431125} , TypeError ) ;
10441126E ( 'ERR_INVALID_URL_SCHEME' ,
1045- ( expected ) => `The URL must be ${ oneOf ( expected , 'scheme' ) } ` , TypeError ) ;
1127+ ( expected ) => {
1128+ if ( typeof expected === 'string' )
1129+ expected = [ expected ] ;
1130+ assert ( expected . length <= 2 ) ;
1131+ const res = expected . length === 2 ?
1132+ `one of scheme ${ expected [ 0 ] } or ${ expected [ 1 ] } ` :
1133+ `of scheme ${ expected [ 0 ] } ` ;
1134+ return `The URL must be ${ res } ` ;
1135+ } , TypeError ) ;
10461136E ( 'ERR_IPC_CHANNEL_CLOSED' , 'Channel closed' , Error ) ;
10471137E ( 'ERR_IPC_DISCONNECTED' , 'IPC channel is already disconnected' , Error ) ;
10481138E ( 'ERR_IPC_ONE_PIPE' , 'Child process can have only one IPC pipe' , Error ) ;
0 commit comments