Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

easy-template-x

Package Overview
Dependencies
Maintainers
1
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

easy-template-x - npm Package Compare versions

Comparing version 0.12.0 to 1.0.0

10

CHANGELOG.md
# Change Log
## [1.0.0 - 2020-08-09](https://github.com/alonrbar/easy-template-x/tree/v1.0.0)
**Stable release** - from now on breaking changes to the public API (the public
interface of `TemplateHandler`) will introduce a new major release.
### Fixed
- Initial content types parsing.
- Bug in paragraph loops (#36).
## [0.12.0 - 2020-08-01](https://github.com/alonrbar/easy-template-x/tree/v0.12.0)

@@ -4,0 +14,0 @@

@@ -17,2 +17,3 @@ import { XmlGeneralNode, XmlNode, XmlParser, XmlTextNode } from '../xml';

splitTextNode(textNode: XmlTextNode, splitIndex: number, addBefore: boolean): XmlTextNode;
splitParagraphByTextNode(paragraph: XmlNode, textNode: XmlTextNode, removeTextNode: boolean): [XmlNode, XmlNode];
joinTextNodesRange(from: XmlTextNode, to: XmlTextNode): void;

@@ -22,2 +23,4 @@ joinParagraphs(first: XmlNode, second: XmlNode): void;

isTextNode(node: XmlNode): boolean;
isRunNode(node: XmlNode): boolean;
isRunPropertiesNode(node: XmlNode): boolean;
isTableCellNode(node: XmlNode): boolean;

@@ -32,2 +35,4 @@ isParagraphNode(node: XmlNode): boolean;

containingTableRowNode(node: XmlNode): XmlNode;
isEmptyTextNode(node: XmlNode): boolean;
isEmptyRun(node: XmlNode): boolean;
}

2

dist/types/xml/xmlNode.d.ts

@@ -40,3 +40,3 @@ import { IMap } from '../types';

removeSiblings(from: XmlNode, to: XmlNode): XmlNode[];
splitByChild(root: XmlNode, markerNode: XmlNode, removeMarkerNode: boolean): [XmlNode, XmlNode];
splitByChild(parent: XmlNode, child: XmlNode, removeChild: boolean): [XmlNode, XmlNode];
findParent(node: XmlNode, predicate: (node: XmlNode) => boolean): XmlNode;

@@ -43,0 +43,0 @@ findParentByName(node: XmlNode, nodeName: string): XmlNode;

{
"name": "easy-template-x",
"version": "0.12.0",
"version": "1.0.0",
"description": "Generate docx documents from templates, in Node or in the browser.",

@@ -5,0 +5,0 @@ "keywords": [

@@ -87,5 +87,5 @@ import { MimeType, MimeTypeHelper } from '../mimeType';

this.contentTypes[contentTypeAttribute];
this.contentTypes[contentTypeAttribute] = true;
}
}
}

@@ -109,2 +109,74 @@ import { XmlGeneralNode, XmlNode, XmlParser, XmlTextNode } from '../xml';

