@@ -26,6 +26,21 @@ const {
26
26
const messages = new Map ( ) ;
27
27
const codes = { } ;
28
28
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
+
29
44
const { kMaxLength } = internalBinding ( 'buffer' ) ;
30
45
31
46
const MainContextError = Error ;
@@ -616,26 +631,6 @@ function isStackOverflowError(err) {
616
631
err . message === maxStack_ErrorMessage ;
617
632
}
618
633
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
-
639
634
// Only use this for integers! Decimal numbers do not work with this function.
640
635
function addNumericalSeparator ( val ) {
641
636
let res = '' ;
@@ -934,27 +929,114 @@ E('ERR_INVALID_ADDRESS_FAMILY', function(addressType, host, port) {
934
929
E ( 'ERR_INVALID_ARG_TYPE' ,
935
930
( name , expected , actual ) => {
936
931
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
+ }
937
944
938
945
// determiner: 'must be' or 'must not be'
939
- let determiner ;
940
946
if ( typeof expected === 'string' && expected . startsWith ( 'not ' ) ) {
941
- determiner = 'must not be' ;
947
+ msg + = 'must not be ' ;
942
948
expected = expected . replace ( / ^ n o t / , '' ) ;
943
949
} else {
944
- determiner = 'must be' ;
950
+ msg + = 'must be ' ;
945
951
}
946
952
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
+ }
954
969
}
955
970
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
+ }
958
1040
return msg ;
959
1041
} , TypeError ) ;
960
1042
E ( 'ERR_INVALID_ARG_VALUE' , ( name , value , reason = 'is invalid' ) => {
@@ -1042,7 +1124,15 @@ E('ERR_INVALID_URL', function(input) {
1042
1124
return `Invalid URL: ${ input } ` ;
1043
1125
} , TypeError ) ;
1044
1126
E ( '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 ) ;
1046
1136
E ( 'ERR_IPC_CHANNEL_CLOSED' , 'Channel closed' , Error ) ;
1047
1137
E ( 'ERR_IPC_DISCONNECTED' , 'IPC channel is already disconnected' , Error ) ;
1048
1138
E ( 'ERR_IPC_ONE_PIPE' , 'Child process can have only one IPC pipe' , Error ) ;
0 commit comments