docx-templates
Advanced tools
Comparing version 4.0.0 to 4.1.0
@@ -0,1 +1,11 @@ | ||
## 4.1.0 (2020-4-25) | ||
## 4.1.0 (2020-04-23) | ||
* #112 Failing on the first error that is encountered while rendering a template is now optional behaviour (but still the default). Use `failFast: false` to collect all errors in a template before failing. This allows for less cumbersome interactive correction of typos or other mistakes in template commands. | ||
* #33 SVGs can now be inserted into the templates directly, based on excellent work by @lwallent. | ||
* #113 fixed a regression caused by relying on incomplete jsZip type definitions. | ||
* #83 fixed a bug that occurred when links were used in FOR loops. | ||
* Added docstrings (thanks @mathe42 !) | ||
## 4.0.0 (2020-04-13) | ||
@@ -2,0 +12,0 @@ |
@@ -103,3 +103,3 @@ "use strict"; | ||
script = new vm_1.default.Script("\n __result__ = eval(__code__);\n ", {}); | ||
context = vm_1.default.createContext(sandbox); // eslint-disable-line new-cap | ||
context = vm_1.default.createContext(sandbox); | ||
script.runInContext(context); | ||
@@ -106,0 +106,0 @@ result = context.__result__; |
import { UserOptions, Node } from './types'; | ||
/** | ||
* Create Report from docx template | ||
* | ||
* example: | ||
* ```js | ||
* createReport({ | ||
* template, | ||
* data: query => graphqlServer.execute(query), | ||
* additionalJsContext: { | ||
* // all of these will be available to JS snippets in your template commands | ||
* foo: 'bar', | ||
* qrCode: async url => { | ||
* // do stuff | ||
* }, | ||
* }, | ||
* cmdDelimiter: '+++', | ||
* literalXmlDelimiter: '||', | ||
* processLineBreaks: true, | ||
* noSandbox: false, | ||
* }); | ||
* ``` | ||
* | ||
* @param options Options for Report | ||
*/ | ||
declare function createReport(options: UserOptions): Promise<Uint8Array>; | ||
/** | ||
* For development and testing purposes. Don't use _probe if you don't know what you are doing | ||
*/ | ||
declare function createReport(options: UserOptions, _probe: 'JS'): Promise<Node>; | ||
/** | ||
* For development and testing purposes. Don't use _probe if you don't know what you are doing | ||
*/ | ||
declare function createReport(options: UserOptions, _probe: 'XML'): Promise<string>; | ||
export default createReport; |
@@ -55,6 +55,6 @@ "use strict"; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var template, data, queryVars, templatePath, literalXmlDelimiter, createOptions, xmlOptions, zip, templateXml, tic, parseResult, jsTemplate, tac, finalTemplate, queryResult, query, result, report1, images1, links1, htmls1, reportXml, numImages, numHtmls, files, images, links, htmls, i, filePath, raw, js0, js, _a, report2, images2, links2, htmls2, xml, segments, documentComponent, contentTypesPath, contentTypesXml, contentTypes_1, ensureContentType, finalContentTypesXml, output; | ||
var template, data, queryVars, templatePath, literalXmlDelimiter, createOptions, xmlOptions, zip, templateXml, tic, parseResult, jsTemplate, tac, finalTemplate, queryResult, query, result, report1, images1, links1, htmls1, reportXml, numImages, numHtmls, files, images, links, htmls, i, filePath, raw, js0, js, result_1, report2, images2, links2, htmls2, xml, segments, documentComponent, contentTypesPath, contentTypesXml, contentTypes_1, ensureContentType, finalContentTypesXml, output; | ||
var _this = this; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
@@ -72,2 +72,3 @@ DEBUG && debug_1.default.debug('Report options:', { attach: options }); | ||
additionalJsContext: options.additionalJsContext || {}, | ||
failFast: options.failFast == null ? true : options.failFast, | ||
}; | ||
@@ -81,3 +82,3 @@ xmlOptions = { literalXmlDelimiter: literalXmlDelimiter }; | ||
case 1: | ||
zip = _b.sent(); | ||
zip = _a.sent(); | ||
// --------------------------------------------------------- | ||
@@ -89,3 +90,3 @@ // Read the 'document.xml' file (the template) and parse it | ||
case 2: | ||
templateXml = _b.sent(); | ||
templateXml = _a.sent(); | ||
if (templateXml == null) | ||
@@ -98,3 +99,3 @@ throw new Error('document.xml could not be found'); | ||
case 3: | ||
parseResult = _b.sent(); | ||
parseResult = _a.sent(); | ||
jsTemplate = parseResult; | ||
@@ -117,11 +118,11 @@ tac = new Date().getTime(); | ||
case 4: | ||
query = _b.sent(); | ||
query = _a.sent(); | ||
DEBUG && debug_1.default.debug("Query: " + (query || 'no query found')); | ||
return [4 /*yield*/, data(query, queryVars)]; | ||
case 5: | ||
queryResult = _b.sent(); | ||
queryResult = _a.sent(); | ||
return [3 /*break*/, 7]; | ||
case 6: | ||
queryResult = data; | ||
_b.label = 7; | ||
_a.label = 7; | ||
case 7: | ||
@@ -137,3 +138,6 @@ // --------------------------------------------------------- | ||
case 8: | ||
result = _b.sent(); | ||
result = _a.sent(); | ||
if (result.status === 'errors') { | ||
throw result.errors; | ||
} | ||
report1 = result.report, images1 = result.images, links1 = result.links, htmls1 = result.htmls; | ||
@@ -158,9 +162,9 @@ if (_probe === 'JS') | ||
case 9: | ||
_b.sent(); | ||
_a.sent(); | ||
return [4 /*yield*/, processLinks(links1, 'document.xml', zip, templatePath)]; | ||
case 10: | ||
_b.sent(); | ||
_a.sent(); | ||
return [4 /*yield*/, processHtmls(htmls1, 'document.xml', zip, templatePath)]; | ||
case 11: | ||
_b.sent(); | ||
_a.sent(); | ||
files = []; | ||
@@ -183,3 +187,3 @@ zip.forEach(function (filePath) { return __awaiter(_this, void 0, void 0, function () { | ||
i = 0; | ||
_b.label = 12; | ||
_a.label = 12; | ||
case 12: | ||
@@ -191,3 +195,3 @@ if (!(i < files.length)) return [3 /*break*/, 20]; | ||
case 13: | ||
raw = _b.sent(); | ||
raw = _a.sent(); | ||
if (raw == null) | ||
@@ -197,7 +201,11 @@ throw new Error(filePath + " could not be read"); | ||
case 14: | ||
js0 = _b.sent(); | ||
js0 = _a.sent(); | ||
js = preprocessTemplate_1.default(js0, createOptions); | ||
return [4 /*yield*/, processTemplate_1.produceJsReport(queryResult, js, createOptions)]; | ||
case 15: | ||
_a = _b.sent(), report2 = _a.report, images2 = _a.images, links2 = _a.links, htmls2 = _a.htmls; | ||
result_1 = _a.sent(); | ||
if (result_1.status === 'errors') { | ||
throw result_1.errors; | ||
} | ||
report2 = result_1.report, images2 = result_1.images, links2 = result_1.links, htmls2 = result_1.htmls; | ||
images = timm_1.merge(images, images2); | ||
@@ -214,10 +222,10 @@ links = timm_1.merge(links, links2); | ||
case 16: | ||
_b.sent(); | ||
_a.sent(); | ||
return [4 /*yield*/, processLinks(links2, 'document.xml', zip, templatePath)]; | ||
case 17: | ||
_b.sent(); | ||
_a.sent(); | ||
return [4 /*yield*/, processHtmls(htmls2, 'document.xml', zip, templatePath)]; | ||
case 18: | ||
_b.sent(); | ||
_b.label = 19; | ||
_a.sent(); | ||
_a.label = 19; | ||
case 19: | ||
@@ -232,3 +240,3 @@ i++; | ||
case 21: | ||
contentTypesXml = _b.sent(); | ||
contentTypesXml = _a.sent(); | ||
if (contentTypesXml == null) | ||
@@ -238,3 +246,3 @@ throw new Error(contentTypesPath + " could not be read"); | ||
case 22: | ||
contentTypes_1 = _b.sent(); | ||
contentTypes_1 = _a.sent(); | ||
ensureContentType = function (extension, contentType) { | ||
@@ -266,3 +274,3 @@ var children = contentTypes_1._children; | ||
zip_1.zipSetText(zip, contentTypesPath, finalContentTypesXml); | ||
_b.label = 23; | ||
_a.label = 23; | ||
case 23: | ||
@@ -275,3 +283,3 @@ // --------------------------------------------------------- | ||
case 24: | ||
output = _b.sent(); | ||
output = _a.sent(); | ||
return [2 /*return*/, output]; | ||
@@ -278,0 +286,0 @@ } |
import { Node, CreateReportOptions, Images, Links, Htmls } from './types'; | ||
declare const extractQuery: (template: Node, options: CreateReportOptions) => Promise<string | undefined>; | ||
declare type ReportOutput = { | ||
status: 'success'; | ||
report: Node; | ||
@@ -8,4 +9,7 @@ images: Images; | ||
htmls: Htmls; | ||
} | { | ||
status: 'errors'; | ||
errors: Error[]; | ||
}; | ||
declare const produceJsReport: (data: any, template: Node, options: CreateReportOptions) => Promise<ReportOutput>; | ||
export { extractQuery, produceJsReport }; |
@@ -87,3 +87,3 @@ "use strict"; | ||
parent_2._tag === 'w:t')) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, processText(null, nodeIn, ctx)]; | ||
return [4 /*yield*/, processText(null, nodeIn, ctx, options.failFast)]; | ||
case 2: | ||
@@ -102,5 +102,5 @@ _a.sent(); | ||
var produceJsReport = function (data, template, options) { return __awaiter(void 0, void 0, void 0, function () { | ||
var out, ctx, nodeIn, nodeOut, move, deltaJump, curLoop, nextSibling, refNode, refNodeLevel, parent_3, tag, fRemoveNode, buffers, nodeOutParent, imgNode, parent_4, linkNode, parent_5, htmlNode, parent_6, tag, newNode, parent_7, newNodeAsTextNode, _a; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
var out, ctx, nodeIn, nodeOut, move, deltaJump, errors, curLoop, nextSibling, refNode, refNodeLevel, parent_3, tag, fRemoveNode, buffers, nodeOutParent, imgNode, parent_4, linkNode, parent_5, htmlNode, parent_6, tag, newNode, parent_7, result, newNodeAsTextNode; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
@@ -132,3 +132,4 @@ out = reportUtils_1.cloneNodeWithoutChildren(template); | ||
deltaJump = 0; | ||
_b.label = 1; | ||
errors = []; | ||
_a.label = 1; | ||
case 1: | ||
@@ -316,12 +317,17 @@ if (!true) return [3 /*break*/, 5]; | ||
parent_7._tag === 'w:t')) return [3 /*break*/, 3]; | ||
newNodeAsTextNode = newNode; | ||
_a = newNodeAsTextNode; | ||
return [4 /*yield*/, processText(data, nodeIn, ctx)]; | ||
return [4 /*yield*/, processText(data, nodeIn, ctx, options.failFast)]; | ||
case 2: | ||
_a._text = _b.sent(); | ||
_b.label = 3; | ||
result = _a.sent(); | ||
if (typeof result === 'string') { | ||
newNodeAsTextNode = newNode; | ||
newNodeAsTextNode._text = result; | ||
} | ||
else { | ||
errors.push.apply(errors, result); | ||
} | ||
_a.label = 3; | ||
case 3: | ||
// Execute the move in the output tree | ||
nodeOut = newNode; | ||
_b.label = 4; | ||
_a.label = 4; | ||
case 4: | ||
@@ -339,8 +345,15 @@ // Correct output tree level in case of a JUMP | ||
return [3 /*break*/, 1]; | ||
case 5: return [2 /*return*/, { | ||
report: out, | ||
images: ctx.images, | ||
links: ctx.links, | ||
htmls: ctx.htmls, | ||
}]; | ||
case 5: | ||
if (errors.length > 0) | ||
return [2 /*return*/, { | ||
status: 'errors', | ||
errors: errors, | ||
}]; | ||
return [2 /*return*/, { | ||
status: 'success', | ||
report: out, | ||
images: ctx.images, | ||
links: ctx.links, | ||
htmls: ctx.htmls, | ||
}]; | ||
} | ||
@@ -350,4 +363,4 @@ }); | ||
exports.produceJsReport = produceJsReport; | ||
var processText = function (data, node, ctx) { return __awaiter(void 0, void 0, void 0, function () { | ||
var cmdDelimiter, text, segments, outText, idx, segment, cmdResultText; | ||
var processText = function (data, node, ctx, failFast) { return __awaiter(void 0, void 0, void 0, function () { | ||
var cmdDelimiter, text, segments, outText, errors, idx, segment, cmdResultText; | ||
return __generator(this, function (_a) { | ||
@@ -365,2 +378,3 @@ switch (_a.label) { | ||
outText = ''; | ||
errors = []; | ||
idx = 0; | ||
@@ -386,7 +400,14 @@ _a.label = 1; | ||
if (cmdResultText != null) { | ||
outText += cmdResultText; | ||
appendTextToTagBuffers(cmdResultText, ctx, { | ||
fCmd: false, | ||
fInsertedText: true, | ||
}); | ||
if (cmdResultText instanceof Error) { | ||
if (failFast) | ||
throw cmdResultText; | ||
errors.push(cmdResultText); | ||
} | ||
else { | ||
outText += cmdResultText; | ||
appendTextToTagBuffers(cmdResultText, ctx, { | ||
fCmd: false, | ||
fInsertedText: true, | ||
}); | ||
} | ||
} | ||
@@ -400,3 +421,6 @@ _a.label = 3; | ||
return [3 /*break*/, 1]; | ||
case 5: return [2 /*return*/, outText]; | ||
case 5: | ||
if (errors.length > 0) | ||
return [2 /*return*/, errors]; | ||
return [2 /*return*/, outText]; | ||
} | ||
@@ -510,3 +534,3 @@ }); | ||
err_1 = _a.sent(); | ||
throw new Error("Error executing command: " + cmd + " " + err_1.message); | ||
return [2 /*return*/, new Error("Error executing command: " + cmd + " " + err_1.message)]; | ||
case 27: return [2 /*return*/]; | ||
@@ -653,69 +677,85 @@ } | ||
}; | ||
/* eslint-disable */ | ||
var imageToContext = function (ctx, img) { | ||
if (!(typeof img.extension === 'string')) { | ||
throw new Error('An extension (e.g. `.png`) needs to be provided when providing an image or a thumbnail.'); | ||
} | ||
ctx.imageId += 1; | ||
var id = String(ctx.imageId); | ||
var relId = "img" + id; | ||
ctx.images[relId] = img; | ||
return relId; | ||
}; | ||
var processImage = function (ctx, imagePars) { return __awaiter(void 0, void 0, void 0, function () { | ||
var cx, cy, id, relId, _a, _b, alt, node, pic, drawing; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
cx = (imagePars.width * 360e3).toFixed(0); | ||
cy = (imagePars.height * 360e3).toFixed(0); | ||
ctx.imageId += 1; | ||
id = String(ctx.imageId); | ||
relId = "img" + id; | ||
_a = ctx.images; | ||
_b = relId; | ||
return [4 /*yield*/, getImageData(imagePars)]; | ||
case 1: | ||
_a[_b] = _c.sent(); | ||
alt = imagePars.alt || 'desc'; | ||
node = reportUtils_1.newNonTextNode; | ||
pic = node('pic:pic', { 'xmlns:pic': 'http://schemas.openxmlformats.org/drawingml/2006/picture' }, [ | ||
node('pic:nvPicPr', {}, [ | ||
node('pic:cNvPr', { id: '0', name: "Picture " + id, descr: alt }), | ||
node('pic:cNvPicPr', {}, [ | ||
node('a:picLocks', { noChangeAspect: '1', noChangeArrowheads: '1' }), | ||
]), | ||
]), | ||
node('pic:blipFill', {}, [ | ||
node('a:blip', { 'r:embed': relId, cstate: 'print' }, [ | ||
node('a:extLst', {}, [ | ||
node('a:ext', { uri: '{28A0092B-C50C-407E-A947-70E740481C1C}' }, [ | ||
node('a14:useLocalDpi', { | ||
'xmlns:a14': 'http://schemas.microsoft.com/office/drawing/2010/main', | ||
val: '0', | ||
}), | ||
]), | ||
]), | ||
]), | ||
node('a:srcRect'), | ||
node('a:stretch', {}, [node('a:fillRect')]), | ||
]), | ||
node('pic:spPr', { bwMode: 'auto' }, [ | ||
node('a:xfrm', {}, [ | ||
node('a:off', { x: '0', y: '0' }), | ||
node('a:ext', { cx: cx, cy: cy }), | ||
]), | ||
node('a:prstGeom', { prst: 'rect' }, [node('a:avLst')]), | ||
node('a:noFill'), | ||
node('a:ln', {}, [node('a:noFill')]), | ||
]), | ||
]); | ||
drawing = node('w:drawing', {}, [ | ||
node('wp:inline', { distT: '0', distB: '0', distL: '0', distR: '0' }, [ | ||
node('wp:extent', { cx: cx, cy: cy }), | ||
node('wp:docPr', { id: id, name: "Picture " + id, descr: alt }), | ||
node('wp:cNvGraphicFramePr', {}, [ | ||
node('a:graphicFrameLocks', { | ||
'xmlns:a': 'http://schemas.openxmlformats.org/drawingml/2006/main', | ||
noChangeAspect: '1', | ||
}), | ||
]), | ||
node('a:graphic', { 'xmlns:a': 'http://schemas.openxmlformats.org/drawingml/2006/main' }, [ | ||
node('a:graphicData', { uri: 'http://schemas.openxmlformats.org/drawingml/2006/picture' }, [pic]), | ||
]), | ||
]), | ||
]); | ||
ctx.pendingImageNode = drawing; | ||
return [2 /*return*/]; | ||
var cx, cy, imgRelId, id, alt, node, extNodes, thumbnail, thumbRelId, pic, drawing; | ||
var _a; | ||
return __generator(this, function (_b) { | ||
cx = (imagePars.width * 360e3).toFixed(0); | ||
cy = (imagePars.height * 360e3).toFixed(0); | ||
imgRelId = imageToContext(ctx, getImageData(imagePars)); | ||
id = String(ctx.imageId); | ||
alt = imagePars.alt || 'desc'; | ||
node = reportUtils_1.newNonTextNode; | ||
extNodes = []; | ||
extNodes.push(node('a:ext', { uri: '{28A0092B-C50C-407E-A947-70E740481C1C}' }, [ | ||
node('a14:useLocalDpi', { | ||
'xmlns:a14': 'http://schemas.microsoft.com/office/drawing/2010/main', | ||
val: '0', | ||
}), | ||
])); | ||
if (ctx.images[imgRelId].extension === '.svg') { | ||
thumbnail = (_a = imagePars.thumbnail) !== null && _a !== void 0 ? _a : { | ||
data: 'bm90aGluZwo=', | ||
extension: '.png', | ||
}; | ||
thumbRelId = imageToContext(ctx, thumbnail); | ||
extNodes.push(node('a:ext', { uri: '{96DAC541-7B7A-43D3-8B79-37D633B846F1}' }, [ | ||
node('asvg:svgBlip', { | ||
'xmlns:asvg': 'http://schemas.microsoft.com/office/drawing/2016/SVG/main', | ||
'r:embed': imgRelId, | ||
}), | ||
])); | ||
// For SVG the thumb is placed where the image normally goes. | ||
imgRelId = thumbRelId; | ||
} | ||
pic = node('pic:pic', { 'xmlns:pic': 'http://schemas.openxmlformats.org/drawingml/2006/picture' }, [ | ||
node('pic:nvPicPr', {}, [ | ||
node('pic:cNvPr', { id: '0', name: "Picture " + id, descr: alt }), | ||
node('pic:cNvPicPr', {}, [ | ||
node('a:picLocks', { noChangeAspect: '1', noChangeArrowheads: '1' }), | ||
]), | ||
]), | ||
node('pic:blipFill', {}, [ | ||
node('a:blip', { 'r:embed': imgRelId, cstate: 'print' }, [ | ||
node('a:extLst', {}, extNodes), | ||
]), | ||
node('a:srcRect'), | ||
node('a:stretch', {}, [node('a:fillRect')]), | ||
]), | ||
node('pic:spPr', { bwMode: 'auto' }, [ | ||
node('a:xfrm', {}, [ | ||
node('a:off', { x: '0', y: '0' }), | ||
node('a:ext', { cx: cx, cy: cy }), | ||
]), | ||
node('a:prstGeom', { prst: 'rect' }, [node('a:avLst')]), | ||
node('a:noFill'), | ||
node('a:ln', {}, [node('a:noFill')]), | ||
]), | ||
]); | ||
drawing = node('w:drawing', {}, [ | ||
node('wp:inline', { distT: '0', distB: '0', distL: '0', distR: '0' }, [ | ||
node('wp:extent', { cx: cx, cy: cy }), | ||
node('wp:docPr', { id: id, name: "Picture " + id, descr: alt }), | ||
node('wp:cNvGraphicFramePr', {}, [ | ||
node('a:graphicFrameLocks', { | ||
'xmlns:a': 'http://schemas.openxmlformats.org/drawingml/2006/main', | ||
noChangeAspect: '1', | ||
}), | ||
]), | ||
node('a:graphic', { 'xmlns:a': 'http://schemas.openxmlformats.org/drawingml/2006/main' }, [ | ||
node('a:graphicData', { uri: 'http://schemas.openxmlformats.org/drawingml/2006/picture' }, [pic]), | ||
]), | ||
]), | ||
]); | ||
ctx.pendingImageNode = drawing; | ||
return [2 /*return*/]; | ||
}); | ||
@@ -722,0 +762,0 @@ }); }; |
@@ -74,3 +74,3 @@ "use strict"; | ||
node._children.forEach(function (child) { | ||
child._parent = node; // eslint-disable-line | ||
child._parent = node; | ||
}); | ||
@@ -87,3 +87,3 @@ return node; | ||
parent._children.push(child); | ||
child._parent = parent; // eslint-disable-line | ||
child._parent = parent; | ||
return child; | ||
@@ -90,0 +90,0 @@ }; |
@@ -33,11 +33,52 @@ /// <reference types="node" /> | ||
export declare type UserOptions = { | ||
/** | ||
* Docx file template as a NodeJS Buffer or Buffer-like object in Browsers. | ||
*/ | ||
template: Buffer; | ||
/** | ||
* Object of data to be injected or a (async) function that resolves to the data. The function gets as an argument the contents of the QUERY command as a string. | ||
*/ | ||
data?: ReportData | QueryResolver; | ||
/** | ||
* Gets injected into data function as second argument. | ||
*/ | ||
queryVars?: any; | ||
/** | ||
* Defines a custom command delimiter. This can be a String e.g. '+++' or an Array of Strings with length 2: ['{', '}'] in which the first element serves as the start delimiter and the second as the end delimiter. | ||
*/ | ||
cmdDelimiter?: string | [string, string]; | ||
/** | ||
* Can be used to change the delimiter in generated XML. | ||
*/ | ||
literalXmlDelimiter?: string; | ||
/** | ||
* Handle linebreaks in result of commands as actual linebreaks (Default: true) | ||
*/ | ||
processLineBreaks?: boolean; | ||
/** | ||
* INSECURE: Set this option to true to disable running all commands in a new JS-VM. USE ONLY WITH TRUSTED TEMPLATES. Beware of arbitrary code injection risks. Can slightly improve performance on complex templates. | ||
*/ | ||
noSandbox?: boolean; | ||
/** | ||
* Custom sandbox. See documentation for details. | ||
*/ | ||
runJs?: RunJSFunc; | ||
/** | ||
* Add functions or other static data to this option to have access to it in your commands. | ||
* | ||
* ```js | ||
* additionalJsContext: { | ||
* qrCode: url => { | ||
* const dataUrl = createQrImage(url, { size: 500 }); | ||
* const data = dataUrl.slice('data:image/gif;base64,'.length); | ||
* return { width: 6, height: 6, data, extension: '.gif' }; | ||
* }, | ||
* } | ||
* ``` | ||
*/ | ||
additionalJsContext?: Object; | ||
/** | ||
* Whether to fail on the first error encountered in the template. Defaults to true. Can be used to collect all errors in a template (e.g. misspelled commands) before failing. | ||
*/ | ||
failFast?: boolean; | ||
}; | ||
@@ -51,2 +92,3 @@ export declare type CreateReportOptions = { | ||
additionalJsContext: Object; | ||
failFast: boolean; | ||
}; | ||
@@ -118,2 +160,3 @@ export declare type Context = { | ||
data: ArrayBuffer | string; | ||
thumbnail?: Image; | ||
extension?: string; | ||
@@ -120,0 +163,0 @@ alt?: string; |
import JSZip from 'jszip'; | ||
declare const zipLoad: (inputFile: ArrayBuffer) => Promise<JSZip>; | ||
declare const zipGetText: (zip: JSZip, filename: string) => Promise<string>; | ||
declare const zipGetText: (zip: JSZip, filename: string) => Promise<string> | null; | ||
declare const zipSetText: (zip: JSZip, filename: string, data: string) => JSZip; | ||
@@ -5,0 +5,0 @@ declare const zipSetBinary: (zip: JSZip, filename: string, data: ArrayBuffer) => JSZip; |
@@ -10,3 +10,6 @@ "use strict"; | ||
var zipGetText = function (zip, filename) { | ||
return zip.file(filename).async('text'); | ||
var file_in_zip = zip.file(filename); | ||
if (!file_in_zip) | ||
return null; | ||
return file_in_zip.async('text'); | ||
}; | ||
@@ -13,0 +16,0 @@ exports.zipGetText = zipGetText; |
{ | ||
"name": "docx-templates", | ||
"version": "4.0.0", | ||
"version": "4.1.0", | ||
"description": "Template-based docx report creation", | ||
@@ -30,3 +30,3 @@ "main": "lib/index.js", | ||
"jest": "jest --watch --coverage", | ||
"test": "yarn prettier-check && yarn testCovFull", | ||
"test": "yarn lint && yarn testCovFull", | ||
"testCovFull": "yarn testCovPrepare && yarn testDev && yarn testCovReport", | ||
@@ -36,4 +36,4 @@ "testCovPrepare": "rm -rf ./coverage .nyc_output .nyc_tmp && mkdir .nyc_tmp", | ||
"testCovReport": "cp -r .nyc_tmp .nyc_output && nyc report --reporter=html --reporter=lcov --reporter=text", | ||
"prettier": "prettier --write \"src/**/*.ts\"", | ||
"prettier-check": "prettier --check \"src/**/*.ts\"" | ||
"lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"", | ||
"lint-fix": "eslint --fix \"src/**/*.{js,jsx,ts,tsx}\"" | ||
}, | ||
@@ -44,3 +44,3 @@ "engines": { | ||
"dependencies": { | ||
"jszip": "^3.2.2", | ||
"jszip": "^3.4.0", | ||
"sax": "1.2.4", | ||
@@ -51,14 +51,21 @@ "timm": "^1.6.2" | ||
"@types/jest": "^25.1.4", | ||
"@types/jszip": "^3.1.7", | ||
"@types/node": "^13.9.2", | ||
"@types/node": "^13.13.2", | ||
"@types/qrcode": "^1.3.4", | ||
"@types/sax": "^1.2.1", | ||
"coveralls": "^3.0.11", | ||
"jest": "^25.1.0", | ||
"@typescript-eslint/eslint-plugin": "^2.29.0", | ||
"@typescript-eslint/parser": "^2.29.0", | ||
"coveralls": "^3.0.13", | ||
"eslint": "^6.8.0", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint-plugin-import": "^2.20.2", | ||
"eslint-plugin-jest": "^23.8.2", | ||
"eslint-plugin-prettier": "^3.1.2", | ||
"jest": "^25.4.0", | ||
"mockdate": "^2.0.5", | ||
"nyc": "^15.0.1", | ||
"prettier": "^2.0.4", | ||
"oao": "^1.8.0", | ||
"prettier": "^2.0.5", | ||
"qrcode": "^1.4.4", | ||
"rimraf": "^3.0.2", | ||
"ts-jest": "^25.2.1", | ||
"ts-jest": "^25.4.0", | ||
"typescript": "^3.8.3" | ||
@@ -65,0 +72,0 @@ }, |
@@ -283,4 +283,28 @@ # Docx-templates [![Build Status](https://travis-ci.org/guigrpa/docx-templates.svg)](https://travis-ci.org/guigrpa/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) | ||
* `data`: either an ArrayBuffer or a base64 string with the image data | ||
* `extension` _[optional]_: e.g. `.png` | ||
* `extension`: e.g. `.png` | ||
* `thumbnail` _[optional]_: when injecting an SVG image, a fallback non-SVG (png/jpg/gif, etc.) image can be provided. This thumbnail is used when SVG images are not supported (e.g. older versions of Word) or when the document is previewed by e.g. Windows Explorer. See usage example below. | ||
In the .docx template: | ||
``` | ||
+++IMAGE injectSvg()+++ | ||
``` | ||
In the `createReport` call: | ||
```js | ||
additionalJsContext: { | ||
injectSvg: () => { | ||
const svg_data = Buffer.from(`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | ||
<rect x="10" y="10" height="100" width="100" style="stroke:#ff0000; fill: #0000ff"/> | ||
</svg>`, 'utf-8'); | ||
// Providing a thumbnail is technically optional, as newer versions of Word will just ignore it. | ||
const thumbnail = { | ||
data: fs.readFileSync('sample.png'), | ||
extension: '.png', | ||
}; | ||
return { width: 6, height: 6, data: svg_data, extension: '.svg', thumbnail }; | ||
} | ||
} | ||
``` | ||
### `LINK` | ||
@@ -393,3 +417,28 @@ | ||
## Error handling | ||
By default, `createReport` will throw an error the moment it encounters a problem with the template, such as a bad command (i.e. it 'fails fast'). In some cases, however, you may want to collect all errors that may exist in the template before failing. For example, this is useful when you are letting your users create templates interactively. You can disable fast-failing by providing the `failFast: false` parameter as shown below. This will make `createReport` throw an array of errors instead of a single error so you can get a more complete picture of what is wrong with the template. | ||
```typescript | ||
try { | ||
createReport({ | ||
template, | ||
data: { | ||
name: 'John', | ||
surname: 'Appleseed', | ||
}, | ||
failFast: false, | ||
}); | ||
} catch (errors) { | ||
if (Array.isArray(errors)) { | ||
// An array of errors likely caused by bad commands in the template. | ||
console.log(errors); | ||
} else { | ||
// Not an array of template errors, indicating something more serious. | ||
throw errors; | ||
} | ||
} | ||
``` | ||
## [Changelog](https://github.com/guigrpa/docx-templates/blob/master/CHANGELOG.md) | ||
@@ -396,0 +445,0 @@ |
@@ -15,6 +15,3 @@ { | ||
"src" | ||
], | ||
"exclude": [ | ||
"src/__tests__/**/*" | ||
] | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
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 2 instances in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
461
9
2
197091
21
32
3449
Updatedjszip@^3.4.0