mikel 0.29.0 → 0.30.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 +61 -1
- package/index.js +58 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -592,7 +592,7 @@ console.log(result); // --> "My name is: John Doe"
|
|
|
592
592
|
|
|
593
593
|
#### Expand function arguments using the spread operator
|
|
594
594
|
|
|
595
|
-
> This feature was
|
|
595
|
+
> This feature was added in `v0.20.0`.
|
|
596
596
|
|
|
597
597
|
You can use the spread operator `...` to expand the arguments of a function. This allows you to pass an array of values as individual arguments to the function, or to pass an object as keyword arguments.
|
|
598
598
|
|
|
@@ -641,6 +641,66 @@ const result = m("Users: {{=fullName ...user1}} and {{=fullName ...user2}}", dat
|
|
|
641
641
|
console.log(result); // --> "Users: John Doe and Alice Smith"
|
|
642
642
|
```
|
|
643
643
|
|
|
644
|
+
Of course, Jose — here’s a version of the **Subexpressions** documentation written to perfectly match the tone, structure, and formatting conventions of the current README.
|
|
645
|
+
It follows the same patterns: short intro, version note, examples, concise explanations, no extra fluff.
|
|
646
|
+
|
|
647
|
+
### Subexpressions
|
|
648
|
+
|
|
649
|
+
> Added in `v0.30.0`.
|
|
650
|
+
|
|
651
|
+
Subexpressions allow you to evaluate a function call inside another function call. They are written using parentheses, and can be used anywhere a normal function argument is allowed. Example:
|
|
652
|
+
|
|
653
|
+
```hbs
|
|
654
|
+
{{=sum (sum 3 4) 3}}
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
In this example, the inner expression is evaluated first:
|
|
658
|
+
|
|
659
|
+
- `(sum 3 4)` → `7`
|
|
660
|
+
- `sum 7 3` → `10`
|
|
661
|
+
|
|
662
|
+
Result:
|
|
663
|
+
|
|
664
|
+
```
|
|
665
|
+
10
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
#### Nested subexpressions
|
|
669
|
+
|
|
670
|
+
Subexpressions can be nested to any depth:
|
|
671
|
+
|
|
672
|
+
```hbs
|
|
673
|
+
{{=sum (sum 1 (sum 2 3)) 4}}
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
#### Using strings inside subexpressions
|
|
677
|
+
|
|
678
|
+
Strings behave the same way inside subexpressions, including quoted strings with spaces:
|
|
679
|
+
|
|
680
|
+
```hbs
|
|
681
|
+
{{=concat "Hello " (upper name)}}
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
If `name = "world"`:
|
|
685
|
+
|
|
686
|
+
```
|
|
687
|
+
Hello WORLD
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
#### Variables inside subexpresspressions
|
|
691
|
+
|
|
692
|
+
You can reference variables or paths normally:
|
|
693
|
+
|
|
694
|
+
```hbs
|
|
695
|
+
{{=sum (sum price tax) shipping}}
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
#### Limitations
|
|
699
|
+
|
|
700
|
+
- Subexpressions are currently supported **only for functions** (`{{=...}}`).
|
|
701
|
+
- Subexpressions inside helper arguments are not yet supported.
|
|
702
|
+
- Parentheses must be balanced; malformed expressions will throw an error.
|
|
703
|
+
|
|
644
704
|
|
|
645
705
|
## API
|
|
646
706
|
|
package/index.js
CHANGED
|
@@ -26,29 +26,80 @@ const untokenize = (ts = [], s = "{{", e = "}}") => {
|
|
|
26
26
|
return ts.length > 0 ? ts.reduce((p, t, i) => p + (i % 2 === 0 ? e : s) + t) : "";
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
// @description tokenize args
|
|
30
|
+
const tokenizeArgs = (str = "", tokens = [], strings = []) => {
|
|
31
|
+
let current = "", depth = 0;
|
|
32
|
+
// 1. replace strings
|
|
33
|
+
str = str.replace(/"([^"\\]|\\.)*"/g, (match) => {
|
|
34
|
+
const id = `__STR${strings.length}__`;
|
|
35
|
+
strings.push(match);
|
|
36
|
+
return id;
|
|
37
|
+
});
|
|
38
|
+
// 2. tokenize arguments
|
|
39
|
+
for (let i = 0; i < str.length; i++) {
|
|
40
|
+
const c = str[i];
|
|
41
|
+
if (c === "(") {
|
|
42
|
+
depth++;
|
|
43
|
+
current = current + c;
|
|
44
|
+
} else if (c === ")") {
|
|
45
|
+
depth--;
|
|
46
|
+
current = current + c;
|
|
47
|
+
} else if (c === " " && depth === 0) {
|
|
48
|
+
if (current.trim()) {
|
|
49
|
+
tokens.push(current.trim());
|
|
50
|
+
}
|
|
51
|
+
current = "";
|
|
52
|
+
} else {
|
|
53
|
+
current = current + c;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// 3. add current token
|
|
57
|
+
if (current.trim()) {
|
|
58
|
+
tokens.push(current.trim());
|
|
59
|
+
}
|
|
60
|
+
// 4. replace strings back and return parsed tokens
|
|
61
|
+
return tokens.map(token => {
|
|
62
|
+
return token.replace(/__STR(\d+)__/g, (_, i) => strings[i]);
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
|
|
29
66
|
// @description parse string arguments
|
|
30
|
-
const parseArgs = (str = "", data = {}, vars = {}, argv = [], opt = {}) => {
|
|
31
|
-
const [t, ...args] = str.trim()
|
|
67
|
+
const parseArgs = (str = "", data = {}, vars = {}, fns = {}, argv = [], opt = {}) => {
|
|
68
|
+
const [t, ...args] = tokenizeArgs(str.trim());
|
|
32
69
|
args.forEach(argStr => {
|
|
33
70
|
if (argStr.includes("=") && !argStr.startsWith(`"`)) {
|
|
34
71
|
const [k, v] = argStr.split("=");
|
|
35
|
-
opt[k] = parse(v, data, vars);
|
|
72
|
+
opt[k] = parse(v, data, vars, fns);
|
|
36
73
|
}
|
|
37
74
|
else if (argStr.startsWith("...")) {
|
|
38
|
-
const value = parse(argStr.replace(/^\.{3}/, ""), data, vars);
|
|
75
|
+
const value = parse(argStr.replace(/^\.{3}/, ""), data, vars, fns);
|
|
39
76
|
if (!!value && typeof value === "object") {
|
|
40
77
|
Array.isArray(value) ? argv.push(...value) : Object.assign(opt, value);
|
|
41
78
|
}
|
|
42
79
|
}
|
|
43
80
|
else {
|
|
44
|
-
argv.push(parse(argStr, data, vars));
|
|
81
|
+
argv.push(parse(argStr, data, vars, fns));
|
|
45
82
|
}
|
|
46
83
|
});
|
|
47
84
|
return [t, argv, opt];
|
|
48
85
|
};
|
|
49
86
|
|
|
87
|
+
// @description evaluate an expression
|
|
88
|
+
const evaluateExpression = (str = "", data = {}, vars = {}, fns = {}) => {
|
|
89
|
+
const [ fnName, args, opt ] = parseArgs(str, data, vars, fns);
|
|
90
|
+
if (typeof fns[fnName] === "function") {
|
|
91
|
+
return fns[fnName]({args, opt, options: opt, data, variables: vars});
|
|
92
|
+
}
|
|
93
|
+
// if no function has been found with this name
|
|
94
|
+
// throw new Error(`Unknown function '${fnName}'`);
|
|
95
|
+
return "";
|
|
96
|
+
};
|
|
97
|
+
|
|
50
98
|
// @description parse a string value to a native type
|
|
51
|
-
const parse = (v, data = {}, vars = {}) => {
|
|
99
|
+
const parse = (v, data = {}, vars = {}, fns = {}) => {
|
|
100
|
+
if (v.startsWith("(") && v.endsWith(")")) {
|
|
101
|
+
return evaluateExpression(v.slice(1, -1).trim(), data, vars, fns);
|
|
102
|
+
}
|
|
52
103
|
if ((v.startsWith(`"`) && v.endsWith(`"`)) || /^-?\d+\.?\d*$/.test(v) || v === "true" || v === "false" || v === "null") {
|
|
53
104
|
return JSON.parse(v);
|
|
54
105
|
}
|
|
@@ -189,10 +240,7 @@ const create = (options = {}) => {
|
|
|
189
240
|
}
|
|
190
241
|
}
|
|
191
242
|
else if (tokens[i].startsWith("=")) {
|
|
192
|
-
|
|
193
|
-
if (typeof ctx.functions[t] === "function") {
|
|
194
|
-
output.push(ctx.functions[t]({args, opt, options: opt, data, variables: vars}) || "");
|
|
195
|
-
}
|
|
243
|
+
output.push(evaluateExpression(tokens[i].slice(1), data, vars, ctx.functions) ?? "");
|
|
196
244
|
}
|
|
197
245
|
else if (tokens[i].startsWith("/")) {
|
|
198
246
|
if (tokens[i].slice(1).trim() !== section) {
|