mikel-eval 0.21.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 +212 -0
- package/index.js +328 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# mikel-eval
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
The `mikel-eval` plugin extends the [mikel](https://github.com/jmjuanes/mikel) templating engine with expression evaluation capabilities. It allows you to evaluate mathematical, string, boolean, and array expressions directly within your templates.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
Install the plugin using **npm** or **yarn**:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# Using npm
|
|
14
|
+
npm install mikel-eval
|
|
15
|
+
|
|
16
|
+
# Using yarn
|
|
17
|
+
yarn add mikel-eval
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
Import and register the plugin with Mikel:
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
import mikel from "mikel";
|
|
26
|
+
import mikelEval from "mikel-eval";
|
|
27
|
+
|
|
28
|
+
// 1. create a new mikel instance for the given template
|
|
29
|
+
const m = mikel.create(`{{x}} * 2 = {{=eval "x * 2"}}`);
|
|
30
|
+
|
|
31
|
+
// 2. register the plugin
|
|
32
|
+
m.use(mikelEval());
|
|
33
|
+
|
|
34
|
+
// 3. render the template with the provided data context
|
|
35
|
+
console.log(m({x: 5})); // --> "5 * 2 = 10"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
You can also use this plugin with the default instance of Mikel:
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import mikel from "mikel";
|
|
42
|
+
import mikelEval from "mikel-eval";
|
|
43
|
+
|
|
44
|
+
// 1. get the options generated by mikelEval
|
|
45
|
+
const options = mikelEval();
|
|
46
|
+
|
|
47
|
+
// 2. render the template with the default instance of mikel
|
|
48
|
+
console.log(mikel(`{{=eval "1 + 2"}}`, {}, options)); // --> "3"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
Supported expressions:
|
|
54
|
+
|
|
55
|
+
- **Math:** `+`, `-`, `*`, `/`, `%`, `^`, and parentheses.
|
|
56
|
+
- **Strings:** Concatenation with `+` and string functions.
|
|
57
|
+
- **Booleans:** logical operators (`&&`, `||`, `!`) and comparisons (`==`, `!=`, `<`, `>`, `<=`, `>=`).
|
|
58
|
+
- **Arrays:** Array functions like `len`, `indexOf`, `join`, `in`.
|
|
59
|
+
- **Functions:** Built-in functions such as `abs`, `sqrt`, `max`, `min`, `replace`, `toUpperCase`, etc.
|
|
60
|
+
|
|
61
|
+
## Built-in Functions
|
|
62
|
+
|
|
63
|
+
The following functions are available by default in expressions evaluated by mikel-eval:
|
|
64
|
+
|
|
65
|
+
### Common Functions
|
|
66
|
+
|
|
67
|
+
#### `len(x)`
|
|
68
|
+
|
|
69
|
+
Returns the length of a string, array, or the number of keys in an object. Examples: `len([1,2,3])` → `3`, `len("abc")` → `3`.
|
|
70
|
+
|
|
71
|
+
#### `in(x, item)`
|
|
72
|
+
|
|
73
|
+
Checks if `item` is present in array, string, or object values. Examples: `in([1,2,3], 2)` → `true`, `in("hello", "ll")` → `true`.
|
|
74
|
+
|
|
75
|
+
#### `type(x)`
|
|
76
|
+
|
|
77
|
+
Returns the JavaScript type of the provided argument as a string. Examples: `type(123)` → `"number"`, `type("abc")` → `"string"`.
|
|
78
|
+
|
|
79
|
+
#### `if(condition, trueValue, falseValue)`
|
|
80
|
+
|
|
81
|
+
Returns `trueValue` if `condition` is `true`, otherwise returns `falseValue`. Example: `if(1 < 2, "yes", "no")` → `"yes"`.
|
|
82
|
+
|
|
83
|
+
### Array Functions
|
|
84
|
+
|
|
85
|
+
#### `indexOf(array, item)`
|
|
86
|
+
|
|
87
|
+
Returns the index of `item` in `array`, or `-1` if not found. Example: `indexOf([1,2,3], 2)` → `1`.
|
|
88
|
+
|
|
89
|
+
#### `join(array, separator)`
|
|
90
|
+
|
|
91
|
+
Joins array elements into a string, separated by `separator` (default: `","`). Example: `join([1,2,3], "-")` → `"1-2-3"`.
|
|
92
|
+
|
|
93
|
+
### Object Functions
|
|
94
|
+
|
|
95
|
+
#### `valueOf(obj, key)`
|
|
96
|
+
|
|
97
|
+
Returns the value of `obj[key]`. Example: `valueOf(myObject, "b")`.
|
|
98
|
+
|
|
99
|
+
### String Functions
|
|
100
|
+
|
|
101
|
+
#### `startsWith(str, prefix)`
|
|
102
|
+
|
|
103
|
+
Returns `true` if `str` starts with `prefix`. Example: `startsWith("hello", "he")` → `true`.
|
|
104
|
+
|
|
105
|
+
#### `endsWith(str, suffix)`
|
|
106
|
+
|
|
107
|
+
Returns `true` if `str` ends with `suffix`. Example: `endsWith("hello", "lo")` → `true`.
|
|
108
|
+
|
|
109
|
+
#### `replace(str, search, replacement)`
|
|
110
|
+
|
|
111
|
+
Replaces all occurrences of `search` in `str` with `replacement`. Example: `replace("foo bar", "bar", "baz")` → `"foo baz"`.
|
|
112
|
+
|
|
113
|
+
#### `toUpperCase(str)`
|
|
114
|
+
|
|
115
|
+
Converts `str` to uppercase. Example: `toUpperCase("abc")` → `"ABC"`.
|
|
116
|
+
|
|
117
|
+
#### `toLowerCase(str)`
|
|
118
|
+
|
|
119
|
+
Converts `str` to lowercase. Example: `toLowerCase("ABC")` → `"abc"`.
|
|
120
|
+
|
|
121
|
+
#### `trim(str)`
|
|
122
|
+
|
|
123
|
+
Removes whitespace from both ends of `str`. Example: `trim(" hello ")` → `"hello"`.
|
|
124
|
+
|
|
125
|
+
### Mathematical Functions
|
|
126
|
+
|
|
127
|
+
#### `min(...args)`
|
|
128
|
+
|
|
129
|
+
Returns the smallest of the provided numbers. Example: `min(1, 2, 3)` → `1`.
|
|
130
|
+
|
|
131
|
+
#### `max(...args)`
|
|
132
|
+
|
|
133
|
+
Returns the largest of the provided numbers. Example: `max(1, 2, 3)` → `3`.
|
|
134
|
+
|
|
135
|
+
#### `abs(x)`
|
|
136
|
+
|
|
137
|
+
Returns the absolute value of `x`. Example: `abs(-5)` → `5`.
|
|
138
|
+
|
|
139
|
+
#### `round(x)`
|
|
140
|
+
|
|
141
|
+
Rounds `x` to the nearest integer. Example: `round(2.5)` → `3`.
|
|
142
|
+
|
|
143
|
+
#### `ceil(x)`
|
|
144
|
+
|
|
145
|
+
Rounds `x` up to the next largest integer. Example: `ceil(2.1)` → `3`.
|
|
146
|
+
|
|
147
|
+
#### `floor(x)`
|
|
148
|
+
|
|
149
|
+
Rounds `x` down to the next smallest integer. Example: `floor(2.9)` → `2`.
|
|
150
|
+
|
|
151
|
+
#### `sqrt(x)`
|
|
152
|
+
|
|
153
|
+
Returns the square root of `x`. Example: `sqrt(16)` → `4`.
|
|
154
|
+
|
|
155
|
+
#### `pow(x, y)`
|
|
156
|
+
|
|
157
|
+
Returns `x` raised to the power of `y`. Example: `pow(2, 3)` → `8`.
|
|
158
|
+
|
|
159
|
+
#### `random()`
|
|
160
|
+
|
|
161
|
+
Returns a random number between 0 (inclusive) and 1 (exclusive). Example: `random()` → `0.123456...`.
|
|
162
|
+
|
|
163
|
+
## API
|
|
164
|
+
|
|
165
|
+
### `=eval` function
|
|
166
|
+
|
|
167
|
+
Evaluates the given expression and prints the result.
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
m(`{{=eval "1 + 2"}}`, {}); // Output: "3"
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
You can use variables from your data context:
|
|
174
|
+
|
|
175
|
+
```javascript
|
|
176
|
+
m(`{{=eval "'Hello ' + name"}}`, { name: "World" }); // Output: "Hello World"
|
|
177
|
+
m(`{{=eval "x * y"}}`, { x: 2, y: 3 }); // Output: "6"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### `#when` helper
|
|
181
|
+
|
|
182
|
+
Renders the block only if the evaluated expression is `true`.
|
|
183
|
+
|
|
184
|
+
```javascript
|
|
185
|
+
m(`{{#when "x > 1"}}x is greater than 1{{/when}}`, { x: 2 }); // Output: "x is greater than 1"
|
|
186
|
+
m(`{{#when "'foo' == 'bar'"}}This will not render{{/when}}`, {}); // Output: ""
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
You use variables from your data context:
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
m(`{{#when "x > 1"}}x is greater than 1{{/when}}`, { x: 2 }); // Output: "x is greater than 1"
|
|
193
|
+
m(`{{#when "x < 1"}}x is less than 1{{/when}}`, { x: 2 }); // Output: ""
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Custom Functions
|
|
197
|
+
|
|
198
|
+
You can provide your own functions via the plugin options:
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
const options = mikelEval({
|
|
202
|
+
functions: {
|
|
203
|
+
double: x => x * 2,
|
|
204
|
+
},
|
|
205
|
+
}));
|
|
206
|
+
|
|
207
|
+
console.log(mikel(`{{=eval "double(5)"}}`, {}, options)); // Output: "10"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
Licensed under the [MIT License](../../LICENSE).
|
package/index.js
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
// @description comparision variables
|
|
2
|
+
const comparationCharacters = ["=", "!", "<", ">", "&", "|"];
|
|
3
|
+
const comparationFunctions = {
|
|
4
|
+
"===": (a, b) => a === b,
|
|
5
|
+
"!==": (a, b) => a !== b,
|
|
6
|
+
"==": (a, b) => a === b,
|
|
7
|
+
"!=": (a, b) => a !== b,
|
|
8
|
+
"<=": (a, b) => a <= b,
|
|
9
|
+
">=": (a, b) => a >= b,
|
|
10
|
+
"<": (a, b) => a < b,
|
|
11
|
+
">": (a, b) => a > b,
|
|
12
|
+
"&&": (a, b) => a && b,
|
|
13
|
+
"||": (a, b) => a || b,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// @description: get a nested object value
|
|
17
|
+
const getIn = (c, p) => {
|
|
18
|
+
return (p === "." ? c : p.split(".").reduce((x, k) => x?.[k], c));
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// parse a next character
|
|
22
|
+
const nextChar = ctx => {
|
|
23
|
+
ctx.pos = ctx.pos + 1;
|
|
24
|
+
ctx.current = (ctx.pos < ctx.str.length) ? ctx.str.charAt(ctx.pos) : -1;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// check if the current character is the provided character
|
|
28
|
+
const checkChar = (ctx, ch) =>{
|
|
29
|
+
while (ctx.current === " ") {
|
|
30
|
+
nextChar(ctx);
|
|
31
|
+
}
|
|
32
|
+
// Check if the current character is the provided character
|
|
33
|
+
if (ctx.current === ch) {
|
|
34
|
+
nextChar(ctx);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// parse a expression: addition or substraaction
|
|
41
|
+
const parseExpression = ctx => {
|
|
42
|
+
let x = parseTerm(ctx);
|
|
43
|
+
for (;;) {
|
|
44
|
+
if (checkChar(ctx, "+")) {
|
|
45
|
+
x = x + parseTerm(ctx); // addition
|
|
46
|
+
}
|
|
47
|
+
else if (checkChar(ctx, "-")) {
|
|
48
|
+
x = x - parseTerm(ctx); // subtraction
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
return x;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// parse a term: multiplication or division
|
|
57
|
+
const parseTerm = ctx => {
|
|
58
|
+
let x = parseComparison(ctx);
|
|
59
|
+
for (;;) {
|
|
60
|
+
if (checkChar(ctx, "*")) {
|
|
61
|
+
x = x * parseComparison(ctx);
|
|
62
|
+
}
|
|
63
|
+
else if (checkChar(ctx, "/")) {
|
|
64
|
+
x = x / parseComparison(ctx);
|
|
65
|
+
}
|
|
66
|
+
else if (checkChar(ctx, "%")) {
|
|
67
|
+
x = x % parseComparison(ctx);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return x;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// parse a comparison
|
|
76
|
+
const parseComparison = ctx => {
|
|
77
|
+
let x = parseFactor(ctx);
|
|
78
|
+
for(;;) {
|
|
79
|
+
// save the current starting position
|
|
80
|
+
const startPos = ctx.pos;
|
|
81
|
+
if (comparationCharacters.indexOf(ctx.current) !== -1) {
|
|
82
|
+
while (comparationCharacters.indexOf(ctx.current) !== -1) {
|
|
83
|
+
nextChar(ctx);
|
|
84
|
+
}
|
|
85
|
+
// get the comparison operator
|
|
86
|
+
const comparator = ctx.str.substring(startPos, ctx.pos)
|
|
87
|
+
if (typeof comparationFunctions[comparator] !== "function") {
|
|
88
|
+
throw new Error(`Unknown operator '${comparator}'`);
|
|
89
|
+
}
|
|
90
|
+
x = comparationFunctions[comparator](x, parseFactor(ctx));
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
return x;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// parse a list of items
|
|
99
|
+
const parseList = ctx => {
|
|
100
|
+
const items = [];
|
|
101
|
+
do {
|
|
102
|
+
items.push(parseExpression(ctx));
|
|
103
|
+
}
|
|
104
|
+
while(checkChar(ctx, ","));
|
|
105
|
+
return items;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// parse a factor
|
|
109
|
+
const parseFactor = ctx => {
|
|
110
|
+
if (checkChar(ctx, "!")) {
|
|
111
|
+
return !parseFactor(ctx);
|
|
112
|
+
}
|
|
113
|
+
if (checkChar(ctx, "+")) {
|
|
114
|
+
return parseFactor(ctx);
|
|
115
|
+
}
|
|
116
|
+
if (checkChar(ctx, "-")) {
|
|
117
|
+
return -parseFactor(ctx);
|
|
118
|
+
}
|
|
119
|
+
let x;
|
|
120
|
+
const startPos = ctx.pos;
|
|
121
|
+
// subexpression
|
|
122
|
+
if (checkChar(ctx, "(")) {
|
|
123
|
+
x = parseExpression(ctx);
|
|
124
|
+
checkChar(ctx, ")");
|
|
125
|
+
}
|
|
126
|
+
else if (checkChar(ctx, "[")) {
|
|
127
|
+
x = parseList(ctx);
|
|
128
|
+
checkChar(ctx, "]");
|
|
129
|
+
}
|
|
130
|
+
// string factor
|
|
131
|
+
else if (checkChar(ctx, "'")) {
|
|
132
|
+
x = "";
|
|
133
|
+
while(ctx.current !== "'") {
|
|
134
|
+
x = x + ctx.current;
|
|
135
|
+
nextChar(ctx);
|
|
136
|
+
}
|
|
137
|
+
checkChar(ctx, "'");
|
|
138
|
+
}
|
|
139
|
+
// digit
|
|
140
|
+
else if ((ctx.current >= "0" && ctx.current <= "9") || ctx.current == ".") {
|
|
141
|
+
while ((ctx.current >= "0" && ctx.current <= "9") || ctx.current == ".") {
|
|
142
|
+
nextChar(ctx);
|
|
143
|
+
}
|
|
144
|
+
x = parseFloat(ctx.str.substring(startPos, ctx.pos));
|
|
145
|
+
}
|
|
146
|
+
// values functions
|
|
147
|
+
else if (ctx.current !== -1 && ctx.current.match(/^[A-Za-z]$/) !== null) {
|
|
148
|
+
while (ctx.current !== -1 && ctx.current.match(/^[a-zA-Z\[\]'"0-9\.\_]$/) !== null) {
|
|
149
|
+
nextChar(ctx);
|
|
150
|
+
}
|
|
151
|
+
let name = ctx.str.substring(startPos, ctx.pos);
|
|
152
|
+
if (name === "null" || name === "true" || name === "false") {
|
|
153
|
+
x = JSON.parse(name); // parse null, true or false
|
|
154
|
+
}
|
|
155
|
+
// check if there is a function to apply
|
|
156
|
+
else if (typeof ctx.functions[name] === "function") {
|
|
157
|
+
if (!checkChar(ctx, "(")) {
|
|
158
|
+
throw new Error(`Unexpected character '${ctx.current}' after function name '${name}'`);
|
|
159
|
+
}
|
|
160
|
+
const args = parseList(ctx);
|
|
161
|
+
if (!checkChar(ctx, ")")) {
|
|
162
|
+
throw new Error(`Unexpected character '${ctx.current}' after function arguments`);
|
|
163
|
+
}
|
|
164
|
+
// execute the function and save the value
|
|
165
|
+
x = ctx.functions[name].apply(null, args);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
x = getIn(ctx.values, name);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
throw new Error(`Unexpected character '${ctx.current}' at position ${ctx.pos}`);
|
|
173
|
+
}
|
|
174
|
+
// exponential operator ?
|
|
175
|
+
if (checkChar(ctx, "^")) {
|
|
176
|
+
x = Math.pow(x, parseFactor(ctx));
|
|
177
|
+
}
|
|
178
|
+
return x;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// @description default functions
|
|
182
|
+
const defaultFunctions = {
|
|
183
|
+
// common functions
|
|
184
|
+
len: x => {
|
|
185
|
+
if (Array.isArray(x) || typeof x === "string") {
|
|
186
|
+
return x.length;
|
|
187
|
+
} else if (typeof x === "object" && x !== null) {
|
|
188
|
+
return Object.keys(x).length;
|
|
189
|
+
}
|
|
190
|
+
throw new Error(`len() cannot be applied to type ${typeof x}`);
|
|
191
|
+
},
|
|
192
|
+
in: (x, item) => {
|
|
193
|
+
if (Array.isArray(x) || typeof x === "string") {
|
|
194
|
+
return x.includes(item);
|
|
195
|
+
}
|
|
196
|
+
else if (typeof x === "object" && x !== null) {
|
|
197
|
+
return Object.values(x).includes(item);
|
|
198
|
+
}
|
|
199
|
+
throw new Error(`in() cannot be applied to type ${typeof x}`);
|
|
200
|
+
},
|
|
201
|
+
type: x => typeof x,
|
|
202
|
+
if: (condition, trueValue, falseValue) => {
|
|
203
|
+
if (typeof condition !== "boolean") {
|
|
204
|
+
throw new Error(`if() expects a boolean condition, got ${typeof condition}`);
|
|
205
|
+
}
|
|
206
|
+
return condition ? trueValue : falseValue;
|
|
207
|
+
},
|
|
208
|
+
// array functions
|
|
209
|
+
indexOf: (array, item) => {
|
|
210
|
+
if (!Array.isArray(array)) {
|
|
211
|
+
throw new Error(`indexOf() expects an array, got ${typeof array}`);
|
|
212
|
+
}
|
|
213
|
+
return array.indexOf(item);
|
|
214
|
+
},
|
|
215
|
+
join: (array, separator = ",") => {
|
|
216
|
+
if (!Array.isArray(array)) {
|
|
217
|
+
throw new Error(`join() expects an array, got ${typeof array}`);
|
|
218
|
+
}
|
|
219
|
+
return array.join(separator);
|
|
220
|
+
},
|
|
221
|
+
// object functions
|
|
222
|
+
valueOf: (obj, key) => {
|
|
223
|
+
if (typeof obj !== "object" || obj === null) {
|
|
224
|
+
throw new Error(`valueOf() expects an object, got ${typeof obj}`);
|
|
225
|
+
}
|
|
226
|
+
return obj[key];
|
|
227
|
+
},
|
|
228
|
+
// string functions
|
|
229
|
+
startsWith: (str, prefix) => {
|
|
230
|
+
if (typeof str !== "string") {
|
|
231
|
+
throw new Error(`startsWith() expects a string, got ${typeof str}`);
|
|
232
|
+
}
|
|
233
|
+
return str.startsWith(prefix);
|
|
234
|
+
},
|
|
235
|
+
endsWith: (str, suffix) => {
|
|
236
|
+
if (typeof str !== "string") {
|
|
237
|
+
throw new Error(`endsWith() expects a string, got ${typeof str}`);
|
|
238
|
+
}
|
|
239
|
+
return str.endsWith(suffix);
|
|
240
|
+
},
|
|
241
|
+
replace: (str, search, replacement) => {
|
|
242
|
+
if (typeof str !== "string") {
|
|
243
|
+
throw new Error(`replace() expects a string, got ${typeof str}`);
|
|
244
|
+
}
|
|
245
|
+
return str.replace(new RegExp(search, "g"), replacement || "");
|
|
246
|
+
},
|
|
247
|
+
toUpperCase: str => {
|
|
248
|
+
if (typeof str !== "string") {
|
|
249
|
+
throw new Error(`toUpperCase() expects a string, got ${typeof str}`);
|
|
250
|
+
}
|
|
251
|
+
return str.toUpperCase();
|
|
252
|
+
},
|
|
253
|
+
toLowerCase: str => {
|
|
254
|
+
if (typeof str !== "string") {
|
|
255
|
+
throw new Error(`toLowerCase() expects a string, got ${typeof str}`);
|
|
256
|
+
}
|
|
257
|
+
return str.toLowerCase();
|
|
258
|
+
},
|
|
259
|
+
trim: str => {
|
|
260
|
+
if (typeof str !== "string") {
|
|
261
|
+
throw new Error(`trim() expects a string, got ${typeof str}`);
|
|
262
|
+
}
|
|
263
|
+
return str.trim();
|
|
264
|
+
},
|
|
265
|
+
// mathematical functions
|
|
266
|
+
min: (...args) => Math.min(...args),
|
|
267
|
+
max: (...args) => Math.max(...args),
|
|
268
|
+
abs: x => Math.abs(x),
|
|
269
|
+
round: x => Math.round(x),
|
|
270
|
+
ceil: x => Math.ceil(x),
|
|
271
|
+
floor: x => Math.floor(x),
|
|
272
|
+
sqrt: x => Math.sqrt(x),
|
|
273
|
+
pow: (x, y) => Math.pow(x, y),
|
|
274
|
+
random: () => Math.random(),
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// @description: evaluate a string expression
|
|
278
|
+
// @param str {string} the expression to evaluate
|
|
279
|
+
// @param options {object} the options to use
|
|
280
|
+
// @param options.values {object} context where the expression will be evaluated
|
|
281
|
+
// @param options.functions {object} functions to use in the expression
|
|
282
|
+
const evaluate = (str = "", options = {}) => {
|
|
283
|
+
const context = {
|
|
284
|
+
pos: 0,
|
|
285
|
+
current: str.charAt(0) || "",
|
|
286
|
+
str: str,
|
|
287
|
+
values: options.values || {},
|
|
288
|
+
functions: {
|
|
289
|
+
...defaultFunctions,
|
|
290
|
+
...(options.functions || {}),
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
const result = parseExpression(context);
|
|
294
|
+
if (context.pos < context.str.length) {
|
|
295
|
+
throw new Error(`Unexpected '${context.current}' at position ${context.pos}`);
|
|
296
|
+
}
|
|
297
|
+
return result;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// @description evaluate plugin
|
|
301
|
+
const evaluatePlugin = (options = {}) => {
|
|
302
|
+
return {
|
|
303
|
+
functions: {
|
|
304
|
+
eval: params => {
|
|
305
|
+
return evaluate(params.args[0], {
|
|
306
|
+
values: params.data,
|
|
307
|
+
functions: options.functions,
|
|
308
|
+
});
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
helpers: {
|
|
312
|
+
when: params => {
|
|
313
|
+
const condition = evaluate(params.args[0], {
|
|
314
|
+
values: params.data,
|
|
315
|
+
functions: options.functions,
|
|
316
|
+
});
|
|
317
|
+
return !!condition ? params.fn(params.data) : "";
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// assign additional options for this plugin
|
|
324
|
+
evaluatePlugin.evaluate = evaluate;
|
|
325
|
+
evaluatePlugin.defaultFunctions = defaultFunctions;
|
|
326
|
+
|
|
327
|
+
// export the evaluate plugin
|
|
328
|
+
export default evaluatePlugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mikel-eval",
|
|
3
|
+
"description": "A mikel plugin for evaluating JavaScript expressions.",
|
|
4
|
+
"version": "0.21.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Josemi Juanes",
|
|
9
|
+
"email": "hello@josemi.xyz"
|
|
10
|
+
},
|
|
11
|
+
"repository": "https://github.com/jmjuanes/mikel",
|
|
12
|
+
"bugs": "https://github.com/jmjuanes/mikel/issues",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": "./index.js",
|
|
15
|
+
"./index.js": "./index.js",
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "node ./test.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"README.md",
|
|
23
|
+
"index.js"
|
|
24
|
+
],
|
|
25
|
+
"keywords": [
|
|
26
|
+
"mikel",
|
|
27
|
+
"plugin",
|
|
28
|
+
"eval",
|
|
29
|
+
"evaluate"
|
|
30
|
+
]
|
|
31
|
+
}
|