From 170309a7edf22d161809c6cfff9de9fb5d169029 Mon Sep 17 00:00:00 2001 From: Josh Kelley Date: Sun, 16 Feb 2025 13:24:15 -0500 Subject: [PATCH] Add Packer.pack and Packer.toArrayBuffer (#2959) * Add Packer.pack and Packer.toArrayBuffer To mirror patchDocument's outputType parameter. See https://github.com/dolanmiu/docx/discussions/2920 * Ignore coverage --------- Co-authored-by: Dolan Miu --- src/export/packer/packer.spec.ts | 27 +++++++++++++++ src/export/packer/packer.ts | 58 +++++++++++++++----------------- src/patcher/from-docx.ts | 17 ++-------- src/util/output-type.ts | 18 ++++++++++ 4 files changed, 74 insertions(+), 46 deletions(-) create mode 100644 src/util/output-type.ts diff --git a/src/export/packer/packer.spec.ts b/src/export/packer/packer.spec.ts index 7a15a6b1d4..ee87335b1d 100644 --- a/src/export/packer/packer.spec.ts +++ b/src/export/packer/packer.spec.ts @@ -187,6 +187,33 @@ describe("Packer", () => { }); }); + describe("#toArrayBuffer()", () => { + it("should create a standard docx file", async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + vi.spyOn((Packer as any).compiler, "compile").mockReturnValue({ + generateAsync: () => vi.fn(), + }); + const str = await Packer.toArrayBuffer(file); + + assert.isDefined(str); + }); + + it("should handle exception if it throws any", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + vi.spyOn((Packer as any).compiler, "compile").mockImplementation(() => { + throw new Error(); + }); + + return Packer.toArrayBuffer(file).catch((error) => { + assert.isDefined(error); + }); + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + }); + describe("#toStream()", () => { it("should create a standard docx file", async () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/src/export/packer/packer.ts b/src/export/packer/packer.ts index e00ebfe3ff..31994b0046 100644 --- a/src/export/packer/packer.ts +++ b/src/export/packer/packer.ts @@ -1,6 +1,7 @@ import { Stream } from "stream"; import { File } from "@file/file"; +import { OutputByType, OutputType } from "@util/output-type"; import { Compiler, IXmlifyedFile } from "./next-compiler"; @@ -21,64 +22,59 @@ const convertPrettifyType = ( prettify === true ? PrettifyType.WITH_2_BLANKS : prettify === false ? undefined : prettify; export class Packer { - public static async toString( + // eslint-disable-next-line require-await + public static async pack( file: File, + type: T, prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType], overrides: readonly IXmlifyedFile[] = [], - ): Promise { + ): Promise { const zip = this.compiler.compile(file, convertPrettifyType(prettify), overrides); - const zipData = await zip.generateAsync({ - type: "string", + return zip.generateAsync({ + type, mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", compression: "DEFLATE", }); + } - return zipData; + public static toString( + file: File, + prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType], + overrides: readonly IXmlifyedFile[] = [], + ): Promise { + return Packer.pack(file, "string", prettify, overrides); } - public static async toBuffer( + public static toBuffer( file: File, prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType], overrides: readonly IXmlifyedFile[] = [], ): Promise { - const zip = this.compiler.compile(file, convertPrettifyType(prettify), overrides); - const zipData = await zip.generateAsync({ - type: "nodebuffer", - mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - compression: "DEFLATE", - }); - - return zipData; + return Packer.pack(file, "nodebuffer", prettify, overrides); } - public static async toBase64String( + public static toBase64String( file: File, prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType], overrides: readonly IXmlifyedFile[] = [], ): Promise { - const zip = this.compiler.compile(file, convertPrettifyType(prettify), overrides); - const zipData = await zip.generateAsync({ - type: "base64", - mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - compression: "DEFLATE", - }); - - return zipData; + return Packer.pack(file, "base64", prettify, overrides); } - public static async toBlob( + public static toBlob( file: File, prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType], overrides: readonly IXmlifyedFile[] = [], ): Promise { - const zip = this.compiler.compile(file, convertPrettifyType(prettify), overrides); - const zipData = await zip.generateAsync({ - type: "blob", - mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - compression: "DEFLATE", - }); + return Packer.pack(file, "blob", prettify, overrides); + } - return zipData; + public static toArrayBuffer( + file: File, + prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType], + overrides: readonly IXmlifyedFile[] = [], + ): Promise { + return Packer.pack(file, "arraybuffer", prettify, overrides); } public static toStream( diff --git a/src/patcher/from-docx.ts b/src/patcher/from-docx.ts index 0412a14076..4d42951534 100644 --- a/src/patcher/from-docx.ts +++ b/src/patcher/from-docx.ts @@ -11,6 +11,7 @@ import { ConcreteHyperlink, ExternalHyperlink, ParagraphChild } from "@file/para import { TargetModeType } from "@file/relationships/relationship/relationship"; import { IContext } from "@file/xml-components"; import { uniqueId } from "@util/convenience-functions"; +import { OutputByType, OutputType } from "@util/output-type"; import { appendContentType } from "./content-types-manager"; import { appendRelationship, getNextRelationshipIndex } from "./relationship-manager"; @@ -47,21 +48,7 @@ type IHyperlinkRelationshipAddition = { export type IPatch = ParagraphPatch | FilePatch; -// From JSZip -type OutputByType = { - readonly base64: string; - // eslint-disable-next-line id-denylist - readonly string: string; - readonly text: string; - readonly binarystring: string; - readonly array: readonly number[]; - readonly uint8array: Uint8Array; - readonly arraybuffer: ArrayBuffer; - readonly blob: Blob; - readonly nodebuffer: Buffer; -}; - -export type PatchDocumentOutputType = keyof OutputByType; +export type PatchDocumentOutputType = OutputType; export type PatchDocumentOptions = { readonly outputType: T; diff --git a/src/util/output-type.ts b/src/util/output-type.ts new file mode 100644 index 0000000000..a2d19d3c44 --- /dev/null +++ b/src/util/output-type.ts @@ -0,0 +1,18 @@ +/* v8 ignore start */ +// Simply type definitions. Can ignore testing and coverage +// From JSZip +export type OutputByType = { + readonly base64: string; + // eslint-disable-next-line id-denylist + readonly string: string; + readonly text: string; + readonly binarystring: string; + readonly array: readonly number[]; + readonly uint8array: Uint8Array; + readonly arraybuffer: ArrayBuffer; + readonly blob: Blob; + readonly nodebuffer: Buffer; +}; + +export type OutputType = keyof OutputByType; +/* v8 ignore stop */