Files
skills/docs/media/fonts/font-compress/src/bin/dispatch.md
2026-05-29 08:20:56 +01:00

3.1 KiB

dispatch

Purpose

Per-file orchestration extracted from the bin entry. Reads a single input file from disk, routes binary inputs (TTF/OTF/WOFF/WOFF2) through the injected FontConverter to produce a TypefaceFont, routes .json inputs through pass-through parsing (no conversion), then either serialises the TypefaceFont back to JSON (when --format=json) or packs it via BinaryFontPacker and writes the resulting .btf. Progress, skip, and error events are streamed through the injected printer / logger. Lives in its own module so the entry can stay small enough to satisfy the file-size budget while keeping a single round-trip testable in isolation.

Features

  • Discriminates input by extension: .ttf/.otf/.woff/.woff2 goes to the converter; .json goes to pass-through validation.
  • Computes destination paths honouring --preserve-structure (mirror the input tree under the output root) vs the flat default.
  • Honours --force: existing outputs are skipped silently unless --force is set, in which case they are overwritten.
  • Captures any throwable from the converter or packer per file, prints a failure line, logs through LoggerLike, and returns { ok: false } so the batch can continue and the entry exits 1.

Usage: Basic

import { FontConverter } from "@suaveplan/font-convert";
import { BinaryFontPacker } from "../packer/binary-font-packer/binary-font-packer.js";
import { dispatchOne } from "./dispatch.js";

const outcome = await dispatchOne(
    "./fonts/Inter-Regular.ttf",
    new FontConverter(),
    new BinaryFontPacker(),
    options,
    "./fonts",
    "./out",
    deps
);
// outcome.ok === true, outcome.compressedBytes > 0

Usage: Advanced

const failures: string[] = [];
for (const file of files) {
    const r = await dispatchOne(file, converter, packer, options, root, out, deps);
    if (!r.ok) failures.push(file);
}
if (failures.length > 0) {
    deps.logger.error("font-compress.cli.batch_partial", { failures });
}

API Reference

Name Signature Notes
dispatchOne (file, converter, packer, options, inputRoot, outputRoot, deps) => Promise<FileOutcome> Never throws; all failures are reported via the returned outcome.
FileOutcome { ok; originalBytes; compressedBytes } compressedBytes === 0 means the file was skipped without overwrite.
DispatchDeps { logger; stdout; stderr } Injected so a test can capture stream output.

Implementation Notes

Destination resolution intentionally does NOT call path.relative — prefix-strip plus replace(/^[\\/]+/) is enough and lets us mirror the upstream legacy semantics where Windows-style separators round-trip unchanged. The dispatcher always creates the parent directory with mkdir({ recursive: true }) before writing, so users do not need to pre-create the output tree. JSON inputs are validated by JSON.parse only — the typeface schema is enforced downstream by BinaryFontPacker, which throws FontCompressError(PACK_FAILED) for malformed shapes, and the catch arm here converts that into a clean per-file failure line.