/**
* Split the paragraph around the specified text node.
*
* @returns Two paragraphs - `left` and `right`. If the `removeTextNode` argument is
* `false` then the original text node is the first text node of `right`.
*/
public splitParagraphByTextNode(paragraph: XmlNode, textNode: XmlTextNode, removeTextNode: boolean): [XmlNode, XmlNode] {
// input validation
const containingParagraph = this.containingParagraphNode(textNode);
if (containingParagraph != paragraph)
throw new Error(`Node '${nameof(textNode)}' is not a descendant of '${nameof(paragraph)}'.`);
const runNode = this.containingRunNode(textNode);
const wordTextNode = this.containingTextNode(textNode);
// create run clone
const leftRun = XmlNode.cloneNode(runNode, false);
const rightRun = runNode;
XmlNode.insertBefore(leftRun, rightRun);
// copy props from original run node (preserve style)
const runProps = rightRun.childNodes.find(node => node.nodeName === DocxParser.RUN_PROPERTIES_NODE);
if (runProps) {
const leftRunProps = XmlNode.cloneNode(runProps, true);
XmlNode.appendChild(leftRun, leftRunProps);
}
// move nodes from 'right' to 'left'
const firstRunChildIndex = (runProps ? 1 : 0);
let curChild = rightRun.childNodes[firstRunChildIndex];
while (curChild != wordTextNode) {
XmlNode.remove(curChild);
XmlNode.appendChild(leftRun, curChild);
curChild = rightRun.childNodes[firstRunChildIndex];
}
// remove text node
if (removeTextNode) {
XmlNode.removeChild(rightRun, firstRunChildIndex);
}
// create paragraph clone
const leftPara = XmlNode.cloneNode(containingParagraph, false);
const rightPara = containingParagraph;
XmlNode.insertBefore(leftPara, rightPara);
// copy props from original paragraph (preserve style)
const paragraphProps = rightPara.childNodes.find(node => node.nodeName === DocxParser.PARAGRAPH_PROPERTIES_NODE);
if (paragraphProps) {
const leftParagraphProps = XmlNode.cloneNode(paragraphProps, true);
XmlNode.appendChild(leftPara, leftParagraphProps);
}
// move nodes from 'right' to 'left'
const firstParaChildIndex = (paragraphProps ? 1 : 0);
curChild = rightPara.childNodes[firstParaChildIndex];
while (curChild != rightRun) {
XmlNode.remove(curChild);
XmlNode.appendChild(leftPara, curChild);
curChild = rightPara.childNodes[firstParaChildIndex];
}
// clean paragraphs - remove empty runs
if (this.isEmptyRun(leftRun))
XmlNode.remove(leftRun);
if (this.isEmptyRun(rightRun))
XmlNode.remove(rightRun);
return [leftPara, rightPara];
}
/**
* Move all text between the 'from' and 'to' nodes to the 'from' node.

@@ -216,2 +288,10 @@ */

public isRunNode(node: XmlNode): boolean {
return node.nodeName === DocxParser.RUN_NODE;
}
public isRunPropertiesNode(node: XmlNode): boolean {
return node.nodeName === DocxParser.RUN_PROPERTIES_NODE;
}
public isTableCellNode(node: XmlNode): boolean {

@@ -294,2 +374,41 @@ return node.nodeName === DocxParser.TABLE_CELL_NODE;

}
//
// advanced node queries
//
public isEmptyTextNode(node: XmlNode): boolean {
if (!this.isTextNode(node))
throw new Error(`Text node expected but '${node.nodeName}' received.`);
if (!node.childNodes?.length)
return true;
const xmlTextNode = node.childNodes[0];
if (!XmlNode.isTextNode(xmlTextNode))
throw new Error("Invalid XML structure. 'w:t' node should contain a single text node only.");
if (!xmlTextNode.textContent)
return true;
return false;
}
public isEmptyRun(node: XmlNode): boolean {
if (!this.isRunNode(node))
throw new Error(`Run node expected but '${node.nodeName}' received.`);
for (const child of (node.childNodes ?? [])) {
if (this.isRunPropertiesNode(child))
continue;
if (this.isTextNode(child) && this.isEmptyTextNode(child))
continue;
return false;
}
return true;
}
}

@@ -24,24 +24,21 @@ import { Tag } from '../../../compilation';

const areSame = (firstParagraph === lastParagraph);
const parent = firstParagraph.parentNode;
const firstParagraphIndex = parent.childNodes.indexOf(firstParagraph);
const lastParagraphIndex = areSame ? firstParagraphIndex : parent.childNodes.indexOf(lastParagraph);
// split first paragraphs
let splitResult = XmlNode.splitByChild(firstParagraph, openTag.xmlTextNode, true);
// split first paragraph
let splitResult = this.utilities.docxParser.splitParagraphByTextNode(firstParagraph, openTag.xmlTextNode, true);
firstParagraph = splitResult[0];
const firstParagraphSplit = splitResult[1];
let afterFirstParagraph = splitResult[1];
if (areSame)
lastParagraph = firstParagraphSplit;
lastParagraph = afterFirstParagraph;
// split last paragraph
splitResult = XmlNode.splitByChild(lastParagraph, closeTag.xmlTextNode, true);
const lastParagraphSplit = splitResult[0];
splitResult = this.utilities.docxParser.splitParagraphByTextNode(lastParagraph, closeTag.xmlTextNode, true);
const beforeLastParagraph = splitResult[0];
lastParagraph = splitResult[1];
if (areSame)
afterFirstParagraph = beforeLastParagraph;
// fix references
XmlNode.removeChild(parent, firstParagraphIndex + 1);
// disconnect splitted paragraph from their parents
XmlNode.remove(afterFirstParagraph);
if (!areSame)
XmlNode.removeChild(parent, lastParagraphIndex);
firstParagraphSplit.parentNode = null;
lastParagraphSplit.parentNode = null;
XmlNode.remove(beforeLastParagraph);

