Skip to content

Node.js Usage ​

Installation ​

sh
npm install @atlowchemi/webfont-generator
sh
pnpm add @atlowchemi/webfont-generator
sh
yarn add @atlowchemi/webfont-generator
sh
bun add @atlowchemi/webfont-generator

Platform support ​

On a supported platform, installation uses a prebuilt addon; consumers do not need Rust or Cargo. Keep optional dependencies enabled so the package manager can install the platform binary. Building this repository from source has separate Rust prerequisites; see the contributor guide.

The package ships prebuilt native binaries for the following platforms:

OSArchitectureTarget triple
macOSx64x86_64-apple-darwin
macOSarm64 (Apple Silicon)aarch64-apple-darwin
Linux (glibc)x64x86_64-unknown-linux-gnu
Linux (glibc)arm64aarch64-unknown-linux-gnu
Linux (glibc)armv7armv7-unknown-linux-gnueabihf
Linux (musl)x64x86_64-unknown-linux-musl
Linux (musl)arm64aarch64-unknown-linux-musl
Windows (MSVC)x64x86_64-pc-windows-msvc
Windows (MSVC)arm64aarch64-pc-windows-msvc

Basic usage ​

ts
import { generateWebfonts } from '@atlowchemi/webfont-generator';

const result = await generateWebfonts({
    files: ['icons/add.svg', 'icons/remove.svg', 'icons/settings.svg'],
    dest: './dist/fonts',
    fontName: 'my-icons',
    types: ['woff2', 'woff'],
});

// Font data is available directly on the result
console.log(result.woff2); // Uint8Array
console.log(result.woff); // Uint8Array

// Generate CSS with default font URLs
const css = result.generateCss();

// Generate CSS with custom URLs
const cssCustom = result.generateCss({ woff2: '/fonts/icons.woff2' });

Color selection ​

By default, each SVG icon is flattened into a monochrome glyph. To preserve SVG colors, set colorGlyphs: true for all glyphs or colorGlyphs: ['logo'] for selected final post-rename names. Omission or [] keeps monochrome output. See colorGlyphs for validation rules and color glyphs for paint semantics and browser/platform limits.

ts
const result = await generateWebfonts({
    files: ['icons/logo.svg', 'icons/add.svg'],
    dest: 'dist/fonts',
    types: ['woff2', 'woff'],
    colorGlyphs: ['logo'],
    incremental: true,
});

// After changing only logo.svg's fill, update the existing result.
result.regenerate({ files: ['icons/logo.svg', 'icons/add.svg'] }, [{ path: 'icons/logo.svg', changeType: 'changed' }]);

The unselected add glyph remains monochrome. In the example above, replace colorGlyphs: ['logo'] with colorGlyphs: true to preserve colors for both icons.

For a multi-variant family, the selected name applies across every variant. Each design can use different SVG colors:

ts
const family = await generateWebfonts({
    dest: 'dist/fonts',
    variants: [
        { name: 'light', files: ['light/logo.svg'], weight: 300, default: true },
        { name: 'bold', files: ['bold/logo.svg'], weight: 700 },
    ],
    colorGlyphs: ['logo'],
    incremental: true,
});
// Each SVG can use different fixed colors. Weight selection chooses its paint and outline.
family.regenerate(
    {
        variants: [
            { variant: 'light', files: ['light/logo.svg'] },
            { variant: 'bold', files: ['bold/logo.svg'] },
        ],
    },
    [{ path: 'bold/logo.svg', changeType: 'changed' }],
);

Named theme colors ​

Set colorSlots: { secondary: '#ff9900' } alongside colorGlyphs to reserve a palette entry for a theme color. Use fill="currentColor" for foreground paths and fill="#ff9900" for secondary paths. A CSS template or cssContext callback receives colorSlots.secondary, the numeric index to use in @font-palette-values's override-colors descriptor. See the live light/dark example.

Color regeneration ​

The existing result and regeneration methods are used; there are no color-specific getters. Paint-only edits change font bytes and their hashes, including generated CSS font URLs. Changing color selection between builds also changes the URL hash; array order and duplicate names do not. Omission and an empty array retain the same monochrome hash. A fresh build and an incremental build of the same final inputs produce matching output. Variant fallback consumers receive their source's paint changes, while blank/fallback membership transitions update the COLR records.

