@@ -8,7 +8,7 @@ import { build, buildString } from './compiler.js'
8
8
import chainingSupported from './utilities/chainingSupported.js'
9
9
import InvalidControlInput from './errors/InvalidControlInput.js'
10
10
import legacyMethods from './legacy.js'
11
- import { downgrade } from './utilities/downgrade.js'
11
+ import { precoerceNumber } from './utilities/downgrade.js'
12
12
13
13
function isDeterministic ( method , engine , buildState ) {
14
14
if ( Array . isArray ( method ) ) {
@@ -56,15 +56,16 @@ const oldAll = createArrayIterativeMethod('every', true)
56
56
const defaultMethods = {
57
57
'+' : ( data ) => {
58
58
if ( ! data ) return 0
59
- if ( typeof data === 'string' ) return + data
60
- if ( typeof data === 'number' ) return + data
61
- if ( typeof data === 'boolean' ) return + data
59
+ if ( typeof data === 'string' ) return precoerceNumber ( + data )
60
+ if ( typeof data === 'number' ) return precoerceNumber ( + data )
61
+ if ( typeof data === 'boolean' ) return precoerceNumber ( + data )
62
62
if ( typeof data === 'object' && ! Array . isArray ( data ) ) throw new Error ( 'NaN' )
63
63
let res = 0
64
64
for ( let i = 0 ; i < data . length ; i ++ ) {
65
65
if ( data [ i ] && typeof data [ i ] === 'object' ) throw new Error ( 'NaN' )
66
66
res += + data [ i ]
67
67
}
68
+ if ( Number . isNaN ( res ) ) throw new Error ( 'NaN' )
68
69
return res
69
70
} ,
70
71
'*' : ( data ) => {
@@ -73,6 +74,7 @@ const defaultMethods = {
73
74
if ( data [ i ] && typeof data [ i ] === 'object' ) throw new Error ( 'NaN' )
74
75
res *= + data [ i ]
75
76
}
77
+ if ( Number . isNaN ( res ) ) throw new Error ( 'NaN' )
76
78
return res
77
79
} ,
78
80
'/' : ( data ) => {
@@ -82,13 +84,14 @@ const defaultMethods = {
82
84
if ( ( data [ i ] && typeof data [ i ] === 'object' ) || ! data [ i ] ) throw new Error ( 'NaN' )
83
85
res /= + data [ i ]
84
86
}
87
+ if ( Number . isNaN ( res ) ) throw new Error ( 'NaN' )
85
88
return res
86
89
} ,
87
90
'-' : ( data ) => {
88
91
if ( ! data ) return 0
89
- if ( typeof data === 'string' ) return - data
90
- if ( typeof data === 'number' ) return - data
91
- if ( typeof data === 'boolean' ) return - data
92
+ if ( typeof data === 'string' ) return precoerceNumber ( - data )
93
+ if ( typeof data === 'number' ) return precoerceNumber ( - data )
94
+ if ( typeof data === 'boolean' ) return precoerceNumber ( - data )
92
95
if ( typeof data === 'object' && ! Array . isArray ( data ) ) throw new Error ( 'NaN' )
93
96
if ( data [ 0 ] && typeof data [ 0 ] === 'object' ) throw new Error ( 'NaN' )
94
97
if ( data . length === 1 ) return - data [ 0 ]
@@ -97,6 +100,7 @@ const defaultMethods = {
97
100
if ( data [ i ] && typeof data [ i ] === 'object' ) throw new Error ( 'NaN' )
98
101
res -= + data [ i ]
99
102
}
103
+ if ( Number . isNaN ( res ) ) throw new Error ( 'NaN' )
100
104
return res
101
105
} ,
102
106
'%' : ( data ) => {
@@ -106,6 +110,7 @@ const defaultMethods = {
106
110
if ( data [ i ] && typeof data [ i ] === 'object' ) throw new Error ( 'NaN' )
107
111
res %= + data [ i ]
108
112
}
113
+ if ( Number . isNaN ( res ) ) throw new Error ( 'NaN' )
109
114
return res
110
115
} ,
111
116
error : ( type ) => {
@@ -281,7 +286,51 @@ const defaultMethods = {
281
286
} ,
282
287
lazy : true
283
288
} ,
284
- '??' : defineCoalesce ( ) ,
289
+ '??' : {
290
+ [ Sync ] : ( data , buildState ) => isSyncDeep ( data , buildState . engine , buildState ) ,
291
+ method : ( arr , _1 , _2 , engine ) => {
292
+ // See "executeInLoop" above
293
+ const executeInLoop = Array . isArray ( arr )
294
+ if ( ! executeInLoop ) arr = engine . run ( arr , _1 , { above : _2 } )
295
+
296
+ let item
297
+ for ( let i = 0 ; i < arr . length ; i ++ ) {
298
+ item = executeInLoop ? engine . run ( arr [ i ] , _1 , { above : _2 } ) : arr [ i ]
299
+ if ( item !== null && item !== undefined ) return item
300
+ }
301
+
302
+ if ( item === undefined ) return null
303
+ return item
304
+ } ,
305
+ asyncMethod : async ( arr , _1 , _2 , engine ) => {
306
+ // See "executeInLoop" above
307
+ const executeInLoop = Array . isArray ( arr )
308
+ if ( ! executeInLoop ) arr = await engine . run ( arr , _1 , { above : _2 } )
309
+
310
+ let item
311
+ for ( let i = 0 ; i < arr . length ; i ++ ) {
312
+ item = executeInLoop ? await engine . run ( arr [ i ] , _1 , { above : _2 } ) : arr [ i ]
313
+ if ( item !== null && item !== undefined ) return item
314
+ }
315
+
316
+ if ( item === undefined ) return null
317
+ return item
318
+ } ,
319
+ deterministic : ( data , buildState ) => isDeterministic ( data , buildState . engine , buildState ) ,
320
+ compile : ( data , buildState ) => {
321
+ if ( ! chainingSupported ) return false
322
+
323
+ if ( Array . isArray ( data ) && data . length ) {
324
+ return `(${ data . map ( ( i , x ) => {
325
+ const built = buildString ( i , buildState )
326
+ if ( Array . isArray ( i ) || ! i || typeof i !== 'object' || x === data . length - 1 ) return built
327
+ return '(' + built + ')'
328
+ } ) . join ( ' ?? ' ) } )`
329
+ }
330
+ return `(${ buildString ( data , buildState ) } ).reduce((a,b) => (a) ?? b, null)`
331
+ } ,
332
+ lazy : true
333
+ } ,
285
334
try : {
286
335
[ Sync ] : ( data , buildState ) => isSyncDeep ( data , buildState . engine , buildState ) ,
287
336
method : ( arr , _1 , _2 , engine ) => {
@@ -327,9 +376,6 @@ const defaultMethods = {
327
376
throw lastError
328
377
} ,
329
378
deterministic : ( data , buildState ) => isDeterministic ( data , buildState . engine , buildState ) ,
330
- compile : ( data , buildState ) => {
331
- return false
332
- } ,
333
379
lazy : true
334
380
} ,
335
381
and : {
@@ -753,64 +799,6 @@ const defaultMethods = {
753
799
}
754
800
}
755
801
756
- /**
757
- * Defines separate coalesce methods
758
- */
759
- function defineCoalesce ( func , panic ) {
760
- let downgrade
761
- if ( func ) downgrade = func
762
- else downgrade = ( a ) => a
763
-
764
- return {
765
- [ Sync ] : ( data , buildState ) => isSyncDeep ( data , buildState . engine , buildState ) ,
766
- method : ( arr , _1 , _2 , engine ) => {
767
- // See "executeInLoop" above
768
- const executeInLoop = Array . isArray ( arr )
769
- if ( ! executeInLoop ) arr = engine . run ( arr , _1 , { above : _2 } )
770
-
771
- let item
772
- for ( let i = 0 ; i < arr . length ; i ++ ) {
773
- item = executeInLoop ? engine . run ( arr [ i ] , _1 , { above : _2 } ) : arr [ i ]
774
- if ( downgrade ( item ) !== null && item !== undefined ) return item
775
- }
776
-
777
- if ( item === undefined ) return null
778
- if ( panic ) throw item
779
- return item
780
- } ,
781
- asyncMethod : async ( arr , _1 , _2 , engine ) => {
782
- // See "executeInLoop" above
783
- const executeInLoop = Array . isArray ( arr )
784
- if ( ! executeInLoop ) arr = await engine . run ( arr , _1 , { above : _2 } )
785
-
786
- let item
787
- for ( let i = 0 ; i < arr . length ; i ++ ) {
788
- item = executeInLoop ? await engine . run ( arr [ i ] , _1 , { above : _2 } ) : arr [ i ]
789
- if ( downgrade ( item ) !== null && item !== undefined ) return item
790
- }
791
-
792
- if ( item === undefined ) return null
793
- if ( panic ) throw item
794
- return item
795
- } ,
796
- deterministic : ( data , buildState ) => isDeterministic ( data , buildState . engine , buildState ) ,
797
- compile : ( data , buildState ) => {
798
- if ( ! chainingSupported ) return false
799
- const funcCall = func ? 'downgrade' : ''
800
- if ( Array . isArray ( data ) && data . length ) {
801
- return `(${ data . map ( ( i , x ) => {
802
- const built = buildString ( i , buildState )
803
- if ( panic && x === data . length - 1 ) return `(typeof ((prev = ${ built } ) || 0).error !== 'undefined' || Number.isNaN(prev) ? (() => { throw prev.error })() : prev)`
804
- if ( Array . isArray ( i ) || ! i || typeof i !== 'object' || x === data . length - 1 ) return built
805
- return `${ funcCall } (` + built + ')'
806
- } ) . join ( ' ?? ' ) } )`
807
- }
808
- return `(${ buildString ( data , buildState ) } ).reduce((a,b) => ${ funcCall } (a) ?? b, null)`
809
- } ,
810
- lazy : true
811
- }
812
- }
813
-
814
802
function createArrayIterativeMethod ( name , useTruthy = false ) {
815
803
return {
816
804
deterministic : ( data , buildState ) => {
@@ -939,15 +927,15 @@ defaultMethods.if.compile = function (data, buildState) {
939
927
* Transforms the operands of the arithmetic operation to numbers.
940
928
*/
941
929
function numberCoercion ( i , buildState ) {
942
- if ( Array . isArray ( i ) ) return 'NaN'
943
- if ( typeof i === 'string' || typeof i === 'number' || typeof i === 'boolean' ) return `(+${ buildString ( i , buildState ) } )`
930
+ if ( Array . isArray ( i ) ) return 'precoerceNumber( NaN) '
931
+ if ( typeof i === 'string' || typeof i === 'number' || typeof i === 'boolean' ) return `precoerceNumber (+${ buildString ( i , buildState ) } )`
944
932
return `(+precoerceNumber(${ buildString ( i , buildState ) } ))`
945
933
}
946
934
947
935
// @ts -ignore Allow custom attribute
948
936
defaultMethods [ '+' ] . compile = function ( data , buildState ) {
949
937
if ( Array . isArray ( data ) ) return `(${ data . map ( i => numberCoercion ( i , buildState ) ) . join ( ' + ' ) } )`
950
- if ( typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean' ) return `(+${ buildString ( data , buildState ) } )`
938
+ if ( typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean' ) return `precoerceNumber (+${ buildString ( data , buildState ) } )`
951
939
return buildState . compile `(Array.isArray(prev = ${ data } ) ? prev.reduce((a,b) => (+a)+(+precoerceNumber(b)), 0) : +precoerceNumber(prev))`
952
940
}
953
941
0 commit comments