From 1ad6a198b889e260bf76ae354c0043be2226006e Mon Sep 17 00:00:00 2001 From: Edgar HIPP Date: Mon, 27 Jan 2025 19:16:52 +0100 Subject: [PATCH] Add syntax.allowUnbalancedLoops option --- CHANGELOG.md | 36 +++++++++++++++++ es6/modules/expand-pair-trait.js | 27 +++++++++++-- es6/tests/e2e/integration.js | 37 ++++++++++++++++++ .../expected-table-unbalanced-loop-2.docx | Bin 0 -> 6227 bytes examples/expected-table-unbalanced-loop.docx | Bin 0 -> 6584 bytes examples/table-unbalanced-loop-2.docx | Bin 0 -> 5896 bytes examples/table-unbalanced-loop.docx | Bin 0 -> 6557 bytes 7 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 examples/expected-table-unbalanced-loop-2.docx create mode 100644 examples/expected-table-unbalanced-loop.docx create mode 100644 examples/table-unbalanced-loop-2.docx create mode 100644 examples/table-unbalanced-loop.docx diff --git a/CHANGELOG.md b/CHANGELOG.md index d6baf0e6..4908df62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,39 @@ +### 3.58.4 + +Make it possible to write unbalanced loops, if specifying the following the option : + +```js +const expressionParser = require("docxtemplater/expressions.js"); +const doc = new Docxtemplater(zip, { + paragraphLoop: true, + linebreaks: true, + syntax: { + allowUnbalancedLoops: true, + }, +}); +doc.render(/* data */); +``` + +Then the following template in a table will work + +```docx +------------------------- +| {#a} | {/a}{#b}{/b} | +------------------------- +``` + +This template will normally throw an "unbalanced_loop_tags" exception. + +The correct fix is to move the {#b} to be inside the {#a} loop like this : + +```docx +------------------------- +| {#a} | {#b}{/b}{/a} | +------------------------- +``` + +However, if you have some templates that were used before v3.19.9 of docxtemplater, and can't change the templates, you can use the `allowUnbalancedLoops` option. + ### 3.58.3 Do not throw an error if sending xml invalid character such as "\u0002" when using TxtTemplater. diff --git a/es6/modules/expand-pair-trait.js b/es6/modules/expand-pair-trait.js index 5d4a78ce..e03b32cc 100644 --- a/es6/modules/expand-pair-trait.js +++ b/es6/modules/expand-pair-trait.js @@ -161,9 +161,13 @@ class ExpandPairTrait { if (!expandTo || fileType === "text") { const left = pair[0].offset; const right = pair[1].offset; - if (left < lastRight) { + if ( + left < lastRight && + !this.docxtemplater.options.syntax.allowUnbalancedLoops + ) { errors.push(getUnbalancedLoopException(pair, lastPair)); } + lastPair = pair; lastRight = right; return [left, right]; @@ -179,7 +183,10 @@ class ExpandPairTrait { } catch (e) { errors.push(e); } - if (left < lastRight) { + if ( + left < lastRight && + !this.docxtemplater.options.syntax.allowUnbalancedLoops + ) { errors.push(getUnbalancedLoopException(pair, lastPair)); } lastRight = right; @@ -192,7 +199,6 @@ class ExpandPairTrait { return { postparsed, errors }; } // Stryker restore all - let currentPairIndex = 0; let innerParts; @@ -207,13 +213,17 @@ class ExpandPairTrait { newParsed.push(part); return newParsed; } + // We're inside the pair if (expandedPair[0] === i) { + // Start pair innerParts = []; } if (pair[0].offset !== i && pair[1].offset !== i) { + // Exclude inner pair indexes innerParts.push(part); } if (expandedPair[1] === i) { + // End pair const basePart = postparsed[pair[0].offset]; basePart.subparsed = postparse(innerParts, { basePart }); basePart.endLindex = pair[1].part.lIndex; @@ -221,6 +231,17 @@ class ExpandPairTrait { delete basePart.expandTo; newParsed.push(basePart); currentPairIndex++; + let expandedPair = expandedPairs[currentPairIndex]; + while (expandedPair && expandedPair[0] < i) { + /* + * If we have : + * expandedPairs =[[5,72],[51,67],[90,106]] + * Then after treating [5,72], we need to treat [90,106] + * Fixed since v3.58.4 + */ + currentPairIndex++; + expandedPair = expandedPairs[currentPairIndex]; + } } return newParsed; }, []); diff --git a/es6/tests/e2e/integration.js b/es6/tests/e2e/integration.js index 87edb0c8..5d75763e 100644 --- a/es6/tests/e2e/integration.js +++ b/es6/tests/e2e/integration.js @@ -1353,3 +1353,40 @@ describe("OptionsTransformer", () => { ); }); }); + +describe("Syntax.allowUnbalancedLoops option", () => { + it("should work with unbalanced loop with allowUnbalancedLoops option", () => { + const doc = createDocV4("table-unbalanced-loop.docx", { + syntax: { + allowUnbalancedLoops: true, + }, + }); + doc.render({ + a: [1, 2, 3], + b: [1, 2, 3], + c: [1, 2, 3], + }); + shouldBeSame({ + doc, + expectedName: "expected-table-unbalanced-loop.docx", + }); + }); + + it("should work with unbalanced loop with allowUnbalancedLoops option (2)", () => { + const doc = createDocV4("table-unbalanced-loop-2.docx", { + syntax: { + allowUnbalancedLoops: true, + }, + }); + + doc.render({ + a: [1, 2, 3], + b: [1, 2, 3], + c: [1, 2, 3], + }); + shouldBeSame({ + doc, + expectedName: "expected-table-unbalanced-loop-2.docx", + }); + }); +}); diff --git a/examples/expected-table-unbalanced-loop-2.docx b/examples/expected-table-unbalanced-loop-2.docx new file mode 100644 index 0000000000000000000000000000000000000000..9d8cb02068b247f8894ff510e77b4d085e2cce39 GIT binary patch literal 6227 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 literal 0 HcmV?d00001 diff --git a/examples/expected-table-unbalanced-loop.docx b/examples/expected-table-unbalanced-loop.docx new file mode 100644 index 0000000000000000000000000000000000000000..6387bef4f3c13900aae7a4ae99422122e3f55d58 GIT binary patch literal 6584 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 literal 0 HcmV?d00001 diff --git a/examples/table-unbalanced-loop.docx b/examples/table-unbalanced-loop.docx new file mode 100644 index 0000000000000000000000000000000000000000..d033b6f71cf8f2205831293a77b75d19a0f39110 GIT binary patch literal 6557 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