ecma-evaluator 2.0.9 → 2.1.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/README.md +11 -495
- package/README.zh-CN.md +11 -494
- package/dist/cjs/Evaluator.d.cts +26 -3
- package/dist/cjs/TemplateParser.d.cts +12 -0
- package/dist/cjs/index.cjs +468 -40
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/Evaluator.d.mts +26 -3
- package/dist/esm/TemplateParser.d.mts +12 -0
- package/dist/esm/index.mjs +468 -40
- package/dist/esm/index.mjs.map +1 -1
- package/package.json +77 -67
package/README.zh-CN.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://996.icu/#/zh_CN)
|
|
4
4
|
[](https://github.com/996icu/996.ICU/blob/master/LICENSE)
|
|
5
|
-

|
|
6
6
|
[](https://badge.fury.io/js/ecma-evaluator)
|
|
7
7
|
|
|
8
8
|
一个小巧、快速且**安全**的 JavaScript 表达式求值器,用于在沙箱环境中安全地执行表达式和模板字符串。
|
|
@@ -11,13 +11,9 @@
|
|
|
11
11
|
|
|
12
12
|
## 特性
|
|
13
13
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
- 🎯 **功能丰富** - 支持大多数 JS 表达式/函数
|
|
18
|
-
- 🔒 **不使用 eval()** - 不使用 `eval()` 或 `Function()` 构造函数
|
|
19
|
-
- 💪 **TypeScript 支持** - 包含 TypeScript 类型定义
|
|
20
|
-
- 📝 **模板字符串** - 使用 `{{ }}` 语法在模板字符串中执行表达式
|
|
14
|
+
- 沙箱环境执行,不使用 `eval()` 或 `Function()`
|
|
15
|
+
- 支持 JavaScript 表达式及 `{{ }}` 模板
|
|
16
|
+
- 内置 TypeScript 类型定义
|
|
21
17
|
|
|
22
18
|
## 安装
|
|
23
19
|
|
|
@@ -30,493 +26,14 @@ npm install ecma-evaluator
|
|
|
30
26
|
```js
|
|
31
27
|
import { evalExpression, evalTemplate } from "ecma-evaluator";
|
|
32
28
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
console.log(result); // 输出: 7
|
|
36
|
-
|
|
37
|
-
// 执行模板
|
|
38
|
-
const text = evalTemplate("Hello {{ name }}!", { name: "World" });
|
|
39
|
-
console.log(text); // 输出: "Hello World!"
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## API 参考
|
|
43
|
-
|
|
44
|
-
### `evalExpression(expression, context?)`
|
|
45
|
-
|
|
46
|
-
使用可选的上下文执行 JavaScript 表达式。
|
|
47
|
-
|
|
48
|
-
**参数:**
|
|
49
|
-
|
|
50
|
-
- `expression` (string): 要执行的 JavaScript 表达式
|
|
51
|
-
- `context` (object, 可选): 包含表达式中使用的变量的对象
|
|
52
|
-
|
|
53
|
-
**返回值:** 执行表达式的结果
|
|
54
|
-
|
|
55
|
-
**示例:**
|
|
56
|
-
|
|
57
|
-
```js
|
|
58
|
-
import { evalExpression } from "ecma-evaluator";
|
|
59
|
-
|
|
60
|
-
// 基础算术
|
|
61
|
-
evalExpression("2 + 3 * 4"); // 14
|
|
62
|
-
|
|
63
|
-
// 使用变量
|
|
64
|
-
evalExpression("x + y", { x: 10, y: 20 }); // 30
|
|
65
|
-
|
|
66
|
-
// 使用内置函数
|
|
67
|
-
evalExpression("Math.max(a, b, c)", { a: 5, b: 15, c: 10 }); // 15
|
|
68
|
-
|
|
69
|
-
// 字符串操作
|
|
70
|
-
evalExpression("greeting + ', ' + name", {
|
|
71
|
-
greeting: "Hello",
|
|
72
|
-
name: "Alice",
|
|
73
|
-
}); // "Hello, Alice"
|
|
74
|
-
|
|
75
|
-
// 数组方法
|
|
76
|
-
evalExpression("[1, 2, 3].map(x => x * 2)"); // [2, 4, 6]
|
|
77
|
-
|
|
78
|
-
// 条件表达式
|
|
79
|
-
evalExpression("score >= 60 ? 'Pass' : 'Fail'", { score: 75 }); // "Pass"
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### `evalTemplate(template, context?, templateParserOptions?)`
|
|
83
|
-
|
|
84
|
-
通过将 `{{ expression }}` 模式替换为其执行后的值来执行模板字符串。
|
|
85
|
-
|
|
86
|
-
**参数:**
|
|
87
|
-
|
|
88
|
-
- `template` (string): 要执行的模板字符串
|
|
89
|
-
- `context` (object, 可选): 包含模板中使用的变量的对象
|
|
90
|
-
- `templateParserOptions` (object, 可选): 模板解析器的选项
|
|
91
|
-
|
|
92
|
-
**返回值:** 执行后的模板字符串
|
|
93
|
-
|
|
94
|
-
**示例:**
|
|
95
|
-
|
|
96
|
-
```js
|
|
97
|
-
import { evalTemplate } from "ecma-evaluator";
|
|
98
|
-
|
|
99
|
-
// 基础变量替换
|
|
100
|
-
evalTemplate("Hello, {{ name }}!", { name: "World" });
|
|
101
|
-
// 输出: "Hello, World!"
|
|
102
|
-
|
|
103
|
-
// 多个表达式
|
|
104
|
-
evalTemplate("{{ a }} + {{ b }} = {{ a + b }}", { a: 10, b: 20 });
|
|
105
|
-
// 输出: "10 + 20 = 30"
|
|
106
|
-
|
|
107
|
-
// 复杂表达式
|
|
108
|
-
evalTemplate("The sum is {{ [1, 2, 3].reduce((a, b) => a + b, 0) }}");
|
|
109
|
-
// 输出: "The sum is 6"
|
|
110
|
-
|
|
111
|
-
// 表达式内的模板字面量
|
|
112
|
-
evalTemplate("{{ `Hello ${name}, welcome!` }}", { name: "Alice" });
|
|
113
|
-
// 输出: "Hello Alice, welcome!"
|
|
114
|
-
|
|
115
|
-
// 日期格式化
|
|
116
|
-
evalTemplate("Today is {{ new Date().toLocaleDateString() }}");
|
|
117
|
-
// 输出: "Today is 11/18/2025" (根据语言环境而异)
|
|
118
|
-
|
|
119
|
-
// 条件渲染
|
|
120
|
-
evalTemplate("Status: {{ isActive ? 'Active' : 'Inactive' }}", { isActive: true });
|
|
121
|
-
// 输出: "Status: Active"
|
|
122
|
-
|
|
123
|
-
// 可选链
|
|
124
|
-
evalTemplate("Value: {{ obj?.prop?.value ?? 'N/A' }}", { obj: null });
|
|
125
|
-
// 输出: "Value: N/A"
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### 错误处理
|
|
129
|
-
|
|
130
|
-
当在模板中引用未定义的变量时,它将被替换为 `"undefined"` 而不是抛出错误:
|
|
131
|
-
|
|
132
|
-
```js
|
|
133
|
-
evalTemplate("Hello {{ name }}!", {}); // "Hello undefined!"
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
对于其他错误(语法错误、类型错误等),将抛出异常:
|
|
137
|
-
|
|
138
|
-
```js
|
|
139
|
-
evalTemplate("{{ 1 + }}", {}); // 抛出 SyntaxError
|
|
140
|
-
evalTemplate("{{ obj.prop }}", { obj: null }); // 抛出 TypeError
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
## 支持的 JavaScript 特性
|
|
144
|
-
|
|
145
|
-
### 运算符
|
|
146
|
-
|
|
147
|
-
#### 算术运算符
|
|
148
|
-
|
|
149
|
-
```js
|
|
150
|
-
evalExpression("10 + 5"); // 15 (加法)
|
|
151
|
-
evalExpression("10 - 5"); // 5 (减法)
|
|
152
|
-
evalExpression("10 * 5"); // 50 (乘法)
|
|
153
|
-
evalExpression("10 / 5"); // 2 (除法)
|
|
154
|
-
evalExpression("10 % 3"); // 1 (取模)
|
|
155
|
-
evalExpression("2 ** 3"); // 8 (幂运算)
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
#### 比较运算符
|
|
159
|
-
|
|
160
|
-
```js
|
|
161
|
-
evalExpression("5 > 3"); // true
|
|
162
|
-
evalExpression("5 >= 5"); // true
|
|
163
|
-
evalExpression("5 < 3"); // false
|
|
164
|
-
evalExpression("5 <= 5"); // true
|
|
165
|
-
evalExpression("5 == '5'"); // true (宽松相等)
|
|
166
|
-
evalExpression("5 === '5'"); // false (严格相等)
|
|
167
|
-
evalExpression("5 != '5'"); // false (宽松不等)
|
|
168
|
-
evalExpression("5 !== '5'"); // true (严格不等)
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
#### 逻辑运算符
|
|
172
|
-
|
|
173
|
-
```js
|
|
174
|
-
evalExpression("true && false"); // false (AND)
|
|
175
|
-
evalExpression("true || false"); // true (OR)
|
|
176
|
-
evalExpression("null ?? 'default'"); // "default" (空值合并)
|
|
177
|
-
evalExpression("!true"); // false (NOT)
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
#### 位运算符
|
|
181
|
-
|
|
182
|
-
```js
|
|
183
|
-
evalExpression("5 & 3"); // 1 (AND)
|
|
184
|
-
evalExpression("5 | 3"); // 7 (OR)
|
|
185
|
-
evalExpression("5 ^ 3"); // 6 (XOR)
|
|
186
|
-
evalExpression("~5"); // -6 (NOT)
|
|
187
|
-
evalExpression("5 << 1"); // 10 (左移)
|
|
188
|
-
evalExpression("5 >> 1"); // 2 (右移)
|
|
189
|
-
evalExpression("5 >>> 1"); // 2 (无符号右移)
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
#### 一元运算符
|
|
193
|
-
|
|
194
|
-
```js
|
|
195
|
-
evalExpression("-5"); // -5 (取负)
|
|
196
|
-
evalExpression("+5"); // 5 (一元加)
|
|
197
|
-
evalExpression("typeof 5"); // "number"
|
|
198
|
-
evalExpression("void 0"); // undefined
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### 数据类型
|
|
202
|
-
|
|
203
|
-
#### 字面量
|
|
204
|
-
|
|
205
|
-
```js
|
|
206
|
-
evalExpression("42"); // Number
|
|
207
|
-
evalExpression("'hello'"); // String
|
|
208
|
-
evalExpression("true"); // Boolean
|
|
209
|
-
evalExpression("null"); // null
|
|
210
|
-
evalExpression("undefined"); // undefined
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
#### 数组
|
|
214
|
-
|
|
215
|
-
```js
|
|
216
|
-
evalExpression("[1, 2, 3]"); // [1, 2, 3]
|
|
217
|
-
evalExpression("[1, 2, 3][1]"); // 2
|
|
218
|
-
evalExpression("[1, 2, 3].length"); // 3
|
|
219
|
-
evalExpression("[1, 2, 3].map(x => x * 2)"); // [2, 4, 6]
|
|
220
|
-
evalExpression("[1, 2, 3].filter(x => x > 1)"); // [2, 3]
|
|
221
|
-
evalExpression("[1, 2, 3].reduce((a, b) => a + b, 0)"); // 6
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
#### 对象
|
|
225
|
-
|
|
226
|
-
```js
|
|
227
|
-
evalExpression("{ a: 1, b: 2 }"); // { a: 1, b: 2 }
|
|
228
|
-
evalExpression("({ a: 1, b: 2 }).a"); // 1
|
|
229
|
-
evalExpression("({ a: 1, b: 2 })['b']"); // 2
|
|
29
|
+
evalExpression("a + b * c", { a: 1, b: 2, c: 3 }); // 7
|
|
30
|
+
evalTemplate("你好,{{ name }}!", { name: "世界" }); // "你好,世界!"
|
|
230
31
|
```
|
|
231
32
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
```js
|
|
235
|
-
evalExpression("`Hello ${'World'}`"); // "Hello World"
|
|
236
|
-
evalExpression("`2 + 2 = ${2 + 2}`", {}); // "2 + 2 = 4"
|
|
237
|
-
evalExpression("`Hello ${name}`", { name: "Bob" }); // "Hello Bob"
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### 函数
|
|
241
|
-
|
|
242
|
-
#### 箭头函数
|
|
243
|
-
|
|
244
|
-
```js
|
|
245
|
-
evalExpression("((x) => x * 2)(5)"); // 10
|
|
246
|
-
evalExpression("[1, 2, 3].map(x => x * 2)"); // [2, 4, 6]
|
|
247
|
-
evalExpression("((a, b) => a + b)(3, 4)"); // 7
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
#### 内置对象和函数
|
|
251
|
-
|
|
252
|
-
```js
|
|
253
|
-
// Math
|
|
254
|
-
evalExpression("Math.max(1, 2, 3)"); // 3
|
|
255
|
-
evalExpression("Math.min(1, 2, 3)"); // 1
|
|
256
|
-
evalExpression("Math.round(4.7)"); // 5
|
|
257
|
-
evalExpression("Math.floor(4.7)"); // 4
|
|
258
|
-
evalExpression("Math.ceil(4.3)"); // 5
|
|
259
|
-
evalExpression("Math.abs(-5)"); // 5
|
|
260
|
-
evalExpression("Math.sqrt(16)"); // 4
|
|
261
|
-
|
|
262
|
-
// 字符串方法
|
|
263
|
-
evalExpression("'hello'.toUpperCase()"); // "HELLO"
|
|
264
|
-
evalExpression("'HELLO'.toLowerCase()"); // "hello"
|
|
265
|
-
evalExpression("'hello world'.split(' ')"); // ["hello", "world"]
|
|
266
|
-
|
|
267
|
-
// 数组方法(仅非变更方法)
|
|
268
|
-
evalExpression("[1,2,3].join(', ')"); // "1, 2, 3"
|
|
269
|
-
evalExpression("[1,2,3].slice(1)"); // [2, 3]
|
|
270
|
-
evalExpression("[1,2,3].concat([4,5])"); // [1, 2, 3, 4, 5]
|
|
271
|
-
|
|
272
|
-
// JSON
|
|
273
|
-
evalExpression("JSON.stringify({ a: 1 })"); // '{"a":1}'
|
|
274
|
-
evalExpression("JSON.parse('{\"a\":1}')"); // { a: 1 }
|
|
275
|
-
|
|
276
|
-
// Date
|
|
277
|
-
evalExpression("new Date(0).getTime()"); // 0
|
|
278
|
-
evalExpression("new Date().getFullYear()"); // 当前年份
|
|
279
|
-
|
|
280
|
-
// Object 方法
|
|
281
|
-
evalExpression("Object.keys({ a: 1, b: 2 })"); // ["a", "b"]
|
|
282
|
-
evalExpression("Object.values({ a: 1, b: 2 })"); // [1, 2]
|
|
283
|
-
|
|
284
|
-
// Number 方法
|
|
285
|
-
evalExpression("Number.parseInt('42')"); // 42
|
|
286
|
-
evalExpression("Number.parseFloat('3.14')"); // 3.14
|
|
287
|
-
evalExpression("Number.isNaN(NaN)"); // true
|
|
288
|
-
evalExpression("Number.isFinite(42)"); // true
|
|
289
|
-
|
|
290
|
-
// 全局函数
|
|
291
|
-
evalExpression("isNaN(NaN)"); // true
|
|
292
|
-
evalExpression("isFinite(Infinity)"); // false
|
|
293
|
-
evalExpression("parseInt('42')"); // 42
|
|
294
|
-
evalExpression("parseFloat('3.14')"); // 3.14
|
|
295
|
-
evalExpression("encodeURIComponent('hello world')"); // "hello%20world"
|
|
296
|
-
evalExpression("decodeURIComponent('hello%20world')"); // "hello world"
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
### 高级特性
|
|
300
|
-
|
|
301
|
-
#### 条件(三元)运算符
|
|
302
|
-
|
|
303
|
-
```js
|
|
304
|
-
evalExpression("5 > 3 ? 'yes' : 'no'"); // "yes"
|
|
305
|
-
evalExpression("age >= 18 ? 'adult' : 'minor'", { age: 20 }); // "adult"
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
#### 可选链
|
|
309
|
-
|
|
310
|
-
```js
|
|
311
|
-
evalExpression("obj?.prop", { obj: null }); // undefined
|
|
312
|
-
evalExpression("obj?.prop?.value", { obj: {} }); // undefined
|
|
313
|
-
evalExpression("arr?.[0]", { arr: null }); // undefined
|
|
314
|
-
evalExpression("func?.()", { func: null }); // undefined
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
#### 成员访问
|
|
318
|
-
|
|
319
|
-
```js
|
|
320
|
-
evalExpression("obj.prop", { obj: { prop: 42 } }); // 42
|
|
321
|
-
evalExpression("obj['prop']", { obj: { prop: 42 } }); // 42
|
|
322
|
-
evalExpression("arr[0]", { arr: [1, 2, 3] }); // 1
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
#### 构造函数表达式
|
|
326
|
-
|
|
327
|
-
```js
|
|
328
|
-
evalExpression("new Date(2024, 0, 1)"); // Date 对象
|
|
329
|
-
evalExpression("new Array(1, 2, 3)"); // [1, 2, 3]
|
|
330
|
-
evalExpression("new Set([1, 2, 2, 3])"); // Set {1, 2, 3}
|
|
331
|
-
evalExpression("new Map([['a', 1], ['b', 2]])"); // Map {"a" => 1, "b" => 2}
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
## 安全特性
|
|
335
|
-
|
|
336
|
-
### 沙箱环境
|
|
337
|
-
|
|
338
|
-
`ecma-evaluator` 在具有多项安全特性的沙箱环境中运行表达式:
|
|
339
|
-
|
|
340
|
-
1. **无法访问 `eval()` 或 `Function()` 构造函数** - 防止动态代码执行
|
|
341
|
-
2. **阻止可变方法** - 阻止变更对象的方法以防止副作用:
|
|
342
|
-
|
|
343
|
-
- Array: `push`, `pop`, `shift`, `unshift`, `splice`, `reverse`, `sort`, `fill`, `copyWithin`
|
|
344
|
-
- Object: `freeze`, `defineProperty`, `defineProperties`, `preventExtensions`, `setPrototypeOf`, `assign`
|
|
345
|
-
- Set/Map: `add`, `set`, `delete`, `clear`
|
|
346
|
-
- Date: 所有 setter 方法 (`setDate`, `setFullYear`, 等)
|
|
347
|
-
- TypedArray: `set`, `fill`, `copyWithin`, `reverse`, `sort`
|
|
348
|
-
|
|
349
|
-
3. **无 `delete` 运算符** - `delete` 运算符被阻止,因为它是一个变更操作
|
|
350
|
-
4. **有限的全局作用域** - 只有安全的内置对象可用(Math、JSON、Array、Object 等)
|
|
351
|
-
5. **无文件系统或网络访问** - 无法访问 Node.js API 或执行 I/O 操作
|
|
352
|
-
6. **无法访问进程或全局变量** - 无法访问 `process`、`global`、`require` 等
|
|
353
|
-
7. **防止原型污染** - 阻止访问原型属性(例如,`__proto__`)
|
|
354
|
-
8. **上下文克隆** - 提供给求值器的上下文对象会被克隆以防止外部修改
|
|
355
|
-
|
|
356
|
-
### 安全的内置对象
|
|
357
|
-
|
|
358
|
-
以下内置对象在沙箱环境中可用:
|
|
359
|
-
|
|
360
|
-
- **数字与数学**: `Number`, `Math`, `Infinity`, `NaN`, `isNaN`, `isFinite`, `parseInt`, `parseFloat`
|
|
361
|
-
- **字符串**: `String`, `encodeURI`, `encodeURIComponent`, `decodeURI`, `decodeURIComponent`
|
|
362
|
-
- **数据结构**: `Array`, `Object`, `Set`, `WeakSet`, `Map`, `WeakMap`
|
|
363
|
-
- **日期与时间**: `Date`
|
|
364
|
-
- **JSON**: `JSON`
|
|
365
|
-
- **类型**: `Boolean`, `Symbol`, `BigInt`, `RegExp`
|
|
366
|
-
- **类型化数组**: `Int8Array`, `Uint8Array`, `Int16Array`, `Uint16Array`, `Int32Array`, `Uint32Array`, `Float32Array`, `Float64Array`, `BigInt64Array`, `BigUint64Array`
|
|
367
|
-
- **错误**: `Error`, `EvalError`, `RangeError`, `ReferenceError`, `SyntaxError`, `TypeError`, `URIError`
|
|
368
|
-
- **Promise**: `Promise`
|
|
369
|
-
|
|
370
|
-
### 错误预防
|
|
371
|
-
|
|
372
|
-
```js
|
|
373
|
-
// ❌ 这些将抛出错误:
|
|
374
|
-
evalExpression("arr.push(1)", { arr: [1, 2, 3] });
|
|
375
|
-
// Error: Array.prototype.push is not allow
|
|
376
|
-
|
|
377
|
-
evalExpression("new Function('return 1')");
|
|
378
|
-
// Error: Cannot use new with Function constructor
|
|
379
|
-
|
|
380
|
-
evalExpression("delete obj.prop", { obj: { prop: 1 } });
|
|
381
|
-
// Error: Delete operator is not allow
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
## 使用场景
|
|
385
|
-
|
|
386
|
-
### 配置与规则引擎
|
|
387
|
-
|
|
388
|
-
```js
|
|
389
|
-
// 执行业务规则
|
|
390
|
-
const rule = "age >= 18 && country === 'US'";
|
|
391
|
-
const isEligible = evalExpression(rule, { age: 25, country: "US" }); // true
|
|
392
|
-
|
|
393
|
-
// 动态定价
|
|
394
|
-
const priceFormula = "basePrice * (1 - discount / 100)";
|
|
395
|
-
const finalPrice = evalExpression(priceFormula, {
|
|
396
|
-
basePrice: 100,
|
|
397
|
-
discount: 20,
|
|
398
|
-
}); // 80
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
### 模板渲染
|
|
402
|
-
|
|
403
|
-
```js
|
|
404
|
-
// 邮件模板
|
|
405
|
-
const emailTemplate = `
|
|
406
|
-
Hello {{ user.name }},
|
|
407
|
-
|
|
408
|
-
Your order #{{ order.id }} has been {{ order.status }}.
|
|
409
|
-
Total: {{ order.total.toFixed(2) }}
|
|
410
|
-
|
|
411
|
-
Thank you for shopping with us!
|
|
412
|
-
`;
|
|
413
|
-
|
|
414
|
-
const email = evalTemplate(emailTemplate, {
|
|
415
|
-
user: { name: "John Doe" },
|
|
416
|
-
order: { id: 12345, status: "shipped", total: 99.99 },
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
// 动态内容
|
|
420
|
-
const greeting = evalTemplate("Good {{ hour < 12 ? 'morning' : hour < 18 ? 'afternoon' : 'evening' }}, {{ name }}!", {
|
|
421
|
-
hour: new Date().getHours(),
|
|
422
|
-
name: "Alice",
|
|
423
|
-
});
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
### 数据转换
|
|
427
|
-
|
|
428
|
-
```js
|
|
429
|
-
// 转换 API 响应
|
|
430
|
-
const transform = "data.items.filter(x => x.active).map(x => x.name)";
|
|
431
|
-
const result = evalExpression(transform, {
|
|
432
|
-
data: {
|
|
433
|
-
items: [
|
|
434
|
-
{ name: "Item 1", active: true },
|
|
435
|
-
{ name: "Item 2", active: false },
|
|
436
|
-
{ name: "Item 3", active: true },
|
|
437
|
-
],
|
|
438
|
-
},
|
|
439
|
-
}); // ["Item 1", "Item 3"]
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
### 表单验证
|
|
443
|
-
|
|
444
|
-
```js
|
|
445
|
-
// 条件验证
|
|
446
|
-
const validationRule = "email.includes('@') && password.length >= 8";
|
|
447
|
-
const isValid = evalExpression(validationRule, {
|
|
448
|
-
email: "user@example.com",
|
|
449
|
-
password: "secretpassword",
|
|
450
|
-
}); // true
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
## 限制
|
|
454
|
-
|
|
455
|
-
1. **无语句** - 仅支持表达式,不支持语句(无 `if`、`for`、`while` 等)
|
|
456
|
-
2. **无变量赋值** - 不能使用赋值运算符(`=`、`+=` 等)
|
|
457
|
-
3. **无可变操作** - 阻止可变的数组/对象方法
|
|
458
|
-
4. **无异步操作** - Promise 可以工作,但不能使用 `await`
|
|
459
|
-
5. **无函数声明** - 仅支持表达式中的箭头函数
|
|
460
|
-
6. **有限的错误恢复** - 语法错误将立即抛出
|
|
461
|
-
7. **无导入/require** - 无法导入外部模块
|
|
462
|
-
|
|
463
|
-
## 高级用法
|
|
464
|
-
|
|
465
|
-
### 自定义求值器实例
|
|
466
|
-
|
|
467
|
-
```js
|
|
468
|
-
import { Evaluator } from "ecma-evaluator";
|
|
469
|
-
|
|
470
|
-
// 创建具有固定上下文的可重用求值器
|
|
471
|
-
const evaluator = new Evaluator({ x: 10, y: 20 });
|
|
472
|
-
|
|
473
|
-
// 使用相同上下文执行多个表达式
|
|
474
|
-
console.log(evaluator.evaluate("x + y")); // 30
|
|
475
|
-
console.log(evaluator.evaluate("x * y")); // 200
|
|
476
|
-
console.log(evaluator.evaluate("Math.max(x, y)")); // 20
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
### 自定义模板解析器
|
|
480
|
-
|
|
481
|
-
```js
|
|
482
|
-
import { evalTemplate } from "ecma-evaluator";
|
|
483
|
-
|
|
484
|
-
evalTemplate(
|
|
485
|
-
"Hello ${ name }!",
|
|
486
|
-
{ name: "World" },
|
|
487
|
-
{
|
|
488
|
-
expressionStart: "${",
|
|
489
|
-
expressionEnd: "}",
|
|
490
|
-
preserveWhitespace: false,
|
|
491
|
-
}
|
|
492
|
-
);
|
|
493
|
-
// 输出: "Hello World!"
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
## 性能提示
|
|
497
|
-
|
|
498
|
-
1. **重用求值器实例** - 当使用相同上下文执行多个表达式时
|
|
499
|
-
2. **避免复杂的嵌套表达式** - 如果可能,将它们分解为更小的部分
|
|
500
|
-
3. **缓存解析的模板** - 如果多次渲染同一模板
|
|
501
|
-
4. **使用简单的变量访问** - 在可能的情况下,使用简单的变量访问而不是复杂的属性链
|
|
502
|
-
|
|
503
|
-
## TypeScript 支持
|
|
504
|
-
|
|
505
|
-
该包包含 TypeScript 类型定义。
|
|
506
|
-
|
|
507
|
-
## 兼容性
|
|
508
|
-
|
|
509
|
-
兼容 Nodejs 和主流浏览器环境。如需支持更老版本的环境,请使用 Babel 进行转译以及 polyfill。
|
|
510
|
-
|
|
511
|
-
```js
|
|
512
|
-
import 'core-js/actual/structured-clone.js';
|
|
513
|
-
import 'core-js/actual/object/has-own.js';
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
## 贡献
|
|
517
|
-
|
|
518
|
-
欢迎贡献!请随时提交 Pull Request。
|
|
33
|
+
## 文档
|
|
519
34
|
|
|
520
|
-
|
|
35
|
+
- [快速开始](/zh-CN/guide/getting-started)
|
|
36
|
+
- [API、支持的特性、安全性和使用说明](/zh-CN/guide/reference)
|
|
37
|
+
- [在线体验](https://axetroy.github.io/ecma-evaluator/zh-CN/#playground)
|
|
521
38
|
|
|
522
|
-
[
|
|
39
|
+
也可以访问[文档网站](https://axetroy.github.io/ecma-evaluator/zh-CN/)。
|
package/dist/cjs/Evaluator.d.cts
CHANGED
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
import { Node } from "acorn";
|
|
2
2
|
|
|
3
|
+
export interface EvaluatorOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Maximum expression length in UTF-16 code units (hard maximum: 100000).
|
|
6
|
+
* @default 10000
|
|
7
|
+
*/
|
|
8
|
+
maxInputLength?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Maximum AST node visits for a single evaluation (hard maximum: 100000).
|
|
11
|
+
* @default 10000
|
|
12
|
+
*/
|
|
13
|
+
maxEvaluationSteps?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Maximum interpreter recursion depth (hard maximum: 2000). Bounds nested
|
|
16
|
+
* expressions so a deeply nested input cannot exhaust the host call stack.
|
|
17
|
+
* @default 1000
|
|
18
|
+
*/
|
|
19
|
+
maxDepth?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
3
22
|
export declare class Evaluator {
|
|
4
|
-
constructor(variables
|
|
23
|
+
constructor(variables?: unknown, options?: EvaluatorOptions);
|
|
5
24
|
|
|
6
|
-
static evaluate<T = unknown>(expression: string, variables
|
|
25
|
+
static evaluate<T = unknown>(expression: string, variables?: unknown): T;
|
|
7
26
|
|
|
8
27
|
evaluate<T = unknown>(expression: string): T;
|
|
9
28
|
}
|
|
10
29
|
|
|
11
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Renders an AST node back to a short source-like string. Returns `null` for
|
|
32
|
+
* node types that have no textual representation.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getNodeString(node: Node): string | null;
|
|
@@ -12,6 +12,18 @@ export interface TemplateParserOptions {
|
|
|
12
12
|
preserveWhitespace?: boolean;
|
|
13
13
|
expressionStart?: string;
|
|
14
14
|
expressionEnd?: string;
|
|
15
|
+
/** Maximum template length in UTF-16 code units. @default 100000 */
|
|
16
|
+
maxTemplateLength?: number;
|
|
17
|
+
/** Maximum parsed token count. @default 1000 */
|
|
18
|
+
maxTokens?: number;
|
|
19
|
+
/** Maximum evaluated expressions in evalTemplate. @default 100 */
|
|
20
|
+
maxExpressions?: number;
|
|
21
|
+
/** Maximum expression length in evalTemplate. @default 10000 */
|
|
22
|
+
maxExpressionLength?: number;
|
|
23
|
+
/** Maximum AST node visits per expression in evalTemplate. @default 10000 */
|
|
24
|
+
maxEvaluationSteps?: number;
|
|
25
|
+
/** Maximum output length in UTF-16 code units. @default 100000 */
|
|
26
|
+
maxOutputLength?: number;
|
|
15
27
|
}
|
|
16
28
|
|
|
17
29
|
export class TemplateParser {
|