@routier/core 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assertions/index.cjs +19 -8
- package/dist/assertions/index.cjs.map +1 -1
- package/dist/assertions/index.d.ts +5 -1
- package/dist/assertions/index.js +21 -9
- package/dist/assertions/index.js.map +1 -1
- package/dist/codegen/blocks.d.ts +17 -1
- package/dist/codegen/handlers/types.d.ts +13 -5
- package/dist/codegen/index.cjs +28 -0
- package/dist/codegen/index.cjs.map +1 -1
- package/dist/codegen/index.js +28 -0
- package/dist/codegen/index.js.map +1 -1
- package/dist/collections/MemoryDataCollection.d.ts +8 -0
- package/dist/collections/index.cjs +141 -4
- package/dist/collections/index.cjs.map +1 -1
- package/dist/collections/index.js +141 -4
- package/dist/collections/index.js.map +1 -1
- package/dist/expressions/callSource.d.ts +41 -0
- package/dist/expressions/evaluate.d.ts +3 -0
- package/dist/expressions/fold.d.ts +7 -0
- package/dist/expressions/index.cjs +1985 -242
- package/dist/expressions/index.cjs.map +1 -1
- package/dist/expressions/index.d.ts +2 -0
- package/dist/expressions/index.js +1997 -243
- package/dist/expressions/index.js.map +1 -1
- package/dist/expressions/parser.d.ts +42 -1
- package/dist/expressions/types.d.ts +45 -26
- package/dist/expressions/utils.d.ts +19 -1
- package/dist/index.cjs +3139 -680
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3479 -997
- package/dist/index.js.map +1 -1
- package/dist/performance/index.cjs +6 -4
- package/dist/performance/index.cjs.map +1 -1
- package/dist/performance/index.js +6 -4
- package/dist/performance/index.js.map +1 -1
- package/dist/pipeline/index.cjs +6 -4
- package/dist/pipeline/index.cjs.map +1 -1
- package/dist/pipeline/index.js +6 -4
- package/dist/pipeline/index.js.map +1 -1
- package/dist/plugins/EphemeralDataPlugin.d.ts +8 -0
- package/dist/plugins/index.cjs +2921 -411
- package/dist/plugins/index.cjs.map +1 -1
- package/dist/plugins/index.js +2863 -343
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/query/QueryOptionsCollection.d.ts +48 -10
- package/dist/plugins/query/describeFilter.d.ts +83 -0
- package/dist/plugins/query/explain.d.ts +71 -9
- package/dist/plugins/query/index.d.ts +2 -0
- package/dist/plugins/query/join.d.ts +4 -1
- package/dist/plugins/query/renames.d.ts +27 -0
- package/dist/plugins/query/types.d.ts +50 -4
- package/dist/plugins/translators/SqlTranslator.d.ts +15 -0
- package/dist/schema/PropertyInfo.d.ts +0 -1
- package/dist/schema/SchemaDefinition.d.ts +8 -0
- package/dist/schema/changeTracker.d.ts +10 -0
- package/dist/schema/index.cjs +298 -288
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +301 -290
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/types.d.ts +8 -7
- package/dist/schema/utils/storageDates.d.ts +25 -0
- package/dist/transfer/index.cjs.map +1 -1
- package/dist/transfer/index.js.map +1 -1
- package/dist/utilities/index.cjs +306 -68
- package/dist/utilities/index.cjs.map +1 -1
- package/dist/utilities/index.js +306 -68
- package/dist/utilities/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/codegen/utils.d.ts +0 -22
|
@@ -4,6 +4,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
4
4
|
Cv: () => (assertString),
|
|
5
5
|
S6: () => (isValueExpression),
|
|
6
6
|
e3: () => (isPropertyExpression),
|
|
7
|
+
fm: () => (isCallExpression),
|
|
7
8
|
vg: () => (isOperatorExpression),
|
|
8
9
|
xH: () => (isComparatorExpression)
|
|
9
10
|
});
|
|
@@ -77,6 +78,11 @@ function isObjectWithType(value) {
|
|
|
77
78
|
*/ function isValueExpression(value) {
|
|
78
79
|
return isObjectWithType(value) && value.type === "value";
|
|
79
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Type guard: narrows `value` to `CallExpression` when it is an object with `type === "call"`.
|
|
83
|
+
*/ function isCallExpression(value) {
|
|
84
|
+
return isObjectWithType(value) && value.type === "call";
|
|
85
|
+
}
|
|
80
86
|
/**
|
|
81
87
|
* Type guard: narrows `value` to `EmptyExpression` when it is an object with `type === "empty"`.
|
|
82
88
|
*/ function isEmptyExpression(value) {
|
|
@@ -89,62 +95,396 @@ function isObjectWithType(value) {
|
|
|
89
95
|
}
|
|
90
96
|
|
|
91
97
|
|
|
98
|
+
},
|
|
99
|
+
429(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
100
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
101
|
+
N: () => (CALL_SOURCE),
|
|
102
|
+
a: () => (renderCallAsJs)
|
|
103
|
+
});
|
|
104
|
+
const CALL_SOURCE = {
|
|
105
|
+
"to-lower-case": {
|
|
106
|
+
form: "method",
|
|
107
|
+
name: "toLowerCase"
|
|
108
|
+
},
|
|
109
|
+
"to-upper-case": {
|
|
110
|
+
form: "method",
|
|
111
|
+
name: "toUpperCase"
|
|
112
|
+
},
|
|
113
|
+
"length": {
|
|
114
|
+
form: "property",
|
|
115
|
+
name: "length"
|
|
116
|
+
},
|
|
117
|
+
"trim": {
|
|
118
|
+
form: "method",
|
|
119
|
+
name: "trim"
|
|
120
|
+
},
|
|
121
|
+
"trim-start": {
|
|
122
|
+
form: "method",
|
|
123
|
+
name: "trimStart"
|
|
124
|
+
},
|
|
125
|
+
"trim-end": {
|
|
126
|
+
form: "method",
|
|
127
|
+
name: "trimEnd"
|
|
128
|
+
},
|
|
129
|
+
"index-of": {
|
|
130
|
+
form: "method",
|
|
131
|
+
name: "indexOf"
|
|
132
|
+
},
|
|
133
|
+
"substring": {
|
|
134
|
+
form: "method",
|
|
135
|
+
name: "substring"
|
|
136
|
+
},
|
|
137
|
+
"concat": {
|
|
138
|
+
form: "method",
|
|
139
|
+
name: "concat"
|
|
140
|
+
},
|
|
141
|
+
"replace": {
|
|
142
|
+
form: "method",
|
|
143
|
+
name: "replace"
|
|
144
|
+
},
|
|
145
|
+
"replace-all": {
|
|
146
|
+
form: "method",
|
|
147
|
+
name: "replaceAll"
|
|
148
|
+
},
|
|
149
|
+
"absolute": {
|
|
150
|
+
form: "function",
|
|
151
|
+
name: "Math.abs"
|
|
152
|
+
},
|
|
153
|
+
"floor": {
|
|
154
|
+
form: "function",
|
|
155
|
+
name: "Math.floor"
|
|
156
|
+
},
|
|
157
|
+
"ceiling": {
|
|
158
|
+
form: "function",
|
|
159
|
+
name: "Math.ceil"
|
|
160
|
+
},
|
|
161
|
+
"round": {
|
|
162
|
+
form: "function",
|
|
163
|
+
name: "Math.round"
|
|
164
|
+
},
|
|
165
|
+
"sign": {
|
|
166
|
+
form: "function",
|
|
167
|
+
name: "Math.sign"
|
|
168
|
+
},
|
|
169
|
+
"square-root": {
|
|
170
|
+
form: "function",
|
|
171
|
+
name: "Math.sqrt"
|
|
172
|
+
},
|
|
173
|
+
"add": {
|
|
174
|
+
form: "operator",
|
|
175
|
+
symbol: "+"
|
|
176
|
+
},
|
|
177
|
+
"subtract": {
|
|
178
|
+
form: "operator",
|
|
179
|
+
symbol: "-"
|
|
180
|
+
},
|
|
181
|
+
"multiply": {
|
|
182
|
+
form: "operator",
|
|
183
|
+
symbol: "*"
|
|
184
|
+
},
|
|
185
|
+
"divide": {
|
|
186
|
+
form: "operator",
|
|
187
|
+
symbol: "/"
|
|
188
|
+
},
|
|
189
|
+
"modulo": {
|
|
190
|
+
form: "operator",
|
|
191
|
+
symbol: "%"
|
|
192
|
+
},
|
|
193
|
+
"utc-year": {
|
|
194
|
+
form: "method",
|
|
195
|
+
name: "getUTCFullYear"
|
|
196
|
+
},
|
|
197
|
+
"utc-month": {
|
|
198
|
+
form: "method",
|
|
199
|
+
name: "getUTCMonth"
|
|
200
|
+
},
|
|
201
|
+
"utc-day-of-month": {
|
|
202
|
+
form: "method",
|
|
203
|
+
name: "getUTCDate"
|
|
204
|
+
},
|
|
205
|
+
"utc-day-of-week": {
|
|
206
|
+
form: "method",
|
|
207
|
+
name: "getUTCDay"
|
|
208
|
+
},
|
|
209
|
+
"utc-hour": {
|
|
210
|
+
form: "method",
|
|
211
|
+
name: "getUTCHours"
|
|
212
|
+
},
|
|
213
|
+
"utc-minute": {
|
|
214
|
+
form: "method",
|
|
215
|
+
name: "getUTCMinutes"
|
|
216
|
+
},
|
|
217
|
+
"utc-second": {
|
|
218
|
+
form: "method",
|
|
219
|
+
name: "getUTCSeconds"
|
|
220
|
+
},
|
|
221
|
+
"utc-millisecond": {
|
|
222
|
+
form: "method",
|
|
223
|
+
name: "getUTCMilliseconds"
|
|
224
|
+
},
|
|
225
|
+
"epoch-ms": {
|
|
226
|
+
form: "method",
|
|
227
|
+
name: "getTime"
|
|
228
|
+
},
|
|
229
|
+
"to-string": {
|
|
230
|
+
form: "function",
|
|
231
|
+
name: "String"
|
|
232
|
+
},
|
|
233
|
+
"to-number": {
|
|
234
|
+
form: "function",
|
|
235
|
+
name: "Number"
|
|
236
|
+
},
|
|
237
|
+
"to-boolean": {
|
|
238
|
+
form: "function",
|
|
239
|
+
name: "Boolean"
|
|
240
|
+
},
|
|
241
|
+
"type-of": {
|
|
242
|
+
form: "prefix",
|
|
243
|
+
keyword: "typeof"
|
|
244
|
+
},
|
|
245
|
+
"some": {
|
|
246
|
+
form: "method",
|
|
247
|
+
name: "some"
|
|
248
|
+
},
|
|
249
|
+
"every": {
|
|
250
|
+
form: "method",
|
|
251
|
+
name: "every"
|
|
252
|
+
},
|
|
253
|
+
// `Math.pow(a, b)` parses to the same call; `**` is the shorter of the two spellings
|
|
254
|
+
"power": {
|
|
255
|
+
form: "operator",
|
|
256
|
+
symbol: "**"
|
|
257
|
+
},
|
|
258
|
+
"bit-and": {
|
|
259
|
+
form: "operator",
|
|
260
|
+
symbol: "&"
|
|
261
|
+
},
|
|
262
|
+
"bit-or": {
|
|
263
|
+
form: "operator",
|
|
264
|
+
symbol: "|"
|
|
265
|
+
},
|
|
266
|
+
"bit-xor": {
|
|
267
|
+
form: "operator",
|
|
268
|
+
symbol: "^"
|
|
269
|
+
},
|
|
270
|
+
"shift-left": {
|
|
271
|
+
form: "operator",
|
|
272
|
+
symbol: "<<"
|
|
273
|
+
},
|
|
274
|
+
"shift-right": {
|
|
275
|
+
form: "operator",
|
|
276
|
+
symbol: ">>"
|
|
277
|
+
},
|
|
278
|
+
"shift-right-unsigned": {
|
|
279
|
+
form: "operator",
|
|
280
|
+
symbol: ">>>"
|
|
281
|
+
},
|
|
282
|
+
"bit-not": {
|
|
283
|
+
form: "prefix",
|
|
284
|
+
keyword: "~"
|
|
285
|
+
},
|
|
286
|
+
"coalesce": {
|
|
287
|
+
form: "operator",
|
|
288
|
+
symbol: "??"
|
|
289
|
+
},
|
|
290
|
+
"conditional": {
|
|
291
|
+
form: "conditional"
|
|
292
|
+
},
|
|
293
|
+
"matches": {
|
|
294
|
+
form: "regex-test"
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
/**
|
|
298
|
+
* A call rendered as the JavaScript that produced it, from operand and argument text already
|
|
299
|
+
* rendered by the caller.
|
|
300
|
+
*
|
|
301
|
+
* Takes strings so one implementation serves a live tree and a serialized one.
|
|
302
|
+
*/ /**
|
|
303
|
+
* Thunked because rendering a side can record a parameter, and `regex-test` emits its argument
|
|
304
|
+
* before its operand — so the two orders have to agree.
|
|
305
|
+
*/ const renderCallAsJs = (call, renderOperand, renderArgs)=>{
|
|
306
|
+
const source = CALL_SOURCE[call];
|
|
307
|
+
if (source == null) {
|
|
308
|
+
const operand = renderOperand();
|
|
309
|
+
return `${operand}.${call}(${renderArgs().join(", ")})`;
|
|
310
|
+
}
|
|
311
|
+
if (source.form === "property") {
|
|
312
|
+
return `${renderOperand()}.${source.name}`;
|
|
313
|
+
}
|
|
314
|
+
if (source.form === "regex-test") {
|
|
315
|
+
const pattern = renderArgs()[0] ?? "?";
|
|
316
|
+
return `${pattern}.test(${renderOperand()})`;
|
|
317
|
+
}
|
|
318
|
+
if (source.form === "method") {
|
|
319
|
+
const operand = renderOperand();
|
|
320
|
+
return `${operand}.${source.name}(${renderArgs().join(", ")})`;
|
|
321
|
+
}
|
|
322
|
+
if (source.form === "function") {
|
|
323
|
+
const operand = renderOperand();
|
|
324
|
+
return `${source.name}(${[
|
|
325
|
+
operand,
|
|
326
|
+
...renderArgs()
|
|
327
|
+
].join(", ")})`;
|
|
328
|
+
}
|
|
329
|
+
if (source.form === "prefix") {
|
|
330
|
+
// `~x`, not `~ x` — a bitwise complement is written tight, unlike `typeof`
|
|
331
|
+
const operand = renderOperand();
|
|
332
|
+
return source.keyword === "~" ? `${source.keyword}${operand}` : `${source.keyword} ${operand}`;
|
|
333
|
+
}
|
|
334
|
+
if (source.form === "conditional") {
|
|
335
|
+
const operand = renderOperand();
|
|
336
|
+
const args = renderArgs();
|
|
337
|
+
return `${operand} ? ${args[0] ?? "?"} : ${args[1] ?? "?"}`;
|
|
338
|
+
}
|
|
339
|
+
const operand = renderOperand();
|
|
340
|
+
return `${[
|
|
341
|
+
operand,
|
|
342
|
+
...renderArgs()
|
|
343
|
+
].join(` ${source.symbol} `)}`;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
|
|
92
347
|
},
|
|
93
348
|
835(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
94
349
|
__webpack_require__.d(__webpack_exports__, {
|
|
95
350
|
t: () => (EXPRESSION_TYPES)
|
|
96
351
|
});
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
|
|
352
|
+
/**
|
|
353
|
+
* A `Record` rather than a list, so adding to `ExpressionType` without adding it here is a compile
|
|
354
|
+
* error. As a list it was not exhaustive, and `call` was silently missing from `isExpression`.
|
|
355
|
+
*/ const EXPRESSION_TYPE_SET = {
|
|
356
|
+
"operator": true,
|
|
357
|
+
"comparator": true,
|
|
358
|
+
"property": true,
|
|
359
|
+
"value": true,
|
|
360
|
+
"call": true,
|
|
361
|
+
"empty": true,
|
|
362
|
+
"not-parsable": true
|
|
363
|
+
};
|
|
364
|
+
const EXPRESSION_TYPES = Object.keys(EXPRESSION_TYPE_SET);
|
|
105
365
|
|
|
106
366
|
|
|
107
367
|
},
|
|
108
368
|
379(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
109
369
|
__webpack_require__.d(__webpack_exports__, {
|
|
110
370
|
Vu: () => (toPredicate),
|
|
371
|
+
Vv: () => (operandValue),
|
|
111
372
|
_3: () => (evaluate),
|
|
373
|
+
gm: () => (UNRESOLVED),
|
|
112
374
|
wS: () => (toStrictPredicate)
|
|
113
375
|
});
|
|
114
376
|
/* import */ var _assertions__rspack_import_0 = __webpack_require__(126);
|
|
115
377
|
|
|
116
378
|
/** Reads a property or literal operand, or `UNRESOLVED` when the node is not one. */ const UNRESOLVED = Symbol("unresolved");
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
379
|
+
const ARITHMETIC = {
|
|
380
|
+
"add": (left, right)=>left + right,
|
|
381
|
+
"subtract": (left, right)=>left - right,
|
|
382
|
+
"multiply": (left, right)=>left * right,
|
|
383
|
+
"divide": (left, right)=>left / right,
|
|
384
|
+
"modulo": (left, right)=>left % right,
|
|
385
|
+
"power": (left, right)=>left ** right,
|
|
386
|
+
"bit-and": (left, right)=>left & right,
|
|
387
|
+
"bit-or": (left, right)=>left | right,
|
|
388
|
+
"bit-xor": (left, right)=>left ^ right,
|
|
389
|
+
"shift-left": (left, right)=>left << right,
|
|
390
|
+
"shift-right": (left, right)=>left >> right,
|
|
391
|
+
"shift-right-unsigned": (left, right)=>left >>> right
|
|
392
|
+
};
|
|
393
|
+
const applyCall = (call, value, args)=>{
|
|
394
|
+
// Above the guard: a template renders null as "null" in JavaScript, so these two are total.
|
|
395
|
+
if (call === "to-string") {
|
|
396
|
+
return String(value);
|
|
397
|
+
}
|
|
398
|
+
if (call === "concat") {
|
|
399
|
+
return [
|
|
400
|
+
value,
|
|
401
|
+
...args
|
|
402
|
+
].map(String).join("");
|
|
403
|
+
}
|
|
404
|
+
// A call applied to an absent value has no answer, and inventing one ("" for a missing string)
|
|
405
|
+
// is how a filter starts matching rows it should not.
|
|
123
406
|
if (value == null) {
|
|
124
407
|
return UNRESOLVED;
|
|
125
408
|
}
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
409
|
+
if (call === "to-lower-case" || call === "to-upper-case") {
|
|
410
|
+
if (typeof value !== "string") {
|
|
411
|
+
return UNRESOLVED;
|
|
412
|
+
}
|
|
413
|
+
const lower = call === "to-lower-case";
|
|
414
|
+
if (args.length === 0 || args[0] == null) {
|
|
415
|
+
return lower ? value.toLowerCase() : value.toUpperCase();
|
|
416
|
+
}
|
|
417
|
+
if (typeof args[0] !== "string") {
|
|
418
|
+
return UNRESOLVED;
|
|
419
|
+
}
|
|
420
|
+
try {
|
|
421
|
+
// An explicit locale is deterministic; dropping it answers a different question in Turkish.
|
|
422
|
+
return lower ? value.toLocaleLowerCase(args[0]) : value.toLocaleUpperCase(args[0]);
|
|
423
|
+
} catch {
|
|
424
|
+
// An invalid language tag throws RangeError; no answer beats the host's default.
|
|
425
|
+
return UNRESOLVED;
|
|
426
|
+
}
|
|
131
427
|
}
|
|
132
|
-
if (
|
|
428
|
+
if (call === "length") {
|
|
133
429
|
return typeof value === "string" || Array.isArray(value) ? value.length : UNRESOLVED;
|
|
134
430
|
}
|
|
431
|
+
if (call === "bit-not") {
|
|
432
|
+
return typeof value === "number" ? ~value : UNRESOLVED;
|
|
433
|
+
}
|
|
434
|
+
if (call === "matches") {
|
|
435
|
+
if (typeof value !== "string" || !(args[0] instanceof RegExp)) {
|
|
436
|
+
return UNRESOLVED;
|
|
437
|
+
}
|
|
438
|
+
// `test` advances `lastIndex` on a global or sticky pattern, and the pattern is shared with
|
|
439
|
+
// the cached template, where a source evaluates fresh in JavaScript.
|
|
440
|
+
return args[0].global || args[0].sticky ? new RegExp(args[0].source, args[0].flags).test(value) : args[0].test(value);
|
|
441
|
+
}
|
|
442
|
+
const arithmetic = ARITHMETIC[call];
|
|
443
|
+
if (arithmetic != null) {
|
|
444
|
+
return typeof value === "number" && typeof args[0] === "number" ? arithmetic(value, args[0]) : UNRESOLVED;
|
|
445
|
+
}
|
|
135
446
|
return UNRESOLVED;
|
|
136
447
|
};
|
|
137
|
-
const
|
|
448
|
+
const operandValue = (expression, row)=>{
|
|
138
449
|
if (expression == null) {
|
|
139
450
|
return UNRESOLVED;
|
|
140
451
|
}
|
|
141
452
|
if ((0,_assertions__rspack_import_0/* .isValueExpression */.S6)(expression)) {
|
|
142
|
-
return
|
|
453
|
+
return expression.value;
|
|
143
454
|
}
|
|
144
455
|
if ((0,_assertions__rspack_import_0/* .isPropertyExpression */.e3)(expression)) {
|
|
145
456
|
// Through the PropertyInfo, so a nested path and a `from`-renamed segment resolve the same
|
|
146
457
|
// way every other consumer of the tree resolves them.
|
|
147
|
-
return
|
|
458
|
+
return expression.property.getValue(row);
|
|
459
|
+
}
|
|
460
|
+
if ((0,_assertions__rspack_import_0/* .isCallExpression */.fm)(expression)) {
|
|
461
|
+
/**
|
|
462
|
+
* `??` and `? :` are the two calls whose whole job is to answer when something is absent, so
|
|
463
|
+
* they run before the guard that refuses an absent operand.
|
|
464
|
+
*/ if (expression.call === "coalesce") {
|
|
465
|
+
const left = operandValue(expression.expression, row);
|
|
466
|
+
return left === UNRESOLVED || left == null ? operandValue(expression.arguments[0], row) : left;
|
|
467
|
+
}
|
|
468
|
+
if (expression.call === "conditional") {
|
|
469
|
+
const condition = evaluate(expression.expression, row);
|
|
470
|
+
if (condition === undefined) {
|
|
471
|
+
return UNRESOLVED;
|
|
472
|
+
}
|
|
473
|
+
return operandValue(expression.arguments[condition === true ? 0 : 1], row);
|
|
474
|
+
}
|
|
475
|
+
const inner = operandValue(expression.expression, row);
|
|
476
|
+
if (inner === UNRESOLVED) {
|
|
477
|
+
return UNRESOLVED;
|
|
478
|
+
}
|
|
479
|
+
const args = [];
|
|
480
|
+
for (const argument of expression.arguments){
|
|
481
|
+
const resolved = operandValue(argument, row);
|
|
482
|
+
if (resolved === UNRESOLVED) {
|
|
483
|
+
return UNRESOLVED;
|
|
484
|
+
}
|
|
485
|
+
args.push(resolved);
|
|
486
|
+
}
|
|
487
|
+
return applyCall(expression.call, inner, args);
|
|
148
488
|
}
|
|
149
489
|
return UNRESOLVED;
|
|
150
490
|
};
|
|
@@ -229,8 +569,8 @@ const evaluateComparator = (comparator, left, right, strict)=>{
|
|
|
229
569
|
return left === false && right === false ? false : undefined;
|
|
230
570
|
}
|
|
231
571
|
if ((0,_assertions__rspack_import_0/* .isComparatorExpression */.xH)(expression)) {
|
|
232
|
-
const left =
|
|
233
|
-
const right =
|
|
572
|
+
const left = operandValue(expression.left, row);
|
|
573
|
+
const right = operandValue(expression.right, row);
|
|
234
574
|
if (left === UNRESOLVED || right === UNRESOLVED) {
|
|
235
575
|
return undefined;
|
|
236
576
|
}
|
|
@@ -268,21 +608,136 @@ const evaluateComparator = (comparator, left, right, strict)=>{
|
|
|
268
608
|
};
|
|
269
609
|
|
|
270
610
|
|
|
611
|
+
},
|
|
612
|
+
43(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
613
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
614
|
+
F5: () => (foldConstantCalls),
|
|
615
|
+
Sv: () => (FOLDABLE),
|
|
616
|
+
br: () => (foldedOperandValue)
|
|
617
|
+
});
|
|
618
|
+
/* import */ var _assertions__rspack_import_0 = __webpack_require__(126);
|
|
619
|
+
/* import */ var _evaluate__rspack_import_3 = __webpack_require__(379);
|
|
620
|
+
/* import */ var _types__rspack_import_2 = __webpack_require__(27);
|
|
621
|
+
/* import */ var _utils__rspack_import_1 = __webpack_require__(63);
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
/** Calls fold may compute. Absent means a plugin declines it, so a new call is opt-in. */ const FOLDABLE = new Set([
|
|
627
|
+
"to-lower-case",
|
|
628
|
+
"to-upper-case",
|
|
629
|
+
"length",
|
|
630
|
+
"bit-not",
|
|
631
|
+
"matches",
|
|
632
|
+
"to-string",
|
|
633
|
+
"concat",
|
|
634
|
+
"add",
|
|
635
|
+
"subtract",
|
|
636
|
+
"multiply",
|
|
637
|
+
"divide",
|
|
638
|
+
"modulo",
|
|
639
|
+
"power",
|
|
640
|
+
"bit-and",
|
|
641
|
+
"bit-or",
|
|
642
|
+
"bit-xor",
|
|
643
|
+
"shift-left",
|
|
644
|
+
"shift-right",
|
|
645
|
+
"shift-right-unsigned",
|
|
646
|
+
"coalesce",
|
|
647
|
+
"conditional"
|
|
648
|
+
]);
|
|
649
|
+
/** `String(value)` on an object is the host's rendering — a Date carries its timezone. */ const COERCES_TO_TEXT = new Set([
|
|
650
|
+
"to-string",
|
|
651
|
+
"concat"
|
|
652
|
+
]);
|
|
653
|
+
const isFrozenPrimitive = (value)=>value == null || typeof value !== "object";
|
|
654
|
+
const readsAProperty = (expression)=>{
|
|
655
|
+
if ((0,_assertions__rspack_import_0/* .isPropertyExpression */.e3)(expression)) {
|
|
656
|
+
return true;
|
|
657
|
+
}
|
|
658
|
+
return (0,_utils__rspack_import_1/* .childrenOf */.LU)(expression).some(readsAProperty);
|
|
659
|
+
};
|
|
660
|
+
/** A `conditional` holds a condition where every other call holds a value. */ const isConstant = (call)=>{
|
|
661
|
+
if (!FOLDABLE.has(call.call) || !call.arguments.every(_assertions__rspack_import_0/* .isValueExpression */.S6)) {
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
if (COERCES_TO_TEXT.has(call.call) && [
|
|
665
|
+
call.expression,
|
|
666
|
+
...call.arguments
|
|
667
|
+
].some((operand)=>(0,_assertions__rspack_import_0/* .isValueExpression */.S6)(operand) && !isFrozenPrimitive(operand.value))) {
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
return call.call === "conditional" ? !readsAProperty(call.expression) : (0,_assertions__rspack_import_0/* .isValueExpression */.S6)(call.expression);
|
|
671
|
+
};
|
|
672
|
+
/** Computes every call whose operand and arguments are all literals. Runs after `bindExpression`. */ const foldConstantCalls = (expression)=>{
|
|
673
|
+
if ((0,_assertions__rspack_import_0/* .isCallExpression */.fm)(expression)) {
|
|
674
|
+
const folded = new _types__rspack_import_2/* .CallExpression */.DG({
|
|
675
|
+
call: expression.call,
|
|
676
|
+
expression: foldConstantCalls(expression.expression),
|
|
677
|
+
arguments: expression.arguments.map(foldConstantCalls)
|
|
678
|
+
});
|
|
679
|
+
if (!isConstant(folded)) {
|
|
680
|
+
return folded;
|
|
681
|
+
}
|
|
682
|
+
const value = (0,_evaluate__rspack_import_3/* .operandValue */.Vv)(folded, {});
|
|
683
|
+
return value === _evaluate__rspack_import_3/* .UNRESOLVED */.gm ? folded : new _types__rspack_import_2/* .ValueExpression */.Ko({
|
|
684
|
+
value
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
if ((0,_assertions__rspack_import_0/* .isComparatorExpression */.xH)(expression)) {
|
|
688
|
+
return new _types__rspack_import_2/* .ComparatorExpression */.bQ({
|
|
689
|
+
comparator: expression.comparator,
|
|
690
|
+
negated: expression.negated,
|
|
691
|
+
strict: expression.strict,
|
|
692
|
+
left: expression.left == null ? undefined : foldConstantCalls(expression.left),
|
|
693
|
+
right: expression.right == null ? undefined : foldConstantCalls(expression.right)
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
if ((0,_assertions__rspack_import_0/* .isOperatorExpression */.vg)(expression)) {
|
|
697
|
+
return new _types__rspack_import_2/* .OperatorExpression */.fw({
|
|
698
|
+
operator: expression.operator,
|
|
699
|
+
left: expression.left == null ? undefined : foldConstantCalls(expression.left),
|
|
700
|
+
right: expression.right == null ? undefined : foldConstantCalls(expression.right)
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
return expression;
|
|
704
|
+
};
|
|
705
|
+
/** The value a literal operand binds as once the calls on it are computed. Throws if it cannot. */ const foldedOperandValue = (operand, calls)=>{
|
|
706
|
+
if (calls.length === 0) {
|
|
707
|
+
return operand.value;
|
|
708
|
+
}
|
|
709
|
+
// `peelCalls` returns calls innermost first, so the last one evaluates the whole chain.
|
|
710
|
+
const outermost = calls[calls.length - 1];
|
|
711
|
+
const value = readsAProperty(outermost) ? _evaluate__rspack_import_3/* .UNRESOLVED */.gm : (0,_evaluate__rspack_import_3/* .operandValue */.Vv)(outermost, {});
|
|
712
|
+
if (value === _evaluate__rspack_import_3/* .UNRESOLVED */.gm) {
|
|
713
|
+
throw new Error(`'${calls.map((call)=>call.call).join("', '")}' cannot be computed on the literal ` + `'${String(operand.value)}'.`);
|
|
714
|
+
}
|
|
715
|
+
return value;
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
|
|
271
719
|
},
|
|
272
720
|
91(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
273
721
|
__webpack_require__.d(__webpack_exports__, {
|
|
274
722
|
MY: () => (toExpression),
|
|
723
|
+
Pl: () => (parseSelector),
|
|
275
724
|
oH: () => (parseFragment),
|
|
276
725
|
pg: () => (combineExpressions)
|
|
277
726
|
});
|
|
278
|
-
/* import */ var
|
|
727
|
+
/* import */ var _utilities__rspack_import_5 = __webpack_require__(581);
|
|
279
728
|
/* import */ var _assertions__rspack_import_0 = __webpack_require__(126);
|
|
280
729
|
/* import */ var _schema__rspack_import_2 = __webpack_require__(537);
|
|
730
|
+
/* import */ var _evaluate__rspack_import_3 = __webpack_require__(379);
|
|
731
|
+
/* import */ var _fold__rspack_import_4 = __webpack_require__(43);
|
|
732
|
+
/* import */ var _utils__rspack_import_6 = __webpack_require__(63);
|
|
281
733
|
/* import */ var _types__rspack_import_1 = __webpack_require__(27);
|
|
282
734
|
|
|
283
735
|
|
|
284
736
|
|
|
285
737
|
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
|
|
286
741
|
// Error message constants
|
|
287
742
|
const ERROR_MESSAGES = {
|
|
288
743
|
PROPERTY_NOT_FOUND: (path)=>`Error parsing query, could not find PropertyInfo for path: ${path}`,
|
|
@@ -328,8 +783,13 @@ const converters = {
|
|
|
328
783
|
};
|
|
329
784
|
// Longest first so multi-character punctuation wins over its prefixes
|
|
330
785
|
const MULTI_CHARACTER_PUNCTUATION = [
|
|
786
|
+
">>>",
|
|
331
787
|
"===",
|
|
332
788
|
"!==",
|
|
789
|
+
"**",
|
|
790
|
+
"<<",
|
|
791
|
+
">>",
|
|
792
|
+
"??",
|
|
333
793
|
"?.",
|
|
334
794
|
"&&",
|
|
335
795
|
"||",
|
|
@@ -366,9 +826,17 @@ const SINGLE_CHARACTER_PUNCTUATION = new Set([
|
|
|
366
826
|
"?",
|
|
367
827
|
":",
|
|
368
828
|
"&",
|
|
369
|
-
"|"
|
|
829
|
+
"|",
|
|
830
|
+
"^",
|
|
831
|
+
"~"
|
|
370
832
|
]);
|
|
371
|
-
|
|
833
|
+
/**
|
|
834
|
+
* A lookup table keyed by source text.
|
|
835
|
+
*
|
|
836
|
+
* Null-prototype: `TRANSFORM_METHODS["toString"]` otherwise returns `Object.prototype.toString`,
|
|
837
|
+
* which is truthy, and the parser reads a method it does not support as one it does.
|
|
838
|
+
*/ const sourceKeyed = (entries)=>Object.assign(Object.create(null), entries);
|
|
839
|
+
const STRING_ESCAPES = sourceKeyed({
|
|
372
840
|
"n": "\n",
|
|
373
841
|
"r": "\r",
|
|
374
842
|
"t": "\t",
|
|
@@ -376,6 +844,24 @@ const STRING_ESCAPES = {
|
|
|
376
844
|
"f": "\f",
|
|
377
845
|
"v": "\v",
|
|
378
846
|
"0": "\0"
|
|
847
|
+
});
|
|
848
|
+
/**
|
|
849
|
+
* Whether a `/` here opens a regex rather than dividing.
|
|
850
|
+
*
|
|
851
|
+
* A regex cannot follow a value. Everything else — the start of the source, an operator, an opening
|
|
852
|
+
* bracket, a comma — is a position where only a regex makes sense.
|
|
853
|
+
*/ const regexCanStartHere = (tokens)=>{
|
|
854
|
+
const previous = tokens[tokens.length - 1];
|
|
855
|
+
if (previous == null) {
|
|
856
|
+
return true;
|
|
857
|
+
}
|
|
858
|
+
if (previous.kind === "number" || previous.kind === "string" || previous.kind === "bigint" || previous.kind === "regex") {
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
if (previous.kind === "identifier") {
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
return previous.value !== ")" && previous.value !== "]";
|
|
379
865
|
};
|
|
380
866
|
const isIdentifierStart = (char)=>/[a-zA-Z_$]/.test(char);
|
|
381
867
|
const isIdentifierPart = (char)=>/[a-zA-Z0-9_$]/.test(char);
|
|
@@ -425,6 +911,51 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
|
|
|
425
911
|
i++;
|
|
426
912
|
continue;
|
|
427
913
|
}
|
|
914
|
+
/**
|
|
915
|
+
* A regex literal, told from division by what came before it.
|
|
916
|
+
*
|
|
917
|
+
* `/` after a value — a number, string, identifier, `)` or `]` — is division. Anywhere else
|
|
918
|
+
* it opens a regex. That is the same rule a JavaScript lexer uses, and it is why `x.a / 2`
|
|
919
|
+
* and `/^a/.test(x.a)` can share a character.
|
|
920
|
+
*/ if (char === "/" && source[i + 1] !== "/" && source[i + 1] !== "*" && regexCanStartHere(tokens)) {
|
|
921
|
+
let value = "";
|
|
922
|
+
let inClass = false;
|
|
923
|
+
let j = i + 1;
|
|
924
|
+
while(j < source.length){
|
|
925
|
+
const current = source[j];
|
|
926
|
+
if (current === "\\") {
|
|
927
|
+
value += current + (source[j + 1] ?? "");
|
|
928
|
+
j += 2;
|
|
929
|
+
continue;
|
|
930
|
+
}
|
|
931
|
+
if (current === "[") {
|
|
932
|
+
inClass = true;
|
|
933
|
+
} else if (current === "]") {
|
|
934
|
+
inClass = false;
|
|
935
|
+
} else if (current === "/" && inClass === false) {
|
|
936
|
+
break;
|
|
937
|
+
} else if (current === "\n") {
|
|
938
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("unterminated regular expression"));
|
|
939
|
+
}
|
|
940
|
+
value += current;
|
|
941
|
+
j++;
|
|
942
|
+
}
|
|
943
|
+
if (j >= source.length) {
|
|
944
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("unterminated regular expression"));
|
|
945
|
+
}
|
|
946
|
+
j++;
|
|
947
|
+
let flags = "";
|
|
948
|
+
while(j < source.length && isIdentifierPart(source[j])){
|
|
949
|
+
flags += source[j];
|
|
950
|
+
j++;
|
|
951
|
+
}
|
|
952
|
+
i = j;
|
|
953
|
+
tokens.push({
|
|
954
|
+
kind: "regex",
|
|
955
|
+
value: `${value}\u0000${flags}`
|
|
956
|
+
});
|
|
957
|
+
continue;
|
|
958
|
+
}
|
|
428
959
|
// Comments
|
|
429
960
|
if (char === "/" && source[i + 1] === "/") {
|
|
430
961
|
while(i < source.length && source[i] !== "\n"){
|
|
@@ -444,6 +975,8 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
|
|
|
444
975
|
if (char === "'" || char === "\"" || char === "`") {
|
|
445
976
|
const quote = char;
|
|
446
977
|
let value = "";
|
|
978
|
+
const chunks = [];
|
|
979
|
+
const expressions = [];
|
|
447
980
|
i++;
|
|
448
981
|
while(i < source.length && source[i] !== quote){
|
|
449
982
|
if (source[i] === "\\") {
|
|
@@ -458,8 +991,43 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
|
|
|
458
991
|
i += 2;
|
|
459
992
|
continue;
|
|
460
993
|
}
|
|
461
|
-
|
|
462
|
-
|
|
994
|
+
/**
|
|
995
|
+
* An interpolation. The literal so far becomes a chunk and the expression source is
|
|
996
|
+
* kept whole, to be parsed by its own stream — nesting means the inner source can
|
|
997
|
+
* hold anything, including another template.
|
|
998
|
+
*/ if (quote === "`" && source[i] === "$" && source[i + 1] === "{") {
|
|
999
|
+
let depth = 1;
|
|
1000
|
+
let expression = "";
|
|
1001
|
+
let at = i + 2;
|
|
1002
|
+
while(at < source.length && depth > 0){
|
|
1003
|
+
const current = source[at];
|
|
1004
|
+
if (current === "{") {
|
|
1005
|
+
depth++;
|
|
1006
|
+
} else if (current === "}") {
|
|
1007
|
+
depth--;
|
|
1008
|
+
if (depth === 0) {
|
|
1009
|
+
break;
|
|
1010
|
+
}
|
|
1011
|
+
} else if (current === "'" || current === '"' || current === "`") {
|
|
1012
|
+
const closing = current;
|
|
1013
|
+
expression += current;
|
|
1014
|
+
at++;
|
|
1015
|
+
while(at < source.length && source[at] !== closing){
|
|
1016
|
+
expression += source[at] === "\\" ? source[at] + (source[at + 1] ?? "") : source[at];
|
|
1017
|
+
at += source[at] === "\\" ? 2 : 1;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
expression += source[at];
|
|
1021
|
+
at++;
|
|
1022
|
+
}
|
|
1023
|
+
if (depth > 0) {
|
|
1024
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("unterminated template interpolation"));
|
|
1025
|
+
}
|
|
1026
|
+
chunks.push(value);
|
|
1027
|
+
expressions.push(expression);
|
|
1028
|
+
value = "";
|
|
1029
|
+
i = at + 1;
|
|
1030
|
+
continue;
|
|
463
1031
|
}
|
|
464
1032
|
value += source[i];
|
|
465
1033
|
i++;
|
|
@@ -468,6 +1036,17 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
|
|
|
468
1036
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED("unterminated string literal"));
|
|
469
1037
|
}
|
|
470
1038
|
i++; // consume closing quote
|
|
1039
|
+
if (expressions.length > 0) {
|
|
1040
|
+
chunks.push(value);
|
|
1041
|
+
tokens.push({
|
|
1042
|
+
kind: "template",
|
|
1043
|
+
value: JSON.stringify({
|
|
1044
|
+
chunks,
|
|
1045
|
+
expressions
|
|
1046
|
+
})
|
|
1047
|
+
});
|
|
1048
|
+
continue;
|
|
1049
|
+
}
|
|
471
1050
|
tokens.push({
|
|
472
1051
|
kind: "string",
|
|
473
1052
|
value
|
|
@@ -511,6 +1090,14 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
|
|
|
511
1090
|
}
|
|
512
1091
|
}
|
|
513
1092
|
}
|
|
1093
|
+
if (source[i] === "n") {
|
|
1094
|
+
i++;
|
|
1095
|
+
tokens.push({
|
|
1096
|
+
kind: "bigint",
|
|
1097
|
+
value: value.replace(/_/g, "")
|
|
1098
|
+
});
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
514
1101
|
tokens.push({
|
|
515
1102
|
kind: "number",
|
|
516
1103
|
value: value.replace(/_/g, "")
|
|
@@ -561,6 +1148,24 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
|
|
|
561
1148
|
constructor(tokens){
|
|
562
1149
|
this.tokens = tokens;
|
|
563
1150
|
}
|
|
1151
|
+
/** Inserts tokens at the cursor, bracketed so they keep their own precedence. */ splice(tokens) {
|
|
1152
|
+
const bracketed = [
|
|
1153
|
+
{
|
|
1154
|
+
kind: "punctuation",
|
|
1155
|
+
value: "("
|
|
1156
|
+
},
|
|
1157
|
+
...tokens,
|
|
1158
|
+
{
|
|
1159
|
+
kind: "punctuation",
|
|
1160
|
+
value: ")"
|
|
1161
|
+
}
|
|
1162
|
+
];
|
|
1163
|
+
this.tokens = [
|
|
1164
|
+
...this.tokens.slice(0, this.index),
|
|
1165
|
+
...bracketed,
|
|
1166
|
+
...this.tokens.slice(this.index)
|
|
1167
|
+
];
|
|
1168
|
+
}
|
|
564
1169
|
get isAtEnd() {
|
|
565
1170
|
return this.index >= this.tokens.length;
|
|
566
1171
|
}
|
|
@@ -575,10 +1180,108 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
|
|
|
575
1180
|
this.index++;
|
|
576
1181
|
return token;
|
|
577
1182
|
}
|
|
1183
|
+
/** The tokens of one statement's value, through the `;` or block end that closes it. */ takeStatementTokens() {
|
|
1184
|
+
const tokens = [];
|
|
1185
|
+
let depth = 0;
|
|
1186
|
+
while(!this.isAtEnd){
|
|
1187
|
+
const token = this.peek();
|
|
1188
|
+
if (token.kind === "punctuation") {
|
|
1189
|
+
if (token.value === "(" || token.value === "[" || token.value === "{") {
|
|
1190
|
+
depth++;
|
|
1191
|
+
} else if (token.value === ")" || token.value === "]" || token.value === "}") {
|
|
1192
|
+
if (depth === 0) {
|
|
1193
|
+
break;
|
|
1194
|
+
}
|
|
1195
|
+
depth--;
|
|
1196
|
+
} else if (token.value === ";" && depth === 0) {
|
|
1197
|
+
this.next();
|
|
1198
|
+
break;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
tokens.push(this.next());
|
|
1202
|
+
}
|
|
1203
|
+
if (tokens.length === 0) {
|
|
1204
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a declaration with no value"));
|
|
1205
|
+
}
|
|
1206
|
+
return tokens;
|
|
1207
|
+
}
|
|
578
1208
|
isPunctuation(value, offset = 0) {
|
|
579
1209
|
const token = this.peek(offset);
|
|
580
1210
|
return token != null && token.kind === "punctuation" && token.value === value;
|
|
581
1211
|
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Whether the group starting here holds a value rather than a condition.
|
|
1214
|
+
*
|
|
1215
|
+
* `(a && b)` is a boolean sub-expression; `(x.name ?? '') === 'ada'` and `(x.name).length` are
|
|
1216
|
+
* values. Only the token after the matching bracket tells them apart, so the decision is made by
|
|
1217
|
+
* looking ahead rather than by parsing one way and catching the failure — a rewind on exception
|
|
1218
|
+
* would swallow a genuine syntax error inside the group and report it as something else.
|
|
1219
|
+
*/ groupIsValue() {
|
|
1220
|
+
let depth = 0;
|
|
1221
|
+
let at = this.index;
|
|
1222
|
+
for(; at < this.tokens.length; at++){
|
|
1223
|
+
const token = this.tokens[at];
|
|
1224
|
+
if (token.kind !== "punctuation") {
|
|
1225
|
+
continue;
|
|
1226
|
+
}
|
|
1227
|
+
if (token.value === "(") {
|
|
1228
|
+
depth++;
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
if (token.value === ")") {
|
|
1232
|
+
depth--;
|
|
1233
|
+
if (depth === 0) {
|
|
1234
|
+
break;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
const after = this.tokens[at + 1];
|
|
1239
|
+
if (after == null || after.kind !== "punctuation") {
|
|
1240
|
+
return false;
|
|
1241
|
+
}
|
|
1242
|
+
return COMPARISON_OPERATORS[after.value] != null || after.value === "." || after.value === "?.";
|
|
1243
|
+
}
|
|
1244
|
+
/** Whether a `?` sits at the top level of what is left, so this is a conditional. */ holdsConditional() {
|
|
1245
|
+
let depth = 0;
|
|
1246
|
+
for(let at = this.index; at < this.tokens.length; at++){
|
|
1247
|
+
const token = this.tokens[at];
|
|
1248
|
+
if (token.kind !== "punctuation") {
|
|
1249
|
+
continue;
|
|
1250
|
+
}
|
|
1251
|
+
if (token.value === "(" || token.value === "[") {
|
|
1252
|
+
depth++;
|
|
1253
|
+
} else if (token.value === ")" || token.value === "]") {
|
|
1254
|
+
depth--;
|
|
1255
|
+
} else if (token.value === "?" && depth === 0) {
|
|
1256
|
+
return true;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
return false;
|
|
1260
|
+
}
|
|
1261
|
+
/** Whether the group starting here is `( … ? … : … )` rather than a plain value. */ groupHoldsConditional() {
|
|
1262
|
+
let depth = 0;
|
|
1263
|
+
for(let at = this.index; at < this.tokens.length; at++){
|
|
1264
|
+
const token = this.tokens[at];
|
|
1265
|
+
if (token.kind !== "punctuation") {
|
|
1266
|
+
continue;
|
|
1267
|
+
}
|
|
1268
|
+
if (token.value === "(") {
|
|
1269
|
+
depth++;
|
|
1270
|
+
continue;
|
|
1271
|
+
}
|
|
1272
|
+
if (token.value === ")") {
|
|
1273
|
+
depth--;
|
|
1274
|
+
if (depth === 0) {
|
|
1275
|
+
return false;
|
|
1276
|
+
}
|
|
1277
|
+
continue;
|
|
1278
|
+
}
|
|
1279
|
+
if (token.value === "?" && depth === 1) {
|
|
1280
|
+
return true;
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
582
1285
|
matchPunctuation(value) {
|
|
583
1286
|
if (this.isPunctuation(value)) {
|
|
584
1287
|
this.index++;
|
|
@@ -592,12 +1295,104 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
|
|
|
592
1295
|
}
|
|
593
1296
|
}
|
|
594
1297
|
}
|
|
595
|
-
|
|
1298
|
+
/**
|
|
1299
|
+
* Calls JavaScript binds LOOSER than a comparison.
|
|
1300
|
+
*
|
|
1301
|
+
* This grammar reads a comparison's operands as values, which puts these tighter than they belong:
|
|
1302
|
+
* `x.flags & 6 === 2` is `x.flags & (6 === 2)` in JavaScript and would be read here as
|
|
1303
|
+
* `(x.flags & 6) === 2`. The two answer differently, so an ungrouped one is refused rather than
|
|
1304
|
+
* reinterpreted — the filter then runs in memory against the caller's own function, which is right by
|
|
1305
|
+
* construction. Brackets say which was meant, and JavaScript itself makes an unbracketed `??` mix a
|
|
1306
|
+
* syntax error for the same reason.
|
|
1307
|
+
*/ const LOOSER_THAN_COMPARISON = [
|
|
1308
|
+
"bit-and",
|
|
1309
|
+
"bit-or",
|
|
1310
|
+
"bit-xor",
|
|
1311
|
+
"coalesce"
|
|
1312
|
+
];
|
|
1313
|
+
const needsBrackets = (operand)=>operand.kind === "arithmetic" && operand.grouped !== true && LOOSER_THAN_COMPARISON.includes(operand.call);
|
|
1314
|
+
/** JavaScript precedence: `*`, `/`, `%` bind tighter than `+` and `-`. */ const MULTIPLICATIVE_OPERATORS = sourceKeyed({
|
|
1315
|
+
"*": "multiply",
|
|
1316
|
+
"/": "divide",
|
|
1317
|
+
"%": "modulo"
|
|
1318
|
+
});
|
|
1319
|
+
const ADDITIVE_OPERATORS = sourceKeyed({
|
|
1320
|
+
"+": "add",
|
|
1321
|
+
"-": "subtract"
|
|
1322
|
+
});
|
|
1323
|
+
const SHIFT_OPERATORS = sourceKeyed({
|
|
1324
|
+
"<<": "shift-left",
|
|
1325
|
+
">>": "shift-right",
|
|
1326
|
+
">>>": "shift-right-unsigned"
|
|
1327
|
+
});
|
|
1328
|
+
const BITWISE_AND_OPERATORS = sourceKeyed({
|
|
1329
|
+
"&": "bit-and"
|
|
1330
|
+
});
|
|
1331
|
+
const BITWISE_XOR_OPERATORS = sourceKeyed({
|
|
1332
|
+
"^": "bit-xor"
|
|
1333
|
+
});
|
|
1334
|
+
const BITWISE_OR_OPERATORS = sourceKeyed({
|
|
1335
|
+
"|": "bit-or"
|
|
1336
|
+
});
|
|
1337
|
+
const COALESCE_OPERATORS = sourceKeyed({
|
|
1338
|
+
"??": "coalesce"
|
|
1339
|
+
});
|
|
1340
|
+
/** Whether a schema property is reachable in here, which decides which side of a comparison it is. */ const containsProperty = (operand)=>{
|
|
1341
|
+
if (operand.kind === "property") {
|
|
1342
|
+
return true;
|
|
1343
|
+
}
|
|
1344
|
+
if (operand.kind === "conditional") {
|
|
1345
|
+
// A comparison always names a schema property, so the condition alone settles it
|
|
1346
|
+
return true;
|
|
1347
|
+
}
|
|
1348
|
+
if (operand.kind === "opaque") {
|
|
1349
|
+
return operand.reads.some(containsProperty);
|
|
1350
|
+
}
|
|
1351
|
+
return operand.kind === "arithmetic" && (containsProperty(operand.left) || containsProperty(operand.right) || operand.extra != null && containsProperty(operand.extra));
|
|
1352
|
+
};
|
|
1353
|
+
const DECLARATION_KEYWORDS = new Set([
|
|
1354
|
+
"const",
|
|
1355
|
+
"let",
|
|
1356
|
+
"var"
|
|
1357
|
+
]);
|
|
1358
|
+
/** An operand whose value only a row can supply. */ const UNKNOWN_UNTIL_ROW = Symbol("unknown until row");
|
|
1359
|
+
/** The empty argument slot of a unary call. Compared by identity, so a real `undefined` still counts. */ const NO_ARGUMENT = Object.freeze({
|
|
1360
|
+
kind: "value",
|
|
1361
|
+
value: undefined,
|
|
1362
|
+
transformer: null,
|
|
1363
|
+
locale: null
|
|
1364
|
+
});
|
|
1365
|
+
const noArgument = ()=>NO_ARGUMENT;
|
|
1366
|
+
/** A predicate no row satisfies. Never reaches a tree: no expression node means "match nothing". */ const NEVER = "never";
|
|
1367
|
+
const and = (left, right)=>{
|
|
1368
|
+
if (_types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(left)) {
|
|
1369
|
+
return right;
|
|
1370
|
+
}
|
|
1371
|
+
if (_types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(right)) {
|
|
1372
|
+
return left;
|
|
1373
|
+
}
|
|
1374
|
+
return new _types__rspack_import_1/* .OperatorExpression */.fw({
|
|
1375
|
+
operator: "&&",
|
|
1376
|
+
left,
|
|
1377
|
+
right
|
|
1378
|
+
});
|
|
1379
|
+
};
|
|
1380
|
+
const or = (left, right)=>{
|
|
1381
|
+
if (_types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(left) || _types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(right)) {
|
|
1382
|
+
return _types__rspack_import_1/* .Expression.EMPTY */.r4.EMPTY;
|
|
1383
|
+
}
|
|
1384
|
+
return new _types__rspack_import_1/* .OperatorExpression */.fw({
|
|
1385
|
+
operator: "||",
|
|
1386
|
+
left,
|
|
1387
|
+
right
|
|
1388
|
+
});
|
|
1389
|
+
};
|
|
1390
|
+
const COMPARATOR_METHODS = sourceKeyed({
|
|
596
1391
|
startsWith: "starts-with",
|
|
597
1392
|
endsWith: "ends-with",
|
|
598
1393
|
includes: "includes"
|
|
599
|
-
};
|
|
600
|
-
const TRANSFORM_METHODS = {
|
|
1394
|
+
});
|
|
1395
|
+
const TRANSFORM_METHODS = sourceKeyed({
|
|
601
1396
|
toLowerCase: {
|
|
602
1397
|
transformer: "to-lower-case",
|
|
603
1398
|
locale: null
|
|
@@ -614,8 +1409,8 @@ const TRANSFORM_METHODS = {
|
|
|
614
1409
|
transformer: "to-upper-case",
|
|
615
1410
|
locale: "en-US"
|
|
616
1411
|
}
|
|
617
|
-
};
|
|
618
|
-
const COMPARISON_OPERATORS = {
|
|
1412
|
+
});
|
|
1413
|
+
const COMPARISON_OPERATORS = sourceKeyed({
|
|
619
1414
|
"==": {
|
|
620
1415
|
comparator: "equals",
|
|
621
1416
|
negated: false,
|
|
@@ -656,7 +1451,7 @@ const COMPARISON_OPERATORS = {
|
|
|
656
1451
|
negated: false,
|
|
657
1452
|
strict: false
|
|
658
1453
|
}
|
|
659
|
-
};
|
|
1454
|
+
});
|
|
660
1455
|
const SWAPPED_COMPARATORS = {
|
|
661
1456
|
"equals": "equals",
|
|
662
1457
|
"greater-than": "less-than",
|
|
@@ -727,16 +1522,21 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
727
1522
|
*/ class ExpressionParser {
|
|
728
1523
|
schema;
|
|
729
1524
|
stream;
|
|
730
|
-
|
|
1525
|
+
scope;
|
|
731
1526
|
paramsName;
|
|
732
1527
|
params;
|
|
1528
|
+
/**
|
|
1529
|
+
* Whether this parses a value selector rather than a filter, and so reads a call it has no node for
|
|
1530
|
+
* as an `OpaqueOperand` instead of refusing it.
|
|
1531
|
+
*/ readsValues;
|
|
733
1532
|
/** Set when a param value shaped the tree itself (e.g. x[p.name]) — such templates cannot be cached. */ structurallyDependsOnParams = false;
|
|
734
|
-
constructor(schema, stream,
|
|
1533
|
+
constructor(schema, stream, scope, paramsName, params, readsValues = false){
|
|
735
1534
|
this.schema = schema;
|
|
736
1535
|
this.stream = stream;
|
|
737
|
-
this.
|
|
1536
|
+
this.scope = scope;
|
|
738
1537
|
this.paramsName = paramsName;
|
|
739
1538
|
this.params = params;
|
|
1539
|
+
this.readsValues = readsValues;
|
|
740
1540
|
}
|
|
741
1541
|
parse() {
|
|
742
1542
|
const expression = this.parseOr();
|
|
@@ -745,6 +1545,245 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
745
1545
|
}
|
|
746
1546
|
return expression;
|
|
747
1547
|
}
|
|
1548
|
+
parseBody() {
|
|
1549
|
+
if (!this.stream.isPunctuation("{")) {
|
|
1550
|
+
return this.parse();
|
|
1551
|
+
}
|
|
1552
|
+
const answer = this.parseBlock();
|
|
1553
|
+
if (!this.stream.isAtEnd) {
|
|
1554
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`unexpected token '${this.stream.peek()?.value}'`));
|
|
1555
|
+
}
|
|
1556
|
+
if (answer === NEVER) {
|
|
1557
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a predicate no row can satisfy"));
|
|
1558
|
+
}
|
|
1559
|
+
return answer;
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* What a value selector returns: one value, or the fields of an object literal.
|
|
1563
|
+
*
|
|
1564
|
+
* A block body is read only when it does nothing but return, which is what a transpiler makes of an
|
|
1565
|
+
* arrow function. Anything more is refused, and the caller falls back to running the function.
|
|
1566
|
+
*/ parseSelector() {
|
|
1567
|
+
const block = this.stream.matchPunctuation("{");
|
|
1568
|
+
if (block) {
|
|
1569
|
+
const keyword = this.stream.next();
|
|
1570
|
+
if (keyword.kind !== "identifier" || keyword.value !== "return") {
|
|
1571
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a selector block that does more than return"));
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
const selected = this.stream.isPunctuation("{") ? this.parseObjectLiteral() : this.stream.isPunctuation("(") && this.stream.isPunctuation("{", 1) ? this.parseBracketedObjectLiteral() : this.parseInterpolation();
|
|
1575
|
+
if (block) {
|
|
1576
|
+
this.stream.matchPunctuation(";");
|
|
1577
|
+
this.stream.expectPunctuation("}");
|
|
1578
|
+
}
|
|
1579
|
+
if (!this.stream.isAtEnd) {
|
|
1580
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`unexpected token '${this.stream.peek()?.value}'`));
|
|
1581
|
+
}
|
|
1582
|
+
return selected;
|
|
1583
|
+
}
|
|
1584
|
+
/** `({ … })`, the arrow body that returns an object. */ parseBracketedObjectLiteral() {
|
|
1585
|
+
this.stream.expectPunctuation("(");
|
|
1586
|
+
const fields = this.parseObjectLiteral();
|
|
1587
|
+
this.stream.expectPunctuation(")");
|
|
1588
|
+
return fields;
|
|
1589
|
+
}
|
|
1590
|
+
/**
|
|
1591
|
+
* The fields of an object literal, each one value.
|
|
1592
|
+
*
|
|
1593
|
+
* A field's value is read without a top-level conditional, which needs brackets here: the look-ahead
|
|
1594
|
+
* for a `?` does not stop at the comma that ends the field.
|
|
1595
|
+
*/ parseObjectLiteral() {
|
|
1596
|
+
this.stream.expectPunctuation("{");
|
|
1597
|
+
const fields = [];
|
|
1598
|
+
while(!this.stream.matchPunctuation("}")){
|
|
1599
|
+
const key = this.stream.next();
|
|
1600
|
+
if (key.kind !== "identifier" && key.kind !== "string") {
|
|
1601
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`the object key '${key.value}'`));
|
|
1602
|
+
}
|
|
1603
|
+
fields.push({
|
|
1604
|
+
name: key.value,
|
|
1605
|
+
operand: this.stream.matchPunctuation(":") ? this.parseValue() : this.parseShorthand(key)
|
|
1606
|
+
});
|
|
1607
|
+
if (!this.stream.matchPunctuation(",")) {
|
|
1608
|
+
this.stream.expectPunctuation("}");
|
|
1609
|
+
break;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
return fields;
|
|
1613
|
+
}
|
|
1614
|
+
/** `{ name }`, which reads what the parameter list bound `name` to. */ parseShorthand(key) {
|
|
1615
|
+
const binding = key.kind === "identifier" ? this.scope.get(key.value) : undefined;
|
|
1616
|
+
if (binding == null || binding.kind === "inlined") {
|
|
1617
|
+
throw new Error(ERROR_MESSAGES.VARIABLE_VALUE(key.value));
|
|
1618
|
+
}
|
|
1619
|
+
return this.parseChain({
|
|
1620
|
+
kind: binding.kind,
|
|
1621
|
+
path: [
|
|
1622
|
+
...binding.path
|
|
1623
|
+
]
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
/** The expression a `{ … }` block answers with. */ parseBlock() {
|
|
1627
|
+
this.stream.expectPunctuation("{");
|
|
1628
|
+
const answer = this.parseStatements();
|
|
1629
|
+
this.stream.expectPunctuation("}");
|
|
1630
|
+
return answer;
|
|
1631
|
+
}
|
|
1632
|
+
/** Statements up to the one that returns. What follows a `return` is never read, as in JavaScript. */ parseStatements() {
|
|
1633
|
+
if (this.stream.isPunctuation("}") || this.stream.isAtEnd) {
|
|
1634
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a block body that returns nothing"));
|
|
1635
|
+
}
|
|
1636
|
+
const keyword = this.stream.peek();
|
|
1637
|
+
if (keyword == null || keyword.kind !== "identifier") {
|
|
1638
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`a statement starting '${keyword?.value}'`));
|
|
1639
|
+
}
|
|
1640
|
+
if (DECLARATION_KEYWORDS.has(keyword.value)) {
|
|
1641
|
+
this.declare();
|
|
1642
|
+
return this.parseStatements();
|
|
1643
|
+
}
|
|
1644
|
+
if (keyword.value === "return") {
|
|
1645
|
+
this.stream.next();
|
|
1646
|
+
const answer = this.parseReturnedCondition();
|
|
1647
|
+
this.stream.matchPunctuation(";");
|
|
1648
|
+
return answer;
|
|
1649
|
+
}
|
|
1650
|
+
if (keyword.value === "if") {
|
|
1651
|
+
return this.parseIfStatement();
|
|
1652
|
+
}
|
|
1653
|
+
if (keyword.value === "switch") {
|
|
1654
|
+
return this.parseSwitchStatement();
|
|
1655
|
+
}
|
|
1656
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`the statement '${keyword.value}'`));
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Binds a `const`/`let`/`var` name to the tokens of its initializer — tokens rather than a parsed
|
|
1660
|
+
* expression, so the name works as an operand, an argument, or a call receiver alike.
|
|
1661
|
+
*/ declare() {
|
|
1662
|
+
this.stream.next();
|
|
1663
|
+
const name = this.stream.next();
|
|
1664
|
+
if (name.kind !== "identifier") {
|
|
1665
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`the declaration '${name.value}'`));
|
|
1666
|
+
}
|
|
1667
|
+
this.stream.expectPunctuation("=");
|
|
1668
|
+
this.scope.set(name.value, {
|
|
1669
|
+
kind: "inlined",
|
|
1670
|
+
tokens: this.stream.takeStatementTokens()
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
/** `return false` on its own, which no row satisfies, and every other returned condition. */ parseReturnedCondition() {
|
|
1674
|
+
const next = this.stream.peek();
|
|
1675
|
+
const after = this.stream.peek(1);
|
|
1676
|
+
const endsHere = after == null || after.kind === "punctuation" && (after.value === ";" || after.value === "}");
|
|
1677
|
+
if (next != null && next.kind === "identifier" && next.value === "false" && endsHere) {
|
|
1678
|
+
this.stream.next();
|
|
1679
|
+
return NEVER;
|
|
1680
|
+
}
|
|
1681
|
+
return this.parseOr();
|
|
1682
|
+
}
|
|
1683
|
+
parseIfStatement() {
|
|
1684
|
+
this.stream.next();
|
|
1685
|
+
this.stream.expectPunctuation("(");
|
|
1686
|
+
const condition = this.parseOr();
|
|
1687
|
+
this.stream.expectPunctuation(")");
|
|
1688
|
+
const whenTrue = this.parseBranch();
|
|
1689
|
+
if (this.stream.peek()?.value === "else") {
|
|
1690
|
+
this.stream.next();
|
|
1691
|
+
return this.either(condition, whenTrue, this.parseBranch());
|
|
1692
|
+
}
|
|
1693
|
+
// Without an `else`, the statements after the `if` are the other branch
|
|
1694
|
+
return this.either(condition, whenTrue, this.parseStatements());
|
|
1695
|
+
}
|
|
1696
|
+
/** One arm of an `if`: a block, or a single statement. */ parseBranch() {
|
|
1697
|
+
return this.stream.isPunctuation("{") ? this.parseBlock() : this.parseStatements();
|
|
1698
|
+
}
|
|
1699
|
+
/** A `switch` over one subject, as the disjunction of its cases. */ parseSwitchStatement() {
|
|
1700
|
+
this.stream.next();
|
|
1701
|
+
this.stream.expectPunctuation("(");
|
|
1702
|
+
const subject = this.parseValue();
|
|
1703
|
+
this.stream.expectPunctuation(")");
|
|
1704
|
+
this.stream.expectPunctuation("{");
|
|
1705
|
+
let matching = null;
|
|
1706
|
+
let pending = [];
|
|
1707
|
+
let everyLabel = [];
|
|
1708
|
+
let byDefault = null;
|
|
1709
|
+
let anyCaseBroke = false;
|
|
1710
|
+
while(!this.stream.matchPunctuation("}")){
|
|
1711
|
+
const label = this.stream.next();
|
|
1712
|
+
if (label.kind !== "identifier" || label.value !== "case" && label.value !== "default") {
|
|
1713
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`'${label.value}' inside a switch`));
|
|
1714
|
+
}
|
|
1715
|
+
if (label.value === "case") {
|
|
1716
|
+
const test = this.buildComparison(subject, COMPARISON_OPERATORS["==="], this.parseValue());
|
|
1717
|
+
pending.push(test);
|
|
1718
|
+
everyLabel.push(test);
|
|
1719
|
+
}
|
|
1720
|
+
this.stream.expectPunctuation(":");
|
|
1721
|
+
// `case 'a':` with no body of its own runs the next case's body
|
|
1722
|
+
if (this.stream.peek()?.value === "case" || this.stream.peek()?.value === "default") {
|
|
1723
|
+
continue;
|
|
1724
|
+
}
|
|
1725
|
+
if (this.stream.peek()?.value === "break") {
|
|
1726
|
+
this.stream.next();
|
|
1727
|
+
this.stream.matchPunctuation(";");
|
|
1728
|
+
anyCaseBroke = true;
|
|
1729
|
+
pending = [];
|
|
1730
|
+
continue;
|
|
1731
|
+
}
|
|
1732
|
+
const body = this.parseCaseBody();
|
|
1733
|
+
if (label.value === "default") {
|
|
1734
|
+
byDefault = body === NEVER ? null : body;
|
|
1735
|
+
continue;
|
|
1736
|
+
}
|
|
1737
|
+
if (body !== NEVER && pending.length > 0) {
|
|
1738
|
+
const reached = pending.reduce((left, right)=>or(left, right));
|
|
1739
|
+
const term = _types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(body) ? reached : and(reached, body);
|
|
1740
|
+
matching = matching == null ? term : or(matching, term);
|
|
1741
|
+
}
|
|
1742
|
+
pending = [];
|
|
1743
|
+
}
|
|
1744
|
+
// Falling out of the switch continues after it, so the statements there are the default too
|
|
1745
|
+
const afterSwitch = byDefault == null && !this.stream.isPunctuation("}") && !this.stream.isAtEnd ? this.parseStatements() : NEVER;
|
|
1746
|
+
if (afterSwitch !== NEVER) {
|
|
1747
|
+
// A `break` also continues after the switch, so its case would take that answer rather
|
|
1748
|
+
// than none — a distinction this rewrite cannot carry
|
|
1749
|
+
if (anyCaseBroke) {
|
|
1750
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a switch that breaks and then falls into more statements"));
|
|
1751
|
+
}
|
|
1752
|
+
byDefault = afterSwitch;
|
|
1753
|
+
}
|
|
1754
|
+
// A `default` runs only when every case failed, wherever it was written
|
|
1755
|
+
if (byDefault != null) {
|
|
1756
|
+
const noCaseMatched = everyLabel.length === 0 ? byDefault : and(this.negateExpression(everyLabel.reduce((left, right)=>or(left, right))), byDefault);
|
|
1757
|
+
matching = matching == null ? noCaseMatched : or(matching, noCaseMatched);
|
|
1758
|
+
}
|
|
1759
|
+
return matching ?? NEVER;
|
|
1760
|
+
}
|
|
1761
|
+
/** One case body, and the `break` that may follow its `return`. */ parseCaseBody() {
|
|
1762
|
+
const answer = this.parseStatements();
|
|
1763
|
+
if (this.stream.peek()?.value === "break") {
|
|
1764
|
+
this.stream.next();
|
|
1765
|
+
this.stream.matchPunctuation(";");
|
|
1766
|
+
}
|
|
1767
|
+
return answer;
|
|
1768
|
+
}
|
|
1769
|
+
/**
|
|
1770
|
+
* The predicate an `if`/`else` answers: `(condition && whenTrue) || (!condition && whenFalse)`,
|
|
1771
|
+
* with each case below that form after a constant branch cancels out.
|
|
1772
|
+
*/ either(condition, whenTrue, whenFalse) {
|
|
1773
|
+
if (whenTrue === NEVER) {
|
|
1774
|
+
return whenFalse === NEVER ? NEVER : and(this.negateExpression(condition), whenFalse);
|
|
1775
|
+
}
|
|
1776
|
+
if (whenFalse === NEVER) {
|
|
1777
|
+
return and(condition, whenTrue);
|
|
1778
|
+
}
|
|
1779
|
+
if (_types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(whenTrue)) {
|
|
1780
|
+
return or(condition, whenFalse);
|
|
1781
|
+
}
|
|
1782
|
+
if (_types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(whenFalse)) {
|
|
1783
|
+
return or(this.negateExpression(condition), whenTrue);
|
|
1784
|
+
}
|
|
1785
|
+
return or(and(condition, whenTrue), and(this.negateExpression(condition), whenFalse));
|
|
1786
|
+
}
|
|
748
1787
|
// || binds loosest, so it sits at the root of the parse
|
|
749
1788
|
parseOr() {
|
|
750
1789
|
let left = this.parseAnd();
|
|
@@ -792,10 +1831,18 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
792
1831
|
/**
|
|
793
1832
|
* Applies `!` to an already-parsed expression: comparators flip their
|
|
794
1833
|
* negated flag, compound expressions distribute via De Morgan's laws.
|
|
1834
|
+
*
|
|
1835
|
+
* Builds a new tree rather than flipping the flag in place, because an `if` uses its condition
|
|
1836
|
+
* twice — once negated — and a shared node would carry the flip into both branches.
|
|
795
1837
|
*/ negateExpression(expression) {
|
|
796
1838
|
if (expression instanceof _types__rspack_import_1/* .ComparatorExpression */.bQ) {
|
|
797
|
-
|
|
798
|
-
|
|
1839
|
+
return new _types__rspack_import_1/* .ComparatorExpression */.bQ({
|
|
1840
|
+
comparator: expression.comparator,
|
|
1841
|
+
negated: !expression.negated,
|
|
1842
|
+
strict: expression.strict,
|
|
1843
|
+
left: expression.left,
|
|
1844
|
+
right: expression.right
|
|
1845
|
+
});
|
|
799
1846
|
}
|
|
800
1847
|
if (expression instanceof _types__rspack_import_1/* .OperatorExpression */.fw && expression.left != null && expression.right != null) {
|
|
801
1848
|
return new _types__rspack_import_1/* .OperatorExpression */.fw({
|
|
@@ -804,27 +1851,127 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
804
1851
|
right: this.negateExpression(expression.right)
|
|
805
1852
|
});
|
|
806
1853
|
}
|
|
807
|
-
throw new Error(ERROR_MESSAGES.UNSUPPORTED("'!' on this expression"));
|
|
1854
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("'!' on this expression"));
|
|
1855
|
+
}
|
|
1856
|
+
parseComparison() {
|
|
1857
|
+
/**
|
|
1858
|
+
* A parenthesised group is either a boolean sub-expression or a VALUE — `(a && b)` against
|
|
1859
|
+
* `(x.name ?? '') === 'ada'` — and which one it is is only known at the closing bracket, by
|
|
1860
|
+
* what follows. So the boolean reading is tried first and rewound if a comparator turns up.
|
|
1861
|
+
*/ if (this.stream.isPunctuation("(") && this.stream.groupIsValue() === false) {
|
|
1862
|
+
this.stream.next();
|
|
1863
|
+
const expression = this.parseOr();
|
|
1864
|
+
this.stream.expectPunctuation(")");
|
|
1865
|
+
return expression;
|
|
1866
|
+
}
|
|
1867
|
+
const left = this.parseValue();
|
|
1868
|
+
const operatorToken = this.stream.peek();
|
|
1869
|
+
if (operatorToken != null && operatorToken.kind === "punctuation" && COMPARISON_OPERATORS[operatorToken.value] != null) {
|
|
1870
|
+
this.stream.next();
|
|
1871
|
+
const right = this.parseValue();
|
|
1872
|
+
return this.buildComparison(left, COMPARISON_OPERATORS[operatorToken.value], right);
|
|
1873
|
+
}
|
|
1874
|
+
return this.buildStandalone(left);
|
|
1875
|
+
}
|
|
1876
|
+
/**
|
|
1877
|
+
* A value, at JavaScript's precedence.
|
|
1878
|
+
*
|
|
1879
|
+
* Lowest first: the conditional operator, then nullish coalescing, then the bitwise levels, then
|
|
1880
|
+
* the shifts, then the arithmetic. Comparison sits between the shifts and the bitwise levels in
|
|
1881
|
+
* JavaScript, but a comparison is a boolean and is handled by `parseComparison` above, so this
|
|
1882
|
+
* chain skips it — a bitwise operand here is always a value.
|
|
1883
|
+
*/ /**
|
|
1884
|
+
* An operand from its own source, sharing this parser's schema and parameter names.
|
|
1885
|
+
*
|
|
1886
|
+
* A structural dependence found inside propagates outward: the template it belongs to cannot be
|
|
1887
|
+
* cached either.
|
|
1888
|
+
*/ parseNested(source) {
|
|
1889
|
+
const nested = new ExpressionParser(this.schema, new TokenStream(tokenize(source)), this.scope, this.paramsName, this.params, this.readsValues);
|
|
1890
|
+
const operand = nested.parseInterpolation();
|
|
1891
|
+
// Leftover tokens mean the interpolation held something this reads only part of. Silently
|
|
1892
|
+
// keeping the part it understood is the worst outcome available: `${x.age > 5 ? "a" : "b"}`
|
|
1893
|
+
// would become `x.age`, and the filter would answer a question nobody asked.
|
|
1894
|
+
if (nested.stream.isAtEnd === false) {
|
|
1895
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("an interpolation this parser reads only part of"));
|
|
1896
|
+
}
|
|
1897
|
+
if (nested.structurallyDependsOnParams === true) {
|
|
1898
|
+
this.structurallyDependsOnParams = true;
|
|
1899
|
+
}
|
|
1900
|
+
return operand;
|
|
1901
|
+
}
|
|
1902
|
+
/**
|
|
1903
|
+
* The whole of one `${…}`.
|
|
1904
|
+
*
|
|
1905
|
+
* A conditional is read here rather than in `parseValue`, because an interpolation is the one
|
|
1906
|
+
* place a conditional appears without brackets around it.
|
|
1907
|
+
*/ parseInterpolation() {
|
|
1908
|
+
if (this.stream.holdsConditional()) {
|
|
1909
|
+
const condition = this.parseOr();
|
|
1910
|
+
this.stream.expectPunctuation("?");
|
|
1911
|
+
const whenTrue = this.parseValue();
|
|
1912
|
+
this.stream.expectPunctuation(":");
|
|
1913
|
+
const whenFalse = this.parseValue();
|
|
1914
|
+
return {
|
|
1915
|
+
kind: "conditional",
|
|
1916
|
+
condition,
|
|
1917
|
+
whenTrue,
|
|
1918
|
+
whenFalse
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1921
|
+
return this.parseValue();
|
|
808
1922
|
}
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1923
|
+
parseValue() {
|
|
1924
|
+
return this.parseCoalesce();
|
|
1925
|
+
}
|
|
1926
|
+
parseCoalesce() {
|
|
1927
|
+
return this.parseBinary(COALESCE_OPERATORS, ()=>this.parseBitwiseOr());
|
|
1928
|
+
}
|
|
1929
|
+
parseBitwiseOr() {
|
|
1930
|
+
return this.parseBinary(BITWISE_OR_OPERATORS, ()=>this.parseBitwiseXor());
|
|
1931
|
+
}
|
|
1932
|
+
parseBitwiseXor() {
|
|
1933
|
+
return this.parseBinary(BITWISE_XOR_OPERATORS, ()=>this.parseBitwiseAnd());
|
|
1934
|
+
}
|
|
1935
|
+
parseBitwiseAnd() {
|
|
1936
|
+
return this.parseBinary(BITWISE_AND_OPERATORS, ()=>this.parseShift());
|
|
1937
|
+
}
|
|
1938
|
+
parseShift() {
|
|
1939
|
+
return this.parseBinary(SHIFT_OPERATORS, ()=>this.parseAdditive());
|
|
1940
|
+
}
|
|
1941
|
+
parseAdditive() {
|
|
1942
|
+
return this.parseBinary(ADDITIVE_OPERATORS, ()=>this.parseMultiplicative());
|
|
1943
|
+
}
|
|
1944
|
+
parseMultiplicative() {
|
|
1945
|
+
return this.parseBinary(MULTIPLICATIVE_OPERATORS, ()=>this.parseExponent());
|
|
1946
|
+
}
|
|
1947
|
+
/** `**` is RIGHT-associative: `2 ** 3 ** 2` is 2 ** 9, not 8 ** 2. */ parseExponent() {
|
|
820
1948
|
const left = this.parseOperand();
|
|
821
|
-
|
|
822
|
-
|
|
1949
|
+
if (this.stream.isPunctuation("**") === false) {
|
|
1950
|
+
return left;
|
|
1951
|
+
}
|
|
1952
|
+
this.stream.next();
|
|
1953
|
+
return {
|
|
1954
|
+
kind: "arithmetic",
|
|
1955
|
+
call: "power",
|
|
1956
|
+
left,
|
|
1957
|
+
right: this.parseExponent()
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
/** Left-associative, so `a - b - c` is `(a - b) - c` rather than `a - (b - c)`. */ parseBinary(operators, next) {
|
|
1961
|
+
let left = next();
|
|
1962
|
+
for(;;){
|
|
1963
|
+
const token = this.stream.peek();
|
|
1964
|
+
if (token == null || token.kind !== "punctuation" || operators[token.value] == null) {
|
|
1965
|
+
return left;
|
|
1966
|
+
}
|
|
823
1967
|
this.stream.next();
|
|
824
|
-
|
|
825
|
-
|
|
1968
|
+
left = {
|
|
1969
|
+
kind: "arithmetic",
|
|
1970
|
+
call: operators[token.value],
|
|
1971
|
+
left,
|
|
1972
|
+
right: next()
|
|
1973
|
+
};
|
|
826
1974
|
}
|
|
827
|
-
return this.buildStandalone(left);
|
|
828
1975
|
}
|
|
829
1976
|
parseOperand() {
|
|
830
1977
|
const token = this.stream.peek();
|
|
@@ -849,6 +1996,131 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
849
1996
|
locale: null
|
|
850
1997
|
};
|
|
851
1998
|
}
|
|
1999
|
+
// A parenthesised VALUE — `(x.price & 1)`, `(x.name ?? '')`. The boolean reading of a group
|
|
2000
|
+
// is handled in parseComparison; by the time an operand sees one it is arithmetic.
|
|
2001
|
+
if (token.kind === "punctuation" && token.value === "(") {
|
|
2002
|
+
const conditional = this.stream.groupHoldsConditional();
|
|
2003
|
+
this.stream.next();
|
|
2004
|
+
if (conditional === true) {
|
|
2005
|
+
const condition = this.parseOr();
|
|
2006
|
+
this.stream.expectPunctuation("?");
|
|
2007
|
+
const whenTrue = this.parseValue();
|
|
2008
|
+
this.stream.expectPunctuation(":");
|
|
2009
|
+
const whenFalse = this.parseValue();
|
|
2010
|
+
this.stream.expectPunctuation(")");
|
|
2011
|
+
return {
|
|
2012
|
+
kind: "conditional",
|
|
2013
|
+
condition,
|
|
2014
|
+
whenTrue,
|
|
2015
|
+
whenFalse
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
const inner = this.parseValue();
|
|
2019
|
+
this.stream.expectPunctuation(")");
|
|
2020
|
+
const grouped = inner.kind === "arithmetic" ? {
|
|
2021
|
+
...inner,
|
|
2022
|
+
grouped: true
|
|
2023
|
+
} : inner;
|
|
2024
|
+
return this.withGroupCall(grouped);
|
|
2025
|
+
}
|
|
2026
|
+
/**
|
|
2027
|
+
* A template with interpolation, folded into `concat`.
|
|
2028
|
+
*
|
|
2029
|
+
* Each `${…}` was kept as source by the tokenizer and is parsed by its own stream, so it can
|
|
2030
|
+
* hold anything an operand can — a property, a param, arithmetic, another template. Empty
|
|
2031
|
+
* chunks are dropped: `${a}${b}` is two operands, not two operands and three empty strings.
|
|
2032
|
+
*/ if (token.kind === "template") {
|
|
2033
|
+
this.stream.next();
|
|
2034
|
+
const { chunks, expressions } = JSON.parse(token.value);
|
|
2035
|
+
const pieces = [];
|
|
2036
|
+
for(let at = 0; at < chunks.length; at++){
|
|
2037
|
+
if (chunks[at].length > 0) {
|
|
2038
|
+
pieces.push({
|
|
2039
|
+
kind: "value",
|
|
2040
|
+
value: chunks[at],
|
|
2041
|
+
transformer: null,
|
|
2042
|
+
locale: null
|
|
2043
|
+
});
|
|
2044
|
+
}
|
|
2045
|
+
if (at < expressions.length) {
|
|
2046
|
+
pieces.push(this.parseNested(expressions[at]));
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
if (pieces.length === 0) {
|
|
2050
|
+
return {
|
|
2051
|
+
kind: "value",
|
|
2052
|
+
value: "",
|
|
2053
|
+
transformer: null,
|
|
2054
|
+
locale: null
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
// One piece and no chunk means no concat to do the coercion, so the conversion has to be
|
|
2058
|
+
// explicit: `` `${x.age}` `` is the STRING "9", not the number 9.
|
|
2059
|
+
if (pieces.length === 1) {
|
|
2060
|
+
const only = pieces[0];
|
|
2061
|
+
const alreadyText = only.kind === "value" && typeof only.value === "string";
|
|
2062
|
+
return alreadyText ? only : {
|
|
2063
|
+
kind: "arithmetic",
|
|
2064
|
+
call: "to-string",
|
|
2065
|
+
left: only,
|
|
2066
|
+
right: noArgument()
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
return pieces.reduce((left, right)=>({
|
|
2070
|
+
kind: "arithmetic",
|
|
2071
|
+
call: "concat",
|
|
2072
|
+
left,
|
|
2073
|
+
right
|
|
2074
|
+
}));
|
|
2075
|
+
}
|
|
2076
|
+
if (token.kind === "bigint") {
|
|
2077
|
+
this.stream.next();
|
|
2078
|
+
return {
|
|
2079
|
+
kind: "value",
|
|
2080
|
+
value: BigInt(token.value),
|
|
2081
|
+
transformer: null,
|
|
2082
|
+
locale: null
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
if (token.kind === "regex") {
|
|
2086
|
+
this.stream.next();
|
|
2087
|
+
const [source, flags] = token.value.split("\u0000");
|
|
2088
|
+
const pattern = {
|
|
2089
|
+
kind: "value",
|
|
2090
|
+
value: new RegExp(source, flags),
|
|
2091
|
+
transformer: null,
|
|
2092
|
+
locale: null
|
|
2093
|
+
};
|
|
2094
|
+
// `/^a/.test(x.name)` — the pattern is the literal, the subject is the argument, and the
|
|
2095
|
+
// tree puts them the other way round: the property is what the call applies to.
|
|
2096
|
+
if (this.stream.isPunctuation(".")) {
|
|
2097
|
+
const method = this.stream.peek(1);
|
|
2098
|
+
if (method != null && method.kind === "identifier" && method.value === "test") {
|
|
2099
|
+
this.stream.next();
|
|
2100
|
+
this.stream.next();
|
|
2101
|
+
this.stream.expectPunctuation("(");
|
|
2102
|
+
const subject = this.parseValue();
|
|
2103
|
+
this.stream.expectPunctuation(")");
|
|
2104
|
+
return {
|
|
2105
|
+
kind: "arithmetic",
|
|
2106
|
+
call: "matches",
|
|
2107
|
+
left: subject,
|
|
2108
|
+
right: pattern
|
|
2109
|
+
};
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
return pattern;
|
|
2113
|
+
}
|
|
2114
|
+
if (token.kind === "punctuation" && token.value === "~") {
|
|
2115
|
+
this.stream.next();
|
|
2116
|
+
// Unary, so the tree carries the operand and no argument
|
|
2117
|
+
return {
|
|
2118
|
+
kind: "arithmetic",
|
|
2119
|
+
call: "bit-not",
|
|
2120
|
+
left: this.parseOperand(),
|
|
2121
|
+
right: noArgument()
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
852
2124
|
if (token.kind === "punctuation" && token.value === "-") {
|
|
853
2125
|
this.stream.next();
|
|
854
2126
|
const numberToken = this.stream.next();
|
|
@@ -906,6 +2178,9 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
906
2178
|
if (argument.kind === "method-call") {
|
|
907
2179
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED("nested method call inside .includes()"));
|
|
908
2180
|
}
|
|
2181
|
+
if (argument.kind === "arithmetic" || argument.kind === "conditional" || argument.kind === "opaque") {
|
|
2182
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("arithmetic inside .includes()"));
|
|
2183
|
+
}
|
|
909
2184
|
return {
|
|
910
2185
|
kind: "method-call",
|
|
911
2186
|
target: array,
|
|
@@ -951,16 +2226,17 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
951
2226
|
locale: null
|
|
952
2227
|
};
|
|
953
2228
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
if (this.paramsName != null && root === this.paramsName) {
|
|
2229
|
+
const binding = this.scope.get(root);
|
|
2230
|
+
if (binding != null) {
|
|
2231
|
+
if (binding.kind === "inlined") {
|
|
2232
|
+
this.stream.splice(binding.tokens);
|
|
2233
|
+
return this.parseOperand();
|
|
2234
|
+
}
|
|
961
2235
|
return this.parseChain({
|
|
962
|
-
kind:
|
|
963
|
-
|
|
2236
|
+
kind: binding.kind,
|
|
2237
|
+
path: [
|
|
2238
|
+
...binding.path
|
|
2239
|
+
]
|
|
964
2240
|
});
|
|
965
2241
|
}
|
|
966
2242
|
// A bare variable from the outer scope — its value cannot be derived from source text
|
|
@@ -970,7 +2246,7 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
970
2246
|
* Parses the segments after an entity/params root: dot access, bracket
|
|
971
2247
|
* access, transform methods and comparator methods.
|
|
972
2248
|
*/ parseChain(options) {
|
|
973
|
-
const path =
|
|
2249
|
+
const path = options.path;
|
|
974
2250
|
let transformer = null;
|
|
975
2251
|
let locale = null;
|
|
976
2252
|
while(true){
|
|
@@ -996,6 +2272,9 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
996
2272
|
if (argument.kind === "method-call") {
|
|
997
2273
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`nested method call inside .${method}()`));
|
|
998
2274
|
}
|
|
2275
|
+
if (argument.kind === "arithmetic" || argument.kind === "conditional" || argument.kind === "opaque") {
|
|
2276
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`arithmetic inside .${method}()`));
|
|
2277
|
+
}
|
|
999
2278
|
return {
|
|
1000
2279
|
kind: "method-call",
|
|
1001
2280
|
target: this.resolveChain(options.kind, path, transformer, locale),
|
|
@@ -1003,9 +2282,20 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
1003
2282
|
argument
|
|
1004
2283
|
};
|
|
1005
2284
|
}
|
|
2285
|
+
if (this.readsValues) {
|
|
2286
|
+
return this.withGroupCall(this.opaqueCall(this.resolveChain(options.kind, path, transformer, locale)));
|
|
2287
|
+
}
|
|
1006
2288
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`method '.${method}()'`));
|
|
1007
2289
|
}
|
|
1008
2290
|
if (transformer != null) {
|
|
2291
|
+
if (this.readsValues) {
|
|
2292
|
+
return this.withGroupCall({
|
|
2293
|
+
kind: "opaque",
|
|
2294
|
+
reads: [
|
|
2295
|
+
this.resolveChain(options.kind, path, transformer, locale)
|
|
2296
|
+
]
|
|
2297
|
+
});
|
|
2298
|
+
}
|
|
1009
2299
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED("property access after a transform method"));
|
|
1010
2300
|
}
|
|
1011
2301
|
path.push(segment.value);
|
|
@@ -1032,12 +2322,15 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
1032
2322
|
// docs/mutation-backlog.md) — every mutation of this four-conjunct guard reroutes
|
|
1033
2323
|
// bracket access between two paths that both collapse to NOT_PARSABLE; the
|
|
1034
2324
|
// experiment recorded there aimed 30 tests at this line and killed none.
|
|
1035
|
-
|
|
1036
|
-
|
|
2325
|
+
const binding = token.kind === "identifier" ? this.scope.get(token.value) : undefined;
|
|
2326
|
+
if (kind === "property" && binding != null && binding.kind === "param") {
|
|
2327
|
+
const paramPath = [
|
|
2328
|
+
...binding.path
|
|
2329
|
+
];
|
|
1037
2330
|
while(this.stream.matchPunctuation(".") || this.stream.matchPunctuation("?.")){
|
|
1038
2331
|
paramPath.push(this.stream.next().value);
|
|
1039
2332
|
}
|
|
1040
|
-
const resolved = resolveParamPath(this.paramsName, paramPath, this.params);
|
|
2333
|
+
const resolved = resolveParamPath(this.paramsName ?? token.value, paramPath, this.params);
|
|
1041
2334
|
if (typeof resolved !== "string") {
|
|
1042
2335
|
throw new ParamDependentParseError(ERROR_MESSAGES.PROPERTY_NOT_FOUND(paramPath.join(".")));
|
|
1043
2336
|
}
|
|
@@ -1090,6 +2383,96 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
1090
2383
|
locale
|
|
1091
2384
|
};
|
|
1092
2385
|
}
|
|
2386
|
+
/**
|
|
2387
|
+
* A call on a parenthesised value: `(x.name).toLowerCase()`, `(x.age + 1).length`. Any operand can
|
|
2388
|
+
* receive one here, unlike a property chain, which carries at most one transform.
|
|
2389
|
+
*/ withGroupCall(operand) {
|
|
2390
|
+
let receiver = operand;
|
|
2391
|
+
while(this.stream.isPunctuation(".") || this.stream.isPunctuation("?.")){
|
|
2392
|
+
const segment = this.stream.peek(1);
|
|
2393
|
+
if (segment == null || segment.kind !== "identifier") {
|
|
2394
|
+
break;
|
|
2395
|
+
}
|
|
2396
|
+
if (segment.value === "length" && !this.stream.isPunctuation("(", 2)) {
|
|
2397
|
+
this.stream.next();
|
|
2398
|
+
this.stream.next();
|
|
2399
|
+
receiver = {
|
|
2400
|
+
kind: "arithmetic",
|
|
2401
|
+
call: "length",
|
|
2402
|
+
left: receiver,
|
|
2403
|
+
right: noArgument()
|
|
2404
|
+
};
|
|
2405
|
+
continue;
|
|
2406
|
+
}
|
|
2407
|
+
const transform = TRANSFORM_METHODS[segment.value];
|
|
2408
|
+
if (transform != null) {
|
|
2409
|
+
this.stream.next();
|
|
2410
|
+
this.stream.next();
|
|
2411
|
+
this.stream.expectPunctuation("(");
|
|
2412
|
+
this.stream.expectPunctuation(")");
|
|
2413
|
+
receiver = {
|
|
2414
|
+
kind: "arithmetic",
|
|
2415
|
+
call: transform.transformer,
|
|
2416
|
+
left: receiver,
|
|
2417
|
+
right: transform.locale == null ? noArgument() : {
|
|
2418
|
+
kind: "value",
|
|
2419
|
+
value: transform.locale,
|
|
2420
|
+
transformer: null,
|
|
2421
|
+
locale: null
|
|
2422
|
+
}
|
|
2423
|
+
};
|
|
2424
|
+
continue;
|
|
2425
|
+
}
|
|
2426
|
+
// A comparator method needs a property target, which only an ungrouped chain produces
|
|
2427
|
+
if (COMPARATOR_METHODS[segment.value] != null && receiver.kind === "property") {
|
|
2428
|
+
this.stream.next();
|
|
2429
|
+
this.stream.next();
|
|
2430
|
+
this.stream.expectPunctuation("(");
|
|
2431
|
+
const argument = this.parseOperand();
|
|
2432
|
+
this.stream.expectPunctuation(")");
|
|
2433
|
+
if (argument.kind !== "property" && argument.kind !== "value" && argument.kind !== "param") {
|
|
2434
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`'.${segment.value}()' on that argument`));
|
|
2435
|
+
}
|
|
2436
|
+
return {
|
|
2437
|
+
kind: "method-call",
|
|
2438
|
+
target: receiver,
|
|
2439
|
+
method: segment.value,
|
|
2440
|
+
argument
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
// Any other member or call of a value, which a selector reads through
|
|
2444
|
+
if (this.readsValues) {
|
|
2445
|
+
this.stream.next();
|
|
2446
|
+
this.stream.next();
|
|
2447
|
+
receiver = this.stream.isPunctuation("(") ? this.opaqueCall(receiver) : {
|
|
2448
|
+
kind: "opaque",
|
|
2449
|
+
reads: [
|
|
2450
|
+
receiver
|
|
2451
|
+
]
|
|
2452
|
+
};
|
|
2453
|
+
continue;
|
|
2454
|
+
}
|
|
2455
|
+
break;
|
|
2456
|
+
}
|
|
2457
|
+
return receiver;
|
|
2458
|
+
}
|
|
2459
|
+
/** A call with no node of its own, from its `(`, kept for what its receiver and arguments read. */ opaqueCall(receiver) {
|
|
2460
|
+
const reads = [
|
|
2461
|
+
receiver
|
|
2462
|
+
];
|
|
2463
|
+
this.stream.expectPunctuation("(");
|
|
2464
|
+
while(!this.stream.matchPunctuation(")")){
|
|
2465
|
+
reads.push(this.parseValue());
|
|
2466
|
+
if (!this.stream.matchPunctuation(",")) {
|
|
2467
|
+
this.stream.expectPunctuation(")");
|
|
2468
|
+
break;
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
return {
|
|
2472
|
+
kind: "opaque",
|
|
2473
|
+
reads
|
|
2474
|
+
};
|
|
2475
|
+
}
|
|
1093
2476
|
withValueTransformer(operand) {
|
|
1094
2477
|
if (this.stream.isPunctuation(".")) {
|
|
1095
2478
|
const method = this.stream.peek(1);
|
|
@@ -1123,12 +2506,26 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
1123
2506
|
if (right.kind === "method-call") {
|
|
1124
2507
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED("method call on the right side of a comparison"));
|
|
1125
2508
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
2509
|
+
// A comparison is a tree a backend renders, and this operand has no node in one
|
|
2510
|
+
if (left.kind === "opaque" || right.kind === "opaque") {
|
|
2511
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a call with no expression form inside a comparison"));
|
|
2512
|
+
}
|
|
2513
|
+
if (needsBrackets(left) || needsBrackets(right)) {
|
|
2514
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a bitwise or nullish operator compared without brackets, which JavaScript reads the other way round"));
|
|
2515
|
+
}
|
|
2516
|
+
if (left.kind === "arithmetic" || right.kind === "arithmetic" || left.kind === "conditional" || right.kind === "conditional") {
|
|
2517
|
+
if (containsProperty(left) === false && containsProperty(right) === false) {
|
|
2518
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("arithmetic that references no schema property"));
|
|
1131
2519
|
}
|
|
2520
|
+
return new _types__rspack_import_1/* .ComparatorExpression */.bQ({
|
|
2521
|
+
comparator: operator.comparator,
|
|
2522
|
+
negated: operator.negated,
|
|
2523
|
+
strict: operator.strict,
|
|
2524
|
+
left: this.createOperandExpression(left),
|
|
2525
|
+
right: this.createOperandExpression(right)
|
|
2526
|
+
});
|
|
2527
|
+
}
|
|
2528
|
+
if (left.kind === "property" && right.kind === "property") {
|
|
1132
2529
|
return new _types__rspack_import_1/* .ComparatorExpression */.bQ({
|
|
1133
2530
|
comparator: operator.comparator,
|
|
1134
2531
|
negated: operator.negated,
|
|
@@ -1137,18 +2534,63 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
1137
2534
|
right: this.createPropertyExpression(right)
|
|
1138
2535
|
});
|
|
1139
2536
|
}
|
|
2537
|
+
// Only a loose comparison coerces. `===` records `strict` and honouring it is the point.
|
|
1140
2538
|
if (left.kind === "property" && right.kind !== "property") {
|
|
1141
|
-
return this.buildPropertyComparator(left, operator, right, /* applyConverter */
|
|
2539
|
+
return this.buildPropertyComparator(left, operator, right, /* applyConverter */ !operator.strict);
|
|
1142
2540
|
}
|
|
1143
2541
|
if (right.kind === "property" && left.kind !== "property") {
|
|
1144
2542
|
const swapped = {
|
|
1145
2543
|
...operator,
|
|
1146
2544
|
comparator: SWAPPED_COMPARATORS[operator.comparator]
|
|
1147
2545
|
};
|
|
1148
|
-
return this.buildPropertyComparator(right, swapped, left, /* applyConverter */
|
|
2546
|
+
return this.buildPropertyComparator(right, swapped, left, /* applyConverter */ !operator.strict);
|
|
2547
|
+
}
|
|
2548
|
+
const settled = this.settleConstantComparison(left, operator, right);
|
|
2549
|
+
if (settled != null) {
|
|
2550
|
+
return settled;
|
|
1149
2551
|
}
|
|
1150
2552
|
throw new Error(ERROR_MESSAGES.UNSUPPORTED("comparison requires a schema property on at least one side"));
|
|
1151
2553
|
}
|
|
2554
|
+
/**
|
|
2555
|
+
* The answer a comparison of two constants gives, when that answer is `true`. The other answer
|
|
2556
|
+
* excludes every row, which has no expression node.
|
|
2557
|
+
*/ settleConstantComparison(left, operator, right) {
|
|
2558
|
+
const leftValue = this.constantOf(left);
|
|
2559
|
+
const rightValue = this.constantOf(right);
|
|
2560
|
+
if (leftValue === UNKNOWN_UNTIL_ROW || rightValue === UNKNOWN_UNTIL_ROW) {
|
|
2561
|
+
return null;
|
|
2562
|
+
}
|
|
2563
|
+
const answer = (0,_evaluate__rspack_import_3/* .evaluate */._3)(new _types__rspack_import_1/* .ComparatorExpression */.bQ({
|
|
2564
|
+
comparator: operator.comparator,
|
|
2565
|
+
negated: operator.negated,
|
|
2566
|
+
strict: operator.strict,
|
|
2567
|
+
left: new _types__rspack_import_1/* .ValueExpression */.Ko({
|
|
2568
|
+
value: leftValue
|
|
2569
|
+
}),
|
|
2570
|
+
right: new _types__rspack_import_1/* .ValueExpression */.Ko({
|
|
2571
|
+
value: rightValue
|
|
2572
|
+
})
|
|
2573
|
+
}), {});
|
|
2574
|
+
if (answer === true) {
|
|
2575
|
+
return _types__rspack_import_1/* .Expression.EMPTY */.r4.EMPTY;
|
|
2576
|
+
}
|
|
2577
|
+
// Params decided this, so the refusal must not be cached against the source: the same filter
|
|
2578
|
+
// with other params can be a tautology.
|
|
2579
|
+
if (left.kind === "param" || right.kind === "param") {
|
|
2580
|
+
throw new ParamDependentParseError(ERROR_MESSAGES.UNSUPPORTED("a params comparison no row satisfies"));
|
|
2581
|
+
}
|
|
2582
|
+
return null;
|
|
2583
|
+
}
|
|
2584
|
+
/** The value an operand holds already, for the operands that do not depend on a row. */ constantOf(operand) {
|
|
2585
|
+
if (operand.kind === "value" && operand.transformer == null) {
|
|
2586
|
+
return operand.value;
|
|
2587
|
+
}
|
|
2588
|
+
if (operand.kind === "param" && operand.transformer == null) {
|
|
2589
|
+
this.structurallyDependsOnParams = true;
|
|
2590
|
+
return resolveParamPath(this.paramsName ?? "params", operand.path, this.params);
|
|
2591
|
+
}
|
|
2592
|
+
return UNKNOWN_UNTIL_ROW;
|
|
2593
|
+
}
|
|
1152
2594
|
buildStandalone(operand) {
|
|
1153
2595
|
if (operand.kind === "method-call") {
|
|
1154
2596
|
return this.buildMethodComparator(operand);
|
|
@@ -1171,6 +2613,21 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
1171
2613
|
locale: null
|
|
1172
2614
|
}, /* applyConverter */ true);
|
|
1173
2615
|
}
|
|
2616
|
+
// A boolean-valued call standing alone IS the predicate
|
|
2617
|
+
if (operand.kind === "arithmetic" && operand.call === "matches") {
|
|
2618
|
+
return new _types__rspack_import_1/* .ComparatorExpression */.bQ({
|
|
2619
|
+
comparator: "equals",
|
|
2620
|
+
negated: false,
|
|
2621
|
+
strict: false,
|
|
2622
|
+
left: this.createOperandExpression(operand),
|
|
2623
|
+
right: new _types__rspack_import_1/* .ValueExpression */.Ko({
|
|
2624
|
+
value: true
|
|
2625
|
+
})
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
if (operand.kind === "arithmetic" || operand.kind === "conditional") {
|
|
2629
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("arithmetic used as a condition rather than compared"));
|
|
2630
|
+
}
|
|
1174
2631
|
// Constant `true` — a tautology, which parseAnd/parseOr simplify away
|
|
1175
2632
|
if (operand.kind === "value" && operand.value === true && operand.transformer == null) {
|
|
1176
2633
|
return _types__rspack_import_1/* .Expression.EMPTY */.r4.EMPTY;
|
|
@@ -1223,12 +2680,6 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
1223
2680
|
right: this.createValueExpression(value, null, /* applyConverter */ false)
|
|
1224
2681
|
});
|
|
1225
2682
|
}
|
|
1226
|
-
// Casing transformers on a property are only meaningful with string-matching
|
|
1227
|
-
// comparators; on relational comparators the plugins would silently
|
|
1228
|
-
// ignore them and return wrong data
|
|
1229
|
-
if (property.transformer != null && !isStringMatch) {
|
|
1230
|
-
throw new Error(ERROR_MESSAGES.UNSUPPORTED("transform method outside of startsWith/endsWith/includes"));
|
|
1231
|
-
}
|
|
1232
2683
|
return new _types__rspack_import_1/* .ComparatorExpression */.bQ({
|
|
1233
2684
|
comparator: operator.comparator,
|
|
1234
2685
|
negated: operator.negated,
|
|
@@ -1237,31 +2688,63 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
1237
2688
|
right: this.createValueExpression(value, property.property, applyConverter)
|
|
1238
2689
|
});
|
|
1239
2690
|
}
|
|
2691
|
+
/**
|
|
2692
|
+
* Any operand as an expression.
|
|
2693
|
+
*
|
|
2694
|
+
* Values inside arithmetic take no paired property: the result is a computed number, so the
|
|
2695
|
+
* property's serializer and type converter do not describe it — the same reason `.length` skips
|
|
2696
|
+
* them.
|
|
2697
|
+
*/ createOperandExpression(operand) {
|
|
2698
|
+
if (operand.kind === "conditional") {
|
|
2699
|
+
return new _types__rspack_import_1/* .CallExpression */.DG({
|
|
2700
|
+
call: "conditional",
|
|
2701
|
+
expression: operand.condition,
|
|
2702
|
+
arguments: [
|
|
2703
|
+
this.createOperandExpression(operand.whenTrue),
|
|
2704
|
+
this.createOperandExpression(operand.whenFalse)
|
|
2705
|
+
]
|
|
2706
|
+
});
|
|
2707
|
+
}
|
|
2708
|
+
if (operand.kind === "arithmetic") {
|
|
2709
|
+
return new _types__rspack_import_1/* .CallExpression */.DG({
|
|
2710
|
+
call: operand.call,
|
|
2711
|
+
expression: this.createOperandExpression(operand.left),
|
|
2712
|
+
arguments: operand.right === NO_ARGUMENT ? [] : operand.extra == null ? [
|
|
2713
|
+
this.createOperandExpression(operand.right)
|
|
2714
|
+
] : [
|
|
2715
|
+
this.createOperandExpression(operand.right),
|
|
2716
|
+
this.createOperandExpression(operand.extra)
|
|
2717
|
+
]
|
|
2718
|
+
});
|
|
2719
|
+
}
|
|
2720
|
+
if (operand.kind === "property") {
|
|
2721
|
+
return this.createPropertyExpression(operand);
|
|
2722
|
+
}
|
|
2723
|
+
if (operand.kind === "method-call") {
|
|
2724
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a method call inside arithmetic"));
|
|
2725
|
+
}
|
|
2726
|
+
if (operand.kind === "opaque") {
|
|
2727
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED("a call with no expression form inside arithmetic"));
|
|
2728
|
+
}
|
|
2729
|
+
return this.createValueExpression(operand, null, /* applyConverter */ false);
|
|
2730
|
+
}
|
|
1240
2731
|
createPropertyExpression(operand) {
|
|
1241
|
-
|
|
2732
|
+
return asCall(new _types__rspack_import_1/* .PropertyExpression */.ep({
|
|
1242
2733
|
property: operand.property
|
|
1243
|
-
});
|
|
1244
|
-
expression.transformer = operand.transformer;
|
|
1245
|
-
expression.locale = operand.locale;
|
|
1246
|
-
return expression;
|
|
2734
|
+
}), operand.transformer, operand.locale);
|
|
1247
2735
|
}
|
|
1248
2736
|
createValueExpression(operand, pairedProperty, applyConverter) {
|
|
1249
2737
|
if (operand.kind === "param") {
|
|
1250
|
-
|
|
2738
|
+
return asCall(new ParamReferenceExpression({
|
|
1251
2739
|
paramPath: operand.path,
|
|
1252
2740
|
pairedProperty,
|
|
1253
2741
|
applyConverter
|
|
1254
|
-
});
|
|
1255
|
-
expression.transformer = operand.transformer;
|
|
1256
|
-
expression.locale = operand.locale;
|
|
1257
|
-
return expression;
|
|
2742
|
+
}), operand.transformer, operand.locale);
|
|
1258
2743
|
}
|
|
1259
2744
|
const expression = new _types__rspack_import_1/* .ValueExpression */.Ko({
|
|
1260
2745
|
value: resolvePairedValue(operand.value, pairedProperty, applyConverter)
|
|
1261
2746
|
});
|
|
1262
|
-
expression.transformer
|
|
1263
|
-
expression.locale = operand.locale;
|
|
1264
|
-
return expression;
|
|
2747
|
+
return asCall(expression, operand.transformer, operand.locale);
|
|
1265
2748
|
}
|
|
1266
2749
|
}
|
|
1267
2750
|
// #endregion
|
|
@@ -1273,28 +2756,19 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
1273
2756
|
*/ const bindExpression = (expression, paramsName, params)=>{
|
|
1274
2757
|
if (expression instanceof ParamReferenceExpression) {
|
|
1275
2758
|
const raw = resolveParamPath(paramsName ?? "params", expression.paramPath, params);
|
|
1276
|
-
|
|
2759
|
+
return new _types__rspack_import_1/* .ValueExpression */.Ko({
|
|
1277
2760
|
value: resolvePairedValue(raw, expression.pairedProperty, expression.applyConverter)
|
|
1278
2761
|
});
|
|
1279
|
-
bound.transformer = expression.transformer;
|
|
1280
|
-
bound.locale = expression.locale;
|
|
1281
|
-
return bound;
|
|
1282
2762
|
}
|
|
1283
2763
|
if (expression instanceof _types__rspack_import_1/* .ValueExpression */.Ko) {
|
|
1284
|
-
|
|
2764
|
+
return new _types__rspack_import_1/* .ValueExpression */.Ko({
|
|
1285
2765
|
value: expression.value
|
|
1286
2766
|
});
|
|
1287
|
-
clone.transformer = expression.transformer;
|
|
1288
|
-
clone.locale = expression.locale;
|
|
1289
|
-
return clone;
|
|
1290
2767
|
}
|
|
1291
2768
|
if (expression instanceof _types__rspack_import_1/* .PropertyExpression */.ep) {
|
|
1292
|
-
|
|
2769
|
+
return new _types__rspack_import_1/* .PropertyExpression */.ep({
|
|
1293
2770
|
property: expression.property
|
|
1294
2771
|
});
|
|
1295
|
-
clone.transformer = expression.transformer;
|
|
1296
|
-
clone.locale = expression.locale;
|
|
1297
|
-
return clone;
|
|
1298
2772
|
}
|
|
1299
2773
|
if (expression instanceof _types__rspack_import_1/* .ComparatorExpression */.bQ) {
|
|
1300
2774
|
return new _types__rspack_import_1/* .ComparatorExpression */.bQ({
|
|
@@ -1312,8 +2786,99 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
1312
2786
|
right: expression.right ? bindExpression(expression.right, paramsName, params) : undefined
|
|
1313
2787
|
});
|
|
1314
2788
|
}
|
|
2789
|
+
if (expression instanceof _types__rspack_import_1/* .CallExpression */.DG) {
|
|
2790
|
+
return new _types__rspack_import_1/* .CallExpression */.DG({
|
|
2791
|
+
call: expression.call,
|
|
2792
|
+
expression: bindExpression(expression.expression, paramsName, params),
|
|
2793
|
+
arguments: expression.arguments.map((argument)=>bindExpression(argument, paramsName, params))
|
|
2794
|
+
});
|
|
2795
|
+
}
|
|
1315
2796
|
return expression;
|
|
1316
2797
|
};
|
|
2798
|
+
/**
|
|
2799
|
+
* Wraps an operand in the call a transform method named, if there was one.
|
|
2800
|
+
*
|
|
2801
|
+
* `Transformer` and `Call` share these three names, so the transform IS the call name. A locale
|
|
2802
|
+
* becomes the call's first argument, which is where it belongs — it qualifies the casing, not the
|
|
2803
|
+
* property.
|
|
2804
|
+
*/ const asCall = (inner, transformer, locale)=>{
|
|
2805
|
+
if (transformer == null) {
|
|
2806
|
+
return inner;
|
|
2807
|
+
}
|
|
2808
|
+
return new _types__rspack_import_1/* .CallExpression */.DG({
|
|
2809
|
+
call: transformer,
|
|
2810
|
+
expression: inner,
|
|
2811
|
+
arguments: locale == null ? [] : [
|
|
2812
|
+
new _types__rspack_import_1/* .ValueExpression */.Ko({
|
|
2813
|
+
value: locale
|
|
2814
|
+
})
|
|
2815
|
+
]
|
|
2816
|
+
});
|
|
2817
|
+
};
|
|
2818
|
+
/** Binds every name a destructuring pattern introduces to the path it reads. */ const bindPattern = (stream, kind, path, scope)=>{
|
|
2819
|
+
if (!stream.matchPunctuation("{")) {
|
|
2820
|
+
const name = stream.next();
|
|
2821
|
+
if (name.kind !== "identifier") {
|
|
2822
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`parameter '${name.value}'`));
|
|
2823
|
+
}
|
|
2824
|
+
scope.set(name.value, {
|
|
2825
|
+
kind,
|
|
2826
|
+
path
|
|
2827
|
+
});
|
|
2828
|
+
return;
|
|
2829
|
+
}
|
|
2830
|
+
while(!stream.matchPunctuation("}")){
|
|
2831
|
+
const key = stream.next();
|
|
2832
|
+
if (key.kind !== "identifier") {
|
|
2833
|
+
throw new Error(ERROR_MESSAGES.UNSUPPORTED(`destructured key '${key.value}'`));
|
|
2834
|
+
}
|
|
2835
|
+
if (stream.matchPunctuation(":")) {
|
|
2836
|
+
bindPattern(stream, kind, [
|
|
2837
|
+
...path,
|
|
2838
|
+
key.value
|
|
2839
|
+
], scope);
|
|
2840
|
+
} else {
|
|
2841
|
+
scope.set(key.value, {
|
|
2842
|
+
kind,
|
|
2843
|
+
path: [
|
|
2844
|
+
...path,
|
|
2845
|
+
key.value
|
|
2846
|
+
]
|
|
2847
|
+
});
|
|
2848
|
+
}
|
|
2849
|
+
if (!stream.matchPunctuation(",")) {
|
|
2850
|
+
stream.expectPunctuation("}");
|
|
2851
|
+
return;
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
};
|
|
2855
|
+
/** Reads a filter's parameter list — the entity alone, or the `[entity, params]` pair — into a scope. */ const buildScope = (parameterNames, hasParams)=>{
|
|
2856
|
+
const stream = new TokenStream(tokenize(parameterNames));
|
|
2857
|
+
const scope = new Map();
|
|
2858
|
+
if (!stream.matchPunctuation("[")) {
|
|
2859
|
+
bindPattern(stream, "property", [], scope);
|
|
2860
|
+
return {
|
|
2861
|
+
scope,
|
|
2862
|
+
paramsName: null
|
|
2863
|
+
};
|
|
2864
|
+
}
|
|
2865
|
+
bindPattern(stream, "property", [], scope);
|
|
2866
|
+
if (hasParams && stream.matchPunctuation(",") && !stream.isPunctuation("]")) {
|
|
2867
|
+
bindPattern(stream, "param", [], scope);
|
|
2868
|
+
}
|
|
2869
|
+
return {
|
|
2870
|
+
scope,
|
|
2871
|
+
paramsName: wholeParamsName(scope)
|
|
2872
|
+
};
|
|
2873
|
+
};
|
|
2874
|
+
/** The name the whole params object was given, when it was not destructured. Error messages only. */ const wholeParamsName = (scope)=>{
|
|
2875
|
+
for (const [name, binding] of scope){
|
|
2876
|
+
if (binding.kind === "param" && binding.path.length === 0) {
|
|
2877
|
+
return name;
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
return null;
|
|
2881
|
+
};
|
|
1317
2882
|
/**
|
|
1318
2883
|
* Splits stringified filter source into parameter names and the expression
|
|
1319
2884
|
* body, unwrapping single-return block bodies.
|
|
@@ -1343,33 +2908,12 @@ const resolveParamPath = (paramsName, path, data)=>{
|
|
|
1343
2908
|
parameterNames = parameterNames.slice(1, -1).trim();
|
|
1344
2909
|
}
|
|
1345
2910
|
}
|
|
1346
|
-
|
|
1347
|
-
let paramsName = null;
|
|
1348
|
-
if (parameterNames.startsWith("[") && parameterNames.endsWith("]")) {
|
|
1349
|
-
const destructured = parameterNames.slice(1, -1).split(",").map((w)=>w.trim());
|
|
1350
|
-
entityName = destructured[0];
|
|
1351
|
-
if (hasParams) {
|
|
1352
|
-
paramsName = destructured[1] ?? null;
|
|
1353
|
-
}
|
|
1354
|
-
} else {
|
|
1355
|
-
entityName = parameterNames;
|
|
1356
|
-
}
|
|
1357
|
-
if (entityName == null || entityName.length === 0) {
|
|
2911
|
+
if (parameterNames.length === 0) {
|
|
1358
2912
|
throw new Error("Invalid Function");
|
|
1359
2913
|
}
|
|
1360
|
-
|
|
1361
|
-
if (body.startsWith("{")) {
|
|
1362
|
-
const inner = body.slice(1, body.lastIndexOf("}")).trim();
|
|
1363
|
-
if (!inner.startsWith("return")) {
|
|
1364
|
-
throw new Error(ERROR_MESSAGES.UNSUPPORTED("block body without a single return statement"));
|
|
1365
|
-
}
|
|
1366
|
-
body = inner.slice("return".length).trim();
|
|
1367
|
-
if (body.endsWith(";")) {
|
|
1368
|
-
body = body.slice(0, -1).trim();
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
2914
|
+
const { scope, paramsName } = buildScope(parameterNames, hasParams);
|
|
1371
2915
|
return {
|
|
1372
|
-
|
|
2916
|
+
scope,
|
|
1373
2917
|
paramsName,
|
|
1374
2918
|
body
|
|
1375
2919
|
};
|
|
@@ -1437,17 +2981,27 @@ const combineExpressions = (...expressions)=>{
|
|
|
1437
2981
|
*/ const parseFragment = (schema, body, rootName)=>{
|
|
1438
2982
|
try {
|
|
1439
2983
|
const stream = new TokenStream(tokenize(body));
|
|
1440
|
-
const
|
|
1441
|
-
|
|
2984
|
+
const scope = new Map([
|
|
2985
|
+
[
|
|
2986
|
+
rootName,
|
|
2987
|
+
{
|
|
2988
|
+
kind: "property",
|
|
2989
|
+
path: []
|
|
2990
|
+
}
|
|
2991
|
+
]
|
|
2992
|
+
]);
|
|
2993
|
+
const parser = new ExpressionParser(schema, stream, scope, null, undefined);
|
|
2994
|
+
return (0,_fold__rspack_import_4/* .foldConstantCalls */.F5)(parser.parse());
|
|
1442
2995
|
} catch {
|
|
1443
2996
|
// The failure is expected and informative — see above — so it is not logged. A caller that
|
|
1444
2997
|
// parses one conjunct against two schemas would otherwise warn on every successful split.
|
|
1445
2998
|
return _types__rspack_import_1/* .Expression.NOT_PARSABLE */.r4.NOT_PARSABLE;
|
|
1446
2999
|
}
|
|
1447
3000
|
};
|
|
3001
|
+
/** What the parser refused, from a throw that may not be an `Error`. */ const refusalOf = (error)=>error instanceof Error ? error.message : String(error);
|
|
1448
3002
|
const toExpression = (schema, fn, params)=>{
|
|
1449
3003
|
const stringifiedFunction = fn.toString();
|
|
1450
|
-
const warn = (error)=>
|
|
3004
|
+
const warn = (error)=>_utilities__rspack_import_5/* .logger.warn */.vF.warn("Error parsing expression", {
|
|
1451
3005
|
error,
|
|
1452
3006
|
collectionName: schema.collectionName,
|
|
1453
3007
|
params,
|
|
@@ -1455,16 +3009,17 @@ const toExpression = (schema, fn, params)=>{
|
|
|
1455
3009
|
});
|
|
1456
3010
|
const cached = getCachedTemplate(schema, stringifiedFunction);
|
|
1457
3011
|
if (cached != null) {
|
|
1458
|
-
// A cached failure — the warning was already logged when it was discovered
|
|
3012
|
+
// A cached failure — the warning was already logged when it was discovered. The template
|
|
3013
|
+
// carries what was refused, and `.explain()` is usually called once the cache is warm.
|
|
1459
3014
|
if (_types__rspack_import_1/* .Expression.isNotParsable */.r4.isNotParsable(cached.template)) {
|
|
1460
|
-
return
|
|
3015
|
+
return cached.template;
|
|
1461
3016
|
}
|
|
1462
3017
|
try {
|
|
1463
|
-
return bindExpression(cached.template, cached.paramsName, params);
|
|
3018
|
+
return (0,_fold__rspack_import_4/* .foldConstantCalls */.F5)(bindExpression(cached.template, cached.paramsName, params));
|
|
1464
3019
|
} catch (error) {
|
|
1465
3020
|
// Binding failures are param-dependent by nature — never cached
|
|
1466
3021
|
warn(error);
|
|
1467
|
-
return _types__rspack_import_1/* .Expression.
|
|
3022
|
+
return _types__rspack_import_1/* .Expression.notParsable */.r4.notParsable(refusalOf(error));
|
|
1468
3023
|
}
|
|
1469
3024
|
}
|
|
1470
3025
|
let paramsName = null;
|
|
@@ -1473,22 +3028,23 @@ const toExpression = (schema, fn, params)=>{
|
|
|
1473
3028
|
try {
|
|
1474
3029
|
const shape = resolveFunctionShape(stringifiedFunction, params != null);
|
|
1475
3030
|
const stream = new TokenStream(tokenize(shape.body));
|
|
1476
|
-
const parser = new ExpressionParser(schema, stream, shape.
|
|
3031
|
+
const parser = new ExpressionParser(schema, stream, shape.scope, shape.paramsName, params);
|
|
1477
3032
|
paramsName = shape.paramsName;
|
|
1478
|
-
template = parser.
|
|
3033
|
+
template = parser.parseBody();
|
|
1479
3034
|
structurallyDependsOnParams = parser.structurallyDependsOnParams;
|
|
1480
3035
|
} catch (error) {
|
|
1481
3036
|
// Cache the failure so a hot query on an unsupported filter doesn't
|
|
1482
3037
|
// re-parse and re-warn on every execution. Param-dependent failures are
|
|
1483
3038
|
// exempt: the same source can succeed with different params.
|
|
3039
|
+
const refused = _types__rspack_import_1/* .Expression.notParsable */.r4.notParsable(refusalOf(error));
|
|
1484
3040
|
if (!(error instanceof ParamDependentParseError)) {
|
|
1485
3041
|
setCachedTemplate(schema, stringifiedFunction, {
|
|
1486
|
-
template:
|
|
3042
|
+
template: refused,
|
|
1487
3043
|
paramsName: null
|
|
1488
3044
|
});
|
|
1489
3045
|
}
|
|
1490
3046
|
warn(error);
|
|
1491
|
-
return
|
|
3047
|
+
return refused;
|
|
1492
3048
|
}
|
|
1493
3049
|
// Templates whose structure was resolved from param values are only
|
|
1494
3050
|
// valid for this exact params object — parse those fresh every time
|
|
@@ -1499,17 +3055,116 @@ const toExpression = (schema, fn, params)=>{
|
|
|
1499
3055
|
});
|
|
1500
3056
|
}
|
|
1501
3057
|
try {
|
|
1502
|
-
return bindExpression(template, paramsName, params);
|
|
3058
|
+
return (0,_fold__rspack_import_4/* .foldConstantCalls */.F5)(bindExpression(template, paramsName, params));
|
|
1503
3059
|
} catch (error) {
|
|
1504
3060
|
warn(error);
|
|
1505
|
-
return _types__rspack_import_1/* .Expression.
|
|
3061
|
+
return _types__rspack_import_1/* .Expression.notParsable */.r4.notParsable(refusalOf(error));
|
|
3062
|
+
}
|
|
3063
|
+
};
|
|
3064
|
+
const collectReads = (operand, into)=>{
|
|
3065
|
+
switch(operand.kind){
|
|
3066
|
+
case "property":
|
|
3067
|
+
into.add(operand.property);
|
|
3068
|
+
return;
|
|
3069
|
+
case "method-call":
|
|
3070
|
+
collectReads(operand.target, into);
|
|
3071
|
+
collectReads(operand.argument, into);
|
|
3072
|
+
return;
|
|
3073
|
+
case "arithmetic":
|
|
3074
|
+
collectReads(operand.left, into);
|
|
3075
|
+
collectReads(operand.right, into);
|
|
3076
|
+
if (operand.extra != null) {
|
|
3077
|
+
collectReads(operand.extra, into);
|
|
3078
|
+
}
|
|
3079
|
+
return;
|
|
3080
|
+
case "conditional":
|
|
3081
|
+
for (const property of (0,_utils__rspack_import_6/* .getProperties */.oY)(operand.condition)){
|
|
3082
|
+
into.add(property);
|
|
3083
|
+
}
|
|
3084
|
+
collectReads(operand.whenTrue, into);
|
|
3085
|
+
collectReads(operand.whenFalse, into);
|
|
3086
|
+
return;
|
|
3087
|
+
case "opaque":
|
|
3088
|
+
for (const read of operand.reads){
|
|
3089
|
+
collectReads(read, into);
|
|
3090
|
+
}
|
|
3091
|
+
return;
|
|
1506
3092
|
}
|
|
1507
3093
|
};
|
|
3094
|
+
const selectedValue = (operand)=>{
|
|
3095
|
+
const found = new Set();
|
|
3096
|
+
collectReads(operand, found);
|
|
3097
|
+
const reads = [
|
|
3098
|
+
...found
|
|
3099
|
+
];
|
|
3100
|
+
return {
|
|
3101
|
+
property: reads.length === 1 ? reads[0] : null,
|
|
3102
|
+
reads,
|
|
3103
|
+
isDirectProperty: operand.kind === "property" && operand.transformer == null
|
|
3104
|
+
};
|
|
3105
|
+
};
|
|
3106
|
+
// Keyed like the template cache. A selector takes no params, so every result is cacheable
|
|
3107
|
+
const selectorCache = new WeakMap();
|
|
3108
|
+
/**
|
|
3109
|
+
* Reads a sort, map, group or `nearest` selector with the grammar filters use, for what its value is
|
|
3110
|
+
* read from.
|
|
3111
|
+
*
|
|
3112
|
+
* Not for evaluating it: the caller's function stays the value, and nothing here renders one. A plugin
|
|
3113
|
+
* decides from the result whether it can run the option. One that orders or projects by column cannot
|
|
3114
|
+
* run a value that is not the property itself, and one that runs the function over stored rows cannot
|
|
3115
|
+
* run it over a renamed property.
|
|
3116
|
+
*
|
|
3117
|
+
* So the grammar is wider here than for a filter. A call it has no node for, such as `getFullYear()`,
|
|
3118
|
+
* is kept for the operands it reads rather than refused, since the function it came from still runs.
|
|
3119
|
+
*
|
|
3120
|
+
* `not-parsable` is not logged. The option runs as it did before the selector was parsed.
|
|
3121
|
+
*
|
|
3122
|
+
* Cached by function source per schema, like `toExpression`. The result is shared, so it is read-only.
|
|
3123
|
+
*/ const parseSelector = (schema, selector)=>{
|
|
3124
|
+
const source = selector.toString();
|
|
3125
|
+
let bySource = selectorCache.get(schema);
|
|
3126
|
+
const cached = bySource?.get(source);
|
|
3127
|
+
if (cached != null) {
|
|
3128
|
+
return cached;
|
|
3129
|
+
}
|
|
3130
|
+
let parsed;
|
|
3131
|
+
try {
|
|
3132
|
+
const shape = resolveFunctionShape(source, false);
|
|
3133
|
+
const parser = new ExpressionParser(schema, new TokenStream(tokenize(shape.body)), shape.scope, null, undefined, true);
|
|
3134
|
+
const selected = parser.parseSelector();
|
|
3135
|
+
parsed = Array.isArray(selected) ? {
|
|
3136
|
+
kind: "object",
|
|
3137
|
+
fields: selected.map((field)=>({
|
|
3138
|
+
name: field.name,
|
|
3139
|
+
...selectedValue(field.operand)
|
|
3140
|
+
}))
|
|
3141
|
+
} : {
|
|
3142
|
+
kind: "value",
|
|
3143
|
+
value: selectedValue(selected)
|
|
3144
|
+
};
|
|
3145
|
+
} catch (error) {
|
|
3146
|
+
parsed = {
|
|
3147
|
+
kind: "not-parsable",
|
|
3148
|
+
reason: refusalOf(error)
|
|
3149
|
+
};
|
|
3150
|
+
}
|
|
3151
|
+
if (bySource == null) {
|
|
3152
|
+
bySource = new Map();
|
|
3153
|
+
selectorCache.set(schema, bySource);
|
|
3154
|
+
}
|
|
3155
|
+
// Stryker disable next-line all: the same resource bound as the template cache's
|
|
3156
|
+
if (bySource.size >= MAX_CACHED_TEMPLATES_PER_SCHEMA) {
|
|
3157
|
+
bySource.clear();
|
|
3158
|
+
}
|
|
3159
|
+
bySource.set(source, parsed);
|
|
3160
|
+
return parsed;
|
|
3161
|
+
}; // #endregion
|
|
1508
3162
|
|
|
1509
3163
|
|
|
1510
3164
|
},
|
|
1511
3165
|
27(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
1512
3166
|
__webpack_require__.d(__webpack_exports__, {
|
|
3167
|
+
DG: () => (CallExpression),
|
|
1513
3168
|
Ko: () => (ValueExpression),
|
|
1514
3169
|
SC: () => (NotParsableExpression),
|
|
1515
3170
|
Sm: () => (EmptyExpression),
|
|
@@ -1521,59 +3176,69 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
1521
3176
|
const valueToJson = (value)=>{
|
|
1522
3177
|
if (value === undefined) {
|
|
1523
3178
|
return {
|
|
1524
|
-
|
|
3179
|
+
undefined: true
|
|
1525
3180
|
};
|
|
1526
3181
|
}
|
|
1527
3182
|
if (value === null) {
|
|
1528
|
-
return
|
|
1529
|
-
k: "raw",
|
|
1530
|
-
v: null
|
|
1531
|
-
};
|
|
3183
|
+
return null;
|
|
1532
3184
|
}
|
|
1533
3185
|
if (value instanceof Date) {
|
|
1534
3186
|
// ISO rather than epoch millis: it survives a human reading the payload, and an invalid
|
|
1535
3187
|
// Date has no ISO form — so it is caught here rather than becoming a silent `null`.
|
|
1536
3188
|
return {
|
|
1537
|
-
|
|
1538
|
-
v: value.toISOString()
|
|
3189
|
+
date: value.toISOString()
|
|
1539
3190
|
};
|
|
1540
3191
|
}
|
|
1541
3192
|
if (Array.isArray(value)) {
|
|
1542
|
-
return
|
|
1543
|
-
k: "array",
|
|
1544
|
-
v: value.map(valueToJson)
|
|
1545
|
-
};
|
|
3193
|
+
return value.map(valueToJson);
|
|
1546
3194
|
}
|
|
1547
3195
|
if (typeof value === "number" && Number.isFinite(value) === false) {
|
|
1548
3196
|
// `JSON.stringify` turns all three of these into `null`, which would compare as a different
|
|
1549
3197
|
// value entirely rather than failing.
|
|
1550
3198
|
return {
|
|
1551
|
-
|
|
1552
|
-
v: Number.isNaN(value) ? "NaN" : value > 0 ? "Infinity" : "-Infinity"
|
|
3199
|
+
number: Number.isNaN(value) ? "NaN" : value > 0 ? "Infinity" : "-Infinity"
|
|
1553
3200
|
};
|
|
1554
3201
|
}
|
|
1555
3202
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
3203
|
+
return value;
|
|
3204
|
+
}
|
|
3205
|
+
if (value instanceof RegExp) {
|
|
3206
|
+
return {
|
|
3207
|
+
regex: {
|
|
3208
|
+
source: value.source,
|
|
3209
|
+
flags: value.flags
|
|
3210
|
+
}
|
|
3211
|
+
};
|
|
3212
|
+
}
|
|
3213
|
+
// `JSON.stringify` throws outright on a bigint rather than losing it quietly, so this is the one
|
|
3214
|
+
// tag that turns a crash into a value
|
|
3215
|
+
if (typeof value === "bigint") {
|
|
1556
3216
|
return {
|
|
1557
|
-
|
|
1558
|
-
v: value
|
|
3217
|
+
bigint: value.toString()
|
|
1559
3218
|
};
|
|
1560
3219
|
}
|
|
1561
3220
|
throw new Error(`Cannot serialize this filter value: only strings, numbers, booleans, null, undefined, Dates and arrays of those can cross a wire. ` + `Received: ${Object.prototype.toString.call(value)}`);
|
|
1562
3221
|
};
|
|
1563
3222
|
const valueFromJson = (value)=>{
|
|
1564
|
-
if (value
|
|
1565
|
-
return
|
|
3223
|
+
if (value === null || typeof value !== "object") {
|
|
3224
|
+
return value;
|
|
3225
|
+
}
|
|
3226
|
+
if (Array.isArray(value)) {
|
|
3227
|
+
return value.map(valueFromJson);
|
|
3228
|
+
}
|
|
3229
|
+
if ("date" in value) {
|
|
3230
|
+
return new Date(value.date);
|
|
1566
3231
|
}
|
|
1567
|
-
if (
|
|
1568
|
-
return
|
|
3232
|
+
if ("undefined" in value) {
|
|
3233
|
+
return undefined;
|
|
1569
3234
|
}
|
|
1570
|
-
if (
|
|
1571
|
-
return value.
|
|
3235
|
+
if ("regex" in value) {
|
|
3236
|
+
return new RegExp(value.regex.source, value.regex.flags);
|
|
1572
3237
|
}
|
|
1573
|
-
if (
|
|
1574
|
-
return value.
|
|
3238
|
+
if ("bigint" in value) {
|
|
3239
|
+
return BigInt(value.bigint);
|
|
1575
3240
|
}
|
|
1576
|
-
return value.
|
|
3241
|
+
return value.number === "NaN" ? Number.NaN : value.number === "Infinity" ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;
|
|
1577
3242
|
};
|
|
1578
3243
|
/**
|
|
1579
3244
|
* The base class for all expression types.
|
|
@@ -1590,6 +3255,9 @@ const valueFromJson = (value)=>{
|
|
|
1590
3255
|
static get NOT_PARSABLE() {
|
|
1591
3256
|
return new NotParsableExpression();
|
|
1592
3257
|
}
|
|
3258
|
+
/** `NOT_PARSABLE`, carrying what the parser refused. */ static notParsable(reason) {
|
|
3259
|
+
return new NotParsableExpression(reason);
|
|
3260
|
+
}
|
|
1593
3261
|
static isEmpty(expression) {
|
|
1594
3262
|
return expression.type === "empty" || expression instanceof EmptyExpression;
|
|
1595
3263
|
}
|
|
@@ -1606,7 +3274,7 @@ const valueFromJson = (value)=>{
|
|
|
1606
3274
|
*
|
|
1607
3275
|
* ## Why it is this small
|
|
1608
3276
|
*
|
|
1609
|
-
* Of the
|
|
3277
|
+
* Of the seven node types a bound tree can contain, exactly one holds anything JSON cannot carry:
|
|
1610
3278
|
* `PropertyExpression`, whose live `PropertyInfo` has functions, a parent chain and caches. It
|
|
1611
3279
|
* reduces to a property PATH — `PropertyInfo.id` IS the dotted path, and `getProperty` is keyed by
|
|
1612
3280
|
* exactly that — so rebinding is one lookup.
|
|
@@ -1621,7 +3289,7 @@ const valueFromJson = (value)=>{
|
|
|
1621
3289
|
if (expression.type === "operator") {
|
|
1622
3290
|
const operator = expression;
|
|
1623
3291
|
return {
|
|
1624
|
-
|
|
3292
|
+
type: "operator",
|
|
1625
3293
|
operator: operator.operator,
|
|
1626
3294
|
...operator.left != null && {
|
|
1627
3295
|
left: Expression.toJson(operator.left)
|
|
@@ -1634,7 +3302,7 @@ const valueFromJson = (value)=>{
|
|
|
1634
3302
|
if (expression.type === "comparator") {
|
|
1635
3303
|
const comparator = expression;
|
|
1636
3304
|
return {
|
|
1637
|
-
|
|
3305
|
+
type: "comparator",
|
|
1638
3306
|
comparator: comparator.comparator,
|
|
1639
3307
|
negated: comparator.negated,
|
|
1640
3308
|
strict: comparator.strict,
|
|
@@ -1646,29 +3314,41 @@ const valueFromJson = (value)=>{
|
|
|
1646
3314
|
}
|
|
1647
3315
|
};
|
|
1648
3316
|
}
|
|
3317
|
+
if (expression.type === "call") {
|
|
3318
|
+
const call = expression;
|
|
3319
|
+
return {
|
|
3320
|
+
type: "call",
|
|
3321
|
+
call: call.call,
|
|
3322
|
+
expression: Expression.toJson(call.expression),
|
|
3323
|
+
arguments: call.arguments.map(Expression.toJson)
|
|
3324
|
+
};
|
|
3325
|
+
}
|
|
1649
3326
|
if (expression.type === "property") {
|
|
1650
3327
|
const property = expression;
|
|
1651
3328
|
return {
|
|
1652
|
-
|
|
3329
|
+
type: "property",
|
|
1653
3330
|
// The dotted path, which is exactly the key `getProperty` is looking up
|
|
1654
|
-
path: property.property.id
|
|
1655
|
-
transformer: property.transformer,
|
|
1656
|
-
locale: property.locale
|
|
3331
|
+
path: property.property.id
|
|
1657
3332
|
};
|
|
1658
3333
|
}
|
|
1659
3334
|
if (expression.type === "value") {
|
|
1660
3335
|
const value = expression;
|
|
1661
3336
|
return {
|
|
1662
|
-
|
|
1663
|
-
value: valueToJson(value.value)
|
|
1664
|
-
|
|
1665
|
-
|
|
3337
|
+
type: "value",
|
|
3338
|
+
value: valueToJson(value.value)
|
|
3339
|
+
};
|
|
3340
|
+
}
|
|
3341
|
+
if (expression.type === "empty") {
|
|
3342
|
+
return {
|
|
3343
|
+
type: "empty"
|
|
1666
3344
|
};
|
|
1667
3345
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
3346
|
+
const reason = expression.reason;
|
|
3347
|
+
return reason == null ? {
|
|
3348
|
+
type: "not-parsable"
|
|
1670
3349
|
} : {
|
|
1671
|
-
|
|
3350
|
+
type: "not-parsable",
|
|
3351
|
+
reason
|
|
1672
3352
|
};
|
|
1673
3353
|
}
|
|
1674
3354
|
/**
|
|
@@ -1684,14 +3364,14 @@ const valueFromJson = (value)=>{
|
|
|
1684
3364
|
* failure here worse than an error.
|
|
1685
3365
|
*/ static fromJson(json, schema) {
|
|
1686
3366
|
const child = (node)=>node == null ? undefined : Expression.fromJson(node, schema);
|
|
1687
|
-
if (json.
|
|
3367
|
+
if (json.type === "operator") {
|
|
1688
3368
|
return new OperatorExpression({
|
|
1689
3369
|
operator: json.operator,
|
|
1690
3370
|
left: child(json.left),
|
|
1691
3371
|
right: child(json.right)
|
|
1692
3372
|
});
|
|
1693
3373
|
}
|
|
1694
|
-
if (json.
|
|
3374
|
+
if (json.type === "comparator") {
|
|
1695
3375
|
return new ComparatorExpression({
|
|
1696
3376
|
comparator: json.comparator,
|
|
1697
3377
|
negated: json.negated,
|
|
@@ -1700,27 +3380,34 @@ const valueFromJson = (value)=>{
|
|
|
1700
3380
|
right: child(json.right)
|
|
1701
3381
|
});
|
|
1702
3382
|
}
|
|
1703
|
-
if (json.
|
|
3383
|
+
if (json.type === "call") {
|
|
3384
|
+
if (json.expression == null) {
|
|
3385
|
+
throw new Error(`Cannot deserialize a filter: a '${json.call}' call carries no operand. ` + `Collection: ${schema.collectionName}.`);
|
|
3386
|
+
}
|
|
3387
|
+
return new CallExpression({
|
|
3388
|
+
call: json.call,
|
|
3389
|
+
expression: Expression.fromJson(json.expression, schema),
|
|
3390
|
+
arguments: (json.arguments ?? []).map((argument)=>Expression.fromJson(argument, schema))
|
|
3391
|
+
});
|
|
3392
|
+
}
|
|
3393
|
+
if (json.type === "property") {
|
|
1704
3394
|
const property = schema.getProperty(json.path);
|
|
1705
3395
|
if (property == null) {
|
|
1706
3396
|
throw new Error(`Cannot deserialize a filter: this schema does not declare the property it names. ` + `Property: ${json.path}, Collection: ${schema.collectionName}. ` + `The two sides disagree about the shape of the data, so the filter cannot be applied.`);
|
|
1707
3397
|
}
|
|
1708
|
-
|
|
3398
|
+
return new PropertyExpression({
|
|
1709
3399
|
property
|
|
1710
3400
|
});
|
|
1711
|
-
rebuilt.transformer = json.transformer;
|
|
1712
|
-
rebuilt.locale = json.locale;
|
|
1713
|
-
return rebuilt;
|
|
1714
3401
|
}
|
|
1715
|
-
if (json.
|
|
1716
|
-
|
|
3402
|
+
if (json.type === "value") {
|
|
3403
|
+
return new ValueExpression({
|
|
1717
3404
|
value: valueFromJson(json.value)
|
|
1718
3405
|
});
|
|
1719
|
-
rebuilt.transformer = json.transformer;
|
|
1720
|
-
rebuilt.locale = json.locale;
|
|
1721
|
-
return rebuilt;
|
|
1722
3406
|
}
|
|
1723
|
-
|
|
3407
|
+
if (json.type === "empty") {
|
|
3408
|
+
return Expression.EMPTY;
|
|
3409
|
+
}
|
|
3410
|
+
return json.reason == null ? Expression.NOT_PARSABLE : Expression.notParsable(json.reason);
|
|
1724
3411
|
}
|
|
1725
3412
|
}
|
|
1726
3413
|
class EmptyExpression extends Expression {
|
|
@@ -1728,6 +3415,11 @@ class EmptyExpression extends Expression {
|
|
|
1728
3415
|
}
|
|
1729
3416
|
class NotParsableExpression extends Expression {
|
|
1730
3417
|
type = "not-parsable";
|
|
3418
|
+
/** What the parser refused, when it knows. `.explain()` prints it beside the source. */ reason;
|
|
3419
|
+
constructor(reason){
|
|
3420
|
+
super();
|
|
3421
|
+
this.reason = reason;
|
|
3422
|
+
}
|
|
1731
3423
|
}
|
|
1732
3424
|
/**
|
|
1733
3425
|
* A class representing a comparison operation (e.g., equals, greater-than).
|
|
@@ -1758,20 +3450,28 @@ class NotParsableExpression extends Expression {
|
|
|
1758
3450
|
*/ class PropertyExpression extends Expression {
|
|
1759
3451
|
/** The type of the expression (always 'property'). */ type = "property";
|
|
1760
3452
|
/** The property info for the path. */ property;
|
|
1761
|
-
transformer = null;
|
|
1762
|
-
locale = null;
|
|
1763
3453
|
constructor(options){
|
|
1764
3454
|
super();
|
|
1765
3455
|
this.property = options.property;
|
|
1766
3456
|
}
|
|
1767
3457
|
}
|
|
3458
|
+
class CallExpression extends Expression {
|
|
3459
|
+
type = "call";
|
|
3460
|
+
call;
|
|
3461
|
+
expression;
|
|
3462
|
+
/** Empty for a unary call. */ arguments;
|
|
3463
|
+
constructor(options){
|
|
3464
|
+
super();
|
|
3465
|
+
this.call = options.call;
|
|
3466
|
+
this.expression = options.expression;
|
|
3467
|
+
this.arguments = options.arguments ?? [];
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
1768
3470
|
/**
|
|
1769
3471
|
* A class representing a literal value.
|
|
1770
3472
|
*/ class ValueExpression extends Expression {
|
|
1771
3473
|
/** The type of the expression (always 'value'). */ type = "value";
|
|
1772
3474
|
/** The literal value. */ value;
|
|
1773
|
-
transformer = null;
|
|
1774
|
-
locale = null;
|
|
1775
3475
|
constructor(options){
|
|
1776
3476
|
super();
|
|
1777
3477
|
this.value = options.value;
|
|
@@ -1782,9 +3482,45 @@ class NotParsableExpression extends Expression {
|
|
|
1782
3482
|
},
|
|
1783
3483
|
63(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
1784
3484
|
__webpack_require__.d(__webpack_exports__, {
|
|
1785
|
-
|
|
1786
|
-
|
|
3485
|
+
CC: () => (peelCalls),
|
|
3486
|
+
LU: () => (childrenOf),
|
|
3487
|
+
jJ: () => (forEach),
|
|
3488
|
+
oY: () => (getProperties)
|
|
1787
3489
|
});
|
|
3490
|
+
/**
|
|
3491
|
+
* Separates an operand from the calls applied to it.
|
|
3492
|
+
*
|
|
3493
|
+
* `null` when there is no operand beneath the calls. Every consumer needs this to decide whether a
|
|
3494
|
+
* comparator side is a property or a value, so it lives here rather than in each translator.
|
|
3495
|
+
*/ function peelCalls(expression) {
|
|
3496
|
+
const calls = [];
|
|
3497
|
+
let current = expression;
|
|
3498
|
+
while(current != null && current.type === "call"){
|
|
3499
|
+
calls.unshift(current);
|
|
3500
|
+
current = current.expression;
|
|
3501
|
+
}
|
|
3502
|
+
return current == null ? null : {
|
|
3503
|
+
operand: current,
|
|
3504
|
+
calls
|
|
3505
|
+
};
|
|
3506
|
+
}
|
|
3507
|
+
function childrenOf(expression) {
|
|
3508
|
+
if (expression.type === "call") {
|
|
3509
|
+
const call = expression;
|
|
3510
|
+
return [
|
|
3511
|
+
call.expression,
|
|
3512
|
+
...call.arguments ?? []
|
|
3513
|
+
].filter((child)=>child != null);
|
|
3514
|
+
}
|
|
3515
|
+
const children = [];
|
|
3516
|
+
if (expression.left != null) {
|
|
3517
|
+
children.push(expression.left);
|
|
3518
|
+
}
|
|
3519
|
+
if (expression.right != null) {
|
|
3520
|
+
children.push(expression.right);
|
|
3521
|
+
}
|
|
3522
|
+
return children;
|
|
3523
|
+
}
|
|
1788
3524
|
/**
|
|
1789
3525
|
* Extracts all properties referenced in an expression
|
|
1790
3526
|
* @param expression The expression to analyze
|
|
@@ -1796,12 +3532,8 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
1796
3532
|
if (expr.type === "property") {
|
|
1797
3533
|
properties.push(expr.property);
|
|
1798
3534
|
}
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
traverse(expr.left);
|
|
1802
|
-
}
|
|
1803
|
-
if (expr.right) {
|
|
1804
|
-
traverse(expr.right);
|
|
3535
|
+
for (const child of childrenOf(expr)){
|
|
3536
|
+
traverse(child);
|
|
1805
3537
|
}
|
|
1806
3538
|
}
|
|
1807
3539
|
traverse(expression);
|
|
@@ -1814,14 +3546,8 @@ function forEach(expression, callback) {
|
|
|
1814
3546
|
if (!callback(expr)) {
|
|
1815
3547
|
return false;
|
|
1816
3548
|
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
if (!traverse(expr.left)) {
|
|
1820
|
-
return false;
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
if (expr.right) {
|
|
1824
|
-
if (!traverse(expr.right)) {
|
|
3549
|
+
for (const child of childrenOf(expr)){
|
|
3550
|
+
if (!traverse(child)) {
|
|
1825
3551
|
return false;
|
|
1826
3552
|
}
|
|
1827
3553
|
}
|
|
@@ -1941,12 +3667,14 @@ const isLogLevel = (value)=>typeof value === 'string' && LOG_LEVELS.includes(val
|
|
|
1941
3667
|
const debug = process.env.DEBUG;
|
|
1942
3668
|
if (debug === 'routier' || debug === '*') return 'debug';
|
|
1943
3669
|
const env = "production"?.toLowerCase();
|
|
1944
|
-
// `test` is deliberately absent. It used to be here, which meant no test suite anywhere
|
|
1945
|
-
// could run Routier quietly. Opt in with DEBUG=routier or ROUTIER_LOG_LEVEL when a test
|
|
1946
|
-
// needs the output.
|
|
1947
3670
|
if (env === 'dev' || env === 'development') return 'debug';
|
|
1948
3671
|
}
|
|
1949
|
-
|
|
3672
|
+
// Warnings are on unless something turns them off.
|
|
3673
|
+
//
|
|
3674
|
+
// Routier warns when a query returns correct rows a slower way than it could, or when a filter
|
|
3675
|
+
// compares types that can never match. Both are the caller's to act on, and a default of
|
|
3676
|
+
// `silent` meant the only people who ever saw them were the ones who already knew to look.
|
|
3677
|
+
return 'warn';
|
|
1950
3678
|
};
|
|
1951
3679
|
let level = resolveLevel();
|
|
1952
3680
|
let rank = RANK[level];
|
|
@@ -2038,28 +3766,43 @@ var __webpack_exports__ = {};
|
|
|
2038
3766
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.
|
|
2039
3767
|
(() => {
|
|
2040
3768
|
__webpack_require__.d(__webpack_exports__, {
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
3769
|
+
CC: () => (/* reexport safe */ _utils__rspack_import_5.CC),
|
|
3770
|
+
DG: () => (/* reexport safe */ _types__rspack_import_4.DG),
|
|
3771
|
+
F5: () => (/* reexport safe */ _fold__rspack_import_2.F5),
|
|
3772
|
+
Ko: () => (/* reexport safe */ _types__rspack_import_4.Ko),
|
|
3773
|
+
LU: () => (/* reexport safe */ _utils__rspack_import_5.LU),
|
|
3774
|
+
MY: () => (/* reexport safe */ _parser__rspack_import_3.MY),
|
|
3775
|
+
Nb: () => (/* reexport safe */ _callSource__rspack_import_0.N),
|
|
3776
|
+
Pl: () => (/* reexport safe */ _parser__rspack_import_3.Pl),
|
|
3777
|
+
SC: () => (/* reexport safe */ _types__rspack_import_4.SC),
|
|
3778
|
+
Sm: () => (/* reexport safe */ _types__rspack_import_4.Sm),
|
|
3779
|
+
Sv: () => (/* reexport safe */ _fold__rspack_import_2.Sv),
|
|
3780
|
+
Vu: () => (/* reexport safe */ _evaluate__rspack_import_1.Vu),
|
|
3781
|
+
Vv: () => (/* reexport safe */ _evaluate__rspack_import_1.Vv),
|
|
3782
|
+
_3: () => (/* reexport safe */ _evaluate__rspack_import_1._3),
|
|
3783
|
+
ax: () => (/* reexport safe */ _callSource__rspack_import_0.a),
|
|
3784
|
+
bQ: () => (/* reexport safe */ _types__rspack_import_4.bQ),
|
|
3785
|
+
br: () => (/* reexport safe */ _fold__rspack_import_2.br),
|
|
3786
|
+
ep: () => (/* reexport safe */ _types__rspack_import_4.ep),
|
|
3787
|
+
fw: () => (/* reexport safe */ _types__rspack_import_4.fw),
|
|
3788
|
+
gm: () => (/* reexport safe */ _evaluate__rspack_import_1.gm),
|
|
3789
|
+
jJ: () => (/* reexport safe */ _utils__rspack_import_5.jJ),
|
|
3790
|
+
oH: () => (/* reexport safe */ _parser__rspack_import_3.oH),
|
|
3791
|
+
oY: () => (/* reexport safe */ _utils__rspack_import_5.oY),
|
|
3792
|
+
pg: () => (/* reexport safe */ _parser__rspack_import_3.pg),
|
|
3793
|
+
r4: () => (/* reexport safe */ _types__rspack_import_4.r4),
|
|
3794
|
+
tA: () => (/* reexport safe */ _constants__rspack_import_6.t),
|
|
3795
|
+
wS: () => (/* reexport safe */ _evaluate__rspack_import_1.wS)
|
|
2057
3796
|
});
|
|
2058
|
-
/* import */ var
|
|
2059
|
-
/* import */ var
|
|
2060
|
-
/* import */ var
|
|
2061
|
-
/* import */ var
|
|
2062
|
-
/* import */ var
|
|
3797
|
+
/* import */ var _callSource__rspack_import_0 = __webpack_require__(429);
|
|
3798
|
+
/* import */ var _evaluate__rspack_import_1 = __webpack_require__(379);
|
|
3799
|
+
/* import */ var _fold__rspack_import_2 = __webpack_require__(43);
|
|
3800
|
+
/* import */ var _parser__rspack_import_3 = __webpack_require__(91);
|
|
3801
|
+
/* import */ var _types__rspack_import_4 = __webpack_require__(27);
|
|
3802
|
+
/* import */ var _utils__rspack_import_5 = __webpack_require__(63);
|
|
3803
|
+
/* import */ var _constants__rspack_import_6 = __webpack_require__(835);
|
|
3804
|
+
|
|
3805
|
+
|
|
2063
3806
|
|
|
2064
3807
|
|
|
2065
3808
|
|
|
@@ -2068,22 +3811,33 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
2068
3811
|
|
|
2069
3812
|
})();
|
|
2070
3813
|
|
|
3814
|
+
var __webpack_exports__CALL_SOURCE = __webpack_exports__.Nb;
|
|
3815
|
+
var __webpack_exports__CallExpression = __webpack_exports__.DG;
|
|
2071
3816
|
var __webpack_exports__ComparatorExpression = __webpack_exports__.bQ;
|
|
2072
3817
|
var __webpack_exports__EXPRESSION_TYPES = __webpack_exports__.tA;
|
|
2073
3818
|
var __webpack_exports__EmptyExpression = __webpack_exports__.Sm;
|
|
2074
3819
|
var __webpack_exports__Expression = __webpack_exports__.r4;
|
|
3820
|
+
var __webpack_exports__FOLDABLE = __webpack_exports__.Sv;
|
|
2075
3821
|
var __webpack_exports__NotParsableExpression = __webpack_exports__.SC;
|
|
2076
3822
|
var __webpack_exports__OperatorExpression = __webpack_exports__.fw;
|
|
2077
3823
|
var __webpack_exports__PropertyExpression = __webpack_exports__.ep;
|
|
3824
|
+
var __webpack_exports__UNRESOLVED = __webpack_exports__.gm;
|
|
2078
3825
|
var __webpack_exports__ValueExpression = __webpack_exports__.Ko;
|
|
3826
|
+
var __webpack_exports__childrenOf = __webpack_exports__.LU;
|
|
2079
3827
|
var __webpack_exports__combineExpressions = __webpack_exports__.pg;
|
|
2080
3828
|
var __webpack_exports__evaluate = __webpack_exports__._3;
|
|
3829
|
+
var __webpack_exports__foldConstantCalls = __webpack_exports__.F5;
|
|
3830
|
+
var __webpack_exports__foldedOperandValue = __webpack_exports__.br;
|
|
2081
3831
|
var __webpack_exports__forEach = __webpack_exports__.jJ;
|
|
2082
3832
|
var __webpack_exports__getProperties = __webpack_exports__.oY;
|
|
3833
|
+
var __webpack_exports__operandValue = __webpack_exports__.Vv;
|
|
2083
3834
|
var __webpack_exports__parseFragment = __webpack_exports__.oH;
|
|
3835
|
+
var __webpack_exports__parseSelector = __webpack_exports__.Pl;
|
|
3836
|
+
var __webpack_exports__peelCalls = __webpack_exports__.CC;
|
|
3837
|
+
var __webpack_exports__renderCallAsJs = __webpack_exports__.ax;
|
|
2084
3838
|
var __webpack_exports__toExpression = __webpack_exports__.MY;
|
|
2085
3839
|
var __webpack_exports__toPredicate = __webpack_exports__.Vu;
|
|
2086
3840
|
var __webpack_exports__toStrictPredicate = __webpack_exports__.wS;
|
|
2087
|
-
export { __webpack_exports__ComparatorExpression as ComparatorExpression, __webpack_exports__EXPRESSION_TYPES as EXPRESSION_TYPES, __webpack_exports__EmptyExpression as EmptyExpression, __webpack_exports__Expression as Expression, __webpack_exports__NotParsableExpression as NotParsableExpression, __webpack_exports__OperatorExpression as OperatorExpression, __webpack_exports__PropertyExpression as PropertyExpression, __webpack_exports__ValueExpression as ValueExpression, __webpack_exports__combineExpressions as combineExpressions, __webpack_exports__evaluate as evaluate, __webpack_exports__forEach as forEach, __webpack_exports__getProperties as getProperties, __webpack_exports__parseFragment as parseFragment, __webpack_exports__toExpression as toExpression, __webpack_exports__toPredicate as toPredicate, __webpack_exports__toStrictPredicate as toStrictPredicate };
|
|
3841
|
+
export { __webpack_exports__CALL_SOURCE as CALL_SOURCE, __webpack_exports__CallExpression as CallExpression, __webpack_exports__ComparatorExpression as ComparatorExpression, __webpack_exports__EXPRESSION_TYPES as EXPRESSION_TYPES, __webpack_exports__EmptyExpression as EmptyExpression, __webpack_exports__Expression as Expression, __webpack_exports__FOLDABLE as FOLDABLE, __webpack_exports__NotParsableExpression as NotParsableExpression, __webpack_exports__OperatorExpression as OperatorExpression, __webpack_exports__PropertyExpression as PropertyExpression, __webpack_exports__UNRESOLVED as UNRESOLVED, __webpack_exports__ValueExpression as ValueExpression, __webpack_exports__childrenOf as childrenOf, __webpack_exports__combineExpressions as combineExpressions, __webpack_exports__evaluate as evaluate, __webpack_exports__foldConstantCalls as foldConstantCalls, __webpack_exports__foldedOperandValue as foldedOperandValue, __webpack_exports__forEach as forEach, __webpack_exports__getProperties as getProperties, __webpack_exports__operandValue as operandValue, __webpack_exports__parseFragment as parseFragment, __webpack_exports__parseSelector as parseSelector, __webpack_exports__peelCalls as peelCalls, __webpack_exports__renderCallAsJs as renderCallAsJs, __webpack_exports__toExpression as toExpression, __webpack_exports__toPredicate as toPredicate, __webpack_exports__toStrictPredicate as toStrictPredicate };
|
|
2088
3842
|
|
|
2089
3843
|
//# sourceMappingURL=index.js.map
|