Named selection is checked again after regeneration renames and membership changes. Removing the last source of a selected logical name fails rather than silently disabling that selection. Selection, parsing, or font-build failures retain the previous output and permit a corrected retry.

The existing write-failure contracts still apply. Ordinary synchronous regeneration restores the previous in-memory result; retry with the changes or a full re-diff. Synchronous variant regeneration retains newly committed in-memory output and pending disk writes; a no-op retry completes those writes. Asynchronous regeneration preserves the receiver and returns a new result only on success; failed writes can be reconciled on retry. See incremental generation.

Multi-variant fonts ​

A multi-variant family groups several designs of the same icons, such as light and bold, into one font file per output format. Match SVG filenames across designs so that add.svg refers to the same icon in each variant. You can then choose a design with a CSS modifier class.

ts
import { generateWebfonts } from '@atlowchemi/webfont-generator';

const result = await generateWebfonts({
    dest: './dist/fonts',
    fontName: 'my-icons',
    variants: [
        { name: 'light', files: ['icons/light/add.svg'], weight: 300, default: true },
        { name: 'bold', files: ['icons/bold/add.svg'], weight: 700 },
    ],
});

Load the generated my-icons.css, then use the icon class with an optional variant modifier:

html
<span class="icon icon-add" aria-hidden="true"></span> <span class="icon icon-add icon--bold" aria-hidden="true"></span>

The first icon uses the default design (light); the second uses bold. Each design needs a unique name and at least one SVG, and exactly one design must be the default. Omit weight to assign weights automatically, or supply increasing weights from 1–1000.

Multi-variant output supports TTF, WOFF, and WOFF2, defaulting to WOFF/WOFF2. For icons that appear in only some designs, choose a missing-glyph policy. Incremental regeneration is available with incremental: true; use the variant methods.

Custom templates receive extra family metadata; see the template context comparison. For stylesheet customization and SCSS, see Templates.

Caching across builds ​

cacheLocation is a plain directory, so any cache your build system already provides can carry it between runs. Restoring an older cache is still useful: icons that didn't change hit even when others did, and anything stale is ignored.

yaml
- uses: actions/cache@v6
  with:
      path: .webfont-cache
      # A new key per icon change saves an updated cache; restore-keys reuse the latest older one.
      key: webfont-${{ runner.os }}-${{ hashFiles('icons/**/*.svg', 'pnpm-lock.yaml') }}
      restore-keys: webfont-${{ runner.os }}-
- run: node scripts/build-icons.mjs # generateWebfonts({ ..., cacheLocation: '.webfont-cache' })
yaml
build-icons:
    script:
        - node scripts/build-icons.mjs # generateWebfonts({ ..., cacheLocation: '.webfont-cache' })
    cache:
        key: webfont-$CI_COMMIT_REF_SLUG
        fallback_keys: [webfont-$CI_DEFAULT_BRANCH]
        paths: [.webfont-cache/]
dockerfile
# syntax=docker/dockerfile:1
FROM node:24 AS icons
WORKDIR /app
COPY . .
# BuildKit keeps the mount between builds; it never becomes part of the image.
RUN --mount=type=cache,id=webfont-cache,target=/app/.webfont-cache \
    node scripts/build-icons.mjs

Only restore trusted caches

Cached fonts are reused as-is, so a cache directory is trusted build output. Only restore caches written by trusted builds, and clear the directory if in doubt. GitHub Actions already isolates caches saved by pull requests from the default branch; with other systems, avoid sharing one cache between untrusted branches and release builds.

Options reference ​

colorSlots ​

  • Type: Record<string, string>
  • Default: omitted (no reserved slots)

Maps semantic names to distinct six-digit #RRGGBB source colors (case-insensitive). Names must be nonempty ASCII letters, digits, hyphens or underscores. Nonempty slots require active colorGlyphs; they do not enable color themselves. Empty maps are equivalent to omission.

