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 <dolan_miu@hotmail.com>
This commit is contained in:
Josh Kelley
2025-02-16 13:24:15 -05:00
committed by GitHub
parent 05fcf6edd4
commit 170309a7ed
4 changed files with 78 additions and 50 deletions

View File

@ -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()", () => { describe("#toStream()", () => {
it("should create a standard docx file", async () => { it("should create a standard docx file", async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@ -1,6 +1,7 @@
import { Stream } from "stream"; import { Stream } from "stream";
import { File } from "@file/file"; import { File } from "@file/file";
import { OutputByType, OutputType } from "@util/output-type";
import { Compiler, IXmlifyedFile } from "./next-compiler"; import { Compiler, IXmlifyedFile } from "./next-compiler";
@ -21,64 +22,59 @@ const convertPrettifyType = (
prettify === true ? PrettifyType.WITH_2_BLANKS : prettify === false ? undefined : prettify; prettify === true ? PrettifyType.WITH_2_BLANKS : prettify === false ? undefined : prettify;
export class Packer { export class Packer {
public static async toString( // eslint-disable-next-line require-await
public static async pack<T extends OutputType>(
file: File,
type: T,
prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType],
overrides: readonly IXmlifyedFile[] = [],
): Promise<OutputByType[T]> {
const zip = this.compiler.compile(file, convertPrettifyType(prettify), overrides);
return zip.generateAsync({
type,
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
compression: "DEFLATE",
});
}
public static toString(
file: File, file: File,
prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType], prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType],
overrides: readonly IXmlifyedFile[] = [], overrides: readonly IXmlifyedFile[] = [],
): Promise<string> { ): Promise<string> {
const zip = this.compiler.compile(file, convertPrettifyType(prettify), overrides); return Packer.pack(file, "string", prettify, overrides);
const zipData = await zip.generateAsync({
type: "string",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
compression: "DEFLATE",
});
return zipData;
} }
public static async toBuffer( public static toBuffer(
file: File, file: File,
prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType], prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType],
overrides: readonly IXmlifyedFile[] = [], overrides: readonly IXmlifyedFile[] = [],
): Promise<Buffer> { ): Promise<Buffer> {
const zip = this.compiler.compile(file, convertPrettifyType(prettify), overrides); return Packer.pack(file, "nodebuffer", prettify, overrides);
const zipData = await zip.generateAsync({
type: "nodebuffer",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
compression: "DEFLATE",
});
return zipData;
} }
public static async toBase64String( public static toBase64String(
file: File, file: File,
prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType], prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType],
overrides: readonly IXmlifyedFile[] = [], overrides: readonly IXmlifyedFile[] = [],
): Promise<string> { ): Promise<string> {
const zip = this.compiler.compile(file, convertPrettifyType(prettify), overrides); return Packer.pack(file, "base64", prettify, overrides);
const zipData = await zip.generateAsync({
type: "base64",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
compression: "DEFLATE",
});
return zipData;
} }
public static async toBlob( public static toBlob(
file: File, file: File,
prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType], prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType],
overrides: readonly IXmlifyedFile[] = [], overrides: readonly IXmlifyedFile[] = [],
): Promise<Blob> { ): Promise<Blob> {
const zip = this.compiler.compile(file, convertPrettifyType(prettify), overrides); return Packer.pack(file, "blob", prettify, overrides);
const zipData = await zip.generateAsync({ }
type: "blob",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
compression: "DEFLATE",
});
return zipData; public static toArrayBuffer(
file: File,
prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType],
overrides: readonly IXmlifyedFile[] = [],
): Promise<ArrayBuffer> {
return Packer.pack(file, "arraybuffer", prettify, overrides);
} }
public static toStream( public static toStream(

View File

@ -11,6 +11,7 @@ import { ConcreteHyperlink, ExternalHyperlink, ParagraphChild } from "@file/para
import { TargetModeType } from "@file/relationships/relationship/relationship"; import { TargetModeType } from "@file/relationships/relationship/relationship";
import { IContext } from "@file/xml-components"; import { IContext } from "@file/xml-components";
import { uniqueId } from "@util/convenience-functions"; import { uniqueId } from "@util/convenience-functions";
import { OutputByType, OutputType } from "@util/output-type";
import { appendContentType } from "./content-types-manager"; import { appendContentType } from "./content-types-manager";
import { appendRelationship, getNextRelationshipIndex } from "./relationship-manager"; import { appendRelationship, getNextRelationshipIndex } from "./relationship-manager";
@ -47,21 +48,7 @@ type IHyperlinkRelationshipAddition = {
export type IPatch = ParagraphPatch | FilePatch; export type IPatch = ParagraphPatch | FilePatch;
// From JSZip export type PatchDocumentOutputType = OutputType;
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 PatchDocumentOptions<T extends PatchDocumentOutputType = PatchDocumentOutputType> = { export type PatchDocumentOptions<T extends PatchDocumentOutputType = PatchDocumentOutputType> = {
readonly outputType: T; readonly outputType: T;

18
src/util/output-type.ts Normal file
View File

@ -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 */