mikel 0.5.0 → 0.7.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 +34 -6
  2. package/index.js +28 -21
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -234,11 +234,11 @@ Custom helper functions receive a single object parameter containing the followi
234
234
 
235
235
  The helper function must return a string, which will be injected into the result string.
236
236
 
237
- ### Variables
237
+ ### Runtime Variables
238
238
 
239
239
  > Added in `v0.4.0`.
240
240
 
241
- Data 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.
241
+ 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
242
 
243
243
  #### @root
244
244
 
@@ -266,7 +266,27 @@ The `@key` variable allows users to retrieve the current key of the object entry
266
266
 
267
267
  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
268
 
269
- ### Custom variables
269
+ #### @first
270
+
271
+ > Added in `v0.7.0`.
272
+
273
+ The `@first` variable allows to check if the current iteration using the `#each` helper is the first item in the array or object.
274
+
275
+ ```
276
+ {{#each items}} {{.}}: {{#if @first}}first item!{{/if}}{{#unless @first}}not first{{/if}} {{/each}}
277
+ ```
278
+
279
+ #### @last
280
+
281
+ > Added in `v0.7.0`.
282
+
283
+ The `@last` variable allows to check if the current iteration using the `#each` helper is the last item in the array or object.
284
+
285
+ ```
286
+ {{#each items}}{{@index}}:{{.}} {{#unless @last}},{{/unless}}{{/each}}
287
+ ```
288
+
289
+ ### Custom runtime variables
270
290
 
271
291
  > Added in `v0.5.0`
272
292
 
@@ -289,7 +309,7 @@ In this example, the custom data variable `customVariable` is defined with the v
289
309
 
290
310
  ## API
291
311
 
292
- ### `m(template, data[, options])`
312
+ ### `mikel(template, data[, options])`
293
313
 
294
314
  Render the given template string with the provided data object.
295
315
 
@@ -303,16 +323,24 @@ Render the given template string with the provided data object.
303
323
  Returns: A string with the rendered output.
304
324
 
305
325
  ```javascript
306
- import m from "mikel";
326
+ import mikel from "mikel";
307
327
 
308
328
  const data = {
309
329
  name: "World",
310
330
  };
311
331
 
312
- const result = m("Hello, {{name}}!", data);
332
+ const result = mikel("Hello, {{name}}!", data);
313
333
  console.log(result); // Output: "Hello, World!"
314
334
  ```
315
335
 
336
+ ### `mikel.escape(str)`
337
+
338
+ This function converts special HTML characters `&`, `<`, `>`, `"`, and `'` to their corresponding HTML entities.
339
+
340
+ ### `mikel.get(object, path)`
341
+
342
+ This function returns the value in `object` following the provided `path` string.
343
+
316
344
  ## License
317
345
 
318
346
  This project is licensed under the [MIT License](LICENSE).
package/index.js CHANGED
@@ -11,47 +11,44 @@ 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
- const helpers = new Map(Object.entries({
14
+ const defaultHelpers = {
15
15
  "each": ({value, fn}) => {
16
- return (typeof value === "object" ? Object.entries(value || {}) : [])
17
- .map((item, index) => fn(item[1], {index: index, key: item[0], value: item[1]}))
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}))
18
19
  .join("");
19
20
  },
20
21
  "if": ({value, fn, context}) => !!value ? fn(context) : "",
21
22
  "unless": ({value, fn, context}) => !!!value ? fn(context) : "",
22
- }));
23
-
24
- const hasHelper = (n, o) => helpers.has(n) || typeof o?.helpers?.[n] === "function";
25
- const getHelper = (n, o) => helpers.get(n) || o?.helpers?.[n];
23
+ };
26
24
 