Indices start at zero in ASCII slot-name order. All slots are reserved even when unused; automatically discovered colors follow them. Changing icon membership or paint cannot move the reserved slots, but adding/removing/renaming slots can. Read colorSlots from the CSS context rather than hardcoding indices. This engine-owned context field overrides any same-named templateOptions value; CSS callbacks can still customize the context.

Matching uses the resolved SVG RGB, across selected glyphs and variants, preserving layer opacity. Two slots cannot use the same RGB value. Unselected glyphs remain monochrome, and foreground paint continues to use the host text color. Slot configuration contributes to CSS font-URL hashes; object insertion order and hex letter case do not. Settings stay fixed during incremental regeneration.

Slots select CSS font-palette entries, not inherited secondary CSS variables. Define explicit colors in named @font-palette-values rules and select them using font-palette. The font still contains its original default colors; browsers without color-font support use the monochrome fallback.

colorGlyphs ​

  • Type: true | string[]
  • Default: omitted (monochrome)
  • Description: Preserve solid SVG paint for all glyphs (true) or selected final post-rename logical names. An empty array disables color. false, null, strings, and mixed-type arrays are invalid. Selection applies across every variant, including resolved fallbacks. Unknown names are rejected.

Duplicates are harmless. Unknown names are reported together in first-occurrence input order. A selected logical name applies to every resolved design, including fallback states; blank states have no color record.

Active color requires TTF, WOFF, or WOFF2. Ordinary defaults include EOT, so specify compatible types; variant defaults are compatible. Color selection contributes to CSS font-URL hashes; array order and repeated names do not change the hash. See color selection for incremental examples and color glyphs for SVG semantics and browser/platform compatibility.

files ​

  • Required for single-variant generation
  • Type: string[]
  • Description: Array of paths to SVG files to include in the font.

Use a non-empty array when generating one design of each icon. Omit this field when variants is provided.

variants ​

  • Required for multi-variant input
  • Type: FontVariant[]
  • Description: Ordered SVG designs for one logical icon family. Requires at least two uniquely named variants, non-empty files in each variant, and exactly one default: true. Explicit weight values are ordered anchors from 1 through 1000. An automatic default resolves to 400; other automatic values resolve outward in steps of 100, or evenly when an anchor interval is too crowded. Every final weight is unique and strictly follows variant order.

Variant names produce CSS modifier classes with CSSOM-escaped selectors. Names do not form output filenames; all variants share one resource per requested modern format.

variantClassPrefix ​

  • Type: string
  • Default: 'icon--' in variant mode
  • Description: Prefix for generated variant modifier classes. Whitespace, NUL, and the legacy templateOptions.variantClassPrefix location are rejected.

missingGlyphs ​

  • Type: { behavior: 'blank' | 'error' | 'fallback'; variant?: string }
  • Default: { behavior: 'blank' } in variant mode
  • Description: Family-wide policy for glyphs missing from a variant. fallback requires the name of an existing variant that contains every logical glyph in the family; other behaviors reject variant. Import MissingGlyphBehavior from the package for the enum values.
ts
import { MissingGlyphBehavior } from '@atlowchemi/webfont-generator';

const blank = { behavior: MissingGlyphBehavior.Blank };
const error = { behavior: MissingGlyphBehavior.Error };
const fallback = { behavior: MissingGlyphBehavior.Fallback, variant: 'Regular' };

Pass one object as missingGlyphs. Blank cells retain the logical icon's advance; error mode rejects missing cells; fallback reuses Regular artwork, so Regular must contain every icon.

dest ​

  • Required
  • Type: string
  • Description: Output directory for generated font files.

fontName ​

  • Type: string
  • Default: 'iconfont'
  • Description: Name of the generated font family. Also used as the base name for output files.

types ​

  • Type: FontType[]
  • Default: ['eot', 'woff', 'woff2'] for single-variant input; ['woff', 'woff2'] for variants
  • Description: Font formats to generate. Single-variant input accepts 'svg', 'ttf', 'eot', 'woff', 'woff2'. Multi-variant input accepts only 'ttf', 'woff', and 'woff2'.

