~/doclang — zsh
NAME

doclang — generate Microsoft Word documents from JavaScript.

DocLang is a TypeScript library that builds professional .docx files from semantic components. Describe the structure of a resume — or any document — and DocLang handles typography, spacing, and layout. The output opens in Word, Google Docs, and LibreOffice.

  • languageTypeScript — ESM plus a browser bundle
  • outputstandard .docx, no HTML or PDF hacks
  • peer depdocx, used for packing
  • licenseMIT
SYNOPSIS
$ npm install doclang docx
$ pnpm add doclang docx

docx is a peer dependency used to pack the final document.

doclang-demo — node build.mjs
$
compiling resume …
✓ docx generated in 42ms
✓ exports/jane_smith.docx (18.2 kB)
$
INSTALLATION

CDN

Import DocLang without a build step. The browser bundle exposes everything on the global DocLang, with Packer on DocLang.docx.

index.html
<!-- index.html -->
<script src="https://unpkg.com/doclang/dist/doclang.min.js"></script>
<script>
  const { Resume, Header, Name } = DocLang;
  const { Packer } = DocLang.docx;
</script>

See README.md for the full CDN setup guide.

USAGE

Compose a document from components, then hand it to exportFile(). Page margins default to 0.8in top/bottom and 1in left/right.

build.mjs
import {
  Resume, Header, Name, Designation, Contact,
  Summary, Experience, ExperienceItem,
  Skills, Skill, Education, EducationItem,
  exportFile,
} from "doclang";

const resume = Resume(
  Header(
    Name("Jane Smith"),
    Designation("Software Engineer"),
    Contact({
      email: "jane@example.com",
      phone: "+1 555 123 4567",
      github: "github.com/janesmith",
    })
  ),
  Summary("Full-stack engineer with 8+ years of experience."),
  Experience(
    ExperienceItem({
      company: "Tech Corp",
      designation: "Senior Engineer",
      duration: "2021 – Present",
      points: [
        "Led microservices migration",
        "Reduced deploy time by 70%",
      ],
    })
  ),
  Skills(
    Skill("TypeScript"),
    Skill("Node.js")
  )
);

await exportFile(resume, {
  fileName: "jane_smith",
  pdf: true,
});

Run it and a jane_smith.docx (plus .pdf when requested) lands in ./exports.

COMPONENTS

Every component returns a SectionComponent — a function that produces paragraphs for the document body. Pass any component or a raw string into Resume() or a container. All accept an optional ResumeStyles override as the last argument.

generic

namesignaturepurpose
HeadingHeading(text, styles?)Large heading paragraph.
SubHeadingSubHeading(text, styles?)Sub-section title.
SectionHeadingSectionHeading(text, styles?)Accent-colored section heading, used by containers.
TextText(text, styles?)Normal paragraph.
SmallTextSmallText(text, styles?)Secondary small text.
BulletBullet(text, styles?)Single bullet point.
BulletListBulletList(items, styles?)List of bullet points.
DividerDivider()Horizontal rule.
SpacerSpacer(points?)Vertical spacing, default 100 twips.

header

namesignaturepurpose
HeaderHeader(...children)Container for name, title, and contact info.
NameName(name, styles?)The candidate's name, large and centered.
DesignationDesignation(title, styles?)Professional title below the name.
ContactContact(info, styles?)Email, phone, links — pipe separated on one line.
AddressAddress(address, styles?)Address line in small text.
PhotoPhoto(config)Inline photo image from buffer data.
PhotoPathPhotoPath(path, config)Photo from a local file path — Node only.

profile

namesignaturepurpose
SummarySummary(text, styles?)Professional summary paragraph.
ObjectiveObjective(text, styles?)Career objective paragraph.

experience

namesignaturepurpose
ExperienceExperience(...items)Container that renders an "Experience" heading.
ExperienceItemExperienceItem(config, styles?)Company + duration, designation, bullet points.
CompanyCompany(name, styles?)Company name for custom layouts.
DurationDuration(text, styles?)Duration for custom layouts.

education

namesignaturepurpose
EducationEducation(...items)Container that renders an "Education" heading.
EducationItemEducationItem(config, styles?)Institution + year, degree below.
InstitutionInstitution(name, styles?)Institution name for custom layouts.
DegreeDegree(name, styles?)Degree for custom layouts.

