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

78 lines
3.1 KiB
Markdown

# 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
```ts
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
```ts
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.