78 lines
3.1 KiB
Markdown
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.
|