easy-template-x 2.1.0 → 3.0.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.
package/README.md CHANGED
@@ -2,10 +2,9 @@
2
2
 
3
3
  Generate docx documents from templates, in Node or in the browser.
4
4
 
5
- [![CircleCI](https://circleci.com/gh/alonrbar/easy-template-x.svg?style=shield)](https://circleci.com/gh/alonrbar/easy-template-x)
5
+ [![ci](https://github.com/alonrbar/easy-template-x/actions/workflows/ci.yaml/badge.svg)](https://github.com/alonrbar/easy-template-x/actions/workflows/ci.yaml)
6
6
  [![npm version](https://img.shields.io/npm/v/easy-template-x.svg)](https://www.npmjs.com/package/easy-template-x)
7
7
  [![npm license](https://img.shields.io/npm/l/easy-template-x.svg)](https://www.npmjs.com/package/easy-template-x)
8
- [![dependencies](https://david-dm.org/alonrbar/easy-template-x.svg)](https://github.com/alonrbar/easy-template-x)
9
8
 
10
9
  - [Node Example](#node-example)
11
10
  - [Browser Example](#browser-example)
@@ -14,10 +13,12 @@ Generate docx documents from templates, in Node or in the browser.
14
13
  - [Text plugin](#text-plugin)
15
14
  - [Loop plugin](#loop-plugin)
16
15
  - [Conditions](#conditions)
16
+ - [Nested Conditions](#nested-conditions)
17
17
  - [Image plugin](#image-plugin)
18
18
  - [Link plugin](#link-plugin)
19
19
  - [Raw xml plugin](#raw-xml-plugin)
20
20
  - [Writing custom plugins](#writing-your-own-plugins)
21
+ - [Listing tags](#listing-tags)
21
22
  - [Scope resolution](#scope-resolution)
22
23
  - [Extensions](#extensions)
23
24
  - [Community Extensions](#community-extensions)
@@ -186,7 +187,7 @@ Output document:
186
187
 
187
188
  You can render content conditionally depending on a boolean value using the same syntax used for loops.
188
189
 
189
- The example below shows two lines being rendered, each with different content depeneding on the truthy value.
190
+ The example below shows two lines being rendered, each with different content depending on the truthy value.
190
191
 
191
192
  Input template:
192
193
 
@@ -207,7 +208,50 @@ Output document:
207
208
 
208
209
  ![output document](./docs/assets/simple-condition-out.png?raw=true)
209
210
 
210
- _For a more powerful conditional syntax see the [alternative syntax](#advanced-syntax-and-custom-resolvers) section._
211
+ #### Nested Conditions
212
+
213
+ Nested conditions are also supported, so you can nest other tags including loop tags and even other conditions in them. When doing so remember to format your data accordingly. See the example below for clarification:
214
+
215
+ Input template:
216
+
217
+ ![input template](./docs/assets/nested-conditions-in.png?raw=true)
218
+
219
+ Input data:
220
+
221
+ Notice how even though `name` and `members` are nested in the template under the `show` condition their values are adjacent to it in the input data.
222
+
223
+ ```javascript
224
+ {
225
+ "teams": [
226
+ {
227
+ show: true,
228
+ name: "A-Team",
229
+ members: [
230
+ { name: "Hannibal" },
231
+ { name: "Face" },
232
+ { name: "Murdock" },
233
+ { name: "Baracus" },
234
+ ]
235
+ },
236
+ {
237
+ show: false,
238
+ name: "B-Team",
239
+ members: [
240
+ { name: "Alice" },
241
+ { name: "Bob" },
242
+ { name: "Charlie" },
243
+ { name: "Dave" },
244
+ ]
245
+ }
246
+ ],
247
+ }
248
+ ```
249
+
250
+ Output document:
251
+
252
+ ![output document](./docs/assets/nested-conditions-out.png?raw=true)
253
+
254
+ _If you are looking for a yet more powerful conditional syntax see the [alternative syntax](#advanced-syntax-and-custom-resolvers) section._
211
255
 
212
256
  ### Image plugin
213
257
 
@@ -340,6 +384,19 @@ export interface RawXmlContent extends PluginContent {
340
384
  }
341
385
  ```
342
386
 
387
+ ## Listing tags
388
+
389
+ You can get the list of [tags](https://github.com/alonrbar/easy-template-x/blob/8a88535ef090fc357cf3523411bef0d0729d10c8/src/compilation/tag.ts) in a template by calling the `parseTags` method as follows:
390
+
391
+ ```typescript
392
+ import { TemplateHandler } from 'easy-template-x';
393
+
394
+ const templateFile = fs.readFileSync('myTemplate.docx');
395
+
396
+ const handler = new TemplateHandler();
397
+ const tags = await handler.parseTags(templateFile);
398
+ ```
399
+
343
400
  ## Scope resolution
344
401
 
345
402
  `easy-template-x` supports tag data scoping. That is, you can reference
@@ -3,8 +3,11 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var xmldom = require('xmldom');
6
+ var getProp = require('lodash.get');
6
7
  var JSZip = require('jszip');
7
8
 
9
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10
+
8
11
  function _interopNamespace(e) {
9
12
  if (e && e.__esModule) return e;
10
13
  var n = Object.create(null);
@@ -14,18 +17,17 @@ function _interopNamespace(e) {
14
17
  var d = Object.getOwnPropertyDescriptor(e, k);
15
18
  Object.defineProperty(n, k, d.get ? d : {
16
19
  enumerable: true,
17
- get: function () {
18
- return e[k];
19
- }
20
+ get: function () { return e[k]; }
20
21
  });
21
22
  }
22
23
  });
23
24
  }
24
- n['default'] = e;
25
+ n["default"] = e;
25
26
  return Object.freeze(n);
26
27
  }
27
28
 
28
29
  var xmldom__namespace = /*#__PURE__*/_interopNamespace(xmldom);
30
+ var getProp__default = /*#__PURE__*/_interopDefaultLegacy(getProp);
29
31
  var JSZip__namespace = /*#__PURE__*/_interopNamespace(JSZip);
30
32
 
31
33
  function _defineProperty(obj, key, value) {
@@ -1118,26 +1120,26 @@ class DelimiterSearcher {
1118
1120
  }
1119
1121
  } // no match
1120
1122
  else {
1121
- //
1122
- // go back to first open node
1123
- //
1124
- // Required for cases where the text has repeating
1125
- // characters that are the same as a delimiter prefix.
1126
- // For instance:
1127
- // Delimiter is '{!' and template text contains the string '{{!'
1128
- //
1129
- if (match.firstMatchIndex !== -1) {
1130
- node = first(match.openNodes);
1131
- textIndex = match.firstMatchIndex;
1132
- } // update state
1133
-
1134
-
1135
- match.reset();
1136
-
1137
- if (textIndex < node.textContent.length - 1) {
1138
- match.openNodes.push(node);
1139
- }
1123
+ //
1124
+ // go back to first open node
1125
+ //
1126
+ // Required for cases where the text has repeating
1127
+ // characters that are the same as a delimiter prefix.
1128
+ // For instance:
1129
+ // Delimiter is '{!' and template text contains the string '{{!'
1130
+ //
1131
+ if (match.firstMatchIndex !== -1) {
1132
+ node = first(match.openNodes);
1133
+ textIndex = match.firstMatchIndex;
1134
+ } // update state
1135
+
1136
+
1137
+ match.reset();
1138
+
1139
+ if (textIndex < node.textContent.length - 1) {
1140
+ match.openNodes.push(node);
1140
1141
  }
1142
+ }
1141
1143
 
1142
1144
  textIndex++;
1143
1145
  }
@@ -1190,8 +1192,6 @@ class DelimiterSearcher {
1190
1192
 
1191
1193
  }
1192
1194
 
1193
- const getProp = require("lodash.get");
1194
-
1195
1195
  class ScopeData {
1196
1196
  static defaultResolver(args) {
1197
1197
  let result;
@@ -1200,7 +1200,7 @@ class ScopeData {
1200
1200
 
1201
1201
  while (result === undefined && curPath.length) {
1202
1202
  curPath.pop();
1203
- result = getProp(args.data, curPath.concat(lastKey));
1203
+ result = getProp__default["default"](args.data, curPath.concat(lastKey));
1204
1204
  }
1205
1205
 
1206
1206
  return result;
@@ -2619,9 +2619,9 @@ class LinkPlugin extends TemplatePlugin {
2619
2619
  }
2620
2620
  } // already isolated
2621
2621
  else {
2622
- XmlNode.insertAfter(linkMarkup, tagRunNode);
2623
- XmlNode.remove(tagRunNode);
2624
- }
2622
+ XmlNode.insertAfter(linkMarkup, tagRunNode);
2623
+ XmlNode.remove(tagRunNode);
2624
+ }
2625
2625
  }
2626
2626
 
2627
2627
  }
@@ -2807,7 +2807,9 @@ class LoopPlugin extends TemplatePlugin {
2807
2807
  async containerTagReplacements(tags, data, context) {
2808
2808
  let value = data.getScopeData(); // Non array value - treat as a boolean condition.
2809
2809
 
2810
- if (!Array.isArray(value)) {
2810
+ const isCondition = !Array.isArray(value);
2811
+
2812
+ if (isCondition) {
2811
2813
  if (!!value) {
2812
2814
  value = [{}];
2813
2815
  } else {
@@ -2833,7 +2835,7 @@ class LoopPlugin extends TemplatePlugin {
2833
2835
  // path to each token and use that to create new tokens instead of
2834
2836
  // search through the text again)
2835
2837
 
2836
- const compiledNodes = await this.compile(repeatedNodes, data, context); // merge back to the document
2838
+ const compiledNodes = await this.compile(isCondition, repeatedNodes, data, context); // merge back to the document
2837
2839
 
2838
2840
  loopStrategy.mergeBack(compiledNodes, firstNode, lastNode);
2839
2841
  }
@@ -2850,7 +2852,7 @@ class LoopPlugin extends TemplatePlugin {
2850
2852
  return allResults;
2851
2853
  }
2852
2854
 
2853
- async compile(nodeGroups, data, context) {
2855
+ async compile(isCondition, nodeGroups, data, context) {
2854
2856
  const compiledNodeGroups = []; // compile each node group with it's relevant data
2855
2857
 
2856
2858
  for (let i = 0; i < nodeGroups.length; i++) {
@@ -2859,9 +2861,9 @@ class LoopPlugin extends TemplatePlugin {
2859
2861
  const dummyRootNode = XmlNode.createGeneralNode('dummyRootNode');
2860
2862
  curNodes.forEach(node => XmlNode.appendChild(dummyRootNode, node)); // compile the new root
2861
2863
 
2862
- data.pathPush(i);
2864
+ const conditionTag = this.updatePathBefore(isCondition, data, i);
2863
2865
  await this.utilities.compiler.compile(dummyRootNode, data, context);
2864
- data.pathPop(); // disconnect from dummy root
2866
+ this.updatePathAfter(isCondition, data, conditionTag); // disconnect from dummy root
2865
2867
 
2866
2868
  const curResult = [];
2867
2869
 
@@ -2876,6 +2878,32 @@ class LoopPlugin extends TemplatePlugin {
2876
2878
  return compiledNodeGroups;
2877
2879
  }
2878
2880
 
2881
+ updatePathBefore(isCondition, data, groupIndex) {
2882
+ // if it's a condition - don't go deeper in the path
2883
+ // (so we need to extract the already pushed condition tag)
2884
+ if (isCondition) {
2885
+ if (groupIndex > 0) {
2886
+ // should never happen - conditions should have at most one (synthetic) child...
2887
+ throw new Error(`Internal error: Unexpected group index ${groupIndex} for boolean condition at path "${data.pathString()}".`);
2888
+ }
2889
+
2890
+ return data.pathPop();
2891
+ } // else, it's an array - push the current index
2892
+
2893
+
2894
+ data.pathPush(groupIndex);
2895
+ return null;
2896
+ }
2897
+
2898
+ updatePathAfter(isCondition, data, conditionTag) {
2899
+ // reverse the "before" path operation
2900
+ if (isCondition) {
2901
+ data.pathPush(conditionTag);
2902
+ } else {
2903
+ data.pathPop();
2904
+ }
2905
+ }
2906
+
2879
2907
  }
2880
2908
 
2881
2909
  class RawXmlPlugin extends TemplatePlugin {
@@ -3280,7 +3308,7 @@ class TemplateHandler {
3280
3308
  constructor(options) {
3281
3309
  var _this$options$extensi, _this$options$extensi2, _this$options$extensi3, _this$options$extensi4;
3282
3310
 
3283
- _defineProperty(this, "version", "2.1.0" );
3311
+ _defineProperty(this, "version", "3.0.2" );
3284
3312
 
3285
3313
  _defineProperty(this, "xmlParser", new XmlParser());
3286
3314
 
@@ -1,4 +1,5 @@
1
1
  import * as xmldom from 'xmldom';
2
+ import getProp from 'lodash.get';
2
3
  import * as JSZip from 'jszip';
3
4
 
4
5
  function _defineProperty(obj, key, value) {
@@ -1091,26 +1092,26 @@ class DelimiterSearcher {
1091
1092
  }
1092
1093
  } // no match
1093
1094
  else {
1094
- //
1095
- // go back to first open node
1096
- //
1097
- // Required for cases where the text has repeating
1098
- // characters that are the same as a delimiter prefix.
1099
- // For instance:
1100
- // Delimiter is '{!' and template text contains the string '{{!'
1101
- //
1102
- if (match.firstMatchIndex !== -1) {
1103
- node = first(match.openNodes);
1104
- textIndex = match.firstMatchIndex;
1105
- } // update state
1106
-
1107
-
1108
- match.reset();
1109
-
1110
- if (textIndex < node.textContent.length - 1) {
1111
- match.openNodes.push(node);
1112
- }
1095
+ //
1096
+ // go back to first open node
1097
+ //
1098
+ // Required for cases where the text has repeating
1099
+ // characters that are the same as a delimiter prefix.
1100
+ // For instance:
1101
+ // Delimiter is '{!' and template text contains the string '{{!'
1102
+ //
1103
+ if (match.firstMatchIndex !== -1) {
1104
+ node = first(match.openNodes);
1105
+ textIndex = match.firstMatchIndex;
1106
+ } // update state
1107
+
1108
+
1109
+ match.reset();
1110
+
1111
+ if (textIndex < node.textContent.length - 1) {
1112
+ match.openNodes.push(node);
1113
1113
  }
1114
+ }
1114
1115
 
1115
1116
  textIndex++;
1116
1117
  }
@@ -1163,8 +1164,6 @@ class DelimiterSearcher {
1163
1164
 
1164
1165
  }
1165
1166
 
1166
- const getProp = require("lodash.get");
1167
-
1168
1167
  class ScopeData {
1169
1168
  static defaultResolver(args) {
1170
1169
  let result;
@@ -2592,9 +2591,9 @@ class LinkPlugin extends TemplatePlugin {
2592
2591
  }
2593
2592
  } // already isolated
2594
2593
  else {
2595
- XmlNode.insertAfter(linkMarkup, tagRunNode);
2596
- XmlNode.remove(tagRunNode);
2597
- }
2594
+ XmlNode.insertAfter(linkMarkup, tagRunNode);
2595
+ XmlNode.remove(tagRunNode);
2596
+ }
2598
2597
  }
2599
2598
 
2600
2599
  }
@@ -2780,7 +2779,9 @@ class LoopPlugin extends TemplatePlugin {
2780
2779
  async containerTagReplacements(tags, data, context) {
2781
2780
  let value = data.getScopeData(); // Non array value - treat as a boolean condition.
2782
2781
 
2783
- if (!Array.isArray(value)) {
2782
+ const isCondition = !Array.isArray(value);
2783
+
2784
+ if (isCondition) {
2784
2785
  if (!!value) {
2785
2786
  value = [{}];
2786
2787
  } else {
@@ -2806,7 +2807,7 @@ class LoopPlugin extends TemplatePlugin {
2806
2807
  // path to each token and use that to create new tokens instead of
2807
2808
  // search through the text again)
2808
2809
 
2809
- const compiledNodes = await this.compile(repeatedNodes, data, context); // merge back to the document
2810
+ const compiledNodes = await this.compile(isCondition, repeatedNodes, data, context); // merge back to the document
2810
2811
 
2811
2812
  loopStrategy.mergeBack(compiledNodes, firstNode, lastNode);
2812
2813
  }
@@ -2823,7 +2824,7 @@ class LoopPlugin extends TemplatePlugin {
2823
2824
  return allResults;
2824
2825
  }
2825
2826
 
2826
- async compile(nodeGroups, data, context) {
2827
+ async compile(isCondition, nodeGroups, data, context) {
2827
2828
  const compiledNodeGroups = []; // compile each node group with it's relevant data
2828
2829
 
2829
2830
  for (let i = 0; i < nodeGroups.length; i++) {
@@ -2832,9 +2833,9 @@ class LoopPlugin extends TemplatePlugin {
2832
2833
  const dummyRootNode = XmlNode.createGeneralNode('dummyRootNode');
2833
2834
  curNodes.forEach(node => XmlNode.appendChild(dummyRootNode, node)); // compile the new root
2834
2835
 
2835
- data.pathPush(i);
2836
+ const conditionTag = this.updatePathBefore(isCondition, data, i);
2836
2837
  await this.utilities.compiler.compile(dummyRootNode, data, context);
2837
- data.pathPop(); // disconnect from dummy root
2838
+ this.updatePathAfter(isCondition, data, conditionTag); // disconnect from dummy root
2838
2839
 
2839
2840
  const curResult = [];
2840
2841
 
@@ -2849,6 +2850,32 @@ class LoopPlugin extends TemplatePlugin {
2849
2850
  return compiledNodeGroups;
2850
2851
  }
2851
2852
 
2853
+ updatePathBefore(isCondition, data, groupIndex) {
2854
+ // if it's a condition - don't go deeper in the path
2855
+ // (so we need to extract the already pushed condition tag)
2856
+ if (isCondition) {
2857
+ if (groupIndex > 0) {
2858
+ // should never happen - conditions should have at most one (synthetic) child...
2859
+ throw new Error(`Internal error: Unexpected group index ${groupIndex} for boolean condition at path "${data.pathString()}".`);
2860
+ }
2861
+
2862
+ return data.pathPop();
2863
+ } // else, it's an array - push the current index
2864
+
2865
+
2866
+ data.pathPush(groupIndex);
2867
+ return null;
2868
+ }
2869
+
2870
+ updatePathAfter(isCondition, data, conditionTag) {
2871
+ // reverse the "before" path operation
2872
+ if (isCondition) {
2873
+ data.pathPush(conditionTag);
2874
+ } else {
2875
+ data.pathPop();
2876
+ }
2877
+ }
2878
+
2852
2879
  }
2853
2880
 
2854
2881
  class RawXmlPlugin extends TemplatePlugin {
@@ -3253,7 +3280,7 @@ class TemplateHandler {
3253
3280
  constructor(options) {
3254
3281
  var _this$options$extensi, _this$options$extensi2, _this$options$extensi3, _this$options$extensi4;
3255
3282
 
3256
- _defineProperty(this, "version", "2.1.0" );
3283
+ _defineProperty(this, "version", "3.0.2" );
3257
3284
 
3258
3285
  _defineProperty(this, "xmlParser", new XmlParser());
3259
3286
 
@@ -8,4 +8,6 @@ export declare class LoopPlugin extends TemplatePlugin {
8
8
  containerTagReplacements(tags: Tag[], data: ScopeData, context: TemplateContext): Promise<void>;
9
9
  private repeat;
10
10
  private compile;
11
+ private updatePathBefore;
12
+ private updatePathAfter;
11
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easy-template-x",
3
- "version": "2.1.0",
3
+ "version": "3.0.2",
4
4
  "description": "Generate docx documents from templates, in Node or in the browser.",
5
5
  "keywords": [
6
6
  "docx",
@@ -39,39 +39,39 @@
39
39
  "release": "yarn clean && yarn quality && yarn build"
40
40
  },
41
41
  "dependencies": {
42
- "jszip": "3.6.0",
42
+ "jszip": "3.10.0",
43
43
  "lodash.get": "4.4.2",
44
- "xmldom": "0.5.0"
44
+ "xmldom": "0.6.0"
45
45
  },
46
46
  "devDependencies": {
47
- "@babel/core": "7.13.10",
48
- "@babel/plugin-proposal-class-properties": "7.13.0",
49
- "@babel/plugin-proposal-nullish-coalescing-operator": "7.13.8",
50
- "@babel/plugin-proposal-object-rest-spread": "7.13.8",
51
- "@babel/plugin-proposal-optional-catch-binding": "7.13.8",
52
- "@babel/plugin-proposal-optional-chaining": "7.13.8",
53
- "@babel/plugin-transform-modules-commonjs": "7.13.8",
54
- "@babel/preset-typescript": "7.13.0",
55
- "@rollup/plugin-replace": "2.4.1",
56
- "@types/jest": "26.0.20",
57
- "@types/jszip": "3.4.0",
58
- "@types/node": "14.14.35",
47
+ "@babel/core": "7.18.6",
48
+ "@babel/plugin-proposal-class-properties": "7.18.6",
49
+ "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
50
+ "@babel/plugin-proposal-object-rest-spread": "7.18.6",
51
+ "@babel/plugin-proposal-optional-catch-binding": "7.18.6",
52
+ "@babel/plugin-proposal-optional-chaining": "7.18.6",
53
+ "@babel/plugin-transform-modules-commonjs": "7.18.6",
54
+ "@babel/preset-typescript": "7.18.6",
55
+ "@rollup/plugin-replace": "4.0.0",
56
+ "@types/jest": "28.1.4",
57
+ "@types/jszip": "3.4.1",
58
+ "@types/node": "18.0.1",
59
59
  "@types/ts-nameof": "4.2.1",
60
- "@types/xmldom": "0.1.30",
61
- "@typescript-eslint/eslint-plugin": "4.18.0",
62
- "@typescript-eslint/parser": "4.18.0",
63
- "babel-jest": "26.6.3",
64
- "babel-loader": "8.2.2",
60
+ "@types/xmldom": "0.1.31",
61
+ "@typescript-eslint/eslint-plugin": "5.30.4",
62
+ "@typescript-eslint/parser": "5.30.4",
63
+ "babel-jest": "28.1.2",
64
+ "babel-loader": "8.2.5",
65
65
  "babel-plugin-ts-nameof": "4.2.1",
66
- "eslint": "7.22.0",
67
- "jest": "26.6.3",
68
- "jest-junit": "12.0.0",
69
- "lorem-ipsum": "2.0.3",
66
+ "eslint": "8.19.0",
67
+ "jest": "28.1.2",
68
+ "jest-junit": "14.0.0",
69
+ "lorem-ipsum": "2.0.8",
70
70
  "rimraf": "3.0.2",
71
- "rollup": "2.41.4",
71
+ "rollup": "2.75.7",
72
72
  "rollup-plugin-auto-external": "2.0.0",
73
- "rollup-plugin-babel": "4.3.3",
73
+ "rollup-plugin-babel": "4.4.0",
74
74
  "rollup-plugin-node-resolve": "5.2.0",
75
- "typescript": "4.2.3"
75
+ "typescript": "4.7.4"
76
76
  }
77
77
  }
@@ -0,0 +1,12 @@
1
+ // https://lodash.com/docs/4.17.15#get
2
+ module "lodash.get" {
3
+
4
+ /**
5
+ * Gets the value at path of object. If the resolved value is undefined, the
6
+ * defaultValue is returned in its place.
7
+ */
8
+ function get(object: any, path: string | string[]): any;
9
+ function get(object: any, path: string | string[], defaultValue: any): any;
10
+
11
+ export default get;
12
+ }
@@ -1,9 +1,8 @@
1
+ import getProp from 'lodash.get';
1
2
  import { TemplateContent, TemplateData } from '../templateData';
2
3
  import { isNumber, last } from '../utils';
3
4
  import { Tag } from './tag';
4
5
 
5
- const getProp = require("lodash.get");
6
-
7
6
  export type PathPart = Tag | number;
8
7
 
9
8
  export interface ScopeDataArgs {
@@ -1,4 +1,4 @@
1
- import { ScopeData, Tag, TemplateContext } from '../../compilation';
1
+ import { PathPart, ScopeData, Tag, TemplateContext } from '../../compilation';
2
2
  import { TemplateData } from '../../templateData';
3
3
  import { last } from '../../utils';
4
4
  import { XmlNode } from '../../xml';
@@ -27,7 +27,8 @@ export class LoopPlugin extends TemplatePlugin {
27
27
  let value = data.getScopeData<TemplateData[]>();
28
28
 
29
29
  // Non array value - treat as a boolean condition.
30
- if (!Array.isArray(value)) {
30
+ const isCondition = !Array.isArray(value);
31
+ if (isCondition) {
31
32
  if (!!value) {
32
33
  value = [{}];
33
34
  } else {
@@ -54,7 +55,7 @@ export class LoopPlugin extends TemplatePlugin {
54
55
  // (this step can be optimized in the future if we'll keep track of the
55
56
  // path to each token and use that to create new tokens instead of
56
57
  // search through the text again)
57
- const compiledNodes = await this.compile(repeatedNodes, data, context);
58
+ const compiledNodes = await this.compile(isCondition, repeatedNodes, data, context);
58
59
 
59
60
  // merge back to the document
60
61
  loopStrategy.mergeBack(compiledNodes, firstNode, lastNode);
@@ -74,7 +75,7 @@ export class LoopPlugin extends TemplatePlugin {
74
75
  return allResults;
75
76
  }
76
77
 
77
- private async compile(nodeGroups: XmlNode[][], data: ScopeData, context: TemplateContext): Promise<XmlNode[][]> {
78
+ private async compile(isCondition: boolean, nodeGroups: XmlNode[][], data: ScopeData, context: TemplateContext): Promise<XmlNode[][]> {
78
79
  const compiledNodeGroups: XmlNode[][] = [];
79
80
 
80
81
  // compile each node group with it's relevant data
@@ -86,9 +87,9 @@ export class LoopPlugin extends TemplatePlugin {
86
87
  curNodes.forEach(node => XmlNode.appendChild(dummyRootNode, node));
87
88
 
88
89
  // compile the new root
89
- data.pathPush(i);
90
+ const conditionTag = this.updatePathBefore(isCondition, data, i);
90
91
  await this.utilities.compiler.compile(dummyRootNode, data, context);
91
- data.pathPop();
92
+ this.updatePathAfter(isCondition, data, conditionTag);
92
93
 
93
94
  // disconnect from dummy root
94
95
  const curResult: XmlNode[] = [];
@@ -101,4 +102,32 @@ export class LoopPlugin extends TemplatePlugin {
101
102
 
102
103
  return compiledNodeGroups;
103
104
  }
105
+
106
+ private updatePathBefore(isCondition: boolean, data: ScopeData, groupIndex: number): PathPart {
107
+
108
+ // if it's a condition - don't go deeper in the path
109
+ // (so we need to extract the already pushed condition tag)
110
+ if (isCondition) {
111
+ if (groupIndex > 0) {
112
+ // should never happen - conditions should have at most one (synthetic) child...
113
+ throw new Error(`Internal error: Unexpected group index ${groupIndex} for boolean condition at path "${data.pathString()}".`);
114
+ }
115
+ return data.pathPop();
116
+ }
117
+
118
+ // else, it's an array - push the current index
119
+ data.pathPush(groupIndex);
120
+ return null;
121
+ }
122
+
123
+ private updatePathAfter(isCondition: boolean, data: ScopeData, conditionTag: PathPart): void {
124
+
125
+ // reverse the "before" path operation
126
+ if (isCondition) {
127
+ data.pathPush(conditionTag);
128
+ } else {
129
+ data.pathPop();
130
+ }
131
+ }
104
132
  }
133
+
package/CHANGELOG.md DELETED
@@ -1,292 +0,0 @@
1
- # Change Log
2
-
3
- ## [2.1.0 - 2021-07-29](https://github.com/alonrbar/easy-template-x/tree/v2.1.0)
4
-
5
- ### Added
6
-
7
- - Add skipEmptyTag option ([#45](https://github.com/alonrbar/easy-template-x/issues/45)).
8
-
9
- ## [2.0.0 - 2021-03-19](https://github.com/alonrbar/easy-template-x/tree/v2.0.0)
10
-
11
- ### Added
12
-
13
- - Support for simple conditions ([docs](https://github.com/alonrbar/easy-template-x#conditions)).
14
- - Support for custom data resolvers - enables advanced syntax support ([docs](https://github.com/alonrbar/easy-template-x#advanced-syntax-and-custom-resolvers)).
15
-
16
- ### Changed
17
-
18
- - **BREAKING** - Container closing tag name is ignored and no longer throws when
19
- the closing tag has different name than the opening one ([docs](https://github.com/alonrbar/easy-template-x#loop-plugin)).
20
-
21
- ## [1.0.1 - 2020-09-25](https://github.com/alonrbar/easy-template-x/tree/v1.0.1)
22
-
23
- ### Changed
24
-
25
- - Update dependencies (jszip, xmldom).
26
-
27
- ## [1.0.0 - 2020-08-09](https://github.com/alonrbar/easy-template-x/tree/v1.0.0)
28
-
29
- **Stable release** - from now on breaking changes to the public API (the public
30
- interface of `TemplateHandler`) will introduce a new major release.
31
-
32
- ### Fixed
33
-
34
- - Initial content types parsing.
35
- - Bug in paragraph loops (#36).
36
-
37
- ## [0.12.0 - 2020-08-01](https://github.com/alonrbar/easy-template-x/tree/v0.12.0)
38
-
39
- ### Added
40
-
41
- - Headers and footers support.
42
-
43
- ## [0.11.1 - 2020-03-29](https://github.com/alonrbar/easy-template-x/tree/v0.11.1)
44
-
45
- ### Fixed
46
-
47
- - Consistent handling of `RawXmlContent` when the `xml` prop is null.
48
-
49
- ## [0.11.0 - 2020-03-29](https://github.com/alonrbar/easy-template-x/tree/v0.11.0)
50
-
51
- ### Added
52
-
53
- - Support for `RawXmlContent.replaceParagraph`.
54
-
55
- ## [0.10.4 - 2020-03-02](https://github.com/alonrbar/easy-template-x/tree/v0.10.4)
56
-
57
- ### Added
58
-
59
- - Expose "Community Extensions" on npm (readme changes).
60
-
61
- ## [0.10.3 - 2020-02-16](https://github.com/alonrbar/easy-template-x/tree/v0.10.3)
62
-
63
- ### Fixed
64
-
65
- - Parsing of tags with custom delimiters.
66
-
67
- ## [0.10.2 - 2020-02-16](https://github.com/alonrbar/easy-template-x/tree/v0.10.2)
68
-
69
- ### Fixed
70
-
71
- - Parsing of tags with custom delimiters.
72
-
73
- ## [0.10.1 - 2020-02-12](https://github.com/alonrbar/easy-template-x/tree/v0.10.1)
74
-
75
- ### Fixed
76
-
77
- - Export extensions types.
78
-
79
- ## [0.10.0 - 2020-02-10](https://github.com/alonrbar/easy-template-x/tree/v0.10.0)
80
-
81
- ### Added
82
-
83
- - Expose `Docx.rawZipFile` property.
84
-
85
- ## [0.9.0 - 2020-02-10](https://github.com/alonrbar/easy-template-x/tree/v0.9.0)
86
-
87
- ### Added
88
-
89
- - Extensions API (#24).
90
-
91
- ## [0.8.3 - 2019-12-27](https://github.com/alonrbar/easy-template-x/tree/v0.8.3)
92
-
93
- ### Added
94
-
95
- - Allow overriding container tag logic using explicit content type.
96
-
97
- ## [0.8.2 - 2019-11-30](https://github.com/alonrbar/easy-template-x/tree/v0.8.2)
98
-
99
- ### Changed
100
-
101
- - `ScopeData.getScopeData` is now generic (#17).
102
- - The `data` argument of `TemplateHandler.process` is now strongly typed.
103
- - Bundle with Rollup instead of Webpack.
104
- - Auto generate typings.
105
-
106
- ## [0.8.1 - 2019-11-02](https://github.com/alonrbar/easy-template-x/tree/v0.8.1)
107
-
108
- ### Fixed
109
-
110
- - Fix typings.
111
-
112
- ## [0.8.0 - 2019-10-20](https://github.com/alonrbar/easy-template-x/tree/v0.8.0)
113
-
114
- ### Changed
115
-
116
- - **BREAKING**: Delimiters can not contain leading or trailing whitespace.
117
- - Loosen up `TemplateHandlerOptions` typings.
118
-
119
- ### Fixed
120
-
121
- - Loop tag names trimming.
122
- - Custom loop delimiters support.
123
- - Zip export and typings.
124
-
125
- ## [0.7.3 - 2019-10-11](https://github.com/alonrbar/easy-template-x/tree/v0.7.3)
126
-
127
- ### Added
128
-
129
- - Link to [live demo](https://codesandbox.io/s/easy-template-x-demo-x4ppu?fontsize=14&module=%2Findex.ts) on CodeSandbox.
130
-
131
- ## [0.7.2 - 2019-10-10](https://github.com/alonrbar/easy-template-x/tree/v0.7.2)
132
-
133
- ### Fixed
134
-
135
- - Re-fix "Binary type 'Buffer' is not supported" on Node.
136
-
137
- ## [0.7.1 - 2019-10-03](https://github.com/alonrbar/easy-template-x/tree/v0.7.1)
138
-
139
- ### Fixed
140
-
141
- - Link plugin in cases where the link tag is not the only node in it's run.
142
-
143
- ## [0.7.0 - 2019-10-02](https://github.com/alonrbar/easy-template-x/tree/v0.7.0)
144
-
145
- ### Added
146
-
147
- - Link plugin.
148
- - `TemplateHandler.version` property.
149
-
150
- ## [0.6.0 - 2019-09-29](https://github.com/alonrbar/easy-template-x/tree/v0.6.0)
151
-
152
- This version removes the notion of a "tag type" and uses instead the notion of "content type". Instead of inferring the type from the tag _prefix_ the type is now explicitly declared in the supplied JSON _data_.
153
-
154
- **Example:**
155
-
156
- _Before:_
157
-
158
- ```text
159
- tag: "{@newPage}"
160
- data: {
161
- newPage: "<w:br w:type="page"/>"
162
- }
163
- ```
164
-
165
- _After:_
166
-
167
- ```text
168
- tag: "{newPage}"
169
- data: {
170
- newPage: {
171
- _type: "rawXml",
172
- xml: "<w:br w:type="page"/>"
173
- }
174
- }
175
- ```
176
-
177
- The only exceptions are the "loop" content type which still uses the "#" opening prefix and "/" closing prefix, and the "text" content type which is the default and does not requires explicitly stating it.
178
-
179
- ### Added
180
-
181
- - Image plugin.
182
- - Support multi-character delimiters.
183
- - Template plugins can be async.
184
- - Improved the docs (readme).
185
-
186
- ### Changed
187
-
188
- - **BREAKING**: `RawXmlPlugin` requires data of the form `{ _type: 'rawXml', xml: string }`.
189
-
190
- ### Removed
191
-
192
- - **BREAKING**: Remove the `Tag.type` property.
193
-
194
- ### Fixed
195
-
196
- - Parsing error in some cases where multiple tags are declared in the same run.
197
-
198
- ## [0.5.2 - 2019-09-11](https://github.com/alonrbar/easy-template-x/tree/v0.5.2)
199
-
200
- ### Fixed
201
-
202
- - "Binary type 'Buffer' is not supported." on Node 12.
203
-
204
- ## [0.5.1 - 2019-06-05](https://github.com/alonrbar/easy-template-x/tree/v0.5.1)
205
-
206
- ### Fixed
207
-
208
- - Handle non-textual values (numbers, booleans...) in TextPlugin.
209
-
210
- ## [0.5.0 - 2019-05-07](https://github.com/alonrbar/easy-template-x/tree/v0.5.0)
211
-
212
- ### Added
213
-
214
- - Loop over lists and table rows.
215
- **Notice**:
216
- The loop logic for tables is a bit different than the logic of the existing
217
- paragraph loop. Instead of repeating the content in between the opening and
218
- closing tags it repeats entire rows (including content in the row that
219
- appears before the opening or after the closing tag). The same goes for
220
- lists - the entire bullet is repeated.
221
- - Throw MalformedFileError when fails to open template file as zip.
222
- - Continuous integration with CircleCI.
223
-
224
- ### Changed
225
-
226
- - Change dev stack to Babel, Jest and ESLint.
227
-
228
- ## [0.4.0 - 2018-12-13](https://github.com/alonrbar/easy-template-x/tree/v0.4.0)
229
-
230
- ### Added
231
-
232
- - Easily find out what tags are present in a given template (TemplateHandler.parseTags).
233
-
234
- ## [0.3.4 - 2018-12-09](https://github.com/alonrbar/easy-template-x/tree/v0.3.4)
235
-
236
- ### Added
237
-
238
- - Full browser example in readme file.
239
-
240
- ## [0.3.3 - 2018-07-17](https://github.com/alonrbar/easy-template-x/tree/v0.3.3)
241
-
242
- ### Added
243
-
244
- - Package keywords for npm visibility.
245
-
246
- ## [0.3.2 - 2018-06-22](https://github.com/alonrbar/easy-template-x/tree/v0.3.2)
247
-
248
- ### Fixed
249
-
250
- - Fix serialization of text nodes with empty values.
251
-
252
- ## [0.3.1 - 2018-06-13](https://github.com/alonrbar/easy-template-x/tree/v0.3.1)
253
-
254
- ### Added
255
-
256
- - Add readme badges
257
-
258
- ## [0.3.0 - 2018-06-13](https://github.com/alonrbar/easy-template-x/tree/v0.3.0)
259
-
260
- ### Added
261
-
262
- - Preserve leading and trailing whitespace
263
- - More info on missing delimiter errors
264
-
265
- ## [0.2.1 - 2018-06-12](https://github.com/alonrbar/easy-template-x/tree/v0.2.1)
266
-
267
- ### Fixed
268
-
269
- - Various bug fixes
270
-
271
- ## [0.2.0 - 2018-06-12](https://github.com/alonrbar/easy-template-x/tree/v0.2.0)
272
-
273
- ### Added
274
-
275
- - Typings file
276
-
277
- ## [0.1.0 - 2018-06-12](https://github.com/alonrbar/easy-template-x/tree/v0.1.0)
278
-
279
- - First version
280
-
281
- ---
282
-
283
- The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
284
-
285
- #### [Types of changes](http://keepachangelog.com)
286
-
287
- - **Added** for new features.
288
- - **Changed** for changes in existing functionality.
289
- - **Deprecated** for soon-to-be removed features.
290
- - **Removed** for now removed features.
291
- - **Fixed** for any bug fixes.
292
- - **Security** in case of vulnerabilities.