@codehz/json-expr 0.6.2 → 0.6.3

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/index.mjs CHANGED
@@ -63,8 +63,8 @@ function createGenerateContext() {
63
63
  /**
64
64
  * 从 AST 生成规范化的代码
65
65
  */
66
- function generate(node) {
67
- return generateWithContext(node, createGenerateContext());
66
+ function generate(node, options = {}) {
67
+ return generateWithContext(node, createGenerateContext(), options);
68
68
  }
69
69
  /** 需要空格分隔的关键字运算符 */
70
70
  const KEYWORD_BINARY_OPERATORS = new Set(["in", "instanceof"]);
@@ -72,46 +72,47 @@ const KEYWORD_UNARY_OPERATORS = new Set(["typeof", "void"]);
72
72
  /**
73
73
  * 带上下文的代码生成
74
74
  */
75
- function generateWithContext(node, ctx) {
76
- switch (node.type) {
77
- case "NumberLiteral": return node.raw;
78
- case "StringLiteral": return JSON.stringify(node.value);
79
- case "BooleanLiteral": return node.value ? "true" : "false";
75
+ function generateWithContext(node, ctx, options = {}) {
76
+ const rewritten = options.rewriteNode?.(node, ctx) ?? node;
77
+ switch (rewritten.type) {
78
+ case "NumberLiteral": return rewritten.raw;
79
+ case "StringLiteral": return JSON.stringify(rewritten.value);
80
+ case "BooleanLiteral": return rewritten.value ? "true" : "false";
80
81
  case "NullLiteral": return "null";
81
- case "Identifier": return node.name;
82
- case "Placeholder": return ctx.paramMapping.get(node.id) ?? `$$${node.id.description}$$`;
82
+ case "Identifier": return rewritten.name;
83
+ case "Placeholder": return ctx.paramMapping.get(rewritten.id) ?? `$$${rewritten.id.description}$$`;
83
84
  case "BinaryExpr": {
84
- const left = wrapIfNeededWithContext(node.left, node, "left", ctx);
85
- const right = wrapIfNeededWithContext(node.right, node, "right", ctx);
86
- const sep = KEYWORD_BINARY_OPERATORS.has(node.operator) ? " " : "";
87
- return `${left}${sep}${node.operator}${sep}${right}`;
85
+ const left = wrapIfNeededWithContext(rewritten.left, rewritten, "left", ctx, options);
86
+ const right = wrapIfNeededWithContext(rewritten.right, rewritten, "right", ctx, options);
87
+ const sep = KEYWORD_BINARY_OPERATORS.has(rewritten.operator) ? " " : "";
88
+ return `${left}${sep}${rewritten.operator}${sep}${right}`;
88
89
  }
89
90
  case "UnaryExpr": {
90
- if (!node.prefix) return generateWithContext(node.argument, ctx) + node.operator;
91
- const arg = wrapIfNeededWithContext(node.argument, node, "argument", ctx);
92
- const sep = KEYWORD_UNARY_OPERATORS.has(node.operator) ? " " : "";
93
- return `${node.operator}${sep}${arg}`;
91
+ if (!rewritten.prefix) return generateWithContext(rewritten.argument, ctx, options) + rewritten.operator;
92
+ const arg = wrapIfNeededWithContext(rewritten.argument, rewritten, "argument", ctx, options);
93
+ const sep = KEYWORD_UNARY_OPERATORS.has(rewritten.operator) ? " " : "";
94
+ return `${rewritten.operator}${sep}${arg}`;
94
95
  }
95
- case "ConditionalExpr": return `${wrapIfNeededWithContext(node.test, node, "test", ctx)}?${wrapIfNeededWithContext(node.consequent, node, "consequent", ctx)}:${wrapIfNeededWithContext(node.alternate, node, "alternate", ctx)}`;
96
+ case "ConditionalExpr": return `${wrapIfNeededWithContext(rewritten.test, rewritten, "test", ctx, options)}?${wrapIfNeededWithContext(rewritten.consequent, rewritten, "consequent", ctx, options)}:${wrapIfNeededWithContext(rewritten.alternate, rewritten, "alternate", ctx, options)}`;
96
97
  case "MemberExpr": {
97
- const object = wrapIfNeededWithContext(node.object, node, "object", ctx);
98
- const property = generateWithContext(node.property, ctx);
99
- const accessor = node.optional ? "?." : node.computed ? "" : ".";
100
- return node.computed ? `${object}${accessor}[${property}]` : `${object}${accessor}${property}`;
98
+ const object = wrapIfNeededWithContext(rewritten.object, rewritten, "object", ctx, options);
99
+ const property = generateWithContext(rewritten.property, ctx, options);
100
+ const accessor = rewritten.optional ? "?." : rewritten.computed ? "" : ".";
101
+ return rewritten.computed ? `${object}${accessor}[${property}]` : `${object}${accessor}${property}`;
101
102
  }
102
103
  case "CallExpr": {
103
- const callee = wrapIfNeededWithContext(node.callee, node, "callee", ctx);
104
- const args = node.arguments.map((arg) => generateWithContext(arg, ctx)).join(",");
105
- return `${node.callee.type === "Identifier" && BUILTIN_CONSTRUCTORS.has(node.callee.name) ? "new " : ""}${callee}${node.optional ? "?." : ""}(${args})`;
106
- }
107
- case "ArrayExpr": return `[${node.elements.map((el) => generateWithContext(el, ctx)).join(",")}]`;
108
- case "ObjectExpr": return `{${node.properties.map((prop) => {
109
- if (prop.shorthand) return generateWithContext(prop.key, ctx);
110
- return `${prop.computed ? `[${generateWithContext(prop.key, ctx)}]` : generateWithContext(prop.key, ctx)}:${generateWithContext(prop.value, ctx)}`;
104
+ const callee = wrapIfNeededWithContext(rewritten.callee, rewritten, "callee", ctx, options);
105
+ const args = rewritten.arguments.map((arg) => generateWithContext(arg, ctx, options)).join(",");
106
+ return `${rewritten.callee.type === "Identifier" && BUILTIN_CONSTRUCTORS.has(rewritten.callee.name) ? "new " : ""}${callee}${rewritten.optional ? "?." : ""}(${args})`;
107
+ }
108
+ case "ArrayExpr": return `[${rewritten.elements.map((el) => generateWithContext(el, ctx, options)).join(",")}]`;
109
+ case "ObjectExpr": return `{${rewritten.properties.map((prop) => {
110
+ if (prop.shorthand) return generateWithContext(prop.key, ctx, options);
111
+ return `${prop.computed ? `[${generateWithContext(prop.key, ctx, options)}]` : generateWithContext(prop.key, ctx, options)}:${generateWithContext(prop.value, ctx, options)}`;
111
112
  }).join(",")}}`;
112
- case "ArrowFunctionExpr": return generateArrowFunction(node, ctx);
113
+ case "ArrowFunctionExpr": return generateArrowFunction(rewritten, ctx, options);
113
114
  default: {
114
- const unknownNode = node;
115
+ const unknownNode = rewritten;
115
116
  throw new Error(`Unknown node type: ${unknownNode.type ?? "unknown"}`);
116
117
  }
117
118
  }
@@ -120,7 +121,7 @@ function generateWithContext(node, ctx) {
120
121
  * 生成箭头函数代码
121
122
  * 为 Placeholder 参数分配唯一名称,避免嵌套 lambda 冲突
122
123
  */
123
- function generateArrowFunction(node, ctx) {
124
+ function generateArrowFunction(node, ctx, options) {
124
125
  const allocatedParams = [];
125
126
  const paramNames = [];
126
127
  for (const param of node.params) if (param.type === "Identifier") paramNames.push(param.name);
@@ -135,7 +136,7 @@ function generateArrowFunction(node, ctx) {
135
136
  ctx.paramMapping.set(param.id, name);
136
137
  }
137
138
  const paramsStr = paramNames.length === 1 ? paramNames[0] : `(${paramNames.join(",")})`;
138
- const bodyStr = node.body.type === "ObjectExpr" ? `(${generateWithContext(node.body, ctx)})` : generateWithContext(node.body, ctx);
139
+ const bodyStr = node.body.type === "ObjectExpr" ? `(${generateWithContext(node.body, ctx, options)})` : generateWithContext(node.body, ctx, options);
139
140
  for (const { id, name } of allocatedParams) {
140
141
  ctx.paramMapping.delete(id);
141
142
  ctx.usedParamNames.delete(name);
@@ -153,9 +154,10 @@ function allocateUniqueName(usedNames) {
153
154
  /**
154
155
  * 判断是否需要括号包裹,并生成代码(带上下文版本)
155
156
  */
156
- function wrapIfNeededWithContext(child, parent, position, ctx) {
157
- const code = generateWithContext(child, ctx);
158
- if (needsParens(child, parent, position)) return `(${code})`;
157
+ function wrapIfNeededWithContext(child, parent, position, ctx, options = {}) {
158
+ const rewrittenChild = options.rewriteNode?.(child, ctx) ?? child;
159
+ const code = generateWithContext(rewrittenChild, ctx, options);
160
+ if (needsParens(rewrittenChild, parent, position)) return `(${code})`;
159
161
  return code;
160
162
  }
161
163
  /**
@@ -199,169 +201,257 @@ function transformIdentifiers(node, transform) {
199
201
  switch (node.type) {
200
202
  case "Identifier": {
201
203
  const result = transform(node.name);
202
- return typeof result === "string" ? {
204
+ if (typeof result !== "string") return result;
205
+ return result === node.name ? node : {
203
206
  ...node,
204
207
  name: result
205
- } : result;
208
+ };
206
209
  }
207
210
  case "Placeholder": return node;
208
- case "BinaryExpr": return {
209
- ...node,
210
- left: transformIdentifiers(node.left, transform),
211
- right: transformIdentifiers(node.right, transform)
212
- };
213
- case "UnaryExpr": return {
214
- ...node,
215
- argument: transformIdentifiers(node.argument, transform)
216
- };
217
- case "ConditionalExpr": return {
218
- ...node,
219
- test: transformIdentifiers(node.test, transform),
220
- consequent: transformIdentifiers(node.consequent, transform),
221
- alternate: transformIdentifiers(node.alternate, transform)
222
- };
223
- case "MemberExpr": return {
224
- ...node,
225
- object: transformIdentifiers(node.object, transform),
226
- property: node.computed ? transformIdentifiers(node.property, transform) : node.property
227
- };
228
- case "CallExpr": return {
229
- ...node,
230
- callee: transformIdentifiers(node.callee, transform),
231
- arguments: node.arguments.map((arg) => transformIdentifiers(arg, transform))
232
- };
233
- case "ArrayExpr": return {
234
- ...node,
235
- elements: node.elements.map((el) => transformIdentifiers(el, transform))
236
- };
237
- case "ObjectExpr": return {
238
- ...node,
239
- properties: node.properties.map((prop) => ({
240
- ...prop,
241
- key: prop.computed ? transformIdentifiers(prop.key, transform) : prop.key,
242
- value: transformIdentifiers(prop.value, transform)
243
- }))
244
- };
211
+ case "BinaryExpr": {
212
+ const left = transformIdentifiers(node.left, transform);
213
+ const right = transformIdentifiers(node.right, transform);
214
+ return left === node.left && right === node.right ? node : {
215
+ ...node,
216
+ left,
217
+ right
218
+ };
219
+ }
220
+ case "UnaryExpr": {
221
+ const argument = transformIdentifiers(node.argument, transform);
222
+ return argument === node.argument ? node : {
223
+ ...node,
224
+ argument
225
+ };
226
+ }
227
+ case "ConditionalExpr": {
228
+ const test = transformIdentifiers(node.test, transform);
229
+ const consequent = transformIdentifiers(node.consequent, transform);
230
+ const alternate = transformIdentifiers(node.alternate, transform);
231
+ return test === node.test && consequent === node.consequent && alternate === node.alternate ? node : {
232
+ ...node,
233
+ test,
234
+ consequent,
235
+ alternate
236
+ };
237
+ }
238
+ case "MemberExpr": {
239
+ const object = transformIdentifiers(node.object, transform);
240
+ const property = node.computed ? transformIdentifiers(node.property, transform) : node.property;
241
+ return object === node.object && property === node.property ? node : {
242
+ ...node,
243
+ object,
244
+ property
245
+ };
246
+ }
247
+ case "CallExpr": {
248
+ const callee = transformIdentifiers(node.callee, transform);
249
+ const arguments_ = mapAstNodes$1(node.arguments, (arg) => transformIdentifiers(arg, transform));
250
+ return callee === node.callee && arguments_ === node.arguments ? node : {
251
+ ...node,
252
+ callee,
253
+ arguments: arguments_
254
+ };
255
+ }
256
+ case "ArrayExpr": {
257
+ const elements = mapAstNodes$1(node.elements, (el) => transformIdentifiers(el, transform));
258
+ return elements === node.elements ? node : {
259
+ ...node,
260
+ elements
261
+ };
262
+ }
263
+ case "ObjectExpr": {
264
+ const properties = mapObjectProperties$1(node.properties, (prop) => {
265
+ const key = prop.computed ? transformIdentifiers(prop.key, transform) : prop.key;
266
+ const value = transformIdentifiers(prop.value, transform);
267
+ return key === prop.key && value === prop.value ? prop : {
268
+ ...prop,
269
+ key,
270
+ value
271
+ };
272
+ });
273
+ return properties === node.properties ? node : {
274
+ ...node,
275
+ properties
276
+ };
277
+ }
245
278
  case "ArrowFunctionExpr": {
246
279
  const paramNames = new Set(node.params.filter((p) => p.type === "Identifier").map((p) => p.name));
247
- return {
280
+ const body = transformIdentifiers(node.body, (name) => paramNames.has(name) ? name : transform(name));
281
+ return body === node.body ? node : {
248
282
  ...node,
249
- body: transformIdentifiers(node.body, (name) => paramNames.has(name) ? name : transform(name))
283
+ body
250
284
  };
251
285
  }
252
286
  default: return node;
253
287
  }
254
288
  }
255
289
  /**
256
- * 转换 AST 中的占位符节点
257
- * 回调函数接收 symbol,返回 Identifier 节点的名称
258
- * 如果返回 null/undefined,则保留原始 Placeholder 节点
290
+ * expr() 专用:将 context 中的变量替换为 Placeholder。
259
291
  */
260
- function transformPlaceholders(node, transform) {
292
+ function transformExprVariables(node, nameToId) {
293
+ return transformIdentifiers(node, (name) => {
294
+ const id = nameToId.get(name);
295
+ return id ? {
296
+ type: "Placeholder",
297
+ id
298
+ } : name;
299
+ });
300
+ }
301
+ /**
302
+ * expr() 专用:单次遍历完成变量替换、子表达式引用计数和延迟编译收集。
303
+ */
304
+ function transformExprIdentifiers(node, nameToId, nameToExprAST) {
305
+ const refCounts = /* @__PURE__ */ new Map();
306
+ const candidateRefs = /* @__PURE__ */ new Map();
307
+ const ast = transformExprIdentifiersWithScope(node, nameToId, nameToExprAST, refCounts, candidateRefs);
308
+ let deferredAsts;
309
+ for (const [name, refs] of candidateRefs) {
310
+ if ((refCounts.get(name) ?? 0) <= 1) {
311
+ replaceNode(refs[0], nameToExprAST.get(name));
312
+ continue;
313
+ }
314
+ if (!deferredAsts) deferredAsts = /* @__PURE__ */ new Map();
315
+ deferredAsts.set(name, nameToExprAST.get(name));
316
+ }
317
+ return {
318
+ ast,
319
+ deferredAsts
320
+ };
321
+ }
322
+ function mapAstNodes$1(nodes, transform) {
323
+ let result;
324
+ for (let i = 0; i < nodes.length; i++) {
325
+ const node = nodes[i];
326
+ const transformed = transform(node);
327
+ if (result) {
328
+ result.push(transformed);
329
+ continue;
330
+ }
331
+ if (transformed !== node) {
332
+ result = nodes.slice(0, i);
333
+ result.push(transformed);
334
+ }
335
+ }
336
+ return result ?? nodes;
337
+ }
338
+ function mapObjectProperties$1(properties, transform) {
339
+ let result;
340
+ for (let i = 0; i < properties.length; i++) {
341
+ const property = properties[i];
342
+ const transformed = transform(property);
343
+ if (result) {
344
+ result.push(transformed);
345
+ continue;
346
+ }
347
+ if (transformed !== property) {
348
+ result = properties.slice(0, i);
349
+ result.push(transformed);
350
+ }
351
+ }
352
+ return result ?? properties;
353
+ }
354
+ function transformExprIdentifiersWithScope(node, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames) {
261
355
  switch (node.type) {
262
- case "Placeholder": {
263
- const name = transform(node.id);
264
- return name != null ? {
265
- type: "Identifier",
266
- name
267
- } : node;
356
+ case "Identifier": {
357
+ if (shadowedNames?.has(node.name)) return node;
358
+ const id = nameToId.get(node.name);
359
+ if (id) return {
360
+ type: "Placeholder",
361
+ id
362
+ };
363
+ if (!nameToExprAST.get(node.name)) return node;
364
+ refCounts.set(node.name, (refCounts.get(node.name) ?? 0) + 1);
365
+ const candidateNode = { ...node };
366
+ const refs = candidateRefs.get(node.name);
367
+ if (refs) refs.push(candidateNode);
368
+ else candidateRefs.set(node.name, [candidateNode]);
369
+ return candidateNode;
370
+ }
371
+ case "Placeholder": return node;
372
+ case "BinaryExpr": {
373
+ const left = transformExprIdentifiersWithScope(node.left, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
374
+ const right = transformExprIdentifiersWithScope(node.right, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
375
+ return left === node.left && right === node.right ? node : {
376
+ ...node,
377
+ left,
378
+ right
379
+ };
380
+ }
381
+ case "UnaryExpr": {
382
+ const argument = transformExprIdentifiersWithScope(node.argument, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
383
+ return argument === node.argument ? node : {
384
+ ...node,
385
+ argument
386
+ };
387
+ }
388
+ case "ConditionalExpr": {
389
+ const test = transformExprIdentifiersWithScope(node.test, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
390
+ const consequent = transformExprIdentifiersWithScope(node.consequent, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
391
+ const alternate = transformExprIdentifiersWithScope(node.alternate, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
392
+ return test === node.test && consequent === node.consequent && alternate === node.alternate ? node : {
393
+ ...node,
394
+ test,
395
+ consequent,
396
+ alternate
397
+ };
398
+ }
399
+ case "MemberExpr": {
400
+ const object = transformExprIdentifiersWithScope(node.object, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
401
+ const property = node.computed ? transformExprIdentifiersWithScope(node.property, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames) : node.property;
402
+ return object === node.object && property === node.property ? node : {
403
+ ...node,
404
+ object,
405
+ property
406
+ };
407
+ }
408
+ case "CallExpr": {
409
+ const callee = transformExprIdentifiersWithScope(node.callee, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
410
+ const arguments_ = mapAstNodes$1(node.arguments, (arg) => transformExprIdentifiersWithScope(arg, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames));
411
+ return callee === node.callee && arguments_ === node.arguments ? node : {
412
+ ...node,
413
+ callee,
414
+ arguments: arguments_
415
+ };
416
+ }
417
+ case "ArrayExpr": {
418
+ const elements = mapAstNodes$1(node.elements, (el) => transformExprIdentifiersWithScope(el, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames));
419
+ return elements === node.elements ? node : {
420
+ ...node,
421
+ elements
422
+ };
423
+ }
424
+ case "ObjectExpr": {
425
+ const properties = mapObjectProperties$1(node.properties, (prop) => {
426
+ const key = prop.computed ? transformExprIdentifiersWithScope(prop.key, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames) : prop.key;
427
+ const value = transformExprIdentifiersWithScope(prop.value, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
428
+ return key === prop.key && value === prop.value ? prop : {
429
+ ...prop,
430
+ key,
431
+ value
432
+ };
433
+ });
434
+ return properties === node.properties ? node : {
435
+ ...node,
436
+ properties
437
+ };
268
438
  }
269
- case "Identifier": return node;
270
- case "BinaryExpr": return {
271
- ...node,
272
- left: transformPlaceholders(node.left, transform),
273
- right: transformPlaceholders(node.right, transform)
274
- };
275
- case "UnaryExpr": return {
276
- ...node,
277
- argument: transformPlaceholders(node.argument, transform)
278
- };
279
- case "ConditionalExpr": return {
280
- ...node,
281
- test: transformPlaceholders(node.test, transform),
282
- consequent: transformPlaceholders(node.consequent, transform),
283
- alternate: transformPlaceholders(node.alternate, transform)
284
- };
285
- case "MemberExpr": return {
286
- ...node,
287
- object: transformPlaceholders(node.object, transform),
288
- property: node.computed ? transformPlaceholders(node.property, transform) : node.property
289
- };
290
- case "CallExpr": return {
291
- ...node,
292
- callee: transformPlaceholders(node.callee, transform),
293
- arguments: node.arguments.map((arg) => transformPlaceholders(arg, transform))
294
- };
295
- case "ArrayExpr": return {
296
- ...node,
297
- elements: node.elements.map((el) => transformPlaceholders(el, transform))
298
- };
299
- case "ObjectExpr": return {
300
- ...node,
301
- properties: node.properties.map((prop) => ({
302
- ...prop,
303
- key: prop.computed ? transformPlaceholders(prop.key, transform) : prop.key,
304
- value: transformPlaceholders(prop.value, transform)
305
- }))
306
- };
307
439
  case "ArrowFunctionExpr": {
308
- const paramSymbols = new Set(node.params.filter((p) => p.type === "Placeholder").map((p) => p.id));
309
- return {
440
+ const paramNames = node.params.filter((p) => p.type === "Identifier");
441
+ const nextShadowedNames = paramNames.length === 0 ? shadowedNames : new Set([...shadowedNames ?? [], ...paramNames.map((param) => param.name)]);
442
+ const body = transformExprIdentifiersWithScope(node.body, nameToId, nameToExprAST, refCounts, candidateRefs, nextShadowedNames);
443
+ return body === node.body ? node : {
310
444
  ...node,
311
- body: transformPlaceholders(node.body, (id) => paramSymbols.has(id) ? null : transform(id))
445
+ body
312
446
  };
313
447
  }
314
448
  default: return node;
315
449
  }
316
450
  }
317
- /**
318
- * 统计 AST 中各标识符名称的出现次数
319
- * 用于判断子表达式是否应该内联(引用次数 = 1 则内联,> 1 则推迟为独立编译)
320
- */
321
- function countIdentifierReferences(node, counts) {
322
- switch (node.type) {
323
- case "Identifier":
324
- counts.set(node.name, (counts.get(node.name) ?? 0) + 1);
325
- break;
326
- case "Placeholder": break;
327
- case "BinaryExpr":
328
- countIdentifierReferences(node.left, counts);
329
- countIdentifierReferences(node.right, counts);
330
- break;
331
- case "UnaryExpr":
332
- countIdentifierReferences(node.argument, counts);
333
- break;
334
- case "ConditionalExpr":
335
- countIdentifierReferences(node.test, counts);
336
- countIdentifierReferences(node.consequent, counts);
337
- countIdentifierReferences(node.alternate, counts);
338
- break;
339
- case "MemberExpr":
340
- countIdentifierReferences(node.object, counts);
341
- if (node.computed) countIdentifierReferences(node.property, counts);
342
- break;
343
- case "CallExpr":
344
- countIdentifierReferences(node.callee, counts);
345
- for (const arg of node.arguments) countIdentifierReferences(arg, counts);
346
- break;
347
- case "ArrayExpr":
348
- for (const el of node.elements) countIdentifierReferences(el, counts);
349
- break;
350
- case "ObjectExpr":
351
- for (const prop of node.properties) {
352
- if (prop.computed) countIdentifierReferences(prop.key, counts);
353
- countIdentifierReferences(prop.value, counts);
354
- }
355
- break;
356
- case "ArrowFunctionExpr":
357
- {
358
- const paramNames = new Set(node.params.filter((p) => p.type === "Identifier").map((p) => p.name));
359
- const bodyCounts = /* @__PURE__ */ new Map();
360
- countIdentifierReferences(node.body, bodyCounts);
361
- for (const [name, count] of bodyCounts) if (!paramNames.has(name)) counts.set(name, (counts.get(name) ?? 0) + count);
362
- }
363
- break;
364
- }
451
+ function replaceNode(target, replacement) {
452
+ const record = target;
453
+ for (const key of Object.keys(record)) delete record[key];
454
+ Object.assign(record, replacement);
365
455
  }
366
456
 
367
457
  //#endregion
@@ -1336,6 +1426,7 @@ function expr(context) {
1336
1426
  return (source) => {
1337
1427
  const deps = /* @__PURE__ */ new Set();
1338
1428
  const nameToId = /* @__PURE__ */ new Map();
1429
+ const nameToExprAST = /* @__PURE__ */ new Map();
1339
1430
  for (const [name, value] of Object.entries(context)) {
1340
1431
  let id = getVariableId(value);
1341
1432
  if (!id && (typeof value === "object" || typeof value === "function") && value !== null) {
@@ -1348,32 +1439,15 @@ function expr(context) {
1348
1439
  } else {
1349
1440
  const meta = (typeof value === "object" || typeof value === "function") && value !== null ? getProxyMetadata(value) : void 0;
1350
1441
  if (meta?.dependencies) for (const dep of meta.dependencies) deps.add(dep);
1442
+ if (meta?.ast) nameToExprAST.set(name, meta.ast);
1351
1443
  }
1352
1444
  }
1353
- const nameToExprAST = /* @__PURE__ */ new Map();
1354
- for (const [name, value] of Object.entries(context)) if ((typeof value === "object" || typeof value === "function") && value !== null) {
1355
- if (nameToId.has(name)) continue;
1356
- const meta = getProxyMetadata(value);
1357
- if (meta?.ast) nameToExprAST.set(name, meta.ast);
1358
- }
1359
1445
  const ast = parse(source);
1360
- const refCounts = /* @__PURE__ */ new Map();
1361
- countIdentifierReferences(ast, refCounts);
1362
- const deferredAsts = /* @__PURE__ */ new Map();
1363
- return createProxyExpressionWithAST(transformIdentifiers(ast, (name) => {
1364
- const id = nameToId.get(name);
1365
- if (id) return {
1366
- type: "Placeholder",
1367
- id
1368
- };
1369
- const exprAST = nameToExprAST.get(name);
1370
- if (exprAST) {
1371
- if ((refCounts.get(name) ?? 0) <= 1) return exprAST;
1372
- deferredAsts.set(name, exprAST);
1373
- return name;
1374
- }
1375
- return name;
1376
- }), deps, deferredAsts.size > 0 ? deferredAsts : void 0);
1446
+ const { ast: transformedAst, deferredAsts } = nameToExprAST.size === 0 ? {
1447
+ ast: transformExprVariables(ast, nameToId),
1448
+ deferredAsts: void 0
1449
+ } : transformExprIdentifiers(ast, nameToId, nameToExprAST);
1450
+ return createProxyExpressionWithAST(transformedAst, deps, deferredAsts);
1377
1451
  };
1378
1452
  }
1379
1453
 
@@ -1640,7 +1714,7 @@ function compile(expression, variables, _options = {}) {
1640
1714
  const ast = serializeArgumentToAST(expression);
1641
1715
  const variableOrder = [];
1642
1716
  const variableToIndex = /* @__PURE__ */ new Map();
1643
- const symbolToName = /* @__PURE__ */ new Map();
1717
+ const placeholderNames = /* @__PURE__ */ new Map();
1644
1718
  for (const [name, value] of Object.entries(variables)) {
1645
1719
  if (!variableToIndex.has(name)) {
1646
1720
  const index = variableOrder.length;
@@ -1648,36 +1722,27 @@ function compile(expression, variables, _options = {}) {
1648
1722
  variableOrder.push(name);
1649
1723
  }
1650
1724
  const id = getVariableId(value);
1651
- if (id) symbolToName.set(id, name);
1725
+ if (id) placeholderNames.set(id, `$[${variableToIndex.get(name)}]`);
1652
1726
  }
1653
1727
  const topLevelExprs = [];
1728
+ const baseScope = { placeholderNames };
1654
1729
  const ctx = {
1655
1730
  nextParamIndex: 0,
1656
1731
  expressionStack: [topLevelExprs],
1657
1732
  nextIndex: variableOrder.length,
1658
- variableCount: variableOrder.length
1733
+ variableCount: variableOrder.length,
1734
+ baseScope
1659
1735
  };
1660
1736
  const deferredIndexMap = /* @__PURE__ */ new Map();
1661
- if (deferredAsts && deferredAsts.size > 0) compileDeferredExprs(deferredAsts, ctx, symbolToName, variableToIndex, deferredIndexMap);
1662
- const placeholderTransformed = transformPlaceholders(ast, (id) => {
1663
- const name = symbolToName.get(id);
1664
- if (!name) return null;
1665
- const index = variableToIndex.get(name);
1666
- return index !== void 0 ? `$[${index}]` : null;
1667
- });
1737
+ if (deferredAsts && deferredAsts.size > 0) compileDeferredExprs(deferredAsts, ctx, baseScope, deferredIndexMap);
1668
1738
  const undefinedVars = [];
1669
- const transformed = transformIdentifiers(placeholderTransformed, (name) => {
1670
- if (/^\$\[\d+\]$/.test(name)) return name;
1671
- if (/^_\d+$/.test(name)) return name;
1672
- const index = variableToIndex.get(name);
1673
- if (index !== void 0) return `$[${index}]`;
1674
- const deferredIdx = deferredIndexMap.get(name);
1675
- if (deferredIdx !== void 0) return `$[${deferredIdx}]`;
1676
- if (!ALLOWED_GLOBALS.has(name)) undefinedVars.push(name);
1677
- return name;
1739
+ const transformed = rewriteAstForCompile(ast, {
1740
+ ...baseScope,
1741
+ deferredIndexMap,
1742
+ undefinedVars
1678
1743
  });
1679
1744
  if (undefinedVars.length > 0) throw new Error(`Undefined variable(s): ${[...new Set(undefinedVars)].join(", ")}`);
1680
- compileAst(transformed, ctx);
1745
+ compileAst(transformed, ctx, baseScope);
1681
1746
  return [variableOrder, ...topLevelExprs];
1682
1747
  }
1683
1748
  /**
@@ -1690,7 +1755,7 @@ function currentExprs(ctx) {
1690
1755
  * 编译推迟的子表达式(因引用计数 >1 未内联)
1691
1756
  * 使用递归处理,自动按依赖拓扑顺序编译
1692
1757
  */
1693
- function compileDeferredExprs(deferredAsts, ctx, symbolToName, variableToIndex, deferredIndexMap) {
1758
+ function compileDeferredExprs(deferredAsts, ctx, baseScope, deferredIndexMap) {
1694
1759
  const processing = /* @__PURE__ */ new Set();
1695
1760
  function compileOne(name) {
1696
1761
  if (processing.has(name)) throw new Error(`Circular reference in deferred expressions: ${name}`);
@@ -1699,91 +1764,175 @@ function compileDeferredExprs(deferredAsts, ctx, symbolToName, variableToIndex,
1699
1764
  const ast = deferredAsts.get(name);
1700
1765
  if (!ast) throw new Error(`Unknown deferred expression: ${name}`);
1701
1766
  processing.add(name);
1702
- const t1 = transformPlaceholders(ast, (id) => {
1703
- const vName = symbolToName.get(id);
1704
- if (!vName) return null;
1705
- const idx = variableToIndex.get(vName);
1706
- return idx !== void 0 ? `$[${idx}]` : null;
1707
- });
1708
1767
  const undefinedVars = [];
1709
- const t2 = transformIdentifiers(t1, (n) => {
1710
- if (/^\$\[\d+\]$/.test(n)) return n;
1711
- if (/^_\d+$/.test(n)) return n;
1712
- const varIdx = variableToIndex.get(n);
1713
- if (varIdx !== void 0) return `$[${varIdx}]`;
1714
- if (deferredAsts.has(n)) return `$[${compileOne(n)}]`;
1715
- if (!ALLOWED_GLOBALS.has(n)) undefinedVars.push(n);
1716
- return n;
1768
+ const transformed = rewriteAstForCompile(ast, {
1769
+ ...baseScope,
1770
+ deferredAsts,
1771
+ deferredIndexMap,
1772
+ onDeferredReference: compileOne,
1773
+ undefinedVars
1717
1774
  });
1718
1775
  if (undefinedVars.length > 0) throw new Error(`Undefined variable(s) in deferred expression "${name}": ${[...new Set(undefinedVars)].join(", ")}`);
1719
- const idx = compileAst(t2, ctx);
1776
+ const idx = compileAst(transformed, ctx, baseScope);
1720
1777
  deferredIndexMap.set(name, idx);
1721
1778
  processing.delete(name);
1722
1779
  return idx;
1723
1780
  }
1724
1781
  for (const name of deferredAsts.keys()) compileOne(name);
1725
1782
  }
1783
+ function rewriteAstForCompile(node, scope) {
1784
+ switch (node.type) {
1785
+ case "Placeholder": {
1786
+ const name = scope.placeholderNames.get(node.id);
1787
+ return name ? {
1788
+ type: "Identifier",
1789
+ name
1790
+ } : node;
1791
+ }
1792
+ case "Identifier": {
1793
+ if (scope.shadowedNames?.has(node.name)) return node;
1794
+ const resolved = resolveIdentifierName(node.name, scope);
1795
+ return resolved === node.name ? node : {
1796
+ ...node,
1797
+ name: resolved
1798
+ };
1799
+ }
1800
+ case "BinaryExpr": {
1801
+ const left = rewriteAstForCompile(node.left, scope);
1802
+ const right = rewriteAstForCompile(node.right, scope);
1803
+ return left === node.left && right === node.right ? node : {
1804
+ ...node,
1805
+ left,
1806
+ right
1807
+ };
1808
+ }
1809
+ case "UnaryExpr": {
1810
+ const argument = rewriteAstForCompile(node.argument, scope);
1811
+ return argument === node.argument ? node : {
1812
+ ...node,
1813
+ argument
1814
+ };
1815
+ }
1816
+ case "ConditionalExpr": {
1817
+ const test = rewriteAstForCompile(node.test, scope);
1818
+ const consequent = rewriteAstForCompile(node.consequent, scope);
1819
+ const alternate = rewriteAstForCompile(node.alternate, scope);
1820
+ return test === node.test && consequent === node.consequent && alternate === node.alternate ? node : {
1821
+ ...node,
1822
+ test,
1823
+ consequent,
1824
+ alternate
1825
+ };
1826
+ }
1827
+ case "MemberExpr": {
1828
+ const object = rewriteAstForCompile(node.object, scope);
1829
+ const property = node.computed ? rewriteAstForCompile(node.property, scope) : node.property;
1830
+ return object === node.object && property === node.property ? node : {
1831
+ ...node,
1832
+ object,
1833
+ property
1834
+ };
1835
+ }
1836
+ case "CallExpr": {
1837
+ const callee = rewriteAstForCompile(node.callee, scope);
1838
+ const arguments_ = mapAstNodes(node.arguments, (arg) => rewriteAstForCompile(arg, scope));
1839
+ return callee === node.callee && arguments_ === node.arguments ? node : {
1840
+ ...node,
1841
+ callee,
1842
+ arguments: arguments_
1843
+ };
1844
+ }
1845
+ case "ArrayExpr": {
1846
+ const elements = mapAstNodes(node.elements, (el) => rewriteAstForCompile(el, scope));
1847
+ return elements === node.elements ? node : {
1848
+ ...node,
1849
+ elements
1850
+ };
1851
+ }
1852
+ case "ObjectExpr": {
1853
+ const properties = mapObjectProperties(node.properties, (prop) => {
1854
+ const key = prop.computed ? rewriteAstForCompile(prop.key, scope) : prop.key;
1855
+ const value = rewriteAstForCompile(prop.value, scope);
1856
+ return key === prop.key && value === prop.value ? prop : {
1857
+ ...prop,
1858
+ key,
1859
+ value
1860
+ };
1861
+ });
1862
+ return properties === node.properties ? node : {
1863
+ ...node,
1864
+ properties
1865
+ };
1866
+ }
1867
+ case "ArrowFunctionExpr": return node;
1868
+ default: return node;
1869
+ }
1870
+ }
1871
+ function resolveIdentifierName(name, scope) {
1872
+ if (/^\$\[\d+\]$/.test(name) || /^_\d+$/.test(name)) return name;
1873
+ const deferredIdx = scope.deferredIndexMap?.get(name);
1874
+ if (deferredIdx !== void 0) return `$[${deferredIdx}]`;
1875
+ if (scope.deferredAsts?.has(name) && scope.onDeferredReference) return `$[${scope.onDeferredReference(name)}]`;
1876
+ if (!ALLOWED_GLOBALS.has(name)) scope.undefinedVars?.push(name);
1877
+ return name;
1878
+ }
1879
+ function extendShadowedNames(shadowedNames, names) {
1880
+ if (names.length === 0) return shadowedNames;
1881
+ return new Set([...shadowedNames ?? [], ...names]);
1882
+ }
1883
+ function mapAstNodes(nodes, transform) {
1884
+ let result;
1885
+ for (let i = 0; i < nodes.length; i++) {
1886
+ const node = nodes[i];
1887
+ const transformed = transform(node);
1888
+ if (result) {
1889
+ result.push(transformed);
1890
+ continue;
1891
+ }
1892
+ if (transformed !== node) {
1893
+ result = nodes.slice(0, i);
1894
+ result.push(transformed);
1895
+ }
1896
+ }
1897
+ return result ?? nodes;
1898
+ }
1899
+ function mapObjectProperties(properties, transform) {
1900
+ let result;
1901
+ for (let i = 0; i < properties.length; i++) {
1902
+ const property = properties[i];
1903
+ const transformed = transform(property);
1904
+ if (result) {
1905
+ result.push(transformed);
1906
+ continue;
1907
+ }
1908
+ if (transformed !== property) {
1909
+ result = properties.slice(0, i);
1910
+ result.push(transformed);
1911
+ }
1912
+ }
1913
+ return result ?? properties;
1914
+ }
1726
1915
  /**
1727
1916
  * 提取 AST 中所有 ArrowFunctionExpr 节点,编译为 FnNode,
1728
1917
  * 并将原始位置替换为 $[N] 标识符引用。
1729
1918
  */
1730
- function extractAndCompileArrowFunctions(node, ctx) {
1731
- switch (node.type) {
1732
- case "ArrowFunctionExpr": return {
1919
+ function compileAst(node, ctx, scope) {
1920
+ if (node.type === "BinaryExpr" && (node.operator === "||" || node.operator === "&&" || node.operator === "??")) return compileShortCircuit(node, ctx, scope);
1921
+ if (node.type === "ConditionalExpr") return compileConditional(node, ctx, scope);
1922
+ if (node.type === "ArrowFunctionExpr") return compileArrowFunction(node, ctx, scope);
1923
+ const exprStr = generate(node, { rewriteNode: (current) => {
1924
+ if (current.type !== "ArrowFunctionExpr") return current;
1925
+ return {
1733
1926
  type: "Identifier",
1734
- name: `$[${compileArrowFunction(node, ctx)}]`
1735
- };
1736
- case "BinaryExpr": return {
1737
- ...node,
1738
- left: extractAndCompileArrowFunctions(node.left, ctx),
1739
- right: extractAndCompileArrowFunctions(node.right, ctx)
1740
- };
1741
- case "UnaryExpr": return {
1742
- ...node,
1743
- argument: extractAndCompileArrowFunctions(node.argument, ctx)
1744
- };
1745
- case "ConditionalExpr": return {
1746
- ...node,
1747
- test: extractAndCompileArrowFunctions(node.test, ctx),
1748
- consequent: extractAndCompileArrowFunctions(node.consequent, ctx),
1749
- alternate: extractAndCompileArrowFunctions(node.alternate, ctx)
1750
- };
1751
- case "MemberExpr": return {
1752
- ...node,
1753
- object: extractAndCompileArrowFunctions(node.object, ctx),
1754
- property: node.computed ? extractAndCompileArrowFunctions(node.property, ctx) : node.property
1927
+ name: `$[${compileArrowFunction(current, ctx, scope)}]`
1755
1928
  };
1756
- case "CallExpr": return {
1757
- ...node,
1758
- callee: extractAndCompileArrowFunctions(node.callee, ctx),
1759
- arguments: node.arguments.map((arg) => extractAndCompileArrowFunctions(arg, ctx))
1760
- };
1761
- case "ArrayExpr": return {
1762
- ...node,
1763
- elements: node.elements.map((el) => extractAndCompileArrowFunctions(el, ctx))
1764
- };
1765
- case "ObjectExpr": return {
1766
- ...node,
1767
- properties: node.properties.map((prop) => ({
1768
- ...prop,
1769
- key: prop.computed ? extractAndCompileArrowFunctions(prop.key, ctx) : prop.key,
1770
- value: extractAndCompileArrowFunctions(prop.value, ctx)
1771
- }))
1772
- };
1773
- default: return node;
1774
- }
1775
- }
1776
- function compileAst(node, ctx) {
1777
- if (node.type === "BinaryExpr" && (node.operator === "||" || node.operator === "&&" || node.operator === "??")) return compileShortCircuit(node, ctx);
1778
- if (node.type === "ConditionalExpr") return compileConditional(node, ctx);
1779
- if (node.type === "ArrowFunctionExpr") return compileArrowFunction(node, ctx);
1780
- const exprStr = generate(extractAndCompileArrowFunctions(node, ctx));
1929
+ } });
1781
1930
  currentExprs(ctx).push(exprStr);
1782
1931
  return ctx.nextIndex++;
1783
1932
  }
1784
- function compileShortCircuit(node, ctx) {
1933
+ function compileShortCircuit(node, ctx, scope) {
1785
1934
  const exprs = currentExprs(ctx);
1786
- const leftIdx = compileAst(node.left, ctx);
1935
+ const leftIdx = compileAst(node.left, ctx, scope);
1787
1936
  const condition = BRANCH_CONDITIONS[node.operator]?.(leftIdx);
1788
1937
  if (!condition) throw new Error(`Unexpected operator: ${node.operator}`);
1789
1938
  const branchIdx = exprs.length;
@@ -1793,16 +1942,16 @@ function compileShortCircuit(node, ctx) {
1793
1942
  0
1794
1943
  ]);
1795
1944
  ctx.nextIndex++;
1796
- compileAst(node.right, ctx);
1945
+ compileAst(node.right, ctx, scope);
1797
1946
  const skipCount = exprs.length - branchIdx - 1;
1798
1947
  exprs[branchIdx][2] = skipCount;
1799
1948
  const phiIdx = ctx.nextIndex++;
1800
1949
  exprs.push(["phi"]);
1801
1950
  return phiIdx;
1802
1951
  }
1803
- function compileConditional(node, ctx) {
1952
+ function compileConditional(node, ctx, scope) {
1804
1953
  const exprs = currentExprs(ctx);
1805
- const testIdx = compileAst(node.test, ctx);
1954
+ const testIdx = compileAst(node.test, ctx, scope);
1806
1955
  const branchIdx = exprs.length;
1807
1956
  exprs.push([
1808
1957
  "br",
@@ -1810,11 +1959,11 @@ function compileConditional(node, ctx) {
1810
1959
  0
1811
1960
  ]);
1812
1961
  ctx.nextIndex++;
1813
- compileAst(node.alternate, ctx);
1962
+ compileAst(node.alternate, ctx, scope);
1814
1963
  const jmpIdx = exprs.length;
1815
1964
  exprs.push(["jmp", 0]);
1816
1965
  ctx.nextIndex++;
1817
- compileAst(node.consequent, ctx);
1966
+ compileAst(node.consequent, ctx, scope);
1818
1967
  const thenEndIdx = exprs.length;
1819
1968
  exprs[branchIdx][2] = jmpIdx - branchIdx;
1820
1969
  exprs[jmpIdx][1] = thenEndIdx - jmpIdx - 1;
@@ -1822,14 +1971,21 @@ function compileConditional(node, ctx) {
1822
1971
  exprs.push(["phi"]);
1823
1972
  return phiIdx;
1824
1973
  }
1825
- function compileArrowFunction(node, ctx) {
1974
+ function compileArrowFunction(node, ctx, scope) {
1826
1975
  const fnIndex = ctx.nextIndex++;
1827
- const paramMapping = /* @__PURE__ */ new Map();
1828
- for (const param of node.params) if (param.type === "Placeholder") paramMapping.set(param.id, `_${ctx.nextParamIndex++}`);
1829
- const transformedBody = transformPlaceholders(node.body, (id) => paramMapping.get(id) ?? null);
1976
+ const placeholderNames = new Map(scope.placeholderNames);
1977
+ const shadowedNames = [];
1978
+ for (const param of node.params) if (param.type === "Placeholder") placeholderNames.set(param.id, `_${ctx.nextParamIndex++}`);
1979
+ else shadowedNames.push(param.name);
1980
+ const lambdaScope = {
1981
+ ...scope,
1982
+ placeholderNames,
1983
+ shadowedNames: extendShadowedNames(scope.shadowedNames, shadowedNames)
1984
+ };
1985
+ const transformedBody = rewriteAstForCompile(node.body, lambdaScope);
1830
1986
  const lambdaStmts = [];
1831
1987
  ctx.expressionStack.push(lambdaStmts);
1832
- compileAst(transformedBody, ctx);
1988
+ compileAst(transformedBody, ctx, lambdaScope);
1833
1989
  ctx.expressionStack.pop();
1834
1990
  const fnNode = [
1835
1991
  "fn",
@@ -1843,9 +1999,40 @@ function compileArrowFunction(node, ctx) {
1843
1999
  //#endregion
1844
2000
  //#region src/core/evaluate.ts
1845
2001
  /**
1846
- * 缓存已构造的求值函数,以提升重复执行性能
2002
+ * 一级缓存:WeakMap - 使用 CompiledData 对象引用作为 key
2003
+ * 优势:O(1) 查找,无序列化开销,自动 GC
2004
+ * 适用:相同引用被重复使用的场景
2005
+ */
2006
+ const weakEvaluatorCache = /* @__PURE__ */ new WeakMap();
2007
+ /**
2008
+ * 二级缓存:LRU Map - 使用编译数据指纹作为 key
2009
+ * 优势:支持内容相同但引用不同的对象,有大小限制避免内存泄漏
2010
+ * 适用:CompiledData 被频繁重建但内容重复的场景
1847
2011
  */
1848
- const evaluatorCache = /* @__PURE__ */ new Map();
2012
+ const MAX_LRU_CACHE_SIZE = 128;
2013
+ const lruEvaluatorCache = /* @__PURE__ */ new Map();
2014
+ /**
2015
+ * 从 LRU 缓存获取求值函数,并将命中项移至末尾(最近使用)
2016
+ */
2017
+ function getFromLruCache(key) {
2018
+ const evaluator = lruEvaluatorCache.get(key);
2019
+ if (evaluator) {
2020
+ lruEvaluatorCache.delete(key);
2021
+ lruEvaluatorCache.set(key, evaluator);
2022
+ }
2023
+ return evaluator;
2024
+ }
2025
+ /**
2026
+ * 向 LRU 缓存写入求值函数,超出限制时淘汰最久未使用的项
2027
+ */
2028
+ function setToLruCache(key, evaluator) {
2029
+ if (lruEvaluatorCache.has(key)) lruEvaluatorCache.delete(key);
2030
+ else if (lruEvaluatorCache.size >= MAX_LRU_CACHE_SIZE) {
2031
+ const firstKey = lruEvaluatorCache.keys().next().value;
2032
+ if (firstKey !== void 0) lruEvaluatorCache.delete(firstKey);
2033
+ }
2034
+ lruEvaluatorCache.set(key, evaluator);
2035
+ }
1849
2036
  /**
1850
2037
  * 判断是否是 FnNode
1851
2038
  */
@@ -2006,21 +2193,29 @@ function translateControlFlow(expressions, variableCount) {
2006
2193
  * ```
2007
2194
  */
2008
2195
  function evaluate(data, values) {
2009
- if (data.length < 1) throw new Error("Invalid compiled data: must have at least variable names");
2010
- const [variableNames, ...expressions] = data;
2011
- if (!Array.isArray(variableNames)) throw new Error("Invalid compiled data: first element must be variable names array");
2012
- for (const varName of variableNames) {
2013
- if (typeof varName !== "string") throw new Error("Invalid compiled data: variable names must be strings");
2014
- if (!(varName in values)) throw new Error(`Missing required variable: ${varName}`);
2196
+ let evaluator = weakEvaluatorCache.get(data);
2197
+ let variableNames;
2198
+ if (evaluator) variableNames = data[0];
2199
+ else {
2200
+ if (data.length < 1) throw new Error("Invalid compiled data: must have at least variable names");
2201
+ variableNames = data[0];
2202
+ if (!Array.isArray(variableNames)) throw new Error("Invalid compiled data: first element must be variable names array");
2203
+ for (let i = 0; i < variableNames.length; i++) if (typeof variableNames[i] !== "string") throw new Error("Invalid compiled data: variable names must be strings");
2204
+ const cacheKey = JSON.stringify(data);
2205
+ evaluator = getFromLruCache(cacheKey);
2206
+ if (!evaluator) {
2207
+ const functionBody = buildEvaluatorFunctionBody(data.slice(1), variableNames.length);
2208
+ evaluator = new Function("$", functionBody);
2209
+ setToLruCache(cacheKey, evaluator);
2210
+ }
2211
+ weakEvaluatorCache.set(data, evaluator);
2015
2212
  }
2213
+ const len = variableNames.length;
2016
2214
  const valueArray = [];
2017
- for (const varName of variableNames) valueArray.push(values[varName]);
2018
- const cacheKey = JSON.stringify(data);
2019
- let evaluator = evaluatorCache.get(cacheKey);
2020
- if (!evaluator) {
2021
- const functionBody = buildEvaluatorFunctionBody(expressions, variableNames.length);
2022
- evaluator = new Function("$", functionBody);
2023
- evaluatorCache.set(cacheKey, evaluator);
2215
+ for (let i = 0; i < len; i++) {
2216
+ const name = variableNames[i];
2217
+ if (!(name in values)) throw new Error(`Missing required variable: ${name}`);
2218
+ valueArray.push(values[name]);
2024
2219
  }
2025
2220
  try {
2026
2221
  return evaluator(valueArray);