mikel 0.6.0 → 0.8.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 +89 -19
- package/index.js +20 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -214,7 +214,7 @@ const data = {
|
|
|
214
214
|
};
|
|
215
215
|
const options = {
|
|
216
216
|
helpers: {
|
|
217
|
-
customHelper:
|
|
217
|
+
customHelper: value => {
|
|
218
218
|
return `Hello, ${value}!`;
|
|
219
219
|
},
|
|
220
220
|
},
|
|
@@ -224,21 +224,38 @@ const result = m(template, data, options);
|
|
|
224
224
|
console.log(result); // Output: "Hello, World!"
|
|
225
225
|
```
|
|
226
226
|
|
|
227
|
-
Custom helper functions receive
|
|
227
|
+
Custom helper functions receive multiple arguments, where the first N arguments are the variables with the helper is called in the template, and the last argument is an options object containing the following keys:
|
|
228
228
|
|
|
229
|
-
- `context`:
|
|
230
|
-
- `
|
|
231
|
-
- `key`: The field used to extract the value from the current context.
|
|
232
|
-
- `options`: The global options object.
|
|
233
|
-
- `fn`: A function that executes the template provided in the helper block and returns a string with the evaluated template in the provided context.
|
|
229
|
+
- `context`: the current context (data) where the helper has been executed.
|
|
230
|
+
- `fn`: a function that executes the template provided in the helper block and returns a string with the evaluated template in the provided context.
|
|
234
231
|
|
|
235
|
-
The helper function must return a string, which will be injected into the result string.
|
|
232
|
+
The helper function must return a string, which will be injected into the result string. Example:
|
|
236
233
|
|
|
237
|
-
|
|
234
|
+
```javascript
|
|
235
|
+
const data = {
|
|
236
|
+
items: [
|
|
237
|
+
{ name: "John" },
|
|
238
|
+
{ name: "Alice" },
|
|
239
|
+
{ name: "Bob" },
|
|
240
|
+
],
|
|
241
|
+
};
|
|
242
|
+
const options = {
|
|
243
|
+
helpers: {
|
|
244
|
+
customEach: (items, opt) => {
|
|
245
|
+
return items.map((item, index) => opt.fn({ ...item, index: index})).join("");
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const result = m("{{#customEach items}}{{index}}: {{name}}, {{/customEach}}", data, options);
|
|
251
|
+
console.log(result); // --> "0: John, 1: Alice, 2: Bob,"
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Runtime Variables
|
|
238
255
|
|
|
239
256
|
> Added in `v0.4.0`.
|
|
240
257
|
|
|
241
|
-
|
|
258
|
+
Runtime Variables in Mikel provide convenient access to special values within your templates. These variables, denoted by the `@` symbol, allow users to interact with specific data contexts or values at runtime. Runtime variables are usually generated by helpers like `#each`.
|
|
242
259
|
|
|
243
260
|
#### @root
|
|
244
261
|
|
|
@@ -266,9 +283,29 @@ The `@key` variable allows users to retrieve the current key of the object entry
|
|
|
266
283
|
|
|
267
284
|
The `@value` variable allows users to retrieve the current value of the object entry when iterating over an object using the `#each` helper. It simplifies access to object values for dynamic rendering and data manipulation.
|
|
268
285
|
|
|
269
|
-
|
|
286
|
+
#### @first
|
|
287
|
+
|
|
288
|
+
> Added in `v0.7.0`.
|
|
289
|
+
|
|
290
|
+
The `@first` variable allows to check if the current iteration using the `#each` helper is the first item in the array or object.
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
{{#each items}} {{.}}: {{#if @first}}first item!{{/if}}{{#unless @first}}not first{{/if}} {{/each}}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
#### @last
|
|
297
|
+
|
|
298
|
+
> Added in `v0.7.0`.
|
|
299
|
+
|
|
300
|
+
The `@last` variable allows to check if the current iteration using the `#each` helper is the last item in the array or object.
|
|
270
301
|
|
|
271
|
-
|
|
302
|
+
```
|
|
303
|
+
{{#each items}}{{@index}}:{{.}} {{#unless @last}},{{/unless}}{{/each}}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Custom runtime variables
|
|
307
|
+
|
|
308
|
+
> Added in `v0.5.0`.
|
|
272
309
|
|
|
273
310
|
Mikel allows users to define custom data variables, providing enhanced flexibility and customization options for templates. These custom data variables can be accessed within the template using the `@` character.
|
|
274
311
|
|
|
@@ -287,18 +324,51 @@ console.log(result); // --> 'Hello, World!'
|
|
|
287
324
|
|
|
288
325
|
In this example, the custom data variable `customVariable` is defined with the value `"World"`, and it can be accessed in the template using `@customVariable`.
|
|
289
326
|
|
|
327
|
+
### Functions
|
|
328
|
+
|
|
329
|
+
> Added in `v0.8.0`.
|
|
330
|
+
|
|
331
|
+
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.
|
|
332
|
+
|
|
333
|
+
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.
|
|
334
|
+
|
|
335
|
+
Example:
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
const data = {
|
|
339
|
+
user: {
|
|
340
|
+
firstName: "John",
|
|
341
|
+
lastName: "Doe",
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
const options = {
|
|
345
|
+
functions: {
|
|
346
|
+
fullName: (firstName, lastName) => {
|
|
347
|
+
return `${firstName} ${lastName}`;
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const result = m("My name is: {{=fullName user.firstName user.lastName}}", data, options);
|
|
353
|
+
console.log(result); // --> "My name is: John Doe"
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
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.
|
|
357
|
+
|
|
358
|
+
|
|
290
359
|
## API
|
|
291
360
|
|
|
292
361
|
### `mikel(template, data[, options])`
|
|
293
362
|
|
|
294
|
-
Render the given template string with the provided data object.
|
|
363
|
+
Render the given template string with the provided data object and options.
|
|
295
364
|
|
|
296
|
-
- `template` (string):
|
|
297
|
-
- `data` (object):
|
|
298
|
-
- `options` (object):
|
|
299
|
-
- `partials` (object):
|
|
300
|
-
- `variables` (object):
|
|
301
|
-
- `helpers` (object):
|
|
365
|
+
- `template` (string): the template string.
|
|
366
|
+
- `data` (object): the data object containing the values to render.
|
|
367
|
+
- `options` (object): an object containing the following optional values:
|
|
368
|
+
- `partials` (object): an object containing the available partials.
|
|
369
|
+
- `variables` (object): an object containing custom data variables.
|
|
370
|
+
- `helpers` (object): an object containing custom helpers.
|
|
371
|
+
- `functions` (object): and object containing custom functions.
|
|
302
372
|
|
|
303
373
|
Returns: A string with the rendered output.
|
|
304
374
|
|
package/index.js
CHANGED
|
@@ -12,16 +12,16 @@ const escape = s => s.toString().replace(/[&<>\"']/g, m => escapedChars[m]);
|
|
|
12
12
|
const get = (c, p) => (p === "." ? c : p.split(".").reduce((x, k) => x?.[k], c)) ?? "";
|
|
13
13
|
|
|
14
14
|
const defaultHelpers = {
|
|
15
|
-
"each": (
|
|
15
|
+
"each": (value, opt) => {
|
|
16
16
|
return (typeof value === "object" ? Object.entries(value || {}) : [])
|
|
17
|
-
.map((item, index) => fn(item[1], {index: index, key: item[0], value: item[1]}))
|
|
17
|
+
.map((item, index, items) => opt.fn(item[1], {index: index, key: item[0], value: item[1], first: index === 0, last: index === items.length - 1}))
|
|
18
18
|
.join("");
|
|
19
19
|
},
|
|
20
|
-
"if": (
|
|
21
|
-
"unless": (
|
|
20
|
+
"if": (value, opt) => !!value ? opt.fn(opt.context) : "",
|
|
21
|
+
"unless": (value, opt) => !!!value ? opt.fn(opt.context) : "",
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
const compile = (tokens, output, context, partials, helpers, vars, index = 0, section = "") => {
|
|
24
|
+
const compile = (tokens, output, context, partials, helpers, vars, fn = {}, index = 0, section = "") => {
|
|
25
25
|
let i = index;
|
|
26
26
|
while (i < tokens.length) {
|
|
27
27
|
if (i % 2 === 0) {
|
|
@@ -34,20 +34,18 @@ const compile = (tokens, output, context, partials, helpers, vars, index = 0, se
|
|
|
34
34
|
output.push(get(context, tokens[i].slice(1).trim()));
|
|
35
35
|
}
|
|
36
36
|
else if (tokens[i].startsWith("#") && typeof helpers[tokens[i].slice(1).trim().split(" ")[0]] === "function") {
|
|
37
|
-
const [t,
|
|
37
|
+
const [t, ...args] = tokens[i].slice(1).trim().split(" ");
|
|
38
38
|
const j = i + 1;
|
|
39
|
-
output.push(helpers[t]({
|
|
39
|
+
output.push(helpers[t](...args.map(v => (v || "").startsWith("@") ? get(vars, v.slice(1)) : get(context, v || ".")), {
|
|
40
40
|
context: context,
|
|
41
|
-
key: v || ".",
|
|
42
|
-
value: get(context, v || "."),
|
|
43
41
|
fn: (blockContext = {}, blockVars = {}, blockOutput = []) => {
|
|
44
|
-
i = compile(tokens, blockOutput, blockContext, partials, helpers, {...vars, ...blockVars, root: vars.root}, j, t);
|
|
42
|
+
i = compile(tokens, blockOutput, blockContext, partials, helpers, {...vars, ...blockVars, root: vars.root}, fn, j, t);
|
|
45
43
|
return blockOutput.join("");
|
|
46
44
|
},
|
|
47
45
|
}));
|
|
48
|
-
// Make sure that this block
|
|
46
|
+
// Make sure that this block is executed at least once
|
|
49
47
|
if (i + 1 === j) {
|
|
50
|
-
i = compile(tokens, [], {},
|
|
48
|
+
i = compile(tokens, [], {}, {}, {}, {}, {}, j, t);
|
|
51
49
|
}
|
|
52
50
|
}
|
|
53
51
|
else if (tokens[i].startsWith("#") || tokens[i].startsWith("^")) {
|
|
@@ -57,18 +55,24 @@ const compile = (tokens, output, context, partials, helpers, vars, index = 0, se
|
|
|
57
55
|
if (!negate && value && Array.isArray(value)) {
|
|
58
56
|
const j = i + 1;
|
|
59
57
|
(value.length > 0 ? value : [""]).forEach(item => {
|
|
60
|
-
i = compile(tokens, value.length > 0 ? output : [], item, partials, helpers, vars, j, t);
|
|
58
|
+
i = compile(tokens, value.length > 0 ? output : [], item, partials, helpers, vars, fn, j, t);
|
|
61
59
|
});
|
|
62
60
|
}
|
|
63
61
|
else {
|
|
64
62
|
const includeOutput = (!negate && !!value) || (negate && !!!value);
|
|
65
|
-
i = compile(tokens, includeOutput ? output : [], context, partials, helpers, vars, i + 1, t);
|
|
63
|
+
i = compile(tokens, includeOutput ? output : [], context, partials, helpers, vars, fn, i + 1, t);
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
else if (tokens[i].startsWith(">")) {
|
|
69
67
|
const [t, v] = tokens[i].slice(1).trim().split(" ");
|
|
70
68
|
if (typeof partials[t] === "string") {
|
|
71
|
-
compile(partials[t].split(tags), output, v ? get(context, v) : context, partials, helpers, vars, 0, "");
|
|
69
|
+
compile(partials[t].split(tags), output, v ? get(context, v) : context, partials, helpers, vars, fn, 0, "");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (tokens[i].startsWith("=")) {
|
|
73
|
+
const [t, ...args] = tokens[i].slice(1).trim().split(" ");
|
|
74
|
+
if (typeof fn[t] === "function") {
|
|
75
|
+
output.push(fn[t](...args.map(v => (v || "").startsWith("@") ? get(vars, v.slice(1)) : get(context, v || "."))) || "");
|
|
72
76
|
}
|
|
73
77
|
}
|
|
74
78
|
else if (tokens[i].startsWith("/")) {
|
|
@@ -90,7 +94,7 @@ const mikel = (str, context = {}, opt = {}, output = []) => {
|
|
|
90
94
|
const partials = Object.assign({}, opt.partials || {});
|
|
91
95
|
const helpers = Object.assign({}, defaultHelpers, opt.helpers || {});
|
|
92
96
|
const variables = Object.assign({}, opt.variables || {}, {root: context});
|
|
93
|
-
compile(str.split(tags), output, context, partials, helpers, variables, 0, "");
|
|
97
|
+
compile(str.split(tags), output, context, partials, helpers, variables, opt.functions || {}, 0, "");
|
|
94
98
|
return output.join("");
|
|
95
99
|
};
|
|
96
100
|
|