diff --git a/README.md b/README.md index 1e7a4e3..0925fd2 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,7 @@ const data = { }; const options = { helpers: { - customHelper: ({context, value, key, options, fn}) => { + customHelper: value => { return `Hello, ${value}!`; }, }, @@ -224,15 +224,32 @@ const result = m(template, data, options); console.log(result); // Output: "Hello, World!" ``` -Custom helper functions receive a single object parameter containing the following fields: +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: -- `context`: The current context (data) where the helper has been executed. -- `value`: The current value passed to the helper. -- `key`: The field used to extract the value from the current context. -- `options`: The global options object. -- `fn`: A function that executes the template provided in the helper block and returns a string with the evaluated template in the provided context. +- `context`: the current context (data) where the helper has been executed. +- `fn`: a function that executes the template provided in the helper block and returns a string with the evaluated template in the provided context. -The helper function must return a string, which will be injected into the result string. +The helper function must return a string, which will be injected into the result string. Example: + +```javascript +const data = { + items: [ + { name: "John" }, + { name: "Alice" }, + { name: "Bob" }, + ], +}; +const options = { + helpers: { + customEach: (items, opt) => { + return items.map((item, index) => opt.fn({ ...item, index: index})).join(""); + }, + }, +}; + +const result = m("{{#customEach items}}{{index}}: {{name}}, {{/customEach}}", data, options); +console.log(result); // --> "0: John, 1: Alice, 2: Bob," +``` ### Runtime Variables diff --git a/index.js b/index.js index 70cdc7a..36cbefb 100644 --- a/index.js +++ b/index.js @@ -12,14 +12,13 @@ const escape = s => s.toString().replace(/[&<>\"']/g, m => escapedChars[m]); const get = (c, p) => (p === "." ? c : p.split(".").reduce((x, k) => x?.[k], c)) ?? ""; const defaultHelpers = { - "each": ({value, fn}) => { - const items = (typeof value === "object" ? Object.entries(value || {}) : []); - return items - .map((item, index) => fn(item[1], {index: index, key: item[0], value: item[1], first: index === 0, last: index === items.length - 1})) + "each": (value, opt) => { + return (typeof value === "object" ? Object.entries(value || {}) : []) + .map((item, index, items) => opt.fn(item[1], {index: index, key: item[0], value: item[1], first: index === 0, last: index === items.length - 1})) .join(""); }, - "if": ({value, fn, context}) => !!value ? fn(context) : "", - "unless": ({value, fn, context}) => !!!value ? fn(context) : "", + "if": (value, opt) => !!value ? opt.fn(opt.context) : "", + "unless": (value, opt) => !!!value ? opt.fn(opt.context) : "", }; const compile = (tokens, output, context, partials, helpers, vars, fn = {}, index = 0, section = "") => { @@ -35,12 +34,10 @@ const compile = (tokens, output, context, partials, helpers, vars, fn = {}, inde output.push(get(context, tokens[i].slice(1).trim())); } else if (tokens[i].startsWith("#") && typeof helpers[tokens[i].slice(1).trim().split(" ")[0]] === "function") { - const [t, v] = tokens[i].slice(1).trim().split(" "); + const [t, ...args] = tokens[i].slice(1).trim().split(" "); const j = i + 1; - output.push(helpers[t]({ + output.push(helpers[t](...args.map(v => (v || "").startsWith("@") ? get(vars, v.slice(1)) : get(context, v || ".")), { context: context, - key: v || ".", - value: (v || "").startsWith("@") ? get(vars, v.slice(1)) : get(context, v || "."), fn: (blockContext = {}, blockVars = {}, blockOutput = []) => { i = compile(tokens, blockOutput, blockContext, partials, helpers, {...vars, ...blockVars, root: vars.root}, fn, j, t); return blockOutput.join(""); diff --git a/test.js b/test.js index d46491f..22899b4 100644 --- a/test.js +++ b/test.js @@ -189,11 +189,20 @@ describe("{{#customHelper }}", () => { it("should allow to execute a simple custom helper", () => { const options = { helpers: { - hello: ({value}) => `Hello ${value}!!`, + hello: value => `Hello ${value}!!`, }, }; assert.equal(m("{{#hello name}}{{/hello}}", {name: "Bob"}, options), "Hello Bob!!"); }); + + it("should allow to provide multiple values to custom helper", () => { + const options = { + helpers: { + concat: (a, b) => [a, b].join(" "), + }, + }; + assert.equal(m("{{#concat a b}}{{/concat}}!", {a: "hello", b: "world"}, options), "hello world!"); + }); }); describe("{{@root}}", () => {