docx-templates
Advanced tools
Comparing version 4.11.4 to 4.11.5
@@ -0,1 +1,6 @@ | ||
## 4.11.5 (2024-03-24) | ||
- [#340](https://github.com/guigrpa/docx-templates/issues/340) Fix for infinite loop bug: don't allow nested IFs on same `w:p` or `w:tr` tag. | ||
- [#356](https://github.com/guigrpa/docx-templates/issues/356) Simplify documentation of INS commands. | ||
- [#355](https://github.com/guigrpa/docx-templates/issues/355) Retain original sandbox errors (from different JavaScript realms) without coercion. | ||
## 4.11.4 (2024-01-12) | ||
@@ -2,0 +7,0 @@ - Replace weak `Object` types of `runJs` arguments. |
@@ -170,2 +170,4 @@ type Buffer = ArrayBufferLike; | ||
textRunPropsNode?: NonTextNode; | ||
pIfCheckMap: Map<Node, string>; | ||
trIfCheckMap: Map<Node, string>; | ||
}; | ||
@@ -287,2 +289,3 @@ type Images = { | ||
declare function isError(err: unknown): err is Error; | ||
/** | ||
@@ -330,2 +333,2 @@ * Thrown when `rejectNullish` is set to `true` and a command returns `null` or `undefined`. | ||
export { CommandExecutionError, CommandSyntaxError, ImageError, IncompleteConditionalStatementError, InternalError, InvalidCommandError, NullishCommandResultError, ObjectCommandResultError, QueryResolver, TemplateParseError, UnterminatedForLoopError, createReport, createReport as default, getMetadata, listCommands }; | ||
export { CommandExecutionError, CommandSyntaxError, ImageError, IncompleteConditionalStatementError, InternalError, InvalidCommandError, NullishCommandResultError, ObjectCommandResultError, QueryResolver, TemplateParseError, UnterminatedForLoopError, createReport, createReport as default, getMetadata, isError, listCommands }; |
import { LoopStatus } from './types'; | ||
export declare function isError(err: unknown): err is Error; | ||
/** | ||
@@ -3,0 +4,0 @@ * Thrown when `rejectNullish` is set to `true` and a command returns `null` or `undefined`. |
@@ -18,3 +18,8 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.UnterminatedForLoopError = exports.IncompleteConditionalStatementError = exports.TemplateParseError = exports.InternalError = exports.ImageError = exports.CommandExecutionError = exports.InvalidCommandError = exports.CommandSyntaxError = exports.ObjectCommandResultError = exports.NullishCommandResultError = void 0; | ||
exports.UnterminatedForLoopError = exports.IncompleteConditionalStatementError = exports.TemplateParseError = exports.InternalError = exports.ImageError = exports.CommandExecutionError = exports.InvalidCommandError = exports.CommandSyntaxError = exports.ObjectCommandResultError = exports.NullishCommandResultError = exports.isError = void 0; | ||
function isError(err) { | ||
return (err instanceof Error || | ||
(typeof err === 'object' && !!err && 'name' in err && 'message' in err)); | ||
} | ||
exports.isError = isError; | ||
/** | ||
@@ -74,3 +79,3 @@ * Thrown when `rejectNullish` is set to `true` and a command returns `null` or `undefined`. | ||
function CommandExecutionError(err, command) { | ||
var _this = _super.call(this, "Error executing command '".concat(command, "': ").concat(err.message)) || this; | ||
var _this = _super.call(this, "Error executing command '".concat(command, "': ").concat(err.name, ": ").concat(err.message)) || this; | ||
Object.setPrototypeOf(_this, CommandExecutionError.prototype); | ||
@@ -77,0 +82,0 @@ _this.command = command; |
@@ -105,3 +105,3 @@ "use strict"; | ||
err_1 = _b.sent(); | ||
e = err_1 instanceof Error ? err_1 : new Error("".concat(err_1)); | ||
e = (0, errors_1.isError)(err_1) ? err_1 : new Error("".concat(err_1)); | ||
if (!(ctx.options.errorHandler != null)) return [3 /*break*/, 10]; | ||
@@ -108,0 +108,0 @@ context = sandbox; |
@@ -80,2 +80,5 @@ "use strict"; | ||
options: options, | ||
// To verfiy we don't have a nested if within the same p or tr tag | ||
pIfCheckMap: new Map(), | ||
trIfCheckMap: new Map(), | ||
}; | ||
@@ -187,5 +190,26 @@ } | ||
}; | ||
var findParentPorTrNode = function (node) { | ||
var parentNode = node._parent; | ||
var resultNode = null; | ||
while (parentNode != null && resultNode == null) { | ||
var parentNodeTag = parentNode._fTextNode ? null : parentNode._tag; | ||
if (parentNodeTag === 'w:p') { | ||
// check also for w:tr tag | ||
var grandParentNode = parentNode._parent != null ? parentNode._parent._parent : null; | ||
if (grandParentNode != null && | ||
!grandParentNode._fTextNode && | ||
grandParentNode._tag === 'w:tr') { | ||
resultNode = grandParentNode; | ||
} | ||
else { | ||
resultNode = parentNode; | ||
} | ||
} | ||
parentNode = parentNode._parent; | ||
} | ||
return resultNode; | ||
}; | ||
function walkTemplate(data, template, ctx, processor) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var out, nodeIn, nodeOut, move, deltaJump, errors, curLoop, nextSibling, parent_3, tag, fRemoveNode, buffers, nodeOutParent, imgNode, captionNodes, parent_4, linkNode, parent_5, htmlNode, parent_6, tag, newNode, newNodeTag, parent_7, result, err, innermost_loop, err; | ||
var out, nodeIn, nodeOut, move, deltaJump, errors, loopCount, curLoop, nextSibling, parent_3, tag, fRemoveNode, buffers, nodeOutParent, imgNode, captionNodes, parent_4, linkNode, parent_5, htmlNode, parent_6, tag, newNode, newNodeTag, parent_7, result, err, innermost_loop, err; | ||
var _a; | ||
@@ -200,2 +224,3 @@ return __generator(this, function (_b) { | ||
errors = []; | ||
loopCount = 0; | ||
_b.label = 1; | ||
@@ -234,4 +259,11 @@ case 1: | ||
parent_3 = nodeIn._parent; | ||
if (parent_3 == null) | ||
if (parent_3 == null) { | ||
debug_1.logger.debug("=== parent is null, breaking after ".concat(loopCount, " loops...")); | ||
return [3 /*break*/, 5]; | ||
} | ||
else if (loopCount > 1000000) { | ||
// adding a emergency exit to avoid infit loops | ||
debug_1.logger.debug("=== parent is still not null after ".concat(loopCount, " loops, something must be wrong ..."), debugPrintNode(parent_3)); | ||
throw new errors_1.InternalError('something went wrong with the document. Please review and try again'); | ||
} | ||
nodeIn = parent_3; | ||
@@ -413,2 +445,3 @@ ctx.level -= 1; | ||
} | ||
loopCount++; | ||
return [3 /*break*/, 1]; | ||
@@ -611,3 +644,3 @@ case 5: | ||
catch (e) { | ||
if (!(e instanceof Error)) | ||
if (!(0, errors_1.isError)(e)) | ||
throw e; | ||
@@ -647,3 +680,3 @@ throw new errors_1.ImageError(e, cmd); | ||
err_1 = _b.sent(); | ||
if (!(err_1 instanceof Error)) | ||
if (!(0, errors_1.isError)(err_1)) | ||
throw err_1; | ||
@@ -706,3 +739,3 @@ if (ctx.options.errorHandler != null) { | ||
var processForIf = function (data, node, ctx, cmd, cmdName, cmdRest) { return __awaiter(void 0, void 0, void 0, function () { | ||
var isIf, forMatch, varName, curLoop, parentLoopLevel, fParentIsExploring, loopOver, shouldRun; | ||
var isIf, forMatch, varName, curLoop, parentPorTrNode, parentPorTrNodeTag, parentLoopLevel, fParentIsExploring, loopOver, shouldRun; | ||
return __generator(this, function (_a) { | ||
@@ -729,2 +762,27 @@ switch (_a.label) { | ||
if (!!(curLoop && curLoop.varName === varName)) return [3 /*break*/, 6]; | ||
// Check whether we already started a nested IF without and END-IF for this p or tr tag | ||
if (isIf) { | ||
parentPorTrNode = findParentPorTrNode(node); | ||
parentPorTrNodeTag = parentPorTrNode != null | ||
? parentPorTrNode._fTextNode | ||
? null | ||
: parentPorTrNode._tag | ||
: null; | ||
if (parentPorTrNode != null) { | ||
if (parentPorTrNodeTag === 'w:p') { | ||
if (ctx.pIfCheckMap.has(parentPorTrNode) && | ||
ctx.pIfCheckMap.get(parentPorTrNode) !== cmd) | ||
throw new errors_1.InvalidCommandError('Invalid IF command nested into another IF command on the same line', cmd); | ||
else | ||
ctx.pIfCheckMap.set(parentPorTrNode, cmd); | ||
} | ||
else if (parentPorTrNodeTag === 'w:tr') { | ||
if (ctx.trIfCheckMap.has(parentPorTrNode) && | ||
ctx.trIfCheckMap.get(parentPorTrNode) !== cmd) | ||
throw new errors_1.InvalidCommandError('Invalid IF command nested into another IF command on the same table row', cmd); | ||
else | ||
ctx.trIfCheckMap.set(parentPorTrNode, cmd); | ||
} | ||
} | ||
} | ||
parentLoopLevel = ctx.loops.length - 1; | ||
@@ -775,2 +833,15 @@ fParentIsExploring = parentLoopLevel >= 0 && ctx.loops[parentLoopLevel].idx === -1; | ||
throw new errors_1.InvalidCommandError("Unexpected ".concat(cmdName, " outside of ").concat(isIf ? 'IF statement' : 'FOR loop', " context"), cmd); | ||
// Reset the if check flag for the corresponding p or tr parent node | ||
var parentPorTrNode = findParentPorTrNode(node); | ||
var parentPorTrNodeTag = parentPorTrNode != null | ||
? parentPorTrNode._fTextNode | ||
? null | ||
: parentPorTrNode._tag | ||
: null; | ||
if (parentPorTrNodeTag === 'w:p') { | ||
ctx.pIfCheckMap.delete(parentPorTrNode); | ||
} | ||
else if (parentPorTrNodeTag === 'w:tr') { | ||
ctx.trIfCheckMap.delete(parentPorTrNode); | ||
} | ||
// First time we visit an END-IF node, we assign it the arbitrary name | ||
@@ -777,0 +848,0 @@ // generated when the IF was processed |
@@ -159,2 +159,4 @@ import { QualifiedAttribute } from 'sax'; | ||
textRunPropsNode?: NonTextNode; | ||
pIfCheckMap: Map<Node, string>; | ||
trIfCheckMap: Map<Node, string>; | ||
}; | ||
@@ -161,0 +163,0 @@ export type Images = { |
{ | ||
"name": "docx-templates", | ||
"version": "4.11.4", | ||
"version": "4.11.5", | ||
"description": "Template-based docx report creation", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -245,38 +245,29 @@ # Docx-templates [![Coverage Status](https://coveralls.io/repos/github/guigrpa/docx-templates/badge.svg?branch=master)](https://coveralls.io/github/guigrpa/docx-templates?branch=master) [![npm version](https://img.shields.io/npm/v/docx-templates.svg)](https://www.npmjs.com/package/docx-templates) | ||
### `QUERY` | ||
### Insert data with the `INS` command ( or using `=`, or nothing at all) | ||
You can use GraphQL, SQL, whatever you want: the query will be passed unchanged to your `data` query resolver. | ||
Inserts the result of a given JavaScript snippet as follows. | ||
Using code like this: | ||
```js | ||
const report = await createReport({ | ||
template, | ||
data: { name: 'John', surname: 'Appleseed' }, | ||
cmdDelimiter: ['+++', '+++'], | ||
}); | ||
``` | ||
+++QUERY | ||
query getData($projectId: Int!) { | ||
project(id: $projectId) { | ||
name | ||
details { year } | ||
people(sortedBy: "name") { name } | ||
} | ||
} | ||
+++ | ||
And a template like this: | ||
``` | ||
+++name+++ +++surname+++ | ||
``` | ||
For the following sections (except where noted), we assume the following dataset: | ||
Will produce a result docx file that looks like this: | ||
```js | ||
const data = { | ||
project: { | ||
name: 'docx-templates', | ||
details: { year: '2016' }, | ||
people: [{ name: 'John', since: 2015 }, { name: 'Robert', since: 2010 }], | ||
}, | ||
}; | ||
``` | ||
John Appleseed | ||
``` | ||
### `INS` (`=`, or nothing at all) | ||
Inserts the result of a given JavaScript snippet: | ||
Alternatively, you can use the more explicit `INS` (insert) command syntax. | ||
``` | ||
+++INS project.name+++ (+++INS project.details.year+++) | ||
or... | ||
+++INS `${project.name} (${$details.year})`+++ | ||
+++INS name+++ +++INS surname+++ | ||
``` | ||
@@ -294,7 +285,6 @@ | ||
You can also use this shorthand notation: | ||
You can also use `=` as shorthand notation instead of `INS`: | ||
``` | ||
+++= project.name+++ (+++= project.details.year+++) | ||
+++= `${project.name} (${$details.year})`+++ | ||
+++= name+++ +++= surname+++ | ||
``` | ||
@@ -305,3 +295,3 @@ | ||
``` | ||
{project.name} ({project.details.year}) | ||
{name} {surname} | ||
``` | ||
@@ -317,2 +307,30 @@ | ||
### `QUERY` | ||
You can use GraphQL, SQL, whatever you want: the query will be passed unchanged to your `data` query resolver. | ||
``` | ||
+++QUERY | ||
query getData($projectId: Int!) { | ||
project(id: $projectId) { | ||
name | ||
details { year } | ||
people(sortedBy: "name") { name } | ||
} | ||
} | ||
+++ | ||
``` | ||
For the following sections (except where noted), we assume the following dataset: | ||
```js | ||
const data = { | ||
project: { | ||
name: 'docx-templates', | ||
details: { year: '2016' }, | ||
people: [{ name: 'John', since: 2015 }, { name: 'Robert', since: 2010 }], | ||
}, | ||
}; | ||
``` | ||
### `EXEC` (`!`) | ||
@@ -319,0 +337,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
1217503
3911
688
2