27
- const compile = (tokens, output, context, opt, index = 0, section = "", vars = {}) => {
25
+ const compile = (tokens, output, context, partials, helpers, vars, index = 0, section = "") => {
28
26
  let i = index;
29
27
  while (i < tokens.length) {
30
28
  if (i % 2 === 0) {
31
29
  output.push(tokens[i]);
32
30
  }
33
31
  else if (tokens[i].startsWith("@")) {
34
- output.push(get({...(opt?.variables), ...vars}, tokens[i].slice(1).trim() ?? "_") ?? "");
32
+ output.push(get(vars, tokens[i].slice(1).trim() ?? "_") ?? "");
35
33
  }
36
34
  else if (tokens[i].startsWith("!")) {
37
35
  output.push(get(context, tokens[i].slice(1).trim()));
38
36
  }
39
- else if (tokens[i].startsWith("#") && hasHelper(tokens[i].slice(1).trim().split(" ")[0], opt)) {
37
+ else if (tokens[i].startsWith("#") && typeof helpers[tokens[i].slice(1).trim().split(" ")[0]] === "function") {
40
38
  const [t, v] = tokens[i].slice(1).trim().split(" ");
41
39
  const j = i + 1;
42
- output.push(getHelper(t, opt)({
40
+ output.push(helpers[t]({
43
41
  context: context,
44
42
  key: v || ".",
45
- value: get(context, v || "."),
46
- options: opt,
43
+ value: (v || "").startsWith("@") ? get(vars, v.slice(1)) : get(context, v || "."),
47
44
  fn: (blockContext = {}, blockVars = {}, blockOutput = []) => {
48
- i = compile(tokens, blockOutput, blockContext, opt, j, t, {root: vars.root, ...blockVars});
45
+ i = compile(tokens, blockOutput, blockContext, partials, helpers, {...vars, ...blockVars, root: vars.root}, j, t);
49
46
  return blockOutput.join("");
50
47
  },
51
48
  }));
52
49
  // Make sure that this block has been executed
53
50
  if (i + 1 === j) {
54
- i = compile(tokens, [], {}, opt, j, t, vars);
51
+ i = compile(tokens, [], {}, partials, helpers, vars, j, t);
55
52
  }
56
53
  }
57
54
  else if (tokens[i].startsWith("#") || tokens[i].startsWith("^")) {
@@ -61,18 +58,18 @@ const compile = (tokens, output, context, opt, index = 0, section = "", vars = {
61
58
  if (!negate && value && Array.isArray(value)) {
62
59
  const j = i + 1;
63
60
  (value.length > 0 ? value : [""]).forEach(item => {
64
- i = compile(tokens, value.length > 0 ? output : [], item, opt, j, t, vars);
61
+ i = compile(tokens, value.length > 0 ? output : [], item, partials, helpers, vars, j, t);
65
62
  });
66
63
  }
67
64
  else {
68
65
  const includeOutput = (!negate && !!value) || (negate && !!!value);
69
- i = compile(tokens, includeOutput ? output : [], context, opt, i + 1, t, vars);
66
+ i = compile(tokens, includeOutput ? output : [], context, partials, helpers, vars, i + 1, t);
70
67
  }
71
68
  }
72
69
  else if (tokens[i].startsWith(">")) {
73
70
  const [t, v] = tokens[i].slice(1).trim().split(" ");
74
- if (typeof opt?.partials?.[t] === "string") {
75
- compile(opt.partials[t].split(tags), output, v ? get(context, v) : context, opt, 0, "", vars);
71
+ if (typeof partials[t] === "string") {
72
+ compile(partials[t].split(tags), output, v ? get(context, v) : context, partials, helpers, vars, 0, "");
76
73
  }
77
74
  }
78
75
  else if (tokens[i].startsWith("/")) {
@@ -89,7 +86,17 @@ const compile = (tokens, output, context, opt, index = 0, section = "", vars = {
89
86
  return i;
90
87
  };
91
88
 
92
- export default (str, context = {}, opt = {}, output = []) => {
93
- compile(str.split(tags), output, context, opt, 0, "", {root: context});
89
+ // @description main compiler function
90
+ const mikel = (str, context = {}, opt = {}, output = []) => {
91
+ const partials = Object.assign({}, opt.partials || {});
92
+ const helpers = Object.assign({}, defaultHelpers, opt.helpers || {});
93
+ const variables = Object.assign({}, opt.variables || {}, {root: context});
94
+ compile(str.split(tags), output, context, partials, helpers, variables, 0, "");
94
95
  return output.join("");
95
96
  };
97
+
98
+ // @description assign utilities
99
+ mikel.escape = escape;
100
+ mikel.get = get;
101
+
102
+ export default mikel;
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.5.0",
4
+ "version": "0.7.0",
5
5
  "type": "module",
6
6
  "author": {
7
7
  "name": "Josemi Juanes",