@@ -23,6 +23,21 @@ const {
2323const messages = new Map ( ) ;
2424const codes = { } ;
2525
26+ const classRegExp = / ^ ( [ A - Z ] [ a - z 0 - 9 ] * ) + $ / ;
27+ // Sorted by a rough estimate on most frequently used entries.
28+ const kTypes = [
29+ 'string' ,
30+ 'function' ,
31+ 'number' ,
32+ 'object' ,
33+ // Accept 'Function' and 'Object' as alternative to the lower cased version.
34+ 'Function' ,
35+ 'Object' ,
36+ 'boolean' ,
37+ 'bigint' ,
38+ 'symbol'
39+ ] ;
40+
2641const { kMaxLength } = internalBinding ( 'buffer' ) ;
2742
2843const MainContextError = Error ;
@@ -610,26 +625,6 @@ function isStackOverflowError(err) {
610625 err . message === maxStack_ErrorMessage ;
611626}
612627
613- function oneOf ( expected , thing ) {
614- assert ( typeof thing === 'string' , '`thing` has to be of type string' ) ;
615- if ( ArrayIsArray ( expected ) ) {
616- const len = expected . length ;
617- assert ( len > 0 ,
618- 'At least one expected value needs to be specified' ) ;
619- expected = expected . map ( ( i ) => String ( i ) ) ;
620- if ( len > 2 ) {
621- return `one of ${ thing } ${ expected . slice ( 0 , len - 1 ) . join ( ', ' ) } , or ` +
622- expected [ len - 1 ] ;
623- } else if ( len === 2 ) {
624- return `one of ${ thing } ${ expected [ 0 ] } or ${ expected [ 1 ] } ` ;
625- } else {
626- return `of ${ thing } ${ expected [ 0 ] } ` ;
627- }
628- } else {
629- return `of ${ thing } ${ String ( expected ) } ` ;
630- }
631- }
632-
633628// Only use this for integers! Decimal numbers do not work with this function.
634629function addNumericalSeparator ( val ) {
635630 let res = '' ;
@@ -926,27 +921,114 @@ E('ERR_INVALID_ADDRESS_FAMILY', function(addressType, host, port) {
926921E ( 'ERR_INVALID_ARG_TYPE' ,
927922 ( name , expected , actual ) => {
928923 assert ( typeof name === 'string' , "'name' must be a string" ) ;
924+ if ( ! ArrayIsArray ( expected ) ) {
925+ expected = [ expected ] ;
926+ }
927+
928+ let msg = 'The ' ;
929+ if ( name . endsWith ( ' argument' ) ) {
930+ // For cases like 'first argument'
931+ msg += `${ name } ` ;
932+ } else {
933+ const type = name . includes ( '.' ) ? 'property' : 'argument' ;
934+ msg += `"${ name } " ${ type } ` ;
935+ }
929936
930937 // determiner: 'must be' or 'must not be'
931- let determiner ;
932938 if ( typeof expected === 'string' && expected . startsWith ( 'not ' ) ) {
933- determiner = 'must not be' ;
939+ msg + = 'must not be ' ;
934940 expected = expected . replace ( / ^ n o t / , '' ) ;
935941 } else {
936- determiner = 'must be' ;
942+ msg + = 'must be ' ;
937943 }
938944
939- let msg ;
940- if ( name . endsWith ( ' argument' ) ) {
941- // For cases like 'first argument'
942- msg = `The ${ name } ${ determiner } ${ oneOf ( expected , 'type' ) } ` ;
943- } else {
944- const type = name . includes ( '.' ) ? 'property' : 'argument' ;
945- msg = `The "${ name } " ${ type } ${ determiner } ${ oneOf ( expected , 'type' ) } ` ;
945+ const types = [ ] ;
946+ const instances = [ ] ;
947+ const other = [ ] ;
948+
949+ for ( const value of expected ) {
950+ assert ( typeof value === 'string' ,
951+ 'All expected entries have to be of type string' ) ;
952+ if ( kTypes . includes ( value ) ) {
953+ types . push ( value . toLowerCase ( ) ) ;
954+ } else if ( classRegExp . test ( value ) ) {
955+ instances . push ( value ) ;
956+ } else {
957+ assert ( value !== 'object' ,
958+ 'The value "object" should be written as "Object"' ) ;
959+ other . push ( value ) ;
960+ }
946961 }
947962
948- // TODO(BridgeAR): Improve the output by showing `null` and similar.
949- msg += `. Received type ${ typeof actual } ` ;
963+ // Special handle `object` in case other instances are allowed to outline
964+ // the differences between each other.
965+ if ( instances . length > 0 ) {
966+ const pos = types . indexOf ( 'object' ) ;
967+ if ( pos !== - 1 ) {
968+ types . splice ( pos , 1 ) ;
969+ instances . push ( 'Object' ) ;
970+ }
971+ }
972+
973+ if ( types . length > 0 ) {
974+ if ( types . length > 2 ) {
975+ const last = types . pop ( ) ;
976+ msg += `one of type ${ types . join ( ', ' ) } , or ${ last } ` ;
977+ } else if ( types . length === 2 ) {
978+ msg += `one of type ${ types [ 0 ] } or ${ types [ 1 ] } ` ;
979+ } else {
980+ msg += `of type ${ types [ 0 ] } ` ;
981+ }
982+ if ( instances . length > 0 || other . length > 0 )
983+ msg += ' or ' ;
984+ }
985+
986+ if ( instances . length > 0 ) {
987+ if ( instances . length > 2 ) {
988+ const last = instances . pop ( ) ;
989+ msg += `an instance of ${ instances . join ( ', ' ) } , or ${ last } ` ;
990+ } else {
991+ msg += `an instance of ${ instances [ 0 ] } ` ;
992+ if ( instances . length === 2 ) {
993+ msg += ` or ${ instances [ 1 ] } ` ;
994+ }
995+ }
996+ if ( other . length > 0 )
997+ msg += ' or ' ;
998+ }
999+
1000+ if ( other . length > 0 ) {
1001+ if ( other . length > 2 ) {
1002+ const last = other . pop ( ) ;
1003+ msg += `one of ${ other . join ( ', ' ) } , or ${ last } ` ;
1004+ } else if ( other . length === 2 ) {
1005+ msg += `one of ${ other [ 0 ] } or ${ other [ 1 ] } ` ;
1006+ } else {
1007+ if ( other [ 0 ] . toLowerCase ( ) !== other [ 0 ] )
1008+ msg += 'an ' ;
1009+ msg += `${ other [ 0 ] } ` ;
1010+ }
1011+ }
1012+
1013+ if ( actual == null ) {
1014+ msg += `. Received ${ actual } ` ;
1015+ } else if ( typeof actual === 'function' && actual . name ) {
1016+ msg += `. Received function ${ actual . name } ` ;
1017+ } else if ( typeof actual === 'object' ) {
1018+ if ( actual . constructor && actual . constructor . name ) {
1019+ msg += `. Received an instance of ${ actual . constructor . name } ` ;
1020+ } else {
1021+ const inspected = lazyInternalUtilInspect ( )
1022+ . inspect ( actual , { depth : - 1 } ) ;
1023+ msg += `. Received ${ inspected } ` ;
1024+ }
1025+ } else {
1026+ let inspected = lazyInternalUtilInspect ( )
1027+ . inspect ( actual , { colors : false } ) ;
1028+ if ( inspected . length > 25 )
1029+ inspected = `${ inspected . slice ( 0 , 25 ) } ...` ;
1030+ msg += `. Received type ${ typeof actual } (${ inspected } )` ;
1031+ }
9501032 return msg ;
9511033 } , TypeError ) ;
9521034E ( 'ERR_INVALID_ARG_VALUE' , ( name , value , reason = 'is invalid' ) => {
@@ -1034,7 +1116,15 @@ E('ERR_INVALID_URL', function(input) {
10341116 return `Invalid URL: ${ input } ` ;
10351117} , TypeError ) ;
10361118E ( 'ERR_INVALID_URL_SCHEME' ,
1037- ( expected ) => `The URL must be ${ oneOf ( expected , 'scheme' ) } ` , TypeError ) ;
1119+ ( expected ) => {
1120+ if ( typeof expected === 'string' )
1121+ expected = [ expected ] ;
1122+ assert ( expected . length <= 2 ) ;
1123+ const res = expected . length === 2 ?
1124+ `one of scheme ${ expected [ 0 ] } or ${ expected [ 1 ] } ` :
1125+ `of scheme ${ expected [ 0 ] } ` ;
1126+ return `The URL must be ${ res } ` ;
1127+ } , TypeError ) ;
10381128E ( 'ERR_IPC_CHANNEL_CLOSED' , 'Channel closed' , Error ) ;
10391129E ( 'ERR_IPC_DISCONNECTED' , 'IPC channel is already disconnected' , Error ) ;
10401130E ( 'ERR_IPC_ONE_PIPE' , 'Child process can have only one IPC pipe' , Error ) ;
0 commit comments