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