diff --git a/es6/tests/e2e/traits.js b/es6/tests/e2e/traits.js new file mode 100644 index 00000000..54100708 --- /dev/null +++ b/es6/tests/e2e/traits.js @@ -0,0 +1,118 @@ +const { expect, makeDocxV4 } = require("../utils.js"); +const Docxtemplater = require("../../docxtemplater.js"); +const { traits, isContent } = Docxtemplater.DocUtils; +const { XTTemplateError } = Docxtemplater.Errors; + +describe("Traits", () => { + it("should work with expandToOne and call onError if multiple tags in same paragraph", () => { + const moduleName = "foo_module/foo"; + const ignoredErrors = []; + const module = { + name: "FooModule", + requiredAPIVersion: "3.0.0", + matchers() { + return [["__", moduleName]]; + }, + postparse(parsed) { + parsed = traits.expandToOne(parsed, { + moduleName, + getInner: ({ part, left, right, postparsed, index }) => { + const paragraphParts = postparsed.slice(left + 1, right); + let error = false; + for (let i = 0, len = paragraphParts.length; i < len; i++) { + const p = paragraphParts[i]; + if (i === index - left - 1) { + continue; + } + if (isContent(p)) { + error = true; + } + } + if (error === true) { + // This error wil be catched by onError and then ignored (and put into ignoredErrs) + const err = new XTTemplateError( + "Foo tag should be the only text in a paragraph" + ); + throw err; + } + return part; + }, + expandTo: "w:p", + onError(opts) { + const { part } = opts; + ignoredErrors.push( + `${opts.rootError.name} ${opts.rootError.message}` + ); + if (part.module === moduleName) { + return "ignore"; + } + }, + }); + return parsed; + }, + render(part) { + if (part.module === moduleName) { + return { + value: "MYVAL", + }; + } + }, + }; + const doc = makeDocxV4("Foo {__user} {__bar}", { + modules: [module], + }).render(); + + expect(ignoredErrors).to.deep.equal([ + "TemplateError Foo tag should be the only text in a paragraph", + ]); + expect(doc.getFullText()).to.be.equal("Foo MYVAL MYVAL"); + }); + + it("should work with expandToOne and call onError if no surrounding paragraphs found", () => { + const moduleName = "foo_module/foo"; + const ignoredErrors = []; + const module = { + name: "FooModule", + requiredAPIVersion: "3.0.0", + matchers() { + return [["__", moduleName]]; + }, + postparse(parsed) { + parsed = traits.expandToOne(parsed, { + moduleName, + expandTo: "w:p", + onError(opts) { + const { part } = opts; + ignoredErrors.push( + `${opts.name || opts.rootError.name} ${opts.message || opts.rootError.message}` + ); + if (part.module === moduleName) { + return "ignore"; + } + }, + error: { + message: "FooModule tag should be the only text in a paragraph", + id: "foo_tag_w_p_noexpand", + }, + }); + return parsed; + }, + render(part) { + if (part.module === moduleName) { + return { + value: "MYVAL", + }; + } + }, + }; + const doc = makeDocxV4("Foo {__user} {__bar}", { + modules: [module], + }).render(); + + expect(ignoredErrors).to.deep.equal([ + "TemplateError FooModule tag should be the only text in a paragraph", + "TemplateError FooModule tag should be the only text in a paragraph", + ]); + expect(doc.getFullText()).to.be.equal("Foo MYVAL MYVAL"); + }); +}); diff --git a/es6/tests/index.js b/es6/tests/index.js index ef2789e9..e61b19aa 100644 --- a/es6/tests/index.js +++ b/es6/tests/index.js @@ -101,6 +101,7 @@ function startTest() { require("./e2e/integration.js"); require("./e2e/doc-props.js"); require("./e2e/modules.js"); + require("./e2e/traits.js"); require("./e2e/pptx.js"); require("./e2e/table.js"); require("./e2e/async.js"); diff --git a/es6/traits.js b/es6/traits.js index db19d417..31e20ef0 100644 --- a/es6/traits.js +++ b/es6/traits.js @@ -205,14 +205,21 @@ function getExpandLimit(part, index, postparsed, options) { right = getRight(postparsed, expandTo, index); } catch (rootError) { if (rootError instanceof XTTemplateError) { - throwExpandNotFound({ + const errProps = { part, rootError, postparsed, expandTo, index, ...options.error, - }); + }; + if (options.onError) { + const errorResult = options.onError(errProps); + if (errorResult === "ignore") { + return; + } + } + throwExpandNotFound(errProps); } throw rootError; } @@ -306,6 +313,18 @@ function expandToOne(postparsed, options) { options ); } catch (error) { + if (options.onError) { + const errorResult = options.onError({ + part: limit.part, + rootError: error, + postparsed, + expandOne, + ...options.errors, + }); + if (errorResult === "ignore") { + continue; + } + } if (error instanceof XTTemplateError) { errors.push(error); } else {