easy-template-x 3.2.0 → 4.0.0
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 +38 -2
- package/dist/cjs/easy-template-x.js +184 -98
- package/dist/es/easy-template-x.js +177 -89
- package/dist/types/compilation/delimiterMark.d.ts +6 -6
- package/dist/types/compilation/delimiterSearcher.d.ts +16 -16
- package/dist/types/compilation/index.d.ts +7 -7
- package/dist/types/compilation/scopeData.d.ts +21 -21
- package/dist/types/compilation/tag.d.ts +14 -12
- package/dist/types/compilation/tagParser.d.ts +13 -13
- package/dist/types/compilation/templateCompiler.d.ts +25 -25
- package/dist/types/compilation/templateContext.d.ts +5 -5
- package/dist/types/delimiters.d.ts +10 -8
- package/dist/types/errors/index.d.ts +11 -10
- package/dist/types/errors/malformedFileError.d.ts +4 -4
- package/dist/types/errors/maxXmlDepthError.d.ts +4 -4
- package/dist/types/errors/missingArgumentError.d.ts +4 -4
- package/dist/types/errors/missingCloseDelimiterError.d.ts +4 -4
- package/dist/types/errors/missingStartDelimiterError.d.ts +4 -4
- package/dist/types/errors/tagOptionsParseError.d.ts +5 -0
- package/dist/types/errors/unclosedTagError.d.ts +4 -4
- package/dist/types/errors/unidentifiedFileTypeError.d.ts +3 -3
- package/dist/types/errors/unknownContentTypeError.d.ts +6 -6
- package/dist/types/errors/unopenedTagError.d.ts +4 -4
- package/dist/types/errors/unsupportedFileTypeError.d.ts +4 -4
- package/dist/types/extensions/extensionOptions.d.ts +5 -5
- package/dist/types/extensions/index.d.ts +2 -2
- package/dist/types/extensions/templateExtension.d.ts +14 -14
- package/dist/types/index.d.ts +13 -13
- package/dist/types/mimeType.d.ts +11 -11
- package/dist/types/office/contentPartType.d.ts +9 -9
- package/dist/types/office/contentTypesFile.d.ts +16 -16
- package/dist/types/office/docx.d.ts +28 -28
- package/dist/types/office/docxParser.d.ts +35 -35
- package/dist/types/office/index.d.ts +4 -4
- package/dist/types/office/mediaFiles.d.ts +14 -14
- package/dist/types/office/relationship.d.ts +11 -11
- package/dist/types/office/rels.d.ts +20 -20
- package/dist/types/office/xmlPart.d.ts +14 -14
- package/dist/types/plugins/defaultPlugins.d.ts +2 -2
- package/dist/types/plugins/image/imageContent.d.ts +12 -12
- package/dist/types/plugins/image/imagePlugin.d.ts +10 -10
- package/dist/types/plugins/image/index.d.ts +2 -2
- package/dist/types/plugins/index.d.ts +8 -8
- package/dist/types/plugins/link/index.d.ts +2 -2
- package/dist/types/plugins/link/linkContent.d.ts +7 -7
- package/dist/types/plugins/link/linkPlugin.d.ts +9 -9
- package/dist/types/plugins/loop/index.d.ts +1 -1
- package/dist/types/plugins/loop/loopPlugin.d.ts +13 -13
- package/dist/types/plugins/loop/loopTagOptions.d.ts +7 -0
- package/dist/types/plugins/loop/strategy/iLoopStrategy.d.ts +14 -14
- package/dist/types/plugins/loop/strategy/index.d.ts +4 -4
- package/dist/types/plugins/loop/strategy/loopListStrategy.d.ts +11 -11
- package/dist/types/plugins/loop/strategy/loopParagraphStrategy.d.ts +11 -11
- package/dist/types/plugins/loop/strategy/loopTableStrategy.d.ts +11 -11
- package/dist/types/plugins/pluginContent.d.ts +6 -6
- package/dist/types/plugins/rawXml/index.d.ts +2 -2
- package/dist/types/plugins/rawXml/rawXmlContent.d.ts +6 -6
- package/dist/types/plugins/rawXml/rawXmlPlugin.d.ts +6 -6
- package/dist/types/plugins/templatePlugin.d.ts +15 -15
- package/dist/types/plugins/text/index.d.ts +1 -1
- package/dist/types/plugins/text/textPlugin.d.ts +11 -11
- package/dist/types/templateData.d.ts +6 -6
- package/dist/types/templateHandler.d.ts +20 -20
- package/dist/types/templateHandlerOptions.d.ts +15 -15
- package/dist/types/types.d.ts +4 -6
- package/dist/types/utils/array.d.ts +6 -6
- package/dist/types/utils/base64.d.ts +3 -3
- package/dist/types/utils/binary.d.ts +11 -12
- package/dist/types/utils/index.d.ts +9 -9
- package/dist/types/utils/number.d.ts +1 -1
- package/dist/types/utils/path.d.ts +5 -5
- package/dist/types/utils/regex.d.ts +3 -3
- package/dist/types/utils/sha1.d.ts +1 -1
- package/dist/types/utils/txt.d.ts +2 -1
- package/dist/types/utils/types.d.ts +3 -3
- package/dist/types/xml/index.d.ts +3 -3
- package/dist/types/xml/xmlDepthTracker.d.ts +7 -7
- package/dist/types/xml/xmlNode.d.ts +58 -49
- package/dist/types/xml/xmlParser.d.ts +8 -8
- package/dist/types/zip/index.d.ts +2 -2
- package/dist/types/zip/jsZipHelper.d.ts +7 -7
- package/dist/types/zip/zip.d.ts +13 -13
- package/dist/types/zip/zipObject.d.ts +13 -13
- package/package.json +25 -19
- package/src/compilation/tag.ts +4 -2
- package/src/compilation/tagParser.ts +32 -13
- package/src/delimiters.ts +3 -1
- package/src/errors/index.ts +1 -0
- package/src/errors/malformedFileError.ts +1 -4
- package/src/errors/maxXmlDepthError.ts +1 -4
- package/src/errors/missingArgumentError.ts +1 -4
- package/src/errors/missingCloseDelimiterError.ts +1 -4
- package/src/errors/missingStartDelimiterError.ts +1 -4
- package/src/errors/tagOptionsParseError.ts +12 -0
- package/src/errors/unclosedTagError.ts +1 -4
- package/src/errors/unidentifiedFileTypeError.ts +1 -4
- package/src/errors/unknownContentTypeError.ts +1 -4
- package/src/errors/unopenedTagError.ts +1 -4
- package/src/errors/unsupportedFileTypeError.ts +1 -4
- package/src/office/docx.ts +4 -1
- package/src/office/docxParser.ts +3 -1
- package/src/plugins/loop/loopPlugin.ts +1 -1
- package/src/plugins/loop/loopTagOptions.ts +14 -0
- package/src/plugins/loop/strategy/loopTableStrategy.ts +22 -3
- package/src/plugins/templatePlugin.ts +0 -2
- package/src/types.ts +1 -3
- package/src/utils/txt.ts +29 -1
- package/src/xml/xmlNode.ts +50 -14
|
@@ -1,49 +1,58 @@
|
|
|
1
|
-
import { IMap } from '../types';
|
|
2
|
-
export declare enum XmlNodeType {
|
|
3
|
-
Text = "Text",
|
|
4
|
-
General = "General"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
import { IMap } from '../types';
|
|
2
|
+
export declare enum XmlNodeType {
|
|
3
|
+
Text = "Text",
|
|
4
|
+
General = "General",
|
|
5
|
+
Comment = "Comment"
|
|
6
|
+
}
|
|
7
|
+
export type XmlNode = XmlTextNode | XmlGeneralNode | XmlCommentNode;
|
|
8
|
+
export interface XmlNodeBase {
|
|
9
|
+
nodeType: XmlNodeType;
|
|
10
|
+
nodeName: string;
|
|
11
|
+
parentNode?: XmlNode;
|
|
12
|
+
childNodes?: XmlNode[];
|
|
13
|
+
nextSibling?: XmlNode;
|
|
14
|
+
}
|
|
15
|
+
export declare const TEXT_NODE_NAME = "#text";
|
|
16
|
+
export declare const COMMENT_NODE_NAME = "#comment";
|
|
17
|
+
export interface XmlTextNode extends XmlNodeBase {
|
|
18
|
+
nodeType: XmlNodeType.Text;
|
|
19
|
+
nodeName: typeof TEXT_NODE_NAME;
|
|
20
|
+
textContent: string;
|
|
21
|
+
}
|
|
22
|
+
export interface XmlCommentNode extends XmlNodeBase {
|
|
23
|
+
nodeType: XmlNodeType.Comment;
|
|
24
|
+
nodeName: typeof COMMENT_NODE_NAME;
|
|
25
|
+
commentContent: string;
|
|
26
|
+
}
|
|
27
|
+
export interface XmlGeneralNode extends XmlNodeBase {
|
|
28
|
+
nodeType: XmlNodeType.General;
|
|
29
|
+
attributes?: IMap<string>;
|
|
30
|
+
}
|
|
31
|
+
export declare const XmlNode: {
|
|
32
|
+
createTextNode(text?: string): XmlTextNode;
|
|
33
|
+
createGeneralNode(name: string): XmlGeneralNode;
|
|
34
|
+
createCommentNode(text?: string): XmlCommentNode;
|
|
35
|
+
encodeValue(str: string): string;
|
|
36
|
+
serialize(node: XmlNode): string;
|
|
37
|
+
fromDomNode(domNode: Node): XmlNode;
|
|
38
|
+
isTextNode(node: XmlNode): node is XmlTextNode;
|
|
39
|
+
isCommentNode(node: XmlNode): node is XmlCommentNode;
|
|
40
|
+
cloneNode<T extends XmlNode>(node: T, deep: boolean): T;
|
|
41
|
+
insertBefore(newNode: XmlNode, referenceNode: XmlNode): void;
|
|
42
|
+
insertAfter(newNode: XmlNode, referenceNode: XmlNode): void;
|
|
43
|
+
insertChild(parent: XmlNode, child: XmlNode, childIndex: number): void;
|
|
44
|
+
appendChild(parent: XmlNode, child: XmlNode): void;
|
|
45
|
+
remove(node: XmlNode): void;
|
|
46
|
+
removeChild: typeof removeChild;
|
|
47
|
+
lastTextChild(node: XmlNode): XmlTextNode;
|
|
48
|
+
removeSiblings(from: XmlNode, to: XmlNode): XmlNode[];
|
|
49
|
+
splitByChild(parent: XmlNode, child: XmlNode, removeChild: boolean): [XmlNode, XmlNode];
|
|
50
|
+
findParent(node: XmlNode, predicate: (node: XmlNode) => boolean): XmlNode;
|
|
51
|
+
findParentByName(node: XmlNode, nodeName: string): XmlNode;
|
|
52
|
+
findChildByName(node: XmlNode, childName: string): XmlNode;
|
|
53
|
+
siblingsInRange(firstNode: XmlNode, lastNode: XmlNode): XmlNode[];
|
|
54
|
+
removeEmptyTextNodes(node: XmlNode): void;
|
|
55
|
+
};
|
|
56
|
+
declare function removeChild(parent: XmlNode, child: XmlNode): XmlNode;
|
|
57
|
+
declare function removeChild(parent: XmlNode, childIndex: number): XmlNode;
|
|
58
|
+
export {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { XmlNode } from './xmlNode';
|
|
2
|
-
export declare class XmlParser {
|
|
3
|
-
private static xmlHeader;
|
|
4
|
-
private static readonly parser;
|
|
5
|
-
parse(str: string): XmlNode;
|
|
6
|
-
domParse(str: string): Document;
|
|
7
|
-
serialize(xmlNode: XmlNode): string;
|
|
8
|
-
}
|
|
1
|
+
import { XmlNode } from './xmlNode';
|
|
2
|
+
export declare class XmlParser {
|
|
3
|
+
private static xmlHeader;
|
|
4
|
+
private static readonly parser;
|
|
5
|
+
parse(str: string): XmlNode;
|
|
6
|
+
domParse(str: string): Document;
|
|
7
|
+
serialize(xmlNode: XmlNode): string;
|
|
8
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './zip';
|
|
2
|
-
export * from './zipObject';
|
|
1
|
+
export * from './zip';
|
|
2
|
+
export * from './zipObject';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as JSZip from 'jszip';
|
|
2
|
-
import { Constructor } from '../types';
|
|
3
|
-
import { Binary } from '../utils';
|
|
4
|
-
export declare class JsZipHelper {
|
|
5
|
-
static toJsZipOutputType(binary: Binary): JSZip.OutputType;
|
|
6
|
-
static toJsZipOutputType(binaryType: Constructor<Binary>): JSZip.OutputType;
|
|
7
|
-
}
|
|
1
|
+
import * as JSZip from 'jszip';
|
|
2
|
+
import { Constructor } from '../types';
|
|
3
|
+
import { Binary } from '../utils';
|
|
4
|
+
export declare class JsZipHelper {
|
|
5
|
+
static toJsZipOutputType(binary: Binary): JSZip.OutputType;
|
|
6
|
+
static toJsZipOutputType(binaryType: Constructor<Binary>): JSZip.OutputType;
|
|
7
|
+
}
|
package/dist/types/zip/zip.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Constructor } from '../types';
|
|
2
|
-
import { Binary } from '../utils';
|
|
3
|
-
import { ZipObject } from './zipObject';
|
|
4
|
-
export declare class Zip {
|
|
5
|
-
private readonly zip;
|
|
6
|
-
static load(file: Binary): Promise<Zip>;
|
|
7
|
-
private constructor();
|
|
8
|
-
getFile(path: string): ZipObject;
|
|
9
|
-
setFile(path: string, content: string | Binary): void;
|
|
10
|
-
isFileExist(path: string): boolean;
|
|
11
|
-
listFiles(): string[];
|
|
12
|
-
export<T extends Binary>(outputType: Constructor<T>): Promise<T>;
|
|
13
|
-
}
|
|
1
|
+
import { Constructor } from '../types';
|
|
2
|
+
import { Binary } from '../utils';
|
|
3
|
+
import { ZipObject } from './zipObject';
|
|
4
|
+
export declare class Zip {
|
|
5
|
+
private readonly zip;
|
|
6
|
+
static load(file: Binary): Promise<Zip>;
|
|
7
|
+
private constructor();
|
|
8
|
+
getFile(path: string): ZipObject;
|
|
9
|
+
setFile(path: string, content: string | Binary): void;
|
|
10
|
+
isFileExist(path: string): boolean;
|
|
11
|
+
listFiles(): string[];
|
|
12
|
+
export<T extends Binary>(outputType: Constructor<T>): Promise<T>;
|
|
13
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import * as JSZip from 'jszip';
|
|
2
|
-
import { Constructor } from '../types';
|
|
3
|
-
import { Binary } from '../utils';
|
|
4
|
-
export declare class ZipObject {
|
|
5
|
-
private readonly zipObject;
|
|
6
|
-
get name(): string;
|
|
7
|
-
set name(value: string);
|
|
8
|
-
get isDirectory(): boolean;
|
|
9
|
-
constructor(zipObject: JSZip.JSZipObject);
|
|
10
|
-
getContentText(): Promise<string>;
|
|
11
|
-
getContentBase64(): Promise<string>;
|
|
12
|
-
getContentBinary<T extends Binary>(outputType: Constructor<T>): Promise<T>;
|
|
13
|
-
}
|
|
1
|
+
import * as JSZip from 'jszip';
|
|
2
|
+
import { Constructor } from '../types';
|
|
3
|
+
import { Binary } from '../utils';
|
|
4
|
+
export declare class ZipObject {
|
|
5
|
+
private readonly zipObject;
|
|
6
|
+
get name(): string;
|
|
7
|
+
set name(value: string);
|
|
8
|
+
get isDirectory(): boolean;
|
|
9
|
+
constructor(zipObject: JSZip.JSZipObject);
|
|
10
|
+
getContentText(): Promise<string>;
|
|
11
|
+
getContentBase64(): Promise<string>;
|
|
12
|
+
getContentBinary<T extends Binary>(outputType: Constructor<T>): Promise<T>;
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "easy-template-x",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Generate docx documents from templates, in Node or in the browser.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"docx",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"bugs": {
|
|
21
21
|
"url": "https://github.com/alonrbar/easy-template-x/issues"
|
|
22
22
|
},
|
|
23
|
+
"type": "module",
|
|
23
24
|
"main": "dist/cjs/easy-template-x.js",
|
|
24
25
|
"module": "dist/es/easy-template-x.js",
|
|
25
26
|
"typings": "dist/types/index.d.ts",
|
|
@@ -38,39 +39,44 @@
|
|
|
38
39
|
"build": "yarn build-types && yarn build-src",
|
|
39
40
|
"release": "yarn clean && yarn quality && yarn build"
|
|
40
41
|
},
|
|
42
|
+
"packageManager": "yarn@4.3.1",
|
|
41
43
|
"dependencies": {
|
|
42
|
-
"@xmldom/xmldom": "0.8.
|
|
44
|
+
"@xmldom/xmldom": "0.8.10",
|
|
45
|
+
"json5": "2.2.3",
|
|
43
46
|
"jszip": "3.10.1",
|
|
44
47
|
"lodash.get": "4.4.2"
|
|
45
48
|
},
|
|
46
49
|
"devDependencies": {
|
|
47
|
-
"@babel/core": "7.
|
|
50
|
+
"@babel/core": "7.24.7",
|
|
48
51
|
"@babel/plugin-proposal-class-properties": "7.18.6",
|
|
49
52
|
"@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
|
|
50
53
|
"@babel/plugin-proposal-object-rest-spread": "7.18.6",
|
|
51
54
|
"@babel/plugin-proposal-optional-catch-binding": "7.18.6",
|
|
52
55
|
"@babel/plugin-proposal-optional-chaining": "7.18.6",
|
|
53
|
-
"@babel/plugin-transform-modules-commonjs": "7.
|
|
54
|
-
"@babel/preset-typescript": "7.
|
|
55
|
-
"@
|
|
56
|
-
"@
|
|
56
|
+
"@babel/plugin-transform-modules-commonjs": "7.24.7",
|
|
57
|
+
"@babel/preset-typescript": "7.24.7",
|
|
58
|
+
"@eslint/js": "9.6.0",
|
|
59
|
+
"@rollup/plugin-replace": "5.0.7",
|
|
60
|
+
"@types/eslint__js": "8.42.3",
|
|
61
|
+
"@types/jest": "29.5.12",
|
|
57
62
|
"@types/jszip": "3.4.1",
|
|
58
|
-
"@types/node": "
|
|
59
|
-
"@types/ts-nameof": "4.2.
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"babel-jest": "28.1.2",
|
|
63
|
-
"babel-loader": "8.2.5",
|
|
63
|
+
"@types/node": "20.14.9",
|
|
64
|
+
"@types/ts-nameof": "4.2.5",
|
|
65
|
+
"babel-jest": "29.7.0",
|
|
66
|
+
"babel-loader": "9.1.3",
|
|
64
67
|
"babel-plugin-ts-nameof": "4.2.1",
|
|
65
|
-
"eslint": "
|
|
66
|
-
"
|
|
67
|
-
"jest
|
|
68
|
+
"eslint": "9.6.0",
|
|
69
|
+
"globals": "15.8.0",
|
|
70
|
+
"jest": "29.7.0",
|
|
71
|
+
"jest-html-reporters": "3.1.7",
|
|
72
|
+
"jest-junit": "16.0.0",
|
|
68
73
|
"lorem-ipsum": "2.0.8",
|
|
69
|
-
"rimraf": "
|
|
70
|
-
"rollup": "
|
|
74
|
+
"rimraf": "5.0.7",
|
|
75
|
+
"rollup": "4.18.0",
|
|
71
76
|
"rollup-plugin-auto-external": "2.0.0",
|
|
72
77
|
"rollup-plugin-babel": "4.4.0",
|
|
73
78
|
"rollup-plugin-node-resolve": "5.2.0",
|
|
74
|
-
"typescript": "
|
|
79
|
+
"typescript": "5.5.3",
|
|
80
|
+
"typescript-eslint": "7.15.0"
|
|
75
81
|
}
|
|
76
82
|
}
|
package/src/compilation/tag.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { IMap } from '../types';
|
|
1
2
|
import { XmlTextNode } from '../xml';
|
|
2
3
|
|
|
3
4
|
export enum TagDisposition {
|
|
@@ -6,12 +7,13 @@ export enum TagDisposition {
|
|
|
6
7
|
SelfClosed = "SelfClosed"
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
export interface Tag {
|
|
10
|
+
export interface Tag {
|
|
10
11
|
name: string;
|
|
12
|
+
options?: IMap<any>;
|
|
11
13
|
/**
|
|
12
14
|
* The full tag text, for instance: "{#my-tag}".
|
|
13
15
|
*/
|
|
14
16
|
rawText: string;
|
|
15
17
|
disposition: TagDisposition;
|
|
16
18
|
xmlTextNode: XmlTextNode;
|
|
17
|
-
}
|
|
19
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import * as JSON5 from 'json5';
|
|
1
2
|
import { Delimiters } from '../delimiters';
|
|
2
|
-
import { MissingArgumentError, MissingCloseDelimiterError, MissingStartDelimiterError } from '../errors';
|
|
3
|
+
import { MissingArgumentError, MissingCloseDelimiterError, MissingStartDelimiterError, TagOptionsParseError } from '../errors';
|
|
3
4
|
import { DocxParser } from '../office';
|
|
4
|
-
import { Regex } from '../utils';
|
|
5
|
+
import { normalizeDoubleQuotes, Regex } from '../utils';
|
|
5
6
|
import { DelimiterMark } from './delimiterMark';
|
|
6
7
|
import { Tag, TagDisposition } from './tag';
|
|
7
8
|
|
|
@@ -18,7 +19,8 @@ export class TagParser {
|
|
|
18
19
|
if (!delimiters)
|
|
19
20
|
throw new MissingArgumentError(nameof(delimiters));
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
const tagOptionsRegex = `${Regex.escape(delimiters.tagOptionsStart)}(?<tagOptions>.*?)${Regex.escape(delimiters.tagOptionsEnd)}`;
|
|
23
|
+
this.tagRegex = new RegExp(`^${Regex.escape(delimiters.tagStart)}(?<tagName>.*?)(${tagOptionsRegex})?${Regex.escape(delimiters.tagEnd)}`, 'm');
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
public parse(delimiters: DelimiterMark[]): Tag[] {
|
|
@@ -136,23 +138,40 @@ export class TagParser {
|
|
|
136
138
|
tag.rawText = tag.xmlTextNode.textContent;
|
|
137
139
|
|
|
138
140
|
const tagParts = this.tagRegex.exec(tag.rawText);
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
+
const tagName = (tagParts.groups?.["tagName"] || '').trim();
|
|
142
|
+
|
|
143
|
+
// Ignoring empty tags.
|
|
144
|
+
if (!tagName?.length) {
|
|
141
145
|
tag.disposition = TagDisposition.SelfClosed;
|
|
142
146
|
return;
|
|
143
147
|
}
|
|
144
148
|
|
|
145
|
-
|
|
149
|
+
// Tag options.
|
|
150
|
+
const tagOptionsText = (tagParts.groups?.["tagOptions"] || '').trim();
|
|
151
|
+
if (tagOptionsText) {
|
|
152
|
+
try {
|
|
153
|
+
tag.options = JSON5.parse("{" + normalizeDoubleQuotes(tagOptionsText) + "}");
|
|
154
|
+
} catch (e) {
|
|
155
|
+
throw new TagOptionsParseError(tag.rawText, e);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Container open tag.
|
|
160
|
+
if (tagName.startsWith(this.delimiters.containerTagOpen)) {
|
|
146
161
|
tag.disposition = TagDisposition.Open;
|
|
147
|
-
tag.name =
|
|
162
|
+
tag.name = tagName.slice(this.delimiters.containerTagOpen.length).trim();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
148
165
|
|
|
149
|
-
|
|
166
|
+
// Container close tag.
|
|
167
|
+
if (tagName.startsWith(this.delimiters.containerTagClose)) {
|
|
150
168
|
tag.disposition = TagDisposition.Close;
|
|
151
|
-
tag.name =
|
|
152
|
-
|
|
153
|
-
} else {
|
|
154
|
-
tag.disposition = TagDisposition.SelfClosed;
|
|
155
|
-
tag.name = tagContent;
|
|
169
|
+
tag.name = tagName.slice(this.delimiters.containerTagClose.length).trim();
|
|
170
|
+
return;
|
|
156
171
|
}
|
|
172
|
+
|
|
173
|
+
// Self-closed tag.
|
|
174
|
+
tag.disposition = TagDisposition.SelfClosed;
|
|
175
|
+
tag.name = tagName;
|
|
157
176
|
}
|
|
158
177
|
}
|
package/src/delimiters.ts
CHANGED
|
@@ -5,6 +5,8 @@ export class Delimiters {
|
|
|
5
5
|
public tagEnd = "}";
|
|
6
6
|
public containerTagOpen = "#";
|
|
7
7
|
public containerTagClose = "/";
|
|
8
|
+
public tagOptionsStart = "[";
|
|
9
|
+
public tagOptionsEnd = "]";
|
|
8
10
|
|
|
9
11
|
constructor(initial?: Partial<Delimiters>) {
|
|
10
12
|
Object.assign(this, initial);
|
|
@@ -27,4 +29,4 @@ export class Delimiters {
|
|
|
27
29
|
throw new Error(`${key} can not contain leading or trailing whitespace.`);
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
|
-
}
|
|
32
|
+
}
|
package/src/errors/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from './maxXmlDepthError';
|
|
|
3
3
|
export * from './missingArgumentError';
|
|
4
4
|
export * from './missingCloseDelimiterError';
|
|
5
5
|
export * from './missingStartDelimiterError';
|
|
6
|
+
export * from './tagOptionsParseError';
|
|
6
7
|
export * from './unclosedTagError';
|
|
7
8
|
export * from './unidentifiedFileTypeError';
|
|
8
9
|
export * from './unknownContentTypeError';
|
|
@@ -6,8 +6,5 @@ export class MalformedFileError extends Error {
|
|
|
6
6
|
super(`Malformed file detected. Make sure the file is a valid ${expectedFileType} file.`);
|
|
7
7
|
|
|
8
8
|
this.expectedFileType = expectedFileType;
|
|
9
|
-
|
|
10
|
-
// typescript hack: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
11
|
-
Object.setPrototypeOf(this, MalformedFileError.prototype);
|
|
12
9
|
}
|
|
13
|
-
}
|
|
10
|
+
}
|
|
@@ -6,8 +6,5 @@ export class MaxXmlDepthError extends Error {
|
|
|
6
6
|
super(`XML maximum depth reached (max depth: ${maxDepth}).`);
|
|
7
7
|
|
|
8
8
|
this.maxDepth = maxDepth;
|
|
9
|
-
|
|
10
|
-
// typescript hack: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
11
|
-
Object.setPrototypeOf(this, MaxXmlDepthError.prototype);
|
|
12
9
|
}
|
|
13
|
-
}
|
|
10
|
+
}
|
|
@@ -6,8 +6,5 @@ export class MissingArgumentError extends Error {
|
|
|
6
6
|
super(`Argument '${argName}' is missing.`);
|
|
7
7
|
|
|
8
8
|
this.argName = argName;
|
|
9
|
-
|
|
10
|
-
// typescript hack: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
11
|
-
Object.setPrototypeOf(this, MissingArgumentError.prototype);
|
|
12
9
|
}
|
|
13
|
-
}
|
|
10
|
+
}
|
|
@@ -6,8 +6,5 @@ export class MissingCloseDelimiterError extends Error {
|
|
|
6
6
|
super(`Close delimiter is missing from '${openDelimiterText}'.`);
|
|
7
7
|
|
|
8
8
|
this.openDelimiterText = openDelimiterText;
|
|
9
|
-
|
|
10
|
-
// typescript hack: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
11
|
-
Object.setPrototypeOf(this, MissingCloseDelimiterError.prototype);
|
|
12
9
|
}
|
|
13
|
-
}
|
|
10
|
+
}
|
|
@@ -6,8 +6,5 @@ export class MissingStartDelimiterError extends Error {
|
|
|
6
6
|
super(`Open delimiter is missing from '${closeDelimiterText}'.`);
|
|
7
7
|
|
|
8
8
|
this.closeDelimiterText = closeDelimiterText;
|
|
9
|
-
|
|
10
|
-
// typescript hack: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
11
|
-
Object.setPrototypeOf(this, MissingStartDelimiterError.prototype);
|
|
12
9
|
}
|
|
13
|
-
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export class TagOptionsParseError extends Error {
|
|
2
|
+
|
|
3
|
+
public readonly tagRawText: string;
|
|
4
|
+
public readonly parseError: Error;
|
|
5
|
+
|
|
6
|
+
constructor(tagRawText: string, parseError: Error) {
|
|
7
|
+
super(`Failed to parse tag options of '${tagRawText}': ${parseError.message}.`);
|
|
8
|
+
|
|
9
|
+
this.tagRawText = tagRawText;
|
|
10
|
+
this.parseError = parseError;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -6,8 +6,5 @@ export class UnclosedTagError extends Error {
|
|
|
6
6
|
super(`Tag '${tagName}' is never closed.`);
|
|
7
7
|
|
|
8
8
|
this.tagName = tagName;
|
|
9
|
-
|
|
10
|
-
// typescript hack: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
11
|
-
Object.setPrototypeOf(this, UnclosedTagError.prototype);
|
|
12
9
|
}
|
|
13
|
-
}
|
|
10
|
+
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
export class UnidentifiedFileTypeError extends Error {
|
|
2
2
|
constructor() {
|
|
3
3
|
super(`The filetype for this file could not be identified, is this file corrupted?`);
|
|
4
|
-
|
|
5
|
-
// typescript hack: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
6
|
-
Object.setPrototypeOf(this, UnidentifiedFileTypeError.prototype);
|
|
7
4
|
}
|
|
8
|
-
}
|
|
5
|
+
}
|
|
@@ -10,8 +10,5 @@ export class UnknownContentTypeError extends Error {
|
|
|
10
10
|
this.contentType = contentType;
|
|
11
11
|
this.tagRawText = tagRawText;
|
|
12
12
|
this.path = path;
|
|
13
|
-
|
|
14
|
-
// typescript hack: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
15
|
-
Object.setPrototypeOf(this, UnknownContentTypeError.prototype);
|
|
16
13
|
}
|
|
17
|
-
}
|
|
14
|
+
}
|
|
@@ -6,8 +6,5 @@ export class UnopenedTagError extends Error {
|
|
|
6
6
|
super(`Tag '${tagName}' is closed but was never opened.`);
|
|
7
7
|
|
|
8
8
|
this.tagName = tagName;
|
|
9
|
-
|
|
10
|
-
// typescript hack: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
11
|
-
Object.setPrototypeOf(this, UnopenedTagError.prototype);
|
|
12
9
|
}
|
|
13
|
-
}
|
|
10
|
+
}
|
|
@@ -6,8 +6,5 @@ export class UnsupportedFileTypeError extends Error {
|
|
|
6
6
|
super(`Filetype "${fileType}" is not supported.`);
|
|
7
7
|
|
|
8
8
|
this.fileType = fileType;
|
|
9
|
-
|
|
10
|
-
// typescript hack: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
11
|
-
Object.setPrototypeOf(this, UnsupportedFileTypeError.prototype);
|
|
12
9
|
}
|
|
13
|
-
}
|
|
10
|
+
}
|
package/src/office/docx.ts
CHANGED
|
@@ -114,7 +114,10 @@ export class Docx {
|
|
|
114
114
|
// find the last section properties
|
|
115
115
|
// see: http://officeopenxml.com/WPsection.php
|
|
116
116
|
const docRoot = await this.mainDocument.xmlRoot();
|
|
117
|
-
const body = docRoot.childNodes
|
|
117
|
+
const body = docRoot.childNodes.find(node => node.nodeName == 'w:body');
|
|
118
|
+
if (body == null)
|
|
119
|
+
return null;
|
|
120
|
+
|
|
118
121
|
const sectionProps = last(body.childNodes.filter(node => node.nodeType === XmlNodeType.General));
|
|
119
122
|
if (sectionProps.nodeName != 'w:sectPr')
|
|
120
123
|
return null;
|
package/src/office/docxParser.ts
CHANGED
|
@@ -208,8 +208,10 @@ export class DocxParser {
|
|
|
208
208
|
}
|
|
209
209
|
while (curWordTextNode) {
|
|
210
210
|
|
|
211
|
-
if (curWordTextNode.nodeName !== DocxParser.TEXT_NODE)
|
|
211
|
+
if (curWordTextNode.nodeName !== DocxParser.TEXT_NODE) {
|
|
212
|
+
curWordTextNode = curWordTextNode.nextSibling;
|
|
212
213
|
continue;
|
|
214
|
+
}
|
|
213
215
|
|
|
214
216
|
// move text to first node
|
|
215
217
|
const curXmlTextNode = XmlNode.lastTextChild(curWordTextNode);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export enum LoopOver {
|
|
2
|
+
/**
|
|
3
|
+
* Loop over the entire row.
|
|
4
|
+
*/
|
|
5
|
+
Row = 'row',
|
|
6
|
+
/**
|
|
7
|
+
* Loop over the content enclosed between the opening and closing tag.
|
|
8
|
+
*/
|
|
9
|
+
Content = 'content'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class LoopTagOptions {
|
|
13
|
+
public loopOver = LoopOver.Content;
|
|
14
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Tag } from '../../../compilation';
|
|
2
2
|
import { XmlNode } from '../../../xml';
|
|
3
3
|
import { PluginUtilities } from '../../templatePlugin';
|
|
4
|
+
import { LoopOver, LoopTagOptions } from '../loopTagOptions';
|
|
4
5
|
import { ILoopStrategy, SplitBeforeResult } from './iLoopStrategy';
|
|
5
6
|
|
|
6
7
|
export class LoopTableStrategy implements ILoopStrategy {
|
|
@@ -12,10 +13,28 @@ export class LoopTableStrategy implements ILoopStrategy {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
public isApplicable(openTag: Tag, closeTag: Tag): boolean {
|
|
15
|
-
const
|
|
16
|
-
if (!
|
|
16
|
+
const openParagraph = this.utilities.docxParser.containingParagraphNode(openTag.xmlTextNode);
|
|
17
|
+
if (!openParagraph.parentNode)
|
|
17
18
|
return false;
|
|
18
|
-
|
|
19
|
+
|
|
20
|
+
if (!this.utilities.docxParser.isTableCellNode(openParagraph.parentNode))
|
|
21
|
+
return false;
|
|
22
|
+
|
|
23
|
+
const closeParagraph = this.utilities.docxParser.containingParagraphNode(closeTag.xmlTextNode);
|
|
24
|
+
if (!closeParagraph.parentNode)
|
|
25
|
+
return false;
|
|
26
|
+
|
|
27
|
+
if (!this.utilities.docxParser.isTableCellNode(closeParagraph.parentNode))
|
|
28
|
+
return false;
|
|
29
|
+
|
|
30
|
+
const options = openTag.options as LoopTagOptions;
|
|
31
|
+
const forceRowLoop = options?.loopOver === LoopOver.Row;
|
|
32
|
+
|
|
33
|
+
// If both tags are in the same cell, assume it's a paragraph loop (iterate content, not rows).
|
|
34
|
+
if (!forceRowLoop && openParagraph.parentNode === closeParagraph.parentNode)
|
|
35
|
+
return false;
|
|
36
|
+
|
|
37
|
+
return true;
|
|
19
38
|
}
|
|
20
39
|
|
|
21
40
|
public splitBefore(openTag: Tag, closeTag: Tag): SplitBeforeResult {
|