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 {