@@ -4,55 +4,108 @@ const babel = require('@babel/parser');
4
4
const expression = require ( 'eval-estree-expression' ) ;
5
5
const { evaluate } = expression ;
6
6
7
- const isObject = v => v !== null && typeof v === 'object' && ! Array . isArray ( v ) ;
7
+ const isAST = value => isObject ( value ) && hasOwnProperty . call ( value , 'type' ) ;
8
+
9
+ const isObject = value => {
10
+ return value !== null && typeof value === 'object' && ! Array . isArray ( value ) ;
11
+ } ;
8
12
9
13
const isPrimitive = value => {
10
14
return value == null || ( typeof value !== 'object' && typeof value !== 'function' ) ;
11
15
} ;
12
16
17
+ const LITERALS = {
18
+ 'undefined' : undefined ,
19
+ 'null' : null ,
20
+ 'true' : true ,
21
+ 'false' : false
22
+ } ;
23
+
13
24
/**
14
- * Returns true if the given value is truthy, or the `left` value is contained within
15
- * the `right` value.
25
+ * Returns true if the given value is truthy, or the `value` ("left") is
26
+ * equal to or contained within the `context` ("right") value. This method is
27
+ * used by the `whence()` function (the main export), but you can use this
28
+ * method directly if you don't want the values to be evaluated.
16
29
*
17
30
* @name equal
18
- * @param {any } `left ` The value to test.
19
- * @param {Object } `right ` The value to compare against.
31
+ * @param {any } `value ` The value to test.
32
+ * @param {Object } `context ` The value to compare against.
20
33
* @param {[type] } `parent`
21
34
* @return {Boolean } Returns true or false.
22
35
* @api public
23
36
*/
24
37
25
- const equal = ( left , right , parent ) => {
26
- if ( left === right ) return true ;
38
+ const equal = ( value , context , options = { } ) => {
39
+ const eq = ( a , b , parent , depth = 0 ) => {
40
+ if ( a === b ) return true ;
27
41
28
- if ( typeof left === 'boolean' && ! parent ) {
29
- if ( isPrimitive ( right ) ) return left === right ;
30
- return left ;
31
- }
42
+ if ( a === 'undefined' || a === 'null' ) {
43
+ return a === b ;
44
+ }
32
45
33
- if ( isPrimitive ( left ) && isObject ( right ) ) {
34
- return Boolean ( right [ left ] ) ;
35
- }
46
+ if ( typeof a === 'boolean' && ( ! parent || parent === context ) ) {
47
+ return typeof b === 'boolean' ? a === b : a ;
48
+ }
36
49
37
- if ( isPrimitive ( left ) && Array . isArray ( right ) ) {
38
- return right . includes ( left ) ;
39
- }
50
+ if ( ( a === 'true' || a === 'false' ) && ( ! parent || parent === context ) ) {
51
+ return a === 'true' ;
52
+ }
40
53
41
- if ( Array . isArray ( left ) ) {
42
- if ( isObject ( right ) ) {
43
- return left . every ( ele => equal ( ele , right , left ) ) ;
54
+ // only call function values at the root
55
+ if ( typeof a === 'function' && depth === 0 ) {
56
+ return a . call ( b , b , options ) ;
44
57
}
45
58
46
- if ( Array . isArray ( right ) ) {
47
- return left . every ( ( ele , i ) => equal ( ele , right [ i ] , left ) ) ;
59
+ if ( typeof a === 'string' && ( isObject ( b ) && b === context || ( depth === 0 && context === undefined ) ) ) {
60
+ if ( options . castBoolean === false || ( b && hasOwnProperty . call ( b , a ) ) ) {
61
+ return Boolean ( b [ a ] ) ;
62
+ }
63
+
64
+ return whence . sync ( a , b , options ) ;
48
65
}
49
- }
50
66
51
- if ( isObject ( left ) ) {
52
- return isObject ( right ) && Object . entries ( left ) . every ( ( [ k , v ] ) => equal ( v , right [ k ] , left ) ) ;
53
- }
67
+ if ( isPrimitive ( a ) && isObject ( b ) ) {
68
+ return Boolean ( b [ a ] ) ;
69
+ }
70
+
71
+ if ( isPrimitive ( a ) && Array . isArray ( b ) ) {
72
+ return b . includes ( a ) ;
73
+ }
74
+
75
+ if ( a instanceof RegExp ) {
76
+ return ! ( b instanceof RegExp ) ? false : a . toString ( ) === b . toString ( ) ;
77
+ }
54
78
55
- return false ;
79
+ if ( a instanceof Date ) {
80
+ return ! ( b instanceof Date ) ? false : a . toString ( ) === b . toString ( ) ;
81
+ }
82
+
83
+ if ( a instanceof Set ) {
84
+ return b instanceof Set && eq ( [ ...a ] , [ ...b ] , a , depth + 1 ) ;
85
+ }
86
+
87
+ if ( a instanceof Map ) {
88
+ return b instanceof Map && [ ...a ] . every ( ( [ k , v ] ) => eq ( v , b . get ( k ) , a , depth + 1 ) ) ;
89
+ }
90
+
91
+ if ( Array . isArray ( a ) ) {
92
+ if ( isObject ( b ) ) {
93
+ return a . every ( ele => eq ( ele , b , a , depth + 1 ) ) ;
94
+ }
95
+
96
+ if ( Array . isArray ( b ) ) {
97
+ return a . every ( ( ele , i ) => eq ( ele , b [ i ] , a , depth + 1 ) ) ;
98
+ }
99
+ }
100
+
101
+ if ( isObject ( a ) ) {
102
+ return isObject ( b ) && Object . entries ( a ) . every ( ( [ k , v ] ) => eq ( v , b [ k ] , a , depth + 1 ) ) ;
103
+ }
104
+
105
+ return false ;
106
+ } ;
107
+
108
+ return eq ( value , context ) ;
56
109
} ;
57
110
58
111
/**
@@ -66,9 +119,9 @@ const equal = (left, right, parent) => {
66
119
* // Resuls in something like this:
67
120
* // Node {
68
121
* // type: 'BinaryExpression',
69
- * // left : Node { type: 'Identifier', name: 'platform' },
122
+ * // value : Node { type: 'Identifier', name: 'platform' },
70
123
* // operator: '===',
71
- * // right : Node {
124
+ * // context : Node {
72
125
* // type: 'StringLiteral',
73
126
* // extra: { rawValue: 'darwin', raw: '"darwin"' },
74
127
* // value: 'darwin'
@@ -115,7 +168,22 @@ const parse = (source, options = {}) => {
115
168
* @api public
116
169
*/
117
170
118
- const whence = ( source , context = { } , options = { } ) => compile ( source , options ) ( context ) ;
171
+ const whence = async ( source , context , options = { } ) => {
172
+ if ( isAST ( source ) ) {
173
+ return compile ( source , options ) ( context ) ;
174
+ }
175
+
176
+ if ( typeof source !== 'string' || ( isPrimitive ( context ) && context !== undefined ) ) {
177
+ return equal ( source , context , options ) ;
178
+ }
179
+
180
+ if ( hasOwnProperty . call ( LITERALS , source ) ) {
181
+ return options . castBoolean !== false ? Boolean ( LITERALS [ source ] ) : LITERALS [ source ] ;
182
+ }
183
+
184
+ const result = compile ( source , options ) ( context ) ;
185
+ return options . castBoolean !== false ? Boolean ( await result ) : result ;
186
+ } ;
119
187
120
188
/**
121
189
* Synchronous version of [whence](#whence). Aliased as `whence.sync()`.
@@ -133,7 +201,22 @@ const whence = (source, context = {}, options = {}) => compile(source, options)(
133
201
* @api public
134
202
*/
135
203
136
- const whenceSync = ( source , context = { } , options = { } ) => compileSync ( source , options ) ( context ) ;
204
+ const whenceSync = ( source , context , options = { } ) => {
205
+ if ( isAST ( source ) ) {
206
+ return compile . sync ( source , options ) ( context ) ;
207
+ }
208
+
209
+ if ( typeof source !== 'string' || ( isPrimitive ( context ) && context !== undefined ) ) {
210
+ return equal ( source , context , options ) ;
211
+ }
212
+
213
+ if ( hasOwnProperty . call ( LITERALS , source ) ) {
214
+ return options . castBoolean !== false ? Boolean ( LITERALS [ source ] ) : LITERALS [ source ] ;
215
+ }
216
+
217
+ const result = compile . sync ( source , options ) ( context ) ;
218
+ return options . castBoolean !== false ? Boolean ( result ) : result ;
219
+ } ;
137
220
138
221
/**
139
222
* Compiles the given expression and returns an async function.
@@ -153,10 +236,11 @@ const whenceSync = (source, context = {}, options = {}) => compileSync(source, o
153
236
*/
154
237
155
238
const compile = ( source , options ) => {
156
- const ast = parse ( source , options ) ;
239
+ const opts = { strictVariables : false , booleanLogicalOperators : true , ...options } ;
240
+ const ast = parse ( source , opts ) ;
157
241
158
242
return context => {
159
- return evaluate ( ast , context , options ) ;
243
+ return evaluate ( ast , context , opts ) ;
160
244
} ;
161
245
} ;
162
246
@@ -178,10 +262,11 @@ const compile = (source, options) => {
178
262
*/
179
263
180
264
const compileSync = ( source , options ) => {
181
- const ast = parse ( source , options ) ;
265
+ const opts = { strictVariables : false , booleanLogicalOperators : true , ...options } ;
266
+ const ast = parse ( source , opts ) ;
182
267
183
268
return context => {
184
- return evaluate . sync ( ast , context , options ) ;
269
+ return evaluate . sync ( ast , context , opts ) ;
185
270
} ;
186
271
} ;
187
272
0 commit comments