order ​

  • Type: FontType[]
  • Default: ['eot', 'woff2', 'woff', 'ttf', 'svg'] (filtered to requested types)
  • Description: Order of @font-face src entries in generated CSS. All values must also appear in types.

css ​

  • Type: boolean
  • Default: true
  • Description: Whether to generate a CSS file.

html ​

  • Type: boolean
  • Default: false
  • Description: Whether to generate an HTML preview file.

writeFiles ​

  • Type: boolean
  • Default: true
  • Description: Whether to write generated files to disk. Set to false for in-memory usage. Writes are non-transactional and can leave a partial bundle if a write fails.

cssTemplate ​

  • Type: string
  • Description: Path to a custom Handlebars template for CSS generation. The template receives the context described in Templates.

htmlTemplate ​

  • Type: string
  • Description: Path to a custom Handlebars template for HTML preview generation.

cssFontsUrl ​

  • Type: string
  • Description: URL prefix for font files in the generated CSS. Defaults to the relative path from cssDest to dest.

cssDest ​

  • Type: string
  • Default: path.join(dest, fontName + '.css')
  • Description: Output path for the generated CSS file.

htmlDest ​

  • Type: string
  • Default: path.join(dest, fontName + '.html')
  • Description: Output path for the generated HTML file.

codepoints ​

  • Type: Record<string, number>
  • Description: Explicit Unicode codepoints for specific glyphs, keyed by glyph name.

startCodepoint ​

  • Type: number
  • Default: 0xF101
  • Description: Starting codepoint for auto-assigned glyphs.

fontHeight ​

  • Type: number
  • Description: Explicit output font height (units per em).

ascent ​

  • Type: number
  • Description: Font ascent value.

descent ​

  • Type: number
  • Description: Font descent value.

normalize ​

  • Type: boolean
  • Default: true
  • Description: Scale icons to the height of the tallest icon.

incremental ​

  • Type: boolean
  • Default: false
  • Description: Retain parsed glyph data on the result so regenerateAsync() or regenerate() can rebuild after file changes without re-parsing the glyphs that didn't change. Enable for watch/dev; leave it off for one-shot builds so the parsed geometry isn't held in memory.

cacheLocation ​

  • Type: string
  • Description: Directory for a persistent generation cache. A run saves per-icon work (parsed and processed glyph outlines) and finished font bytes there; later runs reuse them for icons and formats that didn't change, so a fresh process starts warm. Output is byte-identical to an uncached run (this wrapper always sets formatOptions.ttf.ts; see the timestamp note below for the Rust API and CLI). See caching across builds for CI and Docker setups and performance for measured speedups.
    • An edited icon is reprocessed alone; the others reuse their saved work.
    • When only CSS/HTML options change (templates, cssFontsUrl, templateOptions, …), every font comes from the cache.
    • Formats are cached separately, so builds that differ only in a format option (for example WOFF2 compression quality between dev and production) share the rest. Each format keeps its latest entry plus one for the same icons with different format options.
    • Builds that request different types (for example WOFF2 only in dev, all five in production) still share fonts and parsed glyphs, but processed outlines are kept for the latest types only, so alternating between them reprocesses paths.
    • Without a fixed formatOptions.ttf.ts (Rust API and CLI only), each cached format keeps the TTF head timestamp of the run that created it, so formats in one output can carry different timestamps. Set ts for reproducible output.
    • Entries are keyed by file contents, not paths, so a moved checkout still hits.
    • A single-design font and a variant family with the same fontName replace each other's cache file, so alternating between them runs uncached.
    • The engine owns the *.wfcache files it creates; remove them with clearCache(). Rebuilding the native engine locally without a version change keeps old entries, so clear the cache after such a build.
    • Read or write failures (missing permissions, corrupt or truncated files) never fail generation; the run recomputes, replaces corrupt or truncated files, and leaves an unwritable location uncached. Saved fonts and saved per-icon work carry separate checksums, so accidental corruption is detected rather than reused. Runs whose fonts all come from the cache don't read the per-icon work, so damage limited to it is detected and repaired by the next run that needs it (one with a format to rebuild, or an incremental one). A directory where the cache file belongs is never removed; generation prints a warning and stays uncached until you remove it.
    • Cached fonts are reused as-is, so only restore trusted caches; see the warning under caching across builds.

