Skip to content

Latest commit

 

History

History
293 lines (224 loc) · 9.67 KB

examples.md

File metadata and controls

293 lines (224 loc) · 9.67 KB

word-kit usage examples

Concrete patterns for common workflows. Every snippet imports standalone functions from @office-kit/docx — there is no Docx class to instantiate; Docx is just the (data) type returned by createDocx / openDocx. This shape is what lets the bundler tree-shake any function you don't import.

The snippets are runnable as-is in Node ≥ 20 once the package is installed.

Two distinct "template" workflows

.docx callers tend to mean one of two different things by "template", and word-kit supports both:

Styled-base template (PowerPoint style) Mail-merge template
Template file holds Page setup, header/footer, custom paragraph/run styles, optional cover page. No placeholder text. Body text with {{placeholder}} tokens. Styling is incidental.
Filling step Open the template, then appendParagraph(doc, text, { style: "MyTitle" }) to add new content that inherits the template's design via its style ids. Open the template, then replaceText(doc, /\{\{(\w+)\}\}/g, ...) to swap the tokens in place.
Content origin Generated by the program. Already written in the template.
When to reach for it "Build me a Q3 status report from this data" — output structure differs each time. "Send out 500 invoices, each with a different name and total."
Example below §3 (open + appendParagraph + style id). §2 (run-spanning {{name}} substitution).

The pnpm sample script writes both kinds into ./samples/: 30-styled-base-*.docx for the first, 20-26 mailmerge-*.docx for the second.


1. Generate a fresh .docx from scratch

import {
  addBulletList,
  addPageNumberFooter,
  addTable,
  appendHeading,
  appendParagraph,
  createDocx,
  ensureHeadingStyles,
  PAGE_SIZE_A4,
  setCoreProperties,
  setPageSize,
  toUint8Array,
} from "@office-kit/docx";
import { writeFileSync } from "node:fs";

const doc = createDocx({ paragraphs: [] });

setPageSize(doc, PAGE_SIZE_A4);
setCoreProperties(doc, {
  title: "Quarterly Report",
  creator: "Yamada",
  created: new Date().toISOString(),
});

ensureHeadingStyles(doc, 2);
appendHeading(doc, "Quarterly Report", 1);
appendParagraph(doc, "This document was generated by word-kit.");

addBulletList(doc, ["Revenue ↑ 12%", "Costs ↓ 3%"]);

addTable(doc, [
  ["Metric", "Q3"],
  ["Users", "2,200"],
  ["MRR", "$24k"],
]);

addPageNumberFooter(doc, "Page ", "");

writeFileSync("out.docx", toUint8Array(doc));

2. Build a report on top of a styled-base template (PowerPoint-style)

This is the workflow you reach for when you want to reuse an already-designed .docx (header, footer, page setup, custom styles, optional cover page) and just author fresh content into it. The template carries no {{placeholder}} text — its job is the design.

import { addBulletList, appendParagraph, openDocx, toUint8Array } from "@office-kit/docx";
import { readFileSync, writeFileSync } from "node:fs";

// Designed by hand in Word and saved as report-template.docx. The file
// already defines the styles we'll reach for ("AcmeTitle", "BodyHeading",
// …) plus the company header, page-number footer, and page setup.
const doc = openDocx(readFileSync("report-template.docx"));

appendParagraph(doc, "Quarterly Status — Q3 2026", { style: "AcmeTitle" });
appendParagraph(doc, "Internal — Engineering", { style: "AcmeSubtitle" });

appendParagraph(doc, "Highlights", { style: "BodyHeading" });
addBulletList(doc, [
  "Shipped v2.0 with run-spanning find/replace.",
  "Adopted by three internal teams.",
  "External beta opens next sprint.",
]);

writeFileSync("q3-status.docx", toUint8Array(doc));

Word resolves <w:pStyle w:val="AcmeTitle"/> against the template's styles.xml at render time — the new content automatically inherits the title's bold dark-blue 22pt look without your code redefining it.

If your downstream consumer needs a style that the template doesn't yet have, call addStyle(doc, …) once before appending — that adds a new entry to the styles part. findStyleIdByName(doc, "見出し 1") helps when the template uses localised display names.


3. Fill a {{placeholder}} template (mail-merge style, run-spanning safe)

This is the other "template" pattern: the template is content with holes, and you fill the holes. Use this when the same skeleton goes out to many recipients with only a few values changing.

import { openDocx, replaceTextEverywhere, toUint8Array } from "@office-kit/docx";
import { readFileSync, writeFileSync } from "node:fs";

