453 lines
9.2 KiB
TypeScript
453 lines
9.2 KiB
TypeScript
import Docxtemplater, { DXT } from "./docxtemplater";
|
|
import InspectModule from "./inspect-module";
|
|
import expressionParser from "../expressions";
|
|
import ieExpressionParser from "../expressions-ie11";
|
|
import TxtTemplater from "./text";
|
|
const PizZip: any = require("pizzip");
|
|
import { expectType, expectError } from "tsd";
|
|
|
|
expressionParser.filters.map = function (input: any, key: any): any {
|
|
if (!input) {
|
|
return input;
|
|
}
|
|
|
|
if ("map" in input) {
|
|
return input.map(function (x: any) {
|
|
return x[key];
|
|
});
|
|
}
|
|
};
|
|
|
|
ieExpressionParser.filters.map = function (input: any, key: any): any {
|
|
if (!input) {
|
|
return input;
|
|
}
|
|
|
|
if ("map" in input) {
|
|
return input.map(function (x: any) {
|
|
return x[key];
|
|
});
|
|
}
|
|
};
|
|
|
|
const tDoc = new TxtTemplater("Hello {#users}{name},{/users} how are you ?", {
|
|
parser: expressionParser,
|
|
});
|
|
tDoc.render({ users: [{ name: "John" }, { name: "Baz" }] });
|
|
|
|
const tDoc2 = new TxtTemplater("Hello {#users}{name},{/users} how are you ?", {
|
|
parser: expressionParser,
|
|
});
|
|
tDoc2
|
|
.renderAsync({ users: [{ name: "John" }, { name: "Baz" }] })
|
|
.then(function (result: any) {
|
|
console.log(result.toUpperCase());
|
|
});
|
|
|
|
const doc1 = new Docxtemplater(
|
|
{},
|
|
{
|
|
delimiters: { start: "[[", end: "]]" },
|
|
nullGetter: function (part) {
|
|
expectError(part.foobar);
|
|
if (part.module === "rawxml") {
|
|
return "";
|
|
}
|
|
if (part.type === "placeholder" && part.value === "foobar") {
|
|
return "{Foobar}";
|
|
}
|
|
return "Hello";
|
|
},
|
|
}
|
|
);
|
|
const iModule = new InspectModule();
|
|
doc1.setData({ foo: "bar" });
|
|
doc1.attachModule({
|
|
set: function () {},
|
|
parse: function (placeHolderContent) {
|
|
if (placeHolderContent.indexOf(":hello") === 0) {
|
|
return {
|
|
type: "placeholder",
|
|
module: "mycustomModule",
|
|
value: placeHolderContent.substr(7),
|
|
isEmpty: "foobar",
|
|
};
|
|
}
|
|
return null;
|
|
},
|
|
getFoobar: function () {},
|
|
});
|
|
doc1.attachModule(iModule);
|
|
const tags = iModule.getAllTags();
|
|
const tags2 = iModule.getAllStructuredTags();
|
|
const nullValues = iModule.fullInspected["word/document.xml"].nullValues;
|
|
const firstTag = nullValues.detail[0].part.value;
|
|
const scope = nullValues.detail[0].scopeManager.scopeList[0];
|
|
expectType<string>(firstTag);
|
|
doc1.render();
|
|
|
|
const buf: Buffer = doc1.toBuffer({
|
|
compression: "DEFLATE",
|
|
});
|
|
const blob: Blob = doc1.toBlob({
|
|
compression: "DEFLATE",
|
|
});
|
|
const str: string = doc1.toBase64({
|
|
compression: "DEFLATE",
|
|
});
|
|
const u8: Uint8Array = doc1.toUint8Array({
|
|
compression: "DEFLATE",
|
|
});
|
|
const ab: ArrayBuffer = doc1.toArrayBuffer({
|
|
compression: "DEFLATE",
|
|
});
|
|
|
|
new Docxtemplater(
|
|
{},
|
|
{
|
|
stripInvalidXMLChars: true,
|
|
}
|
|
);
|
|
|
|
new Docxtemplater(
|
|
{},
|
|
{
|
|
stripInvalidXMLChars: false,
|
|
}
|
|
);
|
|
|
|
new Docxtemplater(
|
|
{},
|
|
{
|
|
errorLogging: false,
|
|
}
|
|
);
|
|
|
|
new Docxtemplater(
|
|
{},
|
|
{
|
|
errorLogging: "jsonl",
|
|
}
|
|
);
|
|
|
|
new Docxtemplater(
|
|
{},
|
|
{
|
|
errorLogging: "json",
|
|
}
|
|
);
|
|
|
|
expectError(doc1.foobar());
|
|
expectError(new Docxtemplater(1, 2));
|
|
expectError(new Docxtemplater({}, { delimiters: { start: 1, end: "]]" } }));
|
|
expectError(new Docxtemplater({}, { delimiters: { start: "[[" } }));
|
|
expectError(new Docxtemplater({}, { stripInvalidXMLChars: "yo" }));
|
|
|
|
const doc2 = new Docxtemplater();
|
|
doc2.loadZip(new PizZip("hello"));
|
|
|
|
// Error because parser should return a {get: fn} object
|
|
expectError(
|
|
doc2.setOptions({
|
|
parser: function (tag) {
|
|
return 10;
|
|
},
|
|
})
|
|
);
|
|
|
|
doc2.setOptions({
|
|
parser: function (tag) {
|
|
expectType<string>(tag);
|
|
return {
|
|
get: function (scope, context) {
|
|
const first = context.scopeList[0];
|
|
expectType<DXT.integer>(context.num);
|
|
expectError(context.foobar);
|
|
if (context.meta.part.value === tag) {
|
|
return scope[context.meta.part.value];
|
|
}
|
|
expectError(context.meta.part.other);
|
|
return scope[tag];
|
|
},
|
|
};
|
|
},
|
|
});
|
|
|
|
const doc3 = new Docxtemplater();
|
|
doc3.loadZip(new PizZip("hello"));
|
|
doc3.compile();
|
|
doc3.resolveData({ a: "b" }).then(function () {
|
|
doc3.render();
|
|
});
|
|
doc3.replaceFirstSection = true;
|
|
doc3.replaceLastSection = true;
|
|
const doc4 = new Docxtemplater(new PizZip("hello"));
|
|
doc4.renderAsync({ a: "b" }).then(function () {
|
|
console.log("end");
|
|
});
|
|
const text = doc3.getFullText();
|
|
const text2 = doc3.getFullText("word/heading1.xml");
|
|
|
|
new Docxtemplater(new PizZip("hello"), { errorLogging: false });
|
|
|
|
// Error because getFullText requires a string parameter
|
|
expectError(doc3.getFullText(false));
|
|
expectError(doc3.getFullText(10));
|
|
|
|
const doc5 = new Docxtemplater(new PizZip("hello"), {
|
|
parser: expressionParser,
|
|
});
|
|
|
|
const doc6 = new Docxtemplater(new PizZip("hello"), {
|
|
parser: ieExpressionParser,
|
|
});
|
|
|
|
const doc7 = new Docxtemplater(new PizZip("hello"), {
|
|
parser: expressionParser.configure({
|
|
filters: {
|
|
foo: (a: any) => a,
|
|
bar: (a: any) => a,
|
|
},
|
|
csp: true,
|
|
cache: {},
|
|
literals: { true: true },
|
|
}),
|
|
});
|
|
|
|
const doc8 = new Docxtemplater(new PizZip("hello"), {
|
|
parser: ieExpressionParser.configure({
|
|
filters: {
|
|
foo: (a: any) => a,
|
|
bar: (a: any) => a,
|
|
},
|
|
csp: true,
|
|
cache: {},
|
|
literals: { true: true },
|
|
}),
|
|
});
|
|
|
|
const doc9 = new Docxtemplater(new PizZip("hello"), {
|
|
syntax: {
|
|
allowUnopenedTag: true,
|
|
allowUnclosedTag: true,
|
|
changeDelimiterPrefix: null,
|
|
},
|
|
});
|
|
|
|
const doc10 = new Docxtemplater(new PizZip("hello"), {
|
|
syntax: {
|
|
allowUnopenedTag: true,
|
|
changeDelimiterPrefix: "",
|
|
},
|
|
});
|
|
|
|
function validStartChars(ch: string): boolean {
|
|
return /[a-z]/.test(ch);
|
|
}
|
|
function validContinuationChars(ch: string): boolean {
|
|
return /[a-z]/.test(ch);
|
|
}
|
|
expressionParser.configure({
|
|
isIdentifierStart: validStartChars,
|
|
isIdentifierContinue: validContinuationChars,
|
|
postCompile(tag: string, meta: any, expr: any) {
|
|
meta.tag.resolveFirst = true;
|
|
},
|
|
setIdentifier(
|
|
tag: string,
|
|
value: any,
|
|
scope: any,
|
|
scopeList: any[],
|
|
context: any
|
|
) {
|
|
return new Promise((resolve) => {
|
|
setTimeout(() => {
|
|
scopeList[0][tag] = value;
|
|
resolve(true);
|
|
}, 100);
|
|
});
|
|
},
|
|
});
|
|
ieExpressionParser.configure({
|
|
isIdentifierStart: validStartChars,
|
|
isIdentifierContinue: validContinuationChars,
|
|
postCompile(tag: string, meta: any, expr: any) {
|
|
meta.tag.resolveFirst = true;
|
|
},
|
|
});
|
|
|
|
expressionParser.configure({
|
|
evaluateIdentifier(
|
|
tag: string,
|
|
scope: any,
|
|
scopeList: any[],
|
|
context: any
|
|
) {
|
|
let res = context.num + context.num;
|
|
return res;
|
|
},
|
|
});
|
|
|
|
expressionParser.configure({
|
|
setIdentifier(
|
|
tag: string,
|
|
value: any,
|
|
scope: any,
|
|
scopeList: any[],
|
|
context: any
|
|
) {
|
|
scopeList[0][tag] = value;
|
|
return true;
|
|
},
|
|
});
|
|
|
|
expressionParser.configure({
|
|
postEvaluate(
|
|
result: any,
|
|
tag: string,
|
|
scope: any,
|
|
context: DXT.ParserContext
|
|
) {
|
|
return result;
|
|
},
|
|
});
|
|
|
|
ieExpressionParser.configure({
|
|
postEvaluate(
|
|
result: any,
|
|
tag: string,
|
|
scope: any,
|
|
context: DXT.ParserContext
|
|
) {
|
|
return result;
|
|
},
|
|
});
|
|
|
|
// Define the parameter type for getFileType
|
|
interface FileTypeParams {
|
|
doc: Docxtemplater;
|
|
}
|
|
|
|
const avoidRenderingCoreXMLModule = {
|
|
name: "avoidRenderingCoreXMLModule",
|
|
getFileType({ doc }: FileTypeParams): void {
|
|
doc.targets = doc.targets.filter(function (file: string) {
|
|
if (
|
|
file === "docProps/core.xml" ||
|
|
file === "docProps/app.xml" ||
|
|
file === "docProps/custom.xml"
|
|
) {
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
},
|
|
};
|
|
new Docxtemplater(new PizZip("hello"), {
|
|
modules: [
|
|
avoidRenderingCoreXMLModule,
|
|
{
|
|
optionsTransformer(options, doc) {
|
|
const loopModule = doc.findModule("LoopModule");
|
|
if (loopModule) {
|
|
loopModule.prefix.start = "##";
|
|
}
|
|
return options;
|
|
},
|
|
render(part, options) {
|
|
if (part.type === "placeholder") {
|
|
let value = options.scopeManager.getValue(part.value, {
|
|
part,
|
|
});
|
|
return value;
|
|
}
|
|
return null;
|
|
},
|
|
},
|
|
],
|
|
paragraphLoop: true,
|
|
linebreaks: true,
|
|
});
|
|
|
|
new Docxtemplater(new PizZip("hello"), {
|
|
warnFn: (errors: Error[]) => {
|
|
for (const error of errors) {
|
|
console.log(error.message);
|
|
}
|
|
},
|
|
});
|
|
|
|
interface SetOptions {
|
|
Lexer: any;
|
|
zip: any;
|
|
}
|
|
const fixDocPrCorruptionModule: DXT.Module = {
|
|
set(options: SetOptions) {
|
|
if (options.Lexer) {
|
|
this.Lexer = options.Lexer;
|
|
}
|
|
if (options.zip) {
|
|
this.zip = options.zip;
|
|
}
|
|
},
|
|
on(event) {
|
|
if (event === "attached") {
|
|
this.attached = false;
|
|
}
|
|
if (event !== "syncing-zip") {
|
|
return;
|
|
}
|
|
const zip = this.zip;
|
|
const Lexer = this.Lexer;
|
|
let prId = 1;
|
|
function setSingleAttribute(
|
|
partValue: string,
|
|
attr: string,
|
|
attrValue: string | number
|
|
) {
|
|
const regex = new RegExp(`(<.* ${attr}=")([^"]+)(".*)$`);
|
|
if (regex.test(partValue)) {
|
|
return partValue.replace(regex, `$1${attrValue}$3`);
|
|
}
|
|
let end = partValue.lastIndexOf("/>");
|
|
if (end === -1) {
|
|
end = partValue.lastIndexOf(">");
|
|
}
|
|
return (
|
|
partValue.substr(0, end) +
|
|
` ${attr}="${attrValue}"` +
|
|
partValue.substr(end)
|
|
);
|
|
}
|
|
for (const f of zip.file(/\.xml$/)) {
|
|
let text = f.asText();
|
|
const xmllexed = Lexer.xmlparse(text, {
|
|
text: [],
|
|
other: ["wp:docPr"],
|
|
});
|
|
if (xmllexed.length > 1) {
|
|
text = xmllexed.reduce(function (
|
|
fullText: string,
|
|
part: DXT.Part
|
|
) {
|
|
if (
|
|
part.tag === "wp:docPr" &&
|
|
part.position &&
|
|
["start", "selfclosing"].indexOf(part.position) !== -1
|
|
) {
|
|
return (
|
|
fullText +
|
|
setSingleAttribute(part.value, "id", prId++)
|
|
);
|
|
}
|
|
return fullText + part.value;
|
|
}, "");
|
|
}
|
|
zip.file(f.name, text);
|
|
}
|
|
},
|
|
};
|
|
new Docxtemplater(new PizZip("hello"), {
|
|
modules: [fixDocPrCorruptionModule],
|
|
});
|