threads ​

  • Type: number (a positive integer)
  • Default: one per CPU core
  • Description: How many threads read SVG files and do glyph work at once, including in later regenerate() calls on the result. Set it when several generations run side by side (for example in a monorepo build) or to leave cores free for other work. Output is identical for every value.
    • Glyph work never uses more threads than CPU cores: it keeps every thread busy, so more would only add switching. Reads can go higher, which can help on slow or network storage.
    • Not counted: WOFF2's Brotli compressor runs one extra worker thread, output files are written concurrently, and the async runtime keeps its own threads.
    • Unset, files are read with up to one task per core, and glyph work runs on Rayon's shared thread pool, so the RAYON_NUM_THREADS environment variable still applies.
    • Each value gets its own thread pool, kept for the life of the process so later generations and rebuilds reuse its threads. If the system refuses to start them, generation prints a warning and runs on the shared pool instead.

fixedWidth ​

  • Type: boolean
  • Description: Create a monospace font based on the widest icon.

centerHorizontally ​

  • Type: boolean
  • Description: Center glyphs horizontally based on their bounding box.

centerVertically ​

  • Type: boolean
  • Description: Center glyphs vertically based on their bounding box. This is a convenience alias for formatOptions.svg.centerVertically.

ligature ​

  • Type: boolean
  • Default: true
  • Description: Enable ligature support. When enabled, each glyph can be referenced by its name as a text ligature.

round ​

  • Type: number
  • Description: SVG path coordinate rounding precision.

preserveAspectRatio ​

  • Type: boolean
  • Description: Preserve the aspect ratio of SVG icons. This is a convenience alias for formatOptions.svg.preserveAspectRatio.

optimizeOutput ​

  • Type: boolean
  • Default: false
  • Description: Optimize SVG paths before assembling the font. This may reduce SVG path data, but does not guarantee smaller binary font output; compare the generated sizes for your input set. This is a convenience alias for formatOptions.svg.optimizeOutput.

fontStyle ​

  • Type: string
  • Description: CSS font-style value for the generated @font-face rule.

fontWeight ​

  • Type: string
  • Description: CSS font-weight value for the generated @font-face rule.

formatOptions ​

  • Type: FormatOptions
  • Description: Per-format configuration object with keys svg, ttf, woff, and woff2.
FormatOptions type definition
ts
interface FormatOptions {
    svg?: SvgFormatOptions;
    ttf?: TtfFormatOptions;
    woff?: WoffFormatOptions;
    woff2?: Woff2FormatOptions;
}

interface SvgFormatOptions {
    centerVertically?: boolean;
    fontId?: string;
    metadata?: string;
    optimizeOutput?: boolean;
    preserveAspectRatio?: boolean;
}

interface TtfFormatOptions {
    copyright?: string;
    description?: string;
    // Ignored by the npm package: like upstream @vusion/webfonts-generator, it always writes
    // a fixed timestamp, so output is reproducible. The Rust crate and browser package honor it.
    ts?: number;
    url?: string;
    version?: string;
}

interface WoffFormatOptions {
    metadata?: string; // WOFF metadata XML string
}

interface Woff2FormatOptions {
    // Brotli compression quality, 0 (fastest, largest) to 11 (slowest, smallest).
    // Tunes compression effort only — never changes glyph fidelity. Defaults to 11
    // (smallest output); lower it (e.g. to 10) for faster encoding at a marginal size
    // cost. Must be between 0 and 11; other values are rejected.
    compressionQuality?: number;
}

cssContext ​

  • Type: (context: CssContext) => void, or (context: CssContext<true>) => void for multi-variant options
  • Description: Callback to mutate the Handlebars template context before CSS rendering. See context fields and callback typing.

htmlContext ​

  • Type: (context: HtmlContext) => void, or (context: HtmlContext<true>) => void for multi-variant options
  • Description: Callback to mutate the Handlebars template context before HTML rendering. See context fields and callback typing.

