easy-template-x 7.2.0 → 7.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1074,7 +1074,7 @@ const TagPlacement = Object.freeze({
1074
1074
 
1075
1075
  function tagRegex(delimiters, global = false) {
1076
1076
  const tagOptionsPattern = `${Regex.escape(delimiters.tagOptionsStart)}(?<tagOptions>.*?)${Regex.escape(delimiters.tagOptionsEnd)}`;
1077
- const tagPattern = `${Regex.escape(delimiters.tagStart)}(?<tagName>.*?)(${tagOptionsPattern})?${Regex.escape(delimiters.tagEnd)}`;
1077
+ const tagPattern = `${Regex.escape(delimiters.tagStart)}(?<tagName>.*?)(?:\\s*${tagOptionsPattern})?\\s*${Regex.escape(delimiters.tagEnd)}`;
1078
1078
  const flags = global ? 'gm' : 'm';
1079
1079
  return new RegExp(tagPattern, flags);
1080
1080
  }
@@ -1963,6 +1963,9 @@ class Query {
1963
1963
  isRunPropertiesNode(node) {
1964
1964
  return node.nodeName === OmlNode.W.RunProperties || node.nodeName === OmlNode.A.RunProperties;
1965
1965
  }
1966
+ isTableNode(node) {
1967
+ return node.nodeName === OmlNode.W.Table;
1968
+ }
1966
1969
  isTableCellNode(node) {
1967
1970
  return node.nodeName === OmlNode.W.TableCell;
1968
1971
  }
@@ -3409,6 +3412,11 @@ class LoopParagraphStrategy {
3409
3412
  }
3410
3413
  }
3411
3414
 
3415
+ // We cannot leave table cells completely empty, so we track them.
3416
+ // See: http://officeopenxml.com/WPtableCell.php
3417
+ const firstTableCell = officeMarkup.query.containingTableCellNode(firstParagraph);
3418
+ const lastTableCell = officeMarkup.query.containingTableCellNode(lastParagraph);
3419
+
3412
3420
  // Remove old paragraphs - between first and last paragraph.
3413
3421
  xml.modify.removeSiblings(firstParagraph, lastParagraph);
3414
3422
 
@@ -3417,6 +3425,17 @@ class LoopParagraphStrategy {
3417
3425
  if (firstParagraph !== lastParagraph) {
3418
3426
  xml.modify.remove(lastParagraph);
3419
3427
  }
3428
+
3429
+ // Make sure the table cells are not empty (if they exist).
3430
+ if (newParagraphs.length === 0) {
3431
+ this.fillTableCell(firstTableCell);
3432
+ this.fillTableCell(lastTableCell);
3433
+ }
3434
+ }
3435
+ fillTableCell(tableCell) {
3436
+ if (tableCell && !tableCell.childNodes?.find(node => officeMarkup.query.isParagraphNode(node)) && !tableCell.childNodes?.find(node => officeMarkup.query.isTableNode(node))) {
3437
+ xml.modify.appendChild(tableCell, xml.create.generalNode(OmlNode.W.Paragraph));
3438
+ }
3420
3439
  }
3421
3440
  }
3422
3441
 
@@ -5293,7 +5312,7 @@ class TemplateHandler {
5293
5312
  /**
5294
5313
  * Version number of the `easy-template-x` library.
5295
5314
  */
5296
- version = "7.2.0" ;
5315
+ version = "7.2.2" ;
5297
5316
  constructor(options) {
5298
5317
  this.options = new TemplateHandlerOptions(options);
5299
5318
  const delimiters = this.options.delimiters;
@@ -1072,7 +1072,7 @@ const TagPlacement = Object.freeze({
1072
1072
 
1073
1073
  function tagRegex(delimiters, global = false) {
1074
1074
  const tagOptionsPattern = `${Regex.escape(delimiters.tagOptionsStart)}(?<tagOptions>.*?)${Regex.escape(delimiters.tagOptionsEnd)}`;
1075
- const tagPattern = `${Regex.escape(delimiters.tagStart)}(?<tagName>.*?)(${tagOptionsPattern})?${Regex.escape(delimiters.tagEnd)}`;
1075
+ const tagPattern = `${Regex.escape(delimiters.tagStart)}(?<tagName>.*?)(?:\\s*${tagOptionsPattern})?\\s*${Regex.escape(delimiters.tagEnd)}`;
1076
1076
  const flags = global ? 'gm' : 'm';
1077
1077
  return new RegExp(tagPattern, flags);
1078
1078
  }
@@ -1961,6 +1961,9 @@ class Query {
1961
1961
  isRunPropertiesNode(node) {
1962
1962
  return node.nodeName === OmlNode.W.RunProperties || node.nodeName === OmlNode.A.RunProperties;
1963
1963
  }
1964
+ isTableNode(node) {
1965
+ return node.nodeName === OmlNode.W.Table;
1966
+ }
1964
1967
  isTableCellNode(node) {
1965
1968
  return node.nodeName === OmlNode.W.TableCell;
1966
1969
  }
@@ -3407,6 +3410,11 @@ class LoopParagraphStrategy {
3407
3410
  }
3408
3411
  }
3409
3412
 
3413
+ // We cannot leave table cells completely empty, so we track them.
3414
+ // See: http://officeopenxml.com/WPtableCell.php
3415
+ const firstTableCell = officeMarkup.query.containingTableCellNode(firstParagraph);
3416
+ const lastTableCell = officeMarkup.query.containingTableCellNode(lastParagraph);
3417
+
3410
3418
  // Remove old paragraphs - between first and last paragraph.
3411
3419
  xml.modify.removeSiblings(firstParagraph, lastParagraph);
3412
3420
 
@@ -3415,6 +3423,17 @@ class LoopParagraphStrategy {
3415
3423
  if (firstParagraph !== lastParagraph) {
3416
3424
  xml.modify.remove(lastParagraph);
3417
3425
  }
3426
+
3427
+ // Make sure the table cells are not empty (if they exist).
3428
+ if (newParagraphs.length === 0) {
3429
+ this.fillTableCell(firstTableCell);
3430
+ this.fillTableCell(lastTableCell);
3431
+ }
3432
+ }
3433
+ fillTableCell(tableCell) {
3434
+ if (tableCell && !tableCell.childNodes?.find(node => officeMarkup.query.isParagraphNode(node)) && !tableCell.childNodes?.find(node => officeMarkup.query.isTableNode(node))) {
3435
+ xml.modify.appendChild(tableCell, xml.create.generalNode(OmlNode.W.Paragraph));
3436
+ }
3418
3437
  }
3419
3438
  }
3420
3439
 
@@ -5291,7 +5310,7 @@ class TemplateHandler {
5291
5310
  /**
5292
5311
  * Version number of the `easy-template-x` library.
5293
5312
  */
5294
- version = "7.2.0" ;
5313
+ version = "7.2.2" ;
5295
5314
  constructor(options) {
5296
5315
  this.options = new TemplateHandlerOptions(options);
5297
5316
  const delimiters = this.options.delimiters;
@@ -8,6 +8,7 @@ declare class Query {
8
8
  isTextNode(node: XmlNode): boolean;
9
9
  isRunNode(node: XmlNode): boolean;
10
10
  isRunPropertiesNode(node: XmlNode): boolean;
11
+ isTableNode(node: XmlNode): boolean;
11
12
  isTableCellNode(node: XmlNode): boolean;
12
13
  isParagraphNode(node: XmlNode): boolean;
13
14
  isParagraphPropertiesNode(node: XmlNode): boolean;
@@ -5,4 +5,5 @@ export declare class LoopParagraphStrategy implements ILoopStrategy {
5
5
  isApplicable(openTag: TextNodeTag, closeTag: TextNodeTag, isCondition: boolean): boolean;
6
6
  splitBefore(openTag: TextNodeTag, closeTag: TextNodeTag): SplitBeforeResult;
7
7
  mergeBack(newParagraphs: XmlNode[][], firstParagraph: XmlNode, lastParagraph: XmlNode): void;
8
+ private fillTableCell;
8
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easy-template-x",
3
- "version": "7.2.0",
3
+ "version": "7.2.2",
4
4
  "description": "Generate docx documents from templates, in Node or in the browser.",
5
5
  "keywords": [
6
6
  "docx",
@@ -759,6 +759,59 @@ describe(TagParser, () => {
759
759
  expect(tags[1].rawText).toEqual('{/loop}');
760
760
  });
761
761
 
762
+ test('simple - with whitespace', () => {
763
+
764
+ const text = '{ # loop [opt: "yes"] }{ / loop }';
765
+ const paragraph = parseXml(`
766
+ <w:p><w:r><w:t>${text}</w:t></w:r></w:p>
767
+ `, false);
768
+
769
+ const textNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0);
770
+ const delimiters: TextNodeDelimiterMark[] = [
771
+ {
772
+ placement: TagPlacement.TextNode,
773
+ isOpen: true,
774
+ index: 0,
775
+ xmlTextNode: textNode
776
+ },
777
+ {
778
+ placement: TagPlacement.TextNode,
779
+ isOpen: false,
780
+ index: 22,
781
+ xmlTextNode: textNode
782
+ },
783
+ {
784
+ placement: TagPlacement.TextNode,
785
+ isOpen: true,
786
+ index: 23,
787
+ xmlTextNode: textNode
788
+ },
789
+ {
790
+ placement: TagPlacement.TextNode,
791
+ isOpen: false,
792
+ index: 32,
793
+ xmlTextNode: textNode
794
+ }
795
+ ];
796
+
797
+ const parser = createTagParser();
798
+ const tags = parser.parse(delimiters);
799
+
800
+ expect(tags.length).toEqual(2);
801
+
802
+ // open tag
803
+ expect(tags[0].disposition).toEqual(TagDisposition.Open);
804
+ expect(tags[0].name).toEqual('loop');
805
+ expect(tags[0].options).toEqual({ opt: 'yes' });
806
+ expect(tags[0].rawText).toEqual('{ # loop [opt: "yes"] }');
807
+
808
+ // close tag
809
+ expect(tags[1].disposition).toEqual(TagDisposition.Close);
810
+ expect(tags[1].name).toEqual('loop');
811
+ expect(tags[1].options).toBeFalsy();
812
+ expect(tags[1].rawText).toEqual('{ / loop }');
813
+ });
814
+
762
815
  test('angular parser style with brackets', () => {
763
816
 
764
817
  const text = '{something[0] [[myOpt: 5]]}';
@@ -3,7 +3,7 @@ import { Regex } from "src/utils";
3
3
 
4
4
  export function tagRegex(delimiters: Delimiters, global: boolean = false): RegExp {
5
5
  const tagOptionsPattern = `${Regex.escape(delimiters.tagOptionsStart)}(?<tagOptions>.*?)${Regex.escape(delimiters.tagOptionsEnd)}`;
6
- const tagPattern = `${Regex.escape(delimiters.tagStart)}(?<tagName>.*?)(${tagOptionsPattern})?${Regex.escape(delimiters.tagEnd)}`;
6
+ const tagPattern = `${Regex.escape(delimiters.tagStart)}(?<tagName>.*?)(?:\\s*${tagOptionsPattern})?\\s*${Regex.escape(delimiters.tagEnd)}`;
7
7
  const flags = global ? 'gm' : 'm';
8
8
  return new RegExp(tagPattern, flags);
9
9
  }
@@ -62,6 +62,10 @@ class Query {
62
62
  return node.nodeName === OmlNode.W.RunProperties || node.nodeName === OmlNode.A.RunProperties;
63
63
  }
64
64
 
65
+ public isTableNode(node: XmlNode): boolean {
66
+ return node.nodeName === OmlNode.W.Table;
67
+ }
68
+
65
69
  public isTableCellNode(node: XmlNode): boolean {
66
70
  return node.nodeName === OmlNode.W.TableCell;
67
71
  }
@@ -1,7 +1,7 @@
1
1
  import { TextNodeTag } from "src/compilation";
2
+ import { officeMarkup, OmlNode } from "src/office";
2
3
  import { LoopOver, LoopTagOptions } from "src/plugins/loop/loopTagOptions";
3
- import { officeMarkup } from "src/office";
4
- import { xml, XmlNode } from "src/xml";
4
+ import { xml, XmlGeneralNode, XmlNode } from "src/xml";
5
5
  import { ILoopStrategy, SplitBeforeResult } from "./iLoopStrategy";
6
6
 
7
7
  export class LoopParagraphStrategy implements ILoopStrategy {
@@ -38,6 +38,11 @@ export class LoopParagraphStrategy implements ILoopStrategy {
38
38
  }
39
39
  }
40
40
 
41
+ // We cannot leave table cells completely empty, so we track them.
42
+ // See: http://officeopenxml.com/WPtableCell.php
43
+ const firstTableCell = officeMarkup.query.containingTableCellNode(firstParagraph);
44
+ const lastTableCell = officeMarkup.query.containingTableCellNode(lastParagraph);
45
+
41
46
  // Remove old paragraphs - between first and last paragraph.
42
47
  xml.modify.removeSiblings(firstParagraph, lastParagraph);
43
48
 
@@ -46,5 +51,21 @@ export class LoopParagraphStrategy implements ILoopStrategy {
46
51
  if (firstParagraph !== lastParagraph) {
47
52
  xml.modify.remove(lastParagraph);
48
53
  }
54
+
55
+ // Make sure the table cells are not empty (if they exist).
56
+ if (newParagraphs.length === 0) {
57
+ this.fillTableCell(firstTableCell);
58
+ this.fillTableCell(lastTableCell);
59
+ }
60
+ }
61
+
62
+ private fillTableCell(tableCell: XmlGeneralNode): void {
63
+ if (
64
+ tableCell &&
65
+ !tableCell.childNodes?.find(node => officeMarkup.query.isParagraphNode(node)) &&
66
+ !tableCell.childNodes?.find(node => officeMarkup.query.isTableNode(node))
67
+ ) {
68
+ xml.modify.appendChild(tableCell, xml.create.generalNode(OmlNode.W.Paragraph));
69
+ }
49
70
  }
50
71
  }