Search code examples
reactjstypescriptnpm

NPM Docx: Keep getting


I am trying to create a module to export a docx using the npm docx package. However each time I run it I get the following error (even though the import is there). I checked for circular imports as well but can't identify any:

TypeError: docx_1.HeadingLevel is undefined

My code is as follows:

import { HeadingLevel, Document, Packer, Paragraph, TextRun, ImageRun } from 'docx';

const downloadObjectAsDocx = (exportObj: any, exportName: string) => {
// Function to generate the document
  console.log(exportObj);


  // Create document
  const doc = new Document({
    sections: [
      {
        properties: {},
        children: [
          // Title
          new Paragraph({
            text: exportObj.applicationInfo ? exportObj.applicationInfo.name : '',
            heading: HeadingLevel.TITLE,
          }),
          // Description Header
          new Paragraph({
            text: 'Description',
            heading: HeadingLevel.HEADING_1,
          }),
          // Description Content
          new Paragraph({
            children: [
              new TextRun({
                text: exportObj?.applicationInfo?.description || '',
                break: 1,
              }),
            ],
          }),
          // Architecture Header
          new Paragraph({
            text: 'Architecture',
            heading: HeadingLevel.HEADING_1,
          }),
          // Architecture Image
          new Paragraph({
            children: [
              new ImageRun({
                data: exportObj?.architecture?.image || '', // base64 string from JSON
                transformation: {
                  width: 500,
                  height: 300,
                },
              }),
            ],
          }),
          // Assumptions Header
          new Paragraph({
            text: 'Assumptions',
            heading: HeadingLevel.HEADING_1,
          }),
          // Dynamically added Assumptions

          ...(exportObj.assumptions || []).map((assumption: any) =>
            new Paragraph({
              text: assumption.content,
              bullet: {
                level: 0,
              },
            }),
          ),
        ],
      },
    ],
  });

  // Use Packer to generate a blob and simulate a download
  void Packer.toBlob(doc).then(blob => {
    const url = URL.createObjectURL(blob);
    var downloadAnchorNode = document.createElement('a');
    downloadAnchorNode.setAttribute('href', url);
    downloadAnchorNode.setAttribute('download', exportName + '.docx');
    document.body.appendChild(downloadAnchorNode);
    downloadAnchorNode.click();
    downloadAnchorNode.remove();
  });
};


export default downloadObjectAsDocx;

Stacktrace:

enter image description here


Solution

  • I ran into this with other packages, and I say it's a misuse of typescript enums by the package.

    Here's an example:

    export const HeadingLevel = {
        HEADING_1: "Heading1",
        HEADING_2: "Heading2",
        HEADING_3: "Heading3",
        HEADING_4: "Heading4",
        HEADING_5: "Heading5",
        HEADING_6: "Heading6",
        TITLE: "Title",
    } as const;
    

    By setting this as a const object ... as const instead of a proper enum, the types are technically available for hinting but they aren't really. So it looks like you're doing it correctly but then you run into an error at runtime.

    This should have been declared as a normal enum:

    export enum HeadingLevel {
        HEADING_1: "Heading1",
        HEADING_2: "Heading2",
        HEADING_3: "Heading3",
        HEADING_4: "Heading4",
        HEADING_5: "Heading5",
        HEADING_6: "Heading6",
        TITLE: "Title",
    };
    

    And all would be well. I have had little success trying to get some people to use TS enums as they were intended; it's a whole tabs vs spaces type debate in the JS/TS realm.

    TLDR;

    Instead of using the enum, use the value directly.

    For example, this:

    new Paragraph({
      text: exportObj.applicationInfo ? exportObj.applicationInfo.name : '',
      heading: HeadingLevel.TITLE,
    })
    

    Would become this:

    new Paragraph({
      text: exportObj.applicationInfo ? exportObj.applicationInfo.name : '',
      heading: 'Title',
    })