rename ​

  • Type: (name: string) => string
  • Description: Custom function to derive glyph names from file paths. Receives the file path; must return the glyph name.

templateOptions ​

  • Type: Record<string, any>
  • Description: Additional key-value pairs merged into the Handlebars template context. This is where classPrefix and baseSelector are typically set.

TypeScript metadata ​

result.generateTs(): string returns fixed TypeScript source with icons as a readonly literal tuple, IconName = (typeof icons)[number], and literal iconBaseSelector and iconClassPrefix constants. It works with CSS disabled and includes effective cssContext changes. It describes generation context, not the classes a custom CSS template happens to emit. There is no TS-specific template, override, or callback.

Set ts: true (default: false) to write <dest>/<fontName>.ts; tsDest overrides the path. writeFiles: false suppresses physical output, but does not disable generateTs(). Regeneration refreshes metadata along with the fonts.

ts
const result = await generateWebfonts({ files: ['icons/add.svg'], dest: './fonts', ts: true });
console.log(result.generateTs());

Input types and TypeScript inference ​

GenerateWebfontsOptions accepts either files for a single design or variants for a multi-variant family. Use satisfies to check a reusable configuration without losing its specific input mode. Keep types as a tuple to guarantee the requested outputs:

ts
import { generateWebfonts, type GenerateWebfontsOptions } from '@atlowchemi/webfont-generator';

const options = {
    files: ['icons/add.svg'],
    dest: './dist/fonts',
    types: ['woff2'] as ['woff2'],
} satisfies GenerateWebfontsOptions;

const result = await generateWebfonts(options);
result.woff2; // Uint8Array
result.ttf; // null

Requested formats ​

A literal types list guarantees the selected getters. With types omitted, single-variant input guarantees EOT/WOFF/WOFF2 and multi-variant input guarantees WOFF/WOFF2.

ts
const modern = await generateWebfonts({
    dest: './dist/fonts',
    variants: [
        { name: 'light', files: ['icons/light/add.svg'], default: true },
        { name: 'bold', files: ['icons/bold/add.svg'] },
    ],
});
modern.woff2; // Uint8Array
modern.svg; // null

const svg = await generateWebfonts<'svg'>({
    files: ['icons/add.svg'],
    dest: './dist/fonts',
    types: ['svg'],
});
svg.svg; // string

When formats are selected at runtime, TypeScript cannot guarantee which getters contain data:

ts
import type { FontType } from '@atlowchemi/webfont-generator';

async function build(types: FontType[]) {
    const result = await generateWebfonts({ files: ['icons/add.svg'], dest: './dist/fonts', types });
    if (result.woff2 !== null) {
        console.log(result.woff2.byteLength);
    }
}

Widened option unions and explicit union generics also produce conservative nullable getters. An explicit single-format generic guarantees its getter when types is a nonempty tuple; an explicit union does not guarantee every member was requested. GenerateWebfontsResult<Possible, Guaranteed> records the possible and guaranteed formats; the second parameter defaults to the first. Async regeneration preserves both sets.

Callback input types ​

Callbacks on multi-variant options infer CssContext<true> and HtmlContext<true>. GenerateWebfontsBaseOptions<true> can describe shared options with those callback types. See template context and callback typing for a comparison and examples.

Result type ​

generateWebfonts() returns a Promise<GenerateWebfontsResult> with font data and template methods.

Font data properties ​

Each font format is available as a property on the result. Formats that were not requested return null.

PropertyTypeDescription
svgstring | nullSVG font XML string
ttfUint8Array | nullTrueType font binary
eotUint8Array | nullEOT font binary
woffUint8Array | nullWOFF font binary
woff2Uint8Array | nullWOFF2 font binary

generateCss(urls?) ​

  • Type: (urls?: Partial<Record<FontType, string>>) => string
  • Description: Returns the rendered CSS string. Pass urls to override the default font URLs in @font-face src.

Omit urls to use generated URLs. A supplied map replaces all defaults, so omitted entries are empty. Multi-variant results reject SVG/EOT URLs. See generated CSS for icon selectors and variant modifiers.