const tpl = openDocx(readFileSync("invoice-template.docx"));

// Replace across body + headers + footers + comments + footnotes.
const values: Record<string, string> = {
  name: "山田太郎",
  date: new Date().toLocaleDateString("ja-JP"),
  amount: "¥1,200,000",
};
replaceTextEverywhere(tpl, /\{\{\s*(\w+)\s*\}\}/g, (m) => values[m.captures[0]!] ?? "");

writeFileSync("filled.docx", toUint8Array(tpl));

replaceTextEverywhere is run-spanning safe: even when Word's spell-checker fragments {{name}} into two <w:r> runs, the match is detected and the substitution lands cleanly inside the first run (inheriting its formatting).


4. Mail-merge / fan-out one template into N documents

import {
  clone,
  openDocx,
  replaceTextEverywhere,
  setCoreProperties,
  toUint8Array,
} from "@office-kit/docx";
import { readFileSync, writeFileSync } from "node:fs";

const template = openDocx(readFileSync("template.docx"));

for (const customer of customers) {
  const doc = clone(template);
  replaceTextEverywhere(doc, /\{\{(\w+)\}\}/g, (m) => customer[m.captures[0]!] ?? "");
  setCoreProperties(doc, {
    title: `Invoice for ${customer.name}`,
    creator: "Acme Billing",
    modified: new Date().toISOString(),
  });
  writeFileSync(`out/${customer.id}.docx`, toUint8Array(doc));
}

clone(doc) is a deep copy. Edits do not leak across siblings.


5. Inspect an existing .docx

import {
  bookmarks,
  coreProperties,
  findTextEverywhere,
  footers,
  headers,
  images,
  openDocx,
  outline,
  statistics,
} from "@office-kit/docx";

const doc = openDocx(bytes);

statistics(doc);
// { paragraphs: 42, tables: 3, images: 5, comments: 4, footnotes: 2,
//   endnotes: 0, headings: 8, words: 1240, characters: 7820,
//   charactersNoSpaces: 6480 }

outline(doc);
// [ { paragraph, level: 1, text: "Chapter 1" }, { level: 2, ... } ]

bookmarks(doc); // [{ name, id, paragraph }]
headers(doc); // [{ partName, relId, text }]
footers(doc); // [{ partName, relId, text }]
images(doc); // [{ partName, contentType, data }]
coreProperties(doc); // { title?, creator?, subject?, ... }

// Spot template placeholders before bulk-replacing:
for (const { partName, matches } of findTextEverywhere(doc, /\{\{\w+\}\}/g)) {
  console.log(
    partName,
    matches.map((m) => m.text),
  );
}

6. Accept / reject tracked changes

import { acceptAllRevisions, openDocx, toUint8Array } from "@office-kit/docx";

const doc = openDocx(reviewedBytes);

// Keep insertions, drop deletions:
acceptAllRevisions(doc);

// Or: drop insertions, restore deletions:
// rejectAllRevisions(doc);

const cleanBytes = toUint8Array(doc);

7. Add an image, replace an image

import { addImage, createDocx, images, openDocx, replaceImage } from "@office-kit/docx";
import { readFileSync } from "node:fs";

const doc = createDocx({ paragraphs: ["Logo:"] });
const logo = readFileSync("logo.png");
addImage(doc, logo, {
  widthEmu: 914400,
  heightEmu: 914400,
  altText: "Company logo",
});

// Replace an existing image in a template without redoing rels:
const tpl = openDocx(readFileSync("template.docx"));
const firstImage = images(tpl)[0];
if (firstImage) {
  replaceImage(tpl, firstImage.partName, readFileSync("new-logo.png"));
}

8. Validate before saving

import { validate } from "@office-kit/docx";

const doc = buildSomeDocument();
const issues = validate(doc);
if (issues.some((i) => i.level === "error")) {
  console.error("Refusing to save — would produce a docx that Word repairs:");
  for (const issue of issues) console.error(` [${issue.level}] ${issue.code} ${issue.message}`);
  process.exit(1);
}

9. Browser: load via <input type=file> and save via <a download>

import { fromBlob, replaceText, toBlob } from "@office-kit/docx";

fileInput.addEventListener("change", async () => {
  const file = fileInput.files?.[0];
  if (!file) return;
  const doc = await fromBlob(file);
  replaceText(doc, "{{name}}", "Alice");

  const blob = toBlob(doc);
  const a = document.createElement("a");
  a.href = URL.createObjectURL(blob);
  a.download = "filled.docx";
  a.click();
  URL.revokeObjectURL(a.href);
});