skills

namesignaturepurpose
SkillsSkills(...skills)Container that renders a "Skills" heading.
SkillSkill(name, styles?)Single skill as a bullet point.

projects

namesignaturepurpose
ProjectsProjects(...items)Container that renders a "Projects" heading.
ProjectProject(config, styles?)Name, optional description, points, and link.

certifications

namesignaturepurpose
CertificationsCertifications(...items)Container that renders a "Certifications" heading.
CertificationCertification(config, styles?)Name, optional issuer, and date.

languages

namesignaturepurpose
LanguagesLanguages(...items)Container that renders a "Languages" heading.
LanguageLanguage(config, styles?)Name, optional proficiency.

awards

namesignaturepurpose
AwardsAwards(...items)Container that renders an "Awards" heading.
AwardAward(config, styles?)Title, optional issuer, and date.

references

namesignaturepurpose
ReferencesReferences(...items)Container that renders a "References" heading.
ReferenceReference(config, styles?)Name, "title at company", email | phone.
STYLES

defaultStyles ships a built-in design. createStyles(overrides?) merges a partial override over it — pass any subset of tokens, leave the rest to the defaults. Sizes are in half-points (24 = 12pt), colors are hex without the #, and spacing is in twips.

styles.ts
import { createStyles, Resume, Header, Name, Experience, ExperienceItem } from "doclang";

const styles = createStyles({
  name: { font: "Georgia", size: 36, bold: true, color: "1A1A2E" },
  sectionHeading: { color: "E94560", bold: true },
  text: { font: "Calibri", size: 20 },
});

const resume = Resume(
  Header(Name("John Doe", styles)),
  Experience(
    ExperienceItem({
      company: "Tech Corp",
      designation: "Engineer",
      duration: "2020 – Present",
      points: ["Built things"],
    }, styles)
  )
);

Style tokens

tokenused by
headingHeading
subHeadingSubHeading
sectionHeadingSectionHeading and all section containers
nameName
designationDesignation, ExperienceItem, EducationItem
textText, Summary, Objective
smallTextSmallText, Address
bulletBullet, BulletList
companyCompany, Institution, Skill, Project, Certification, Language, Award
durationDuration, and experience / education / certification / award dates
contactContact
skillSkill

Each token is a StyleToken with optional font, size, bold, italics, uppercase, color, spacing, alignment, and border fields.

API

exportFile(document, options?)

The recommended way to save a document. Handles DOCX generation, file writing, and optional PDF conversion — no need to import Packer, fs, or child_process.

optiontypedefaultpurpose
outputDirstring"./exports"Directory to save files in, created automatically.
fileNamestring"resume"Base file name without extension.
pdfbooleanfalseConvert DOCX to PDF with LibreOffice.

Returns { docx: string, pdf?: string } with absolute paths. Throws meaningful errors if any step fails. PDF conversion requires LibreOffice (soffice) on PATH.

export.ts
const { docx } = await exportFile(resume, {
  outputDir: "./exports",
  fileName: "john_doe",
});

const { docx, pdf } = await exportFile(resume, {
  outputDir: "./exports",
  fileName: "john_doe",
  pdf: true, // requires soffice
});

Utilities

namesignaturepurpose
formatContactformatContact(info): stringFormats a ContactInfo object into a pipe-separated string.
normalizeListnormalizeList(items): string[]Filters out undefined, null, and empty entries.
sectionIdsectionId(label): stringCreates a section separator name, e.g. "section_experience".

Types

Configuration objects — ContactInfo, PhotoConfig, ExperienceItemConfig, EducationItemConfig, ProjectConfig, CertificationConfig, LanguageConfig, AwardConfig, ReferenceConfig — mirror the component signatures above. The core types:

types.ts
type SectionComponent = () => Paragraph[];

interface StyleToken {
  font?: string;
  size?: number;     // half-points, 24 = 12pt
  bold?: boolean;
  italics?: boolean;
  uppercase?: boolean;
  color?: string;    // hex, no "#"
  spacing?: { before?: number; after?: number; line?: number };
  alignment?: "left" | "center" | "right";
}

Full signatures for every config object live in REFERENCE.md.