@@ -51,7 +48,6 @@ // extract all paragraphs in between

if (areSame) {
this.utilities.docxParser.joinParagraphs(firstParagraphSplit, lastParagraphSplit);
middleParagraphs = [firstParagraphSplit];
middleParagraphs = [afterFirstParagraph];
} else {
const inBetween = XmlNode.removeSiblings(firstParagraph, lastParagraph);
middleParagraphs = [firstParagraphSplit].concat(inBetween).concat(lastParagraphSplit);
middleParagraphs = [afterFirstParagraph].concat(inBetween).concat(beforeLastParagraph);
}

@@ -58,0 +54,0 @@

@@ -384,39 +384,42 @@ import { MissingArgumentError } from '../errors';

/**
* Split the original node into two sibling nodes.
* Returns both nodes.
* Split the original node into two sibling nodes. Returns both nodes.
*
* @param root The node to split
* @param markerNode The node that marks the split position.
* @param parent The node to split
* @param child The node that marks the split position.
* @param removeChild Should this method remove the child while splitting.
*
* @returns Two nodes - `left` and `right`. If the `removeChild` argument is
* `false` then the original child node is the first child of `right`.
*/
splitByChild(root: XmlNode, markerNode: XmlNode, removeMarkerNode: boolean): [XmlNode, XmlNode] {
splitByChild(parent: XmlNode, child: XmlNode, removeChild: boolean): [XmlNode, XmlNode] {
// find the split path
const path = getDescendantPath(root, markerNode);
if (child.parentNode != parent)
throw new Error(`Node '${nameof(child)}' is not a direct child of '${nameof(parent)}'.`);
// split
const split = XmlNode.cloneNode(root, false);
const childIndex = path[0] + 1;
while (childIndex < root.childNodes.length) {
const curChild = root.childNodes[childIndex];
XmlNode.remove(curChild);
XmlNode.appendChild(split, curChild);
// create childless clone 'left'
const left = XmlNode.cloneNode(parent, false);
if (parent.parentNode) {
XmlNode.insertBefore(left, parent);
}
const right = parent;
if (root.parentNode) {
XmlNode.insertAfter(split, root);
// move nodes from 'right' to 'left'
let curChild = right.childNodes[0];
while (curChild != child) {
XmlNode.remove(curChild);
XmlNode.appendChild(left, curChild);
curChild = right.childNodes[0];
}
// remove marker node
if (removeMarkerNode && root.childNodes.length) {
XmlNode.removeChild(root, root.childNodes.length - 1);
// remove child
if (removeChild) {
XmlNode.removeChild(right, 0);
}
return [root, split];
return [left, right];
},
findParent(node: XmlNode, predicate: (node: XmlNode) => boolean): XmlNode {
if (!node)
return null;
while (node.parentNode) {
while (node) {

@@ -564,20 +567,2 @@ if (predicate(node))

function getDescendantPath(root: XmlNode, descendant: XmlNode): number[] {
const path: number[] = [];
let node = descendant;
while (node !== root) {
const parent = node.parentNode;
if (!parent)
throw new Error(`Argument ${nameof(descendant)} is not a descendant of ${nameof(root)}`);
const curChildIndex = parent.childNodes.indexOf(node);
path.push(curChildIndex);
node = parent;
}
return path.reverse();
}
function recursiveRemoveEmptyTextNodes(node: XmlNode): XmlNode {

@@ -584,0 +569,0 @@

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc