@commentware-private/documents

The functions the template library exports, for writing a template and for rendering one to a file. Experimental.

@commentware-private/documents exports the functions that define a template and render it to a PDF or an XLSX file.

The library is experimental, so a function may change or be removed in any release, with no notice first. A project imports each by that name. react, react-dom, zod and exceljs are peers: a template builds its elements, schema and workbook with the project’s own copies, so the project installs them beside the library.

defineTemplate

Usage: defineTemplate({ schema, supportedOutputs, mockData, config })

Defines a template. A template file’s default export is its result.

  • schema is a zod schema for the params. Every render checks the params against it.
  • supportedOutputs lists "pdf", "xlsx" or both. A template that lists "xlsx" also renders to "pdf", printed from its workbook.
  • mockData is params the schema accepts, which the preview starts from.
  • config({ params }) answers the template’s metadata and renderers for those params:
    • metadata.fileName is the file’s name without its extension.
    • metadata.pdf is { name, width, height, margin? }, the page as CSS lengths in mm, cm, in, pt or px, and margin is { top?, right?, bottom?, left? } in the same lengths, each 0 by default. A template with a component needs metadata.pdf. Every page of the PDF is exactly this size.
    • metadata.xlsx is { sheetName?, pdf? }, where pdf lays the workbook out when it is printed: fit is "scale-to-fit", the default, or "auto", and align is "left", the default, "center" or "right". A printed workbook is on an A4 page with 10mm margins unless metadata.pdf names another page.
    • renderers.pdf is { component }, where component is an async function returning the React element drawn on the page, or the result of renderXlsxToPdf.
    • renderers.xlsx is { workbook }, where workbook is an async function returning an exceljs workbook.

This template builds a spreadsheet of SKUs and prices:

src/templates/price-list.tsxtsx
import ExcelJS from "exceljs";
import { z } from "zod";
import { defineTemplate, xlsxStyles } from "@commentware-private/documents";

export default defineTemplate({
  schema: z.object({
    items: z.array(z.object({ sku: z.string(), price: z.number() })),
  }),
  supportedOutputs: ["xlsx"],
  mockData: { items: [{ sku: "WA-001", price: 29.99 }] },
  config: async ({ params }) => ({
    metadata: { fileName: "price-list", xlsx: { sheetName: "Prices" } },
    renderers: {
      xlsx: {
        workbook: async () => {
          const workbook = new ExcelJS.Workbook();
          const sheet = workbook.addWorksheet("Prices");
          sheet.addRow(["SKU", "Price"]).font = xlsxStyles.headerRow.font;
          for (const item of params.items) {
            sheet.addRow([item.sku, item.price]).getCell(2).numFmt =
              xlsxStyles.currency;
          }
          return workbook;
        },
      },
    },
  }),
});

defineConfig

Usage: defineConfig({ templates, ... })

Types the default export of documents.config.ts. Every option is on documents.config.ts.

createRenderer

Usage: createRenderer({ templates, stylesheet?, browser? })

Returns a renderer for a project’s templates. templates and stylesheet are the two exports of the templates.ts that commentware-documents build writes; browser is { executablePath? }, as in the config. The renderer’s types know every template’s id, its params and its outputs, so a wrong one does not compile.

render

Usage: renderer.render(id, params, output)

Renders one template to "pdf" or "xlsx" and answers { bytes, fileName, mimeType }, where fileName carries the extension. The first PDF starts Chrome, which stays up until close.

Params the template’s schema does not accept are refused, with each problem at its path:

The params for "thank-you" do not match its schema:
✖ Invalid input: expected string, received number
  → at name

An id no template has is refused with the ids there are, as in No template has the id "thank-yuo". The templates are: price-list, thank-you., and an output the template does not render to with the ones it does, as in "thank-you" does not render to xlsx. It renders to pdf.

metadata

Usage: renderer.metadata(id, params)

Answers the template’s metadata for those params, and every output it renders to, without rendering it. For the price list above:

{
  fileName: 'price-list',
  xlsx: { sheetName: 'Prices' },
  supportedOutputs: [ 'xlsx', 'pdf' ]
}

close

Usage: renderer.close()

Stops the Chrome that PDFs are printed with. A later PDF starts it again.

renderXlsxToPdf

Usage: renderXlsxToPdf(workbook, options?)

Makes a template’s PDF a print of a workbook. workbook is an async function returning an exceljs workbook, as renderers.xlsx.workbook is, and options takes the same fit and align as metadata.xlsx.pdf. Use it as renderers.pdf when the PDF prints a different workbook from the XLSX file: pdf: renderXlsxToPdf(summaryWorkbook, { fit: "auto" }).

fetchAndAddImage

Usage: fetchAndAddImage(workbook, sheet, url, range, options?)

Fetches the image at url, converts an SVG to PNG because Excel draws no SVG, fits it to the cell range, such as "A1:B3", and places it. options.fit is "fill", the default, "contain" or "cover"; options.position places a contained or covered image, "center" by default; options.editAs is "twoCell", the default, "oneCell" or "absolute", as exceljs takes it. A range that is not two cell addresses is refused, naming it.

addImageToWorksheet

Usage: addImageToWorksheet(workbook, sheet, { buffer, extension, range, editAs?, imageWidth?, imageHeight? })

Places image bytes the template already has, as a "png", "jpeg" or "gif", on the cell range, unchanged. With editAs of "oneCell" or "absolute", imageWidth and imageHeight in pixels keep Excel from stretching it.

xlsxStyles

Type: an object

Styles a workbook template reuses: headerRow, a bold font and a centered alignment, and the number formats currency, percentage, date and dateTime.

Documentation

Type to search…

↑↓ navigate↵ selectEsc close