litestar-vite-plugin 0.28.0 → 0.29.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.
@@ -0,0 +1,524 @@
1
+ const ALLOWED_GLOBALS = Object.freeze({ JSON, Math });
2
+ const STOP_PROTOS = new Set([Object.prototype, Array.prototype, Function.prototype, String.prototype, Number.prototype, Boolean.prototype]);
3
+ const BANNED_PROPS = new Set([
4
+ "constructor",
5
+ "__proto__",
6
+ "prototype",
7
+ "__defineGetter__",
8
+ "__defineSetter__",
9
+ "__lookupGetter__",
10
+ "__lookupSetter__",
11
+ "caller",
12
+ "callee",
13
+ "arguments",
14
+ "apply",
15
+ "call",
16
+ "bind",
17
+ ]);
18
+ const RESERVED = new Set(["function", "this", "new", "import", "delete", "void", "typeof", "instanceof", "in", "class", "return", "var", "let", "const", "async", "await", "yield"]);
19
+ const BINARY_PRECEDENCE = Object.freeze({
20
+ "??": 1,
21
+ "||": 2,
22
+ "&&": 3,
23
+ "==": 4,
24
+ "!=": 4,
25
+ "===": 4,
26
+ "!==": 4,
27
+ "<": 5,
28
+ "<=": 5,
29
+ ">": 5,
30
+ ">=": 5,
31
+ "+": 6,
32
+ "-": 6,
33
+ "*": 7,
34
+ "/": 7,
35
+ "%": 7,
36
+ });
37
+ const PUNCTUATION = ["===", "!==", "&&", "||", "??", "<=", ">=", "==", "!=", ".", "(", ")", "{", "}", ",", "?", ":", "!", "+", "-", "*", "/", "%", "<", ">"];
38
+ function readEscape(source, start) {
39
+ const character = source[start];
40
+ if (character === undefined)
41
+ return null;
42
+ if (character === "u") {
43
+ if (source[start + 1] === "{") {
44
+ const close = source.indexOf("}", start + 2);
45
+ if (close === -1)
46
+ return null;
47
+ const digits = source.slice(start + 2, close);
48
+ if (!/^[0-9a-f]+$/i.test(digits))
49
+ return null;
50
+ const codePoint = Number.parseInt(digits, 16);
51
+ if (!Number.isFinite(codePoint) || codePoint > 0x10ffff)
52
+ return null;
53
+ return { end: close + 1, value: String.fromCodePoint(codePoint) };
54
+ }
55
+ const digits = source.slice(start + 1, start + 5);
56
+ if (!/^[0-9a-f]{4}$/i.test(digits))
57
+ return null;
58
+ return { end: start + 5, value: String.fromCharCode(Number.parseInt(digits, 16)) };
59
+ }
60
+ if (character === "x") {
61
+ const digits = source.slice(start + 1, start + 3);
62
+ if (!/^[0-9a-f]{2}$/i.test(digits))
63
+ return null;
64
+ return { end: start + 3, value: String.fromCharCode(Number.parseInt(digits, 16)) };
65
+ }
66
+ const escapes = {
67
+ b: "\b",
68
+ f: "\f",
69
+ n: "\n",
70
+ r: "\r",
71
+ t: "\t",
72
+ v: "\v",
73
+ };
74
+ return { end: start + 1, value: escapes[character] ?? character };
75
+ }
76
+ function readString(source, start) {
77
+ const quote = source[start];
78
+ if (quote !== "'" && quote !== '"')
79
+ return null;
80
+ let value = "";
81
+ for (let index = start + 1; index < source.length; index += 1) {
82
+ const character = source[index];
83
+ if (character === quote)
84
+ return { end: index + 1, value };
85
+ if (character === "\\") {
86
+ const escaped = readEscape(source, index + 1);
87
+ if (!escaped)
88
+ return null;
89
+ value += escaped.value;
90
+ index = escaped.end - 1;
91
+ continue;
92
+ }
93
+ if (character === "\n" || character === "\r")
94
+ return null;
95
+ value += character;
96
+ }
97
+ return null;
98
+ }
99
+ function findTemplateExpressionEnd(source, start) {
100
+ let depth = 1;
101
+ for (let index = start; index < source.length; index += 1) {
102
+ const character = source[index];
103
+ if (character === "'" || character === '"') {
104
+ const quoted = readString(source, index);
105
+ if (!quoted)
106
+ return null;
107
+ index = quoted.end - 1;
108
+ continue;
109
+ }
110
+ if (character === "`") {
111
+ const nested = readTemplate(source, index);
112
+ if (!nested)
113
+ return null;
114
+ index = nested.end - 1;
115
+ continue;
116
+ }
117
+ if (character === "{")
118
+ depth += 1;
119
+ if (character === "}") {
120
+ depth -= 1;
121
+ if (depth === 0)
122
+ return index;
123
+ }
124
+ }
125
+ return null;
126
+ }
127
+ function readTemplate(source, start) {
128
+ if (source[start] !== "`")
129
+ return null;
130
+ const strings = [];
131
+ const expressions = [];
132
+ let value = "";
133
+ for (let index = start + 1; index < source.length; index += 1) {
134
+ const character = source[index];
135
+ if (character === "\\") {
136
+ const escaped = readEscape(source, index + 1);
137
+ if (!escaped)
138
+ return null;
139
+ value += escaped.value;
140
+ index = escaped.end - 1;
141
+ continue;
142
+ }
143
+ if (character === "`") {
144
+ strings.push(value);
145
+ return { end: index + 1, template: { expressions, strings } };
146
+ }
147
+ if (character === "$" && source[index + 1] === "{") {
148
+ strings.push(value);
149
+ value = "";
150
+ const close = findTemplateExpressionEnd(source, index + 2);
151
+ if (close === null)
152
+ return null;
153
+ expressions.push(source.slice(index + 2, close));
154
+ index = close;
155
+ continue;
156
+ }
157
+ value += character;
158
+ }
159
+ return null;
160
+ }
161
+ function isIdentifierStart(character) {
162
+ return character !== undefined && /[A-Za-z_$]/.test(character);
163
+ }
164
+ function isIdentifierPart(character) {
165
+ return character !== undefined && /[A-Za-z0-9_$]/.test(character);
166
+ }
167
+ function tokenize(source) {
168
+ const tokens = [];
169
+ for (let index = 0; index < source.length;) {
170
+ const character = source[index];
171
+ if (/\s/.test(character)) {
172
+ index += 1;
173
+ continue;
174
+ }
175
+ if (character === "'" || character === '"') {
176
+ const quoted = readString(source, index);
177
+ if (!quoted)
178
+ return null;
179
+ tokens.push({ kind: "str", value: quoted.value });
180
+ index = quoted.end;
181
+ continue;
182
+ }
183
+ if (character === "`") {
184
+ const template = readTemplate(source, index);
185
+ if (!template)
186
+ return null;
187
+ tokens.push({ kind: "template", template: template.template });
188
+ index = template.end;
189
+ continue;
190
+ }
191
+ if (/\d/.test(character)) {
192
+ const match = source.slice(index).match(/^(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/i);
193
+ if (!match)
194
+ return null;
195
+ tokens.push({ kind: "num", value: match[0] });
196
+ index += match[0].length;
197
+ continue;
198
+ }
199
+ if (isIdentifierStart(character)) {
200
+ let end = index + 1;
201
+ while (isIdentifierPart(source[end]))
202
+ end += 1;
203
+ tokens.push({ kind: "ident", value: source.slice(index, end) });
204
+ index = end;
205
+ continue;
206
+ }
207
+ if (character === "[" || character === "]" || source.startsWith("=>", index))
208
+ return null;
209
+ if (source.startsWith("//", index) || source.startsWith("/*", index))
210
+ return null;
211
+ const punctuation = PUNCTUATION.find((candidate) => source.startsWith(candidate, index));
212
+ if (!punctuation)
213
+ return null;
214
+ tokens.push({ kind: "punct", value: punctuation });
215
+ index += punctuation.length;
216
+ }
217
+ return tokens;
218
+ }
219
+ class Parser {
220
+ tokens;
221
+ index = 0;
222
+ constructor(tokens) {
223
+ this.tokens = tokens;
224
+ }
225
+ parse() {
226
+ const node = this.parseExpression();
227
+ return node && this.index === this.tokens.length ? node : null;
228
+ }
229
+ peek() {
230
+ return this.tokens[this.index];
231
+ }
232
+ takePunctuation(value) {
233
+ const token = this.peek();
234
+ if (token?.kind !== "punct" || token.value !== value)
235
+ return false;
236
+ this.index += 1;
237
+ return true;
238
+ }
239
+ parseExpression(minimumPrecedence = 0) {
240
+ let left = this.parseUnary();
241
+ if (!left)
242
+ return null;
243
+ while (true) {
244
+ const token = this.peek();
245
+ if (token?.kind !== "punct")
246
+ break;
247
+ const precedence = BINARY_PRECEDENCE[token.value];
248
+ if (precedence === undefined || precedence < minimumPrecedence)
249
+ break;
250
+ this.index += 1;
251
+ const right = this.parseExpression(precedence + 1);
252
+ if (!right)
253
+ return null;
254
+ left = { type: "Binary", operator: token.value, left, right };
255
+ }
256
+ if (minimumPrecedence === 0 && this.takePunctuation("?")) {
257
+ const yes = this.parseExpression();
258
+ if (!yes || !this.takePunctuation(":"))
259
+ return null;
260
+ const no = this.parseExpression();
261
+ if (!no)
262
+ return null;
263
+ left = { type: "Ternary", condition: left, yes, no };
264
+ }
265
+ return left;
266
+ }
267
+ parseUnary() {
268
+ const token = this.peek();
269
+ if (token?.kind === "punct" && (token.value === "!" || token.value === "+" || token.value === "-")) {
270
+ this.index += 1;
271
+ const value = this.parseUnary();
272
+ return value ? { type: "Unary", operator: token.value, value } : null;
273
+ }
274
+ return this.parsePostfix();
275
+ }
276
+ parsePostfix() {
277
+ let node = this.parsePrimary();
278
+ if (!node)
279
+ return null;
280
+ while (true) {
281
+ if (this.takePunctuation(".")) {
282
+ const property = this.peek();
283
+ if (property?.kind !== "ident" || BANNED_PROPS.has(property.value) || RESERVED.has(property.value)) {
284
+ return null;
285
+ }
286
+ this.index += 1;
287
+ node = { type: "Member", object: node, name: property.value };
288
+ continue;
289
+ }
290
+ if (this.takePunctuation("(")) {
291
+ const args = [];
292
+ if (!this.takePunctuation(")")) {
293
+ while (true) {
294
+ const argument = this.parseExpression();
295
+ if (!argument)
296
+ return null;
297
+ args.push(argument);
298
+ if (this.takePunctuation(")"))
299
+ break;
300
+ if (!this.takePunctuation(","))
301
+ return null;
302
+ }
303
+ }
304
+ node = { type: "Call", callee: node, args };
305
+ continue;
306
+ }
307
+ return node;
308
+ }
309
+ }
310
+ parsePrimary() {
311
+ const token = this.peek();
312
+ if (!token)
313
+ return null;
314
+ if (token.kind === "num") {
315
+ this.index += 1;
316
+ return { type: "Literal", value: Number(token.value) };
317
+ }
318
+ if (token.kind === "str") {
319
+ this.index += 1;
320
+ return { type: "Literal", value: token.value };
321
+ }
322
+ if (token.kind === "template") {
323
+ this.index += 1;
324
+ const expressions = [];
325
+ for (const source of token.template.expressions) {
326
+ const expression = parseSource(source);
327
+ if (!expression)
328
+ return null;
329
+ expressions.push(expression);
330
+ }
331
+ return { type: "Template", strings: token.template.strings, expressions };
332
+ }
333
+ if (token.kind === "ident") {
334
+ this.index += 1;
335
+ if (token.value === "true")
336
+ return { type: "Literal", value: true };
337
+ if (token.value === "false")
338
+ return { type: "Literal", value: false };
339
+ if (token.value === "null")
340
+ return { type: "Literal", value: null };
341
+ if (token.value === "undefined")
342
+ return { type: "Literal", value: undefined };
343
+ if (RESERVED.has(token.value) || BANNED_PROPS.has(token.value))
344
+ return null;
345
+ return { type: "Ident", name: token.value };
346
+ }
347
+ if (this.takePunctuation("(")) {
348
+ const expression = this.parseExpression();
349
+ return expression && this.takePunctuation(")") ? expression : null;
350
+ }
351
+ if (this.takePunctuation("{"))
352
+ return this.parseObject();
353
+ return null;
354
+ }
355
+ parseObject() {
356
+ const entries = [];
357
+ if (this.takePunctuation("}"))
358
+ return { type: "Object", entries };
359
+ while (true) {
360
+ const keyToken = this.peek();
361
+ if (!keyToken || (keyToken.kind !== "ident" && keyToken.kind !== "str"))
362
+ return null;
363
+ const key = keyToken.value;
364
+ if (BANNED_PROPS.has(key))
365
+ return null;
366
+ this.index += 1;
367
+ if (!this.takePunctuation(":"))
368
+ return null;
369
+ const value = this.parseExpression();
370
+ if (!value)
371
+ return null;
372
+ entries.push([key, value]);
373
+ if (this.takePunctuation("}"))
374
+ return { type: "Object", entries };
375
+ if (!this.takePunctuation(","))
376
+ return null;
377
+ }
378
+ }
379
+ }
380
+ function parseSource(source) {
381
+ const tokens = tokenize(source);
382
+ return tokens ? new Parser(tokens).parse() : null;
383
+ }
384
+ function resolveIdent(name, context) {
385
+ let object = context;
386
+ while (object !== null && object !== undefined && !STOP_PROTOS.has(object)) {
387
+ if (Object.prototype.hasOwnProperty.call(object, name)) {
388
+ return object[name];
389
+ }
390
+ object = Object.getPrototypeOf(object);
391
+ }
392
+ return Object.prototype.hasOwnProperty.call(ALLOWED_GLOBALS, name) ? ALLOWED_GLOBALS[name] : undefined;
393
+ }
394
+ function isPlainReceiver(value) {
395
+ const valueType = typeof value;
396
+ if (valueType === "string" || valueType === "number" || valueType === "boolean" || valueType === "function") {
397
+ return true;
398
+ }
399
+ if (value === null || valueType !== "object")
400
+ return false;
401
+ const firstPrototype = Object.getPrototypeOf(value);
402
+ if (firstPrototype === null || STOP_PROTOS.has(firstPrototype))
403
+ return true;
404
+ const secondPrototype = Object.getPrototypeOf(firstPrototype);
405
+ return secondPrototype === null || STOP_PROTOS.has(secondPrototype);
406
+ }
407
+ function readMember(object, name) {
408
+ if (object === null || object === undefined || !isPlainReceiver(object))
409
+ return undefined;
410
+ return object[name];
411
+ }
412
+ function add(left, right) {
413
+ if (typeof left === "string" || typeof right === "string")
414
+ return String(left) + String(right);
415
+ return Number(left) + Number(right);
416
+ }
417
+ function looselyEqual(left, right) {
418
+ if (left === right)
419
+ return true;
420
+ if (left == null && right == null)
421
+ return true;
422
+ if (typeof left === "boolean" || typeof right === "boolean")
423
+ return Number(left) === Number(right);
424
+ if ((typeof left === "number" && typeof right === "string") || (typeof left === "string" && typeof right === "number")) {
425
+ return Number(left) === Number(right);
426
+ }
427
+ return false;
428
+ }
429
+ function compare(left, right, operator) {
430
+ const comparableLeft = typeof left === "string" && typeof right === "string" ? left : Number(left);
431
+ const comparableRight = typeof left === "string" && typeof right === "string" ? right : Number(right);
432
+ if (operator === "<")
433
+ return comparableLeft < comparableRight;
434
+ if (operator === "<=")
435
+ return comparableLeft <= comparableRight;
436
+ if (operator === ">")
437
+ return comparableLeft > comparableRight;
438
+ return comparableLeft >= comparableRight;
439
+ }
440
+ function evaluateBinary(node, context) {
441
+ const left = evaluate(node.left, context);
442
+ if (node.operator === "&&")
443
+ return left ? evaluate(node.right, context) : left;
444
+ if (node.operator === "||")
445
+ return left ? left : evaluate(node.right, context);
446
+ if (node.operator === "??")
447
+ return left === null || left === undefined ? evaluate(node.right, context) : left;
448
+ const right = evaluate(node.right, context);
449
+ if (node.operator === "+")
450
+ return add(left, right);
451
+ if (node.operator === "-")
452
+ return Number(left) - Number(right);
453
+ if (node.operator === "*")
454
+ return Number(left) * Number(right);
455
+ if (node.operator === "/")
456
+ return Number(left) / Number(right);
457
+ if (node.operator === "%")
458
+ return Number(left) % Number(right);
459
+ if (node.operator === "===")
460
+ return left === right;
461
+ if (node.operator === "!==")
462
+ return left !== right;
463
+ if (node.operator === "==")
464
+ return looselyEqual(left, right);
465
+ if (node.operator === "!=")
466
+ return !looselyEqual(left, right);
467
+ return compare(left, right, node.operator);
468
+ }
469
+ function evaluate(node, context) {
470
+ if (node.type === "Literal")
471
+ return node.value;
472
+ if (node.type === "Ident")
473
+ return resolveIdent(node.name, context);
474
+ if (node.type === "Member")
475
+ return readMember(evaluate(node.object, context), node.name);
476
+ if (node.type === "Call") {
477
+ const args = node.args.map((argument) => evaluate(argument, context));
478
+ if (node.callee.type === "Member") {
479
+ const receiver = evaluate(node.callee.object, context);
480
+ const callable = readMember(receiver, node.callee.name);
481
+ return typeof callable === "function" ? Reflect.apply(callable, receiver, args) : undefined;
482
+ }
483
+ const callable = evaluate(node.callee, context);
484
+ return typeof callable === "function" ? Reflect.apply(callable, undefined, args) : undefined;
485
+ }
486
+ if (node.type === "Unary") {
487
+ const value = evaluate(node.value, context);
488
+ if (node.operator === "!")
489
+ return !value;
490
+ if (node.operator === "-")
491
+ return -Number(value);
492
+ return Number(value);
493
+ }
494
+ if (node.type === "Binary")
495
+ return evaluateBinary(node, context);
496
+ if (node.type === "Ternary") {
497
+ return evaluate(node.condition, context) ? evaluate(node.yes, context) : evaluate(node.no, context);
498
+ }
499
+ if (node.type === "Object") {
500
+ const result = {};
501
+ for (const [key, value] of node.entries)
502
+ result[key] = evaluate(value, context);
503
+ return result;
504
+ }
505
+ let result = node.strings[0] ?? "";
506
+ for (let index = 0; index < node.expressions.length; index += 1) {
507
+ result += String(evaluate(node.expressions[index], context));
508
+ result += node.strings[index + 1] ?? "";
509
+ }
510
+ return result;
511
+ }
512
+ export function compileExpression(source) {
513
+ const syntaxTree = parseSource(source);
514
+ if (!syntaxTree)
515
+ return null;
516
+ return (context) => {
517
+ try {
518
+ return evaluate(syntaxTree, context);
519
+ }
520
+ catch {
521
+ return undefined;
522
+ }
523
+ };
524
+ }
@@ -51,7 +51,7 @@ interface Ctx {
51
51
  $parent?: Ctx;
52
52
  $index?: number;
53
53
  $key?: string;
54
- $event?: Event;
54
+ $event?: Readonly<Record<string, unknown>>;
55
55
  route?: RouteFn;
56
56
  navigate?: (name: string, params?: Record<string, string | number>) => void;
57
57
  [key: string]: unknown;