generateHtml(urls?) ​

  • Type: (urls?: Partial<Record<FontType, string>>) => string
  • Description: Returns the rendered HTML preview string. Pass urls to override font URLs in the embedded stylesheet.

URL overrides follow the same complete-replacement rule as generateCss. The built-in preview shows one icon grid using the default design. See HTML previews.

regenerate(files, changes?) ​

  • Type: (files: RegenerationFiles, changes?: GlyphChangeEntry[] | null) => void
  • Requires: the result was produced with incremental: true (throws otherwise).
  • Supported input: { files: string[] } for ordinary fonts, or { variants: VariantFileSet[] } for multi-variant families. Supply exactly one source, matching the result's mode.
  • Description: Rebuilds every requested font format after file changes, reusing cached geometry for the glyphs that didn't change (and reusing the rendered CSS/HTML when the glyph names and codepoints are unchanged). files is the complete file set after the change, in the order a fresh build would use (e.g. your glob result); the rebuilt glyphs are ordered to match it, so the result is byte-identical to a fresh generateWebfonts() of that set — additions included. Any file omitted from files is dropped; added/changed files named in changes are read from disk and re-parsed. Omit changes or pass null to re-read/hash every current file and infer added/changed/removed paths automatically. Outputs are refreshed in memory, and — when the result was created with writeFiles: true — refreshed fonts are written to disk too, while unchanged CSS/HTML companion files are skipped. Results generated with cssContext or htmlContext callbacks cannot be regenerated because those JavaScript callbacks cannot be re-run by the synchronous method. Intended for dev/watch rebuilds.
ts
let files = ['/icons/add.svg', '/icons/search.svg'];
const result = await generateWebfonts({ files, dest, incremental: true });
// ...on a watch event:
result.regenerate({ files }, [{ path: '/icons/add.svg', changeType: 'changed' }]);
// ...or when watcher hints are unavailable/untrusted:
result.regenerate({ files });
result.woff2; // refreshed bytes

Each entry is a GlyphChangeEntry:

ts
interface GlyphChangeEntry {
    path: string;
    changeType: 'added' | 'changed' | 'removed';
    // Resolved glyph name if you apply a custom rename. Added files default to
    // the file stem; changed files default to the current name; removed files ignore it.
    name?: string;
}

regenerateAsync(files, changes?) ​

  • Type: (files: RegenerationFiles, changes?: GlyphChangeEntry[] | null) => Promise<GenerateWebfontsResult>
  • Requires: the result was produced with incremental: true (rejects otherwise).
  • Supported input: the same complete source shape as regenerate.
  • Description: Performs the same rebuild as regenerate() off the Node.js event loop and resolves with a replacement result. The receiver remains readable and unchanged while the rebuild runs and after failure. Assign the replacement before starting another rebuild; overlapping calls from the same result lineage reject. In-memory state is replaced only on success, but writes made with writeFiles: true are not transactional.
ts
let files = ['/icons/add.svg', '/icons/search.svg'];
let result = await generateWebfonts({ files, dest, incremental: true });
result = await result.regenerateAsync({ files }, [{ path: '/icons/add.svg', changeType: 'changed' }]);

Single-file actions ​

Results generated with incremental: true support single actions without supplying the full file list:

SynchronousAsync (returns replacement result)Effect
add(path, name?, variant?)addAsync(path, name?, variant?)Read a new file and append it; optional name overrides the file stem. Families require a target variant.
update(path, name?)updateAsync(path, name?)Re-read an existing file in every consuming design, preserving its position and its name unless replaced.
remove(path)removeAsync(path)Remove an existing path from every design, preserving remaining order. Does not delete the file on disk.
ts
result = await result.addAsync('icons/search.svg');
result = await result.updateAsync('icons/search.svg', 'search');
result = await result.removeAsync('icons/old.svg');
// For a family, append to one configured design:
family = await family.addAsync('bold/search.svg', undefined, 'bold');

Duplicate additions, unknown update/remove paths, and removals that empty a design reject. The helpers use the current retained membership, including changes made through regenerate. They inherit regeneration's callback restrictions, output writing, failure handling, and async snapshot/overlap rules. Assign async replacements before the next action. Use regenerate for batches, explicit ordering, or assigning existing paths to other variants.

