mikel 0.7.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.
Files changed (3) hide show
  1. package/README.md +66 -16
  2. package/index.js +21 -18
  3. 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: ({context, value, key, options, fn}) => {
217
+ customHelper: value => {
218
218
  return `Hello, ${value}!`;
219
219
  },
220
220
  },
@@ -224,15 +224,32 @@ const result = m(template, data, options);
224
224
  console.log(result); // Output: "Hello, World!"
225
225
  ```
226
226
 
227
- Custom helper functions receive a single object parameter containing the following fields:
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`: The current context (data) where the helper has been executed.
230
- - `value`: The current value passed to the helper.
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:
233
+
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
+ ```
236
253
 
237
254
  ### Runtime Variables
238
255
 
@@ -288,7 +305,7 @@ The `@last` variable allows to check if the current iteration using the `#each`
288
305
 
289
306
  ### Custom runtime variables
290
307
 
291
- > Added in `v0.5.0`
308
+ > Added in `v0.5.0`.
292
309
 
293
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.
294
311
 
@@ -307,18 +324,51 @@ console.log(result); // --> 'Hello, World!'
307
324
 
308
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`.
309
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
+
310
359
  ## API
311
360
 
312
361
  ### `mikel(template, data[, options])`
313
362
 
314
- Render the given template string with the provided data object.
363
+ Render the given template string with the provided data object and options.
315
364
 
316
- - `template` (string): The Mustache template string.
317
- - `data` (object): The data object containing the values to render.
318
- - `options` (object): An object containing the following optional values:
319
- - `partials` (object): An object containing the available partials.
320
- - `variables` (object): An object containing custom data variables.
321
- - `helpers` (object): An object containing custom helpers.
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.
322
372
 
323
373
  Returns: A string with the rendered output.
324
374
 
package/index.js CHANGED
@@ -12,17 +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": ({value, fn}) => {
16
- const items = (typeof value === "object" ? Object.entries(value || {}) : []);
17
- return items
18
- .map((item, index) => fn(item[1], {index: index, key: item[0], value: item[1], first: index === 0, last: index === items.length - 1}))
15
+ "each": (value, opt) => {
16
+ return (typeof value === "object" ? Object.entries(value || {}) : [])
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}))
19
18
  .join("");
20
19
  },
21
- "if": ({value, fn, context}) => !!value ? fn(context) : "",
22
- "unless": ({value, fn, context}) => !!!value ? fn(context) : "",
20
+ "if": (value, opt) => !!value ? opt.fn(opt.context) : "",
21
+ "unless": (value, opt) => !!!value ? opt.fn(opt.context) : "",
23
22
  };
24
23
 
25
- const compile = (tokens, output, context, partials, helpers, vars, index = 0, section = "") => {
24
+ const compile = (tokens, output, context, partials, helpers, vars, fn = {}, index = 0, section = "") => {
26
25
  let i = index;
27
26
  while (i < tokens.length) {
28
27
  if (i % 2 === 0) {
@@ -35,20 +34,18 @@ const compile = (tokens, output, context, partials, helpers, vars, index = 0, se
35
34
  output.push(get(context, tokens[i].slice(1).trim()));
36
35
  }
37
36
  else if (tokens[i].startsWith("#") && typeof helpers[tokens[i].slice(1).trim().split(" ")[0]] === "function") {
38
- const [t, v] = tokens[i].slice(1).trim().split(" ");
37
+ const [t, ...args] = tokens[i].slice(1).trim().split(" ");
39
38
  const j = i + 1;
40
- output.push(helpers[t]({
39
+ output.push(helpers[t](...args.map(v => (v || "").startsWith("@") ? get(vars, v.slice(1)) : get(context, v || ".")), {
41
40
  context: context,
42
- key: v || ".",
43
- value: (v || "").startsWith("@") ? get(vars, v.slice(1)) : get(context, v || "."),
44
41
  fn: (blockContext = {}, blockVars = {}, blockOutput = []) => {
45
- 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);
46
43
  return blockOutput.join("");
47
44
  },
48
45
  }));
49
- // Make sure that this block has been executed
46
+ // Make sure that this block is executed at least once
50
47
  if (i + 1 === j) {
51
- i = compile(tokens, [], {}, partials, helpers, vars, j, t);
48
+ i = compile(tokens, [], {}, {}, {}, {}, {}, j, t);
52
49
  }
53
50
  }
54
51
  else if (tokens[i].startsWith("#") || tokens[i].startsWith("^")) {
@@ -58,18 +55,24 @@ const compile = (tokens, output, context, partials, helpers, vars, index = 0, se
58
55
  if (!negate && value && Array.isArray(value)) {
59
56
  const j = i + 1;
60
57
  (value.length > 0 ? value : [""]).forEach(item => {
61
- 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);
62
59
  });
63
60
  }
64
61
  else {
65
62
  const includeOutput = (!negate && !!value) || (negate && !!!value);
66
- 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);
67
64
  }
68
65
  }
69
66
  else if (tokens[i].startsWith(">")) {
70
67
  const [t, v] = tokens[i].slice(1).trim().split(" ");
71
68
  if (typeof partials[t] === "string") {
72
- 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 || "."))) || "");
73
76
  }
74
77
  }
75
78
  else if (tokens[i].startsWith("/")) {
@@ -91,7 +94,7 @@ const mikel = (str, context = {}, opt = {}, output = []) => {
91
94
  const partials = Object.assign({}, opt.partials || {});
92
95
  const helpers = Object.assign({}, defaultHelpers, opt.helpers || {});
93
96
  const variables = Object.assign({}, opt.variables || {}, {root: context});
94
- compile(str.split(tags), output, context, partials, helpers, variables, 0, "");
97
+ compile(str.split(tags), output, context, partials, helpers, variables, opt.functions || {}, 0, "");
95
98
  return output.join("");
96
99
  };
97
100
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mikel",
3
3
  "description": "Micro templating library with zero dependencies",
4
- "version": "0.7.0",
4
+ "version": "0.8.0",
5
5
  "type": "module",
6
6
  "author": {
7
7
  "name": "Josemi Juanes",