mikel 0.11.1 → 0.13.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 +49 -9
- package/index.js +31 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,6 +110,27 @@ const result = m("User: {{> user currentUser}}", data, {partials});
|
|
|
110
110
|
// Output: 'User: John Doe <john@example.com>'
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
#### Keyword arguments in partials
|
|
114
|
+
|
|
115
|
+
> This feature was added in `v0.13.0`.
|
|
116
|
+
|
|
117
|
+
You can provide keyword arguments in partials to generate a new context object using the provided keywords.
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
const data = {
|
|
121
|
+
name: "John Doe",
|
|
122
|
+
email: "john@example.com",
|
|
123
|
+
};
|
|
124
|
+
const partials = {
|
|
125
|
+
user: "{{userName}} <{{userEmail}}>",
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const result = m("User: {{>user userName=name userEmail=email }}", data, {partials});
|
|
129
|
+
// Output: 'User: John Doe <john@example.com>'
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Please note that providing keyword arguments and a custom context to a partial is not supported. On this situation, the partial will be evaluated only with the custom context.
|
|
133
|
+
|
|
113
134
|
### Built-in helpers
|
|
114
135
|
|
|
115
136
|
> Added in `v0.4.0`.
|
|
@@ -167,6 +188,17 @@ const data = {
|
|
|
167
188
|
console.log(m("{{#each values}}{{@key}}: {{@value}}, {{/each}}", data)); // --> 'foo: 0, bar: 1, '
|
|
168
189
|
```
|
|
169
190
|
|
|
191
|
+
The `each` helper also supports the following options, provided as keyword arguments:
|
|
192
|
+
- `skip`: number of first items to skip (default is `0`).
|
|
193
|
+
- `limit`: allows to limit the number of items to display (default equals to the length of the items list).
|
|
194
|
+
|
|
195
|
+
Example:
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
console.log(m("{{each values limit=2}}{{.}}{{/each}}", {values: [0, 1, 2, 3]})); // --> '01'
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
|
|
170
202
|
#### if
|
|
171
203
|
|
|
172
204
|
The `if` helper renders the block only if the condition is truthy.
|
|
@@ -239,6 +271,7 @@ console.log(m("{{#with autor}}{{name}} <{{email}}>{{/with}}", data)); // --> 'Bo
|
|
|
239
271
|
### Custom Helpers
|
|
240
272
|
|
|
241
273
|
> Added in `v0.5.0`.
|
|
274
|
+
> Breaking change introduced in `v0.12.0`.
|
|
242
275
|
|
|
243
276
|
Custom helpers should be provided as an object in the `options.helpers` field, where each key represents the name of the helper and the corresponding value is a function defining the helper's behavior.
|
|
244
277
|
|
|
@@ -251,8 +284,8 @@ const data = {
|
|
|
251
284
|
};
|
|
252
285
|
const options = {
|
|
253
286
|
helpers: {
|
|
254
|
-
customHelper:
|
|
255
|
-
return `Hello, ${
|
|
287
|
+
customHelper: params => {
|
|
288
|
+
return `Hello, ${params.args[0]}!`;
|
|
256
289
|
},
|
|
257
290
|
},
|
|
258
291
|
};
|
|
@@ -261,8 +294,10 @@ const result = m(template, data, options);
|
|
|
261
294
|
console.log(result); // Output: "Hello, World!"
|
|
262
295
|
```
|
|
263
296
|
|
|
264
|
-
Custom helper functions receive
|
|
297
|
+
Custom helper functions receive a single object as argument, containing the following keys:
|
|
265
298
|
|
|
299
|
+
- `args`: an array containing the variables with the helper is called in the template.
|
|
300
|
+
- `opt`: an object containing the keyword arguments provided to the helper.
|
|
266
301
|
- `context`: the current context (data) where the helper has been executed.
|
|
267
302
|
- `fn`: a function that executes the template provided in the helper block and returns a string with the evaluated template in the provided context.
|
|
268
303
|
|
|
@@ -278,8 +313,8 @@ const data = {
|
|
|
278
313
|
};
|
|
279
314
|
const options = {
|
|
280
315
|
helpers: {
|
|
281
|
-
customEach: (
|
|
282
|
-
return
|
|
316
|
+
customEach: ({args, fn}) => {
|
|
317
|
+
return args[0].map((item, index) => fn({ ...item, index: index})).join("");
|
|
283
318
|
},
|
|
284
319
|
},
|
|
285
320
|
};
|
|
@@ -343,11 +378,18 @@ The `@last` variable allows to check if the current iteration using the `#each`
|
|
|
343
378
|
### Functions
|
|
344
379
|
|
|
345
380
|
> Added in `v0.8.0`.
|
|
381
|
+
> Breaking change introduced in `v0.12.0`.
|
|
346
382
|
|
|
347
383
|
Mikel allows users to define custom functions that can be used within templates to perform dynamic operations. Functions can be invoked in the template using the `=` character, followed by the function name and the variables to be provided to the function. Variables should be separated by spaces.
|
|
348
384
|
|
|
349
385
|
Functions should be provided in the `options.functions` field of the options object when rendering a template. Each function is defined by a name and a corresponding function that performs the desired operation.
|
|
350
386
|
|
|
387
|
+
Functions will receive a single object as argument, containing the following keys:
|
|
388
|
+
|
|
389
|
+
- `args`: an array containing the variables with the function is called in the template.
|
|
390
|
+
- `opt`: an object containing the keyword arguments provided to the function.
|
|
391
|
+
- `context`: the current context (data) where the function has been executed.
|
|
392
|
+
|
|
351
393
|
Example:
|
|
352
394
|
|
|
353
395
|
```javascript
|
|
@@ -359,8 +401,8 @@ const data = {
|
|
|
359
401
|
};
|
|
360
402
|
const options = {
|
|
361
403
|
functions: {
|
|
362
|
-
fullName: (
|
|
363
|
-
return `${
|
|
404
|
+
fullName: ({args}) => {
|
|
405
|
+
return `${args[0]} ${args[1]}`;
|
|
364
406
|
}
|
|
365
407
|
},
|
|
366
408
|
};
|
|
@@ -369,8 +411,6 @@ const result = m("My name is: {{=fullName user.firstName user.lastName}}", data,
|
|
|
369
411
|
console.log(result); // --> "My name is: John Doe"
|
|
370
412
|
```
|
|
371
413
|
|
|
372
|
-
In this example, the custom function `fullName` is defined to take two arguments, `firstName` and `lastName`, and return the full name. The template then uses this function to concatenate and render the full name.
|
|
373
|
-
|
|
374
414
|
|
|
375
415
|
## API
|
|
376
416
|
|
package/index.js
CHANGED
|
@@ -11,6 +11,18 @@ const escape = s => s.toString().replace(/[&<>\"']/g, m => escapedChars[m]);
|
|
|
11
11
|
|
|
12
12
|
const get = (c, p) => (p === "." ? c : p.split(".").reduce((x, k) => x?.[k], c)) ?? "";
|
|
13
13
|
|
|
14
|
+
// @description parse string arguments
|
|
15
|
+
const parseArgs = (argString = "", context = {}, vars = {}) => {
|
|
16
|
+
const [t, ...args] = argString.trim().match(/(?:[^\s"]+|"[^"]*")+/g);
|
|
17
|
+
const argv = args.filter(a => !a.includes("=")).map(a => parse(a, context, vars));
|
|
18
|
+
const opt = Object.fromEntries(args.filter(a => a.includes("=")).map(a => {
|
|
19
|
+
const [k, v] = a.split("=");
|
|
20
|
+
return [k, parse(v, context, vars)];
|
|
21
|
+
}));
|
|
22
|
+
return [t, argv, opt];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// @description parse a string value to a native type
|
|
14
26
|
const parse = (v, context = {}, vars = {}) => {
|
|
15
27
|
if ((v.startsWith(`"`) && v.endsWith(`"`)) || /^-?\d+\.?\d*$/.test(v) || v === "true" || v === "false" || v === "null") {
|
|
16
28
|
return JSON.parse(v);
|
|
@@ -32,16 +44,18 @@ const frontmatter = (str = "", parser = null) => {
|
|
|
32
44
|
|
|
33
45
|
// @description default helpers
|
|
34
46
|
const defaultHelpers = {
|
|
35
|
-
"each":
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
"each": p => {
|
|
48
|
+
const items = typeof p.args[0] === "object" ? Object.entries(p.args[0] || {}) : [];
|
|
49
|
+
const limit = Math.min(items.length - (p.opt.skip || 0), p.opt.limit || items.length);
|
|
50
|
+
return items.slice(p.opt.skip || 0, (p.opt.skip || 0) + limit)
|
|
51
|
+
.map((item, index) => p.fn(item[1], {index: index, key: item[0], value: item[1], first: index === 0, last: index === items.length - 1}))
|
|
38
52
|
.join("");
|
|
39
53
|
},
|
|
40
|
-
"if":
|
|
41
|
-
"unless":
|
|
42
|
-
"eq":
|
|
43
|
-
"ne":
|
|
44
|
-
"with":
|
|
54
|
+
"if": p => !!p.args[0] ? p.fn(p.context) : "",
|
|
55
|
+
"unless": p => !!!p.args[0] ? p.fn(p.context) : "",
|
|
56
|
+
"eq": p => p.args[0] === p.args[1] ? p.fn(p.context) : "",
|
|
57
|
+
"ne": p => p.args[0] !== p.args[1] ? p.fn(p.context) : "",
|
|
58
|
+
"with": p => p.fn(p.args[0]),
|
|
45
59
|
};
|
|
46
60
|
|
|
47
61
|
// @description create a new instance of mikel
|
|
@@ -64,9 +78,11 @@ const create = (template = "", options = {}) => {
|
|
|
64
78
|
output.push(get(context, tokens[i].slice(1).trim()));
|
|
65
79
|
}
|
|
66
80
|
else if (tokens[i].startsWith("#") && typeof helpers[tokens[i].slice(1).trim().split(" ")[0]] === "function") {
|
|
67
|
-
const [t,
|
|
81
|
+
const [t, args, opt] = parseArgs(tokens[i].slice(1), context, vars);
|
|
68
82
|
const j = i + 1;
|
|
69
|
-
output.push(helpers[t](
|
|
83
|
+
output.push(helpers[t]({
|
|
84
|
+
args: args,
|
|
85
|
+
opt: opt,
|
|
70
86
|
context: context,
|
|
71
87
|
fn: (blockContext = {}, blockVars = {}, blockOutput = []) => {
|
|
72
88
|
i = compile(tokens, blockOutput, blockContext, {...vars, ...blockVars, root: vars.root}, j, t);
|
|
@@ -94,15 +110,16 @@ const create = (template = "", options = {}) => {
|
|
|
94
110
|
}
|
|
95
111
|
}
|
|
96
112
|
else if (tokens[i].startsWith(">")) {
|
|
97
|
-
const [t,
|
|
113
|
+
const [t, args, opt] = parseArgs(tokens[i].slice(1), context, vars);
|
|
98
114
|
if (typeof partials[t] === "string") {
|
|
99
|
-
|
|
115
|
+
const newCtx = args.length > 0 ? args[0] : (Object.keys(opt).length > 0 ? opt : context);
|
|
116
|
+
compile(partials[t].split(tags), output, newCtx, vars, 0, "");
|
|
100
117
|
}
|
|
101
118
|
}
|
|
102
119
|
else if (tokens[i].startsWith("=")) {
|
|
103
|
-
const [t,
|
|
120
|
+
const [t, args, opt] = parseArgs(tokens[i].slice(1), context, vars);
|
|
104
121
|
if (typeof functions[t] === "function") {
|
|
105
|
-
output.push(functions[t](
|
|
122
|
+
output.push(functions[t]({args, opt, context}) || "");
|
|
106
123
|
}
|
|
107
124
|
}
|
|
108
125
|
else if (tokens[i].startsWith("/")) {
|