Variant regeneration ​

Generate with incremental: true to retain per-design geometry caches. Regeneration keeps the configured designs and weights fixed; adding, removing, or reordering designs requires a fresh generation. Changes to SVG membership, names, and contents are supported within each design.

ts
type RegenerationFiles = { files: string[]; variants?: never } | { files?: never; variants: VariantFileSet[] };

interface VariantFileSet {
    variant: string;
    files: string[];
}
ts
import { generateWebfonts, type RegenerationFiles } from '@atlowchemi/webfont-generator';

let result = await generateWebfonts({
    dest: './dist/fonts',
    incremental: true,
    variants: [
        { name: 'light', files: ['light/add.svg'], default: true },
        { name: 'bold', files: ['bold/add.svg'] },
    ],
});

const files: RegenerationFiles = {
    variants: [
        { variant: 'light', files: ['light/add.svg'] },
        { variant: 'bold', files: ['bold/add.svg'] },
    ],
};
result = await result.regenerateAsync(files, [{ path: 'bold/add.svg', changeType: 'changed' }]);
// Omit changes to read every file and infer changes, including additions and removals.
result = await result.regenerateAsync(files);

Every call requires every configured design exactly once. Each files list is authoritative for that design's membership and order. Lists may share paths and use different orders; their position in the update does not change configured design order. Unknown/duplicate design names, empty lists, duplicate paths within a design, duplicate change hints, and inconsistent change types are rejected before sources are read.

Change hints describe paths across the family: added means new to the family, changed means an existing path still used in the final family, and removed means no design uses it afterward. A content or name hint applies to every final design referencing that path. To add an existing path to another design, move it, or remove only one membership, edit the per-design file lists; no change hint is needed unless the contents or name also changed. New memberships are loaded even with an explicit empty changes list. Existing unhinted files retain their contents and names; full re-diff reads all files, preserves existing names, and derives new names from filenames.

Effective changes rebuild the requested shared font outputs while reusing eligible parsed and processed geometry. Union membership, codepoints, shared metrics, and fallback dependencies are recomputed. No-op updates reuse in-memory outputs; pending disk writes can still be retried.

Validation, loading, and font-build failures preserve the previous result. Synchronous write failures leave the new in-memory result committed and may leave partial disk output; retry the update or re-diff to finish writing. Async calls leave the receiver unchanged on failure and return a replacement only on success; retry from the receiver after rejection. Overlapping calls and regeneration from a replaced result are rejected. Writes are not transactional, so an async rejection may still follow partial disk writes. Callback-created results cannot be regenerated.

Migrating regeneration calls ​

The regeneration input is a breaking change for both methods. Wrap ordinary file arrays in { files }; the optional second argument keeps its existing shape.

ts
// Before
result.regenerate(files, changes);
result = await result.regenerateAsync(files);

// After
result.regenerate({ files }, changes);
result = await result.regenerateAsync({ files });

Use { variants: [...] } for families as shown above. A bare array is no longer accepted.

clearCache(cacheLocation) ​

Remove the files the generation cache created in cacheLocation, including leftovers from interrupted writes. Other files in the directory are left untouched, and a missing directory resolves with zero counts.

ts
import { clearCache } from '@atlowchemi/webfont-generator';

const { filesRemoved, bytesRemoved } = await clearCache('.webfont-cache');

Templates ​

See the shared Templates reference for context fields, custom templates, and the SCSS mixin with examples.

The package exports default Handlebars template paths via a subpath export:

ts
import * as templates from '@atlowchemi/webfont-generator/templates';

console.log(templates.css); // absolute path to default CSS template
console.log(templates.scss); // absolute path to default SCSS template
console.log(templates.html); // absolute path to default HTML template

These paths can be passed to cssTemplate or htmlTemplate when you want to use the built-in templates as a starting point for customization.

The templates namespace is also available on the function itself:

ts
import { generateWebfonts } from '@atlowchemi/webfont-generator';

console.log(generateWebfonts.templates.css);

See also ​

Released under the MIT License.