@@ -23,6 +23,21 @@ const {
23
23
const messages = new Map ( ) ;
24
24
const codes = { } ;
25
25
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
+
26
41
const { kMaxLength } = internalBinding ( 'buffer' ) ;
27
42
28
43
const MainContextError = Error ;
@@ -610,26 +625,6 @@ function isStackOverflowError(err) {
610
625
err . message === maxStack_ErrorMessage ;
611
626
}
612
627
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
-
633
628
// Only use this for integers! Decimal numbers do not work with this function.
634
629
function addNumericalSeparator ( val ) {
635
630
let res = '' ;
@@ -926,27 +921,114 @@ E('ERR_INVALID_ADDRESS_FAMILY', function(addressType, host, port) {
926
921
E ( 'ERR_INVALID_ARG_TYPE' ,
927
922
( name , expected , actual ) => {
928
923
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
+ }
929
936
930
937
// determiner: 'must be' or 'must not be'
931
- let determiner ;
932
938
if ( typeof expected === 'string' && expected . startsWith ( 'not ' ) ) {
933
- determiner = 'must not be' ;
939
+ msg + = 'must not be ' ;
934
940
expected = expected . replace ( / ^ n o t / , '' ) ;
935
941
} else {
936
- determiner = 'must be' ;
942
+ msg + = 'must be ' ;
937
943
}
938
944
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
+ }
946
961
}
947
962
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
+ }
950
1032
return msg ;
951
1033
} , TypeError ) ;
952
1034
E ( 'ERR_INVALID_ARG_VALUE' , ( name , value , reason = 'is invalid' ) => {
@@ -1034,7 +1116,15 @@ E('ERR_INVALID_URL', function(input) {
1034
1116
return `Invalid URL: ${ input } ` ;
1035
1117
} , TypeError ) ;
1036
1118
E ( '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 ) ;
1038
1128
E ( 'ERR_IPC_CHANNEL_CLOSED' , 'Channel closed' , Error ) ;
1039
1129
E ( 'ERR_IPC_DISCONNECTED' , 'IPC channel is already disconnected' , Error ) ;
1040
1130
E ( 'ERR_IPC_ONE_PIPE' , 'Child process can have only one IPC pipe' , Error ) ;
0 commit comments