diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4b991fc..33dc5d12 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+### 3.52.0
+
+Add `syntax.allowUnclosedTag` option.
+
+This allows to write : `Hello {user` and not have an error in your template.
+
### 3.51.2
Improve typescript typings :
diff --git a/es6/docxtemplater.d.ts b/es6/docxtemplater.d.ts
index a0c9d518..d023edc2 100644
--- a/es6/docxtemplater.d.ts
+++ b/es6/docxtemplater.d.ts
@@ -82,6 +82,7 @@ declare namespace DXT {
interface Syntax {
allowUnopenedTag?: boolean;
+ allowUnclosedTag?: boolean;
changeDelimiterPrefix?: string | null;
}
diff --git a/es6/docxtemplater.test-d.ts b/es6/docxtemplater.test-d.ts
index da57f2d6..6b790881 100644
--- a/es6/docxtemplater.test-d.ts
+++ b/es6/docxtemplater.test-d.ts
@@ -189,6 +189,7 @@ const doc8 = new Docxtemplater(new PizZip("hello"), {
const doc9 = new Docxtemplater(new PizZip("hello"), {
syntax: {
allowUnopenedTag: true,
+ allowUnclosedTag: true,
changeDelimiterPrefix: null,
},
});
diff --git a/es6/lexer.js b/es6/lexer.js
index 1d26fa2e..003d5f84 100644
--- a/es6/lexer.js
+++ b/es6/lexer.js
@@ -126,9 +126,22 @@ function getDelimiterErrors(delimiterMatches, fullText, syntaxOptions) {
lastDelimiterOffset,
delimiterOffset - lastDelimiterOffset + lastDelimiterLength + 4
);
+ if (!syntaxOptions.allowUnclosedTag) {
+ errors.push(
+ getDuplicateOpenTagException({
+ xtag,
+ offset: lastDelimiterOffset,
+ })
+ );
+ lastDelimiterMatch = currDelimiterMatch;
+ delimiterAcc.push({ ...currDelimiterMatch, error: true });
+ return delimiterAcc;
+ }
+ }
+ if (!syntaxOptions.allowUnclosedTag) {
errors.push(
- getDuplicateOpenTagException({
- xtag,
+ getUnclosedTagException({
+ xtag: wordToUtf8(xtag),
offset: lastDelimiterOffset,
})
);
@@ -136,15 +149,7 @@ function getDelimiterErrors(delimiterMatches, fullText, syntaxOptions) {
delimiterAcc.push({ ...currDelimiterMatch, error: true });
return delimiterAcc;
}
- errors.push(
- getUnclosedTagException({
- xtag: wordToUtf8(xtag),
- offset: lastDelimiterOffset,
- })
- );
- lastDelimiterMatch = currDelimiterMatch;
- delimiterAcc.push({ ...currDelimiterMatch, error: true });
- return delimiterAcc;
+ delimiterAcc.pop();
}
if (!inDelimiter && position === "end") {
@@ -177,7 +182,7 @@ function getDelimiterErrors(delimiterMatches, fullText, syntaxOptions) {
return delimiterAcc;
}
- inDelimiter = !inDelimiter;
+ inDelimiter = position === "start";
lastDelimiterMatch = currDelimiterMatch;
delimiterAcc.push(currDelimiterMatch);
return delimiterAcc;
@@ -191,12 +196,16 @@ function getDelimiterErrors(delimiterMatches, fullText, syntaxOptions) {
lastDelimiterOffset,
fullText.length - lastDelimiterOffset
);
- errors.push(
- getUnclosedTagException({
- xtag: wordToUtf8(xtag),
- offset: lastDelimiterOffset,
- })
- );
+ if (!syntaxOptions.allowUnclosedTag) {
+ errors.push(
+ getUnclosedTagException({
+ xtag: wordToUtf8(xtag),
+ offset: lastDelimiterOffset,
+ })
+ );
+ } else {
+ delimiterWithErrors.pop();
+ }
}
return {
diff --git a/es6/tests/e2e/fixtures.js b/es6/tests/e2e/fixtures.js
index f198d11c..4d3ae164 100644
--- a/es6/tests/e2e/fixtures.js
+++ b/es6/tests/e2e/fixtures.js
@@ -1656,47 +1656,47 @@ const fixtures = [
},
{
it: "should work well with $index angular parser",
- content: "{#todos}{#$index==0}FIRST {/}{text} {/todos}",
+ content: "{#list}{#$index==0}FIRST {/}{text} {/list}",
...noInternals,
options: {
parser: expressionParser,
},
- scope: { todos: [{ text: "Hello" }, { text: "Other todo" }] },
- result: 'FIRST Hello Other todo ',
+ scope: { list: [{ text: "Hello" }, { text: "Other item" }] },
+ result: 'FIRST Hello Other item ',
},
{
it: "should work well with $index inside condition angular parser",
content:
- "{#todos}{#important}!!{$index+1}{text}{/}{^important}?{$index+1}{text}{/}{/}",
+ "{#list}{#important}!!{$index+1}{text}{/}{^important}?{$index+1}{text}{/}{/}",
...noInternals,
options: {
parser: expressionParser,
},
scope: {
- todos: [
+ list: [
{ important: true, text: "Hello" },
- { text: "Other todo" },
+ { text: "Other item" },
{ important: true, text: "Bye" },
],
},
- result: '!!1Hello?2Other todo!!3Bye',
+ result: '!!1Hello?2Other item!!3Bye',
},
{
it: "should work well with $index inside condition angular parser",
content:
- "{#todos}{#important}!!{$index+1}{text}{/}{^important}?{$index+1}{text}{/}{/}",
+ "{#list}{#important}!!{$index+1}{text}{/}{^important}?{$index+1}{text}{/}{/}",
...noInternals,
options: {
parser: angularParserIE11,
},
scope: {
- todos: [
+ list: [
{ important: true, text: "Hello" },
- { text: "Other todo" },
+ { text: "Other item" },
{ important: true, text: "Bye" },
],
},
- result: '!!1Hello?2Other todo!!3Bye',
+ result: '!!1Hello?2Other item!!3Bye',
},
{
it: "should work well with nested conditions inside table",
@@ -2136,6 +2136,70 @@ http://errors.angularjs.org/"NG_VERSION_FULL"/$parse/lexerr?p0=Unexpected%20next
world
+ `,
+ },
+ {
+ it: "should not fail on triple open tag if allowUnclosedTag is true",
+ ...noInternals,
+ content: `
+
+ Hello {{{
+
+
+ lastName
+
+
+ } world
+
+ `,
+ options: {
+ syntax: {
+ allowUnclosedTag: true,
+ },
+ },
+ scope: { firstName: "John", lastName: "Doe" },
+ result: `
+
+ Hello {{Doe
+
+
+
+
+
+ world
+
+ `,
+ },
+ {
+ it: "should not fail on SPACED unclosed tag if allowUnclosedTag is true",
+ ...noInternals,
+ content: `
+
+ Hello {firstName {
+
+
+ lastName
+
+
+ } world
+
+ `,
+ options: {
+ syntax: {
+ allowUnclosedTag: true,
+ },
+ },
+ scope: { firstName: "John", lastName: "Doe" },
+ result: `
+
+ Hello {firstName Doe
+
+
+
+
+
+ world
+
`,
},
{
@@ -2168,6 +2232,65 @@ http://errors.angularjs.org/"NG_VERSION_FULL"/$parse/lexerr?p0=Unexpected%20next
} world} } }
+ `,
+ },
+ {
+ it: "should not fail if allowUnclosedTag on 'Hello {' string",
+ ...noInternals,
+ content: "Hello {",
+ options: {
+ syntax: {
+ allowUnclosedTag: true,
+ allowUnopenedTag: true,
+ },
+ },
+ scope: { firstName: "John", lastName: "Doe" },
+ result: "Hello {",
+ },
+ {
+ it: "should not fail if allowUnclosedTag on 'Hello }' string",
+ ...noInternals,
+ content: "Hello }",
+ options: {
+ syntax: {
+ allowUnclosedTag: true,
+ allowUnopenedTag: true,
+ },
+ },
+ scope: { firstName: "John", lastName: "Doe" },
+ result: "Hello }",
+ },
+ {
+ it: "should not fail on double delimiters if allowUnclosedTag and allowUnopenedTag is true",
+ ...noInternals,
+ content: `
+
+ Hello {{
+
+
+ lastName
+
+
+ }}
+
+ `,
+ options: {
+ syntax: {
+ allowUnclosedTag: true,
+ allowUnopenedTag: true,
+ },
+ },
+ scope: { firstName: "John", lastName: "Doe" },
+ result: `
+
+ Hello {Doe
+
+
+
+
+
+ }
+
`,
},
{