# Troubleshooting Common CLI issues and how to fix them. Each entry follows the same structure: the error you see, why it happens, and steps to resolve it. --- ## Destination directory does not exist ### What error appeared The CLI exits with code **1** and prints a message similar to: ```text Error: Destination directory "path/to/your/fonts" does not exist. Use --dest-create (-m) to create it. ``` You may also see progress output before the error when `--verbose` is enabled, for example: ```text Generating SVG font... Error: Destination directory "path/to/your/fonts" does not exist. Use --dest-create (-m) to create it. ``` On older versions, the same situation could surface as a raw filesystem error instead: ```text Error: ENOENT: no such file or directory, open 'path/to/your/fonts/webfont.woff2' ``` In those cases the process could still exit with code **0** even though no font files were written. ### Why it usually happens - **`--dest` / `-d` points to a folder that was never created.** Webfont does not create the output directory unless you ask it to. - **The path is relative to your current working directory.** A path that looks correct in your editor may not exist from the shell directory where you run the command. - **A build script or CI job runs before `mkdir`.** Scripts often assume `dist/fonts` or `public/icons` already exist. - **A typo in the path** (extra segment, wrong casing on case-sensitive filesystems, or a missing parent directory). Font generation can finish before files are written. If the destination folder is missing, the write step fails even though earlier steps (and verbose logs) may have already run. ### Steps to try to resolve 1. **Confirm the path exists from the same directory where you run webfont.** ```shell ls -la path/to/your/fonts ``` If the directory is missing, either create it yourself or use step 2. 2. **Ask webfont to create the destination directory.** ```shell webfont "src/icons/*.svg" -d path/to/your/fonts --dest-create ``` Short form: ```shell webfont "src/icons/*.svg" -d path/to/your/fonts -m ``` 3. **Create the directory manually** (useful in CI or when you want explicit control): ```shell mkdir -p path/to/your/fonts webfont "src/icons/*.svg" -d path/to/your/fonts ``` 4. **Use an absolute path for `--dest`** if your script changes working directories: ```shell webfont "src/icons/*.svg" -d "$(pwd)/dist/fonts" ``` 5. **Check write permissions** on the parent directory if creation still fails: ```shell mkdir -p path/to/your/fonts touch path/to/your/fonts/.write-test && rm path/to/your/fonts/.write-test ``` 6. **When using the programmatic API**, set `destCreate: true` if the folder may not exist yet: ```js import { webfont } from "webfont"; await webfont({ files: "src/icons/*.svg", dest: "dist/fonts", destCreate: true, }); ``` 7. **Upgrade webfont** if you see exit code **0** with no output files and only an `ENOENT` message. Current releases fail with exit code **1** and the clearer destination error above. If the problem persists, open an issue with the full command, your working directory, and the complete terminal output. --- ## `npm warn Unknown env config "devdir"` ### What error appeared Every `npm` command prints a warning similar to: ```text npm warn Unknown env config "devdir". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. ``` The command still succeeds; only the warning is noisy. ### Why it usually happens - **Cursor (or some IDE sandboxes) inject `npm_config_devdir`** into the shell environment so node-gyp uses a sandbox cache directory. - **npm 11.2+ warns** because `devdir` is not an official npm config key — it is a legacy node-gyp convention. - **This repository does not set `devdir`.** The project `.npmrc` only contains `save-exact=true`. Confirm the source in your terminal: ```shell echo "$npm_config_devdir" ``` If the path contains `cursor-sandbox-cache` (or similar), the IDE injected it — not webfont. ### Steps to try to resolve 1. **Clear the variable for one command:** ```shell npm_config_devdir= npm test ``` 2. **Clear it for the current shell session:** ```shell unset npm_config_devdir ``` 3. **Cursor / VS Code integrated terminal** — this repo ships `.vscode/settings.json` with `npm_config_devdir` cleared. Open a **new** terminal or reload the window (*Developer: Reload Window*) so the setting applies. Existing terminals keep the old environment. 4. **CI and plain shells** outside Cursor are unaffected; no change is required there. This warning does not indicate a broken install or a webfont bug. --- ## Config `files` ignored when using `--config` ### What error appeared You pass `--config webfont.config.json` with a `files` array in the config, but webfont only processes SVG paths from the command line — or shows help when you omit positional arguments. Example config: ```json { "files": ["src/icons/a.svg", "src/icons/b.svg"], "dest": "dist/icons", "fontName": "my-icons" } ``` **Older releases (before the fix in [#696](https://github.com/itgalaxy/webfont/pull/696)):** ```shell webfont --config webfont.config.json -d dist/icons # Shows help or does not pick up both files from config ``` If you also pass SVG paths on the CLI while `files` is set in the config, older releases may silently prefer CLI input only. **After the fix ships** (upgrade required), combining both causes a clear error: ```text Error: Cannot specify input files on the command line when `files` is set in the config file ``` ### Why it usually happens - **The CLI treated positional arguments as the only input source.** Config `files` was merged inside `webfont()`, but the CLI required at least one path on the command line before running. - **`files` in config and CLI input are mutually exclusive by design** — use one or the other to avoid ambiguity about which globs win. ### Steps to try to resolve 1. **Upgrade webfont** to a version that includes the fix for [#2](https://github.com/itgalaxy/webfont/issues/2) (see release notes / [#696](https://github.com/itgalaxy/webfont/pull/696)). Then run **without** positional SVG arguments: ```shell webfont --config webfont.config.json -d dist/icons ``` 2. **Until you upgrade**, pass all globs on the command line and use the config only for other options (`fontName`, `dest`, `formats`, etc.): ```shell webfont "src/icons/a.svg" "src/icons/b.svg" --config webfont.config.json -d dist/icons ``` Ensure the config file does **not** contain a `files` key in this mode. 3. **Programmatic API** — pass `files` directly (config discovery still merges other keys): ```js import { webfont } from "webfont"; await webfont({ files: ["src/icons/a.svg", "src/icons/b.svg"], configFile: "webfont.config.json", }); ``` 4. **Do not mix** CLI positional paths with `files` in the config after upgrading — pick one source of truth. If the problem persists after upgrading, open an issue with your config file, full command, and webfont version (`webfont --version` or `npm ls webfont`). --- ## Icon details missing after export ### What error appeared There is often **no error**. The font builds successfully, but part of the icon is missing in the browser — for example the dot on a letter in a LinkedIn-style logo ([#175](https://github.com/itgalaxy/webfont/issues/175)). The same SVG looks correct in Inkscape, Illustrator, or Affinity Designer. ### Why it usually happens - **`fill-rule: evenodd` on compound paths.** Many design tools export icons as one path with multiple subpaths and `fill-rule: evenodd` (often via an inline `style` on the root ``). Browsers still draw that SVG correctly. - **Icon fonts use the nonzero fill rule.** When the SVG is converted to a font glyph, `fill-rule` is not preserved. Counter-shapes and holes can disappear or invert — the geometry is often still in the glyph path, but it renders wrong. - **`--normalize` does not fix fill-rule.** Normalization adjusts metrics and path coordinates; it does not change evenodd semantics ([#80](https://github.com/itgalaxy/webfont/issues/80)). - **Small viewBoxes can make detail loss worse** for complex paths. Try a larger artboard (for example 512×512) and `fontHeight: 600` with `normalize: true` when paths are very small ([#80](https://github.com/itgalaxy/webfont/issues/80#issuecomment-435621410)). Run with **`--verbose`** to log a warning when webfont detects `fill-rule: evenodd` in a source SVG. **Alpha — broader diagnostics:** use **`--svg-diagnose`** (CLI) or `svgTools: { diagnose: true }` (API) to also flag stroke-only SVGs, `` symbol references ([#612](https://github.com/itgalaxy/webfont/issues/612)), and unsupported elements (``, ``, ``). webfont does not auto-fix these — preprocess with [`glyphContentTransformFn`](./packages/webfont/docs/configuration.md#glyphcontenttransformfn) when needed (see [ADR 0011](docs/adr/0011-no-svg-outline-stroke-dependency.md)). ### Steps to try to resolve 1. **Inspect the SVG with nonzero fill rule.** Temporarily change `fill-rule:evenodd` to `fill-rule:nonzero` in the file and reopen it in a vector editor. Missing pieces usually indicate path direction or compound-path issues, not a silent webfont failure. 2. **Fix the source in your design tool (recommended).** - Merge shapes with **Unite** / **Union**, then **Object → Compound Path → Make** (Illustrator: Ctrl/Cmd+8) so holes use correct winding ([#175](https://github.com/itgalaxy/webfont/issues/175#issuecomment-966653174)). - Avoid leaving `fill-rule: evenodd` on icons destined for icon fonts. - Export at a **larger size** (for example 512×512) when the tool produces fragile small paths. 3. **Tune font metrics for fine detail:** ```js await webfont({ files: "src/icons/**/*.svg", normalize: true, fontHeight: 600, }); ``` 4. **Preprocess difficult SVGs** with [`glyphContentTransformFn`](./packages/webfont/docs/configuration.md#glyphcontenttransformfn) (for example retrace or flatten strokes with [`svg-outline-stroke`](https://github.com/elrumordelaluz/outline-stroke) or [svg-fixer](https://github.com/oslllo/svg-fixer), installed in your project) before generating the font. 5. **Remove percentage `width` / `height`** on the root `` if a preprocessor errors; keep a numeric `viewBox` instead. See also [MDN: fill-rule](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule). --- ## Stroke-only SVGs produce blank icons ### What error appeared On releases **after** the [#327](https://github.com/itgalaxy/webfont/issues/327) fix, webfont **fails the run** with a message similar to: ```text Empty glyph path(s) in SVG font output for: wave (icons/wave.svg). Stroke-only SVGs (fill="none" with stroke) often produce empty glyphs because svgicons2svgfont does not convert strokes. ... ``` On **older releases**, the command may exit successfully but the icon is **invisible** in the browser — the font file exists, yet the glyph path is empty (`d=""`). ### Why it usually happens - The SVG uses **strokes** instead of **filled paths** (`fill="none"` with `stroke`, or `` / `` elements). - **svgicons2svgfont** (the library that merges SVGs into a font) does **not** convert strokes to outlines. Stroke geometry is dropped, so the exported glyph has no path data. - Design tools and browsers still preview the SVG correctly; only the font export is affected. ### Steps to try to resolve 1. **Convert strokes to fills** in your design tool (Outline Stroke / Expand / Object to Path) before export. 2. **Preprocess in your build** with [`glyphContentTransformFn`](./packages/webfont/docs/configuration.md#glyphcontenttransformfn). webfont **does not ship** stroke converters — install a tool in **your** project (for example [`svg-outline-stroke`](https://github.com/elrumordelaluz/outline-stroke) or [svg-fixer](https://github.com/oslllo/svg-fixer)) and call it from the hook (see [ADR 0011](docs/adr/0011-no-svg-outline-stroke-dependency.md)). 3. **Scan sources before converting:** `webfont icons/*.svg --svg-diagnose` (or `svgTools: { diagnose: true }` in the API) logs stroke-only and unsupported-element warnings without changing files. 4. **Optional SVGO cleanup:** `webfont icons/*.svg --optimize-svg` (or `optimizeSvg: true`) removes comments and editor metadata before conversion — it does **not** convert strokes. Use **before** `glyphContentTransformFn` when you need both cleanup and stroke outlining ([#724](https://github.com/itgalaxy/webfont/issues/724)). 5. **Optimize with SVGO** only after strokes are outlines — aggressive SVGO presets alone do not fix stroke-only artwork for icon fonts. --- ## SVG transform and `` references ### What error appeared There is often **no build error**. The font generates, but a glyph is **empty**, **wrong**, or **missing a flip/offset** that looks correct in the browser. Source SVGs may look like: ```xml ``` See [#612](https://github.com/itgalaxy/webfont/issues/612). ### Why it usually happens - **svgicons2svgfont** (the library that merges SVGs into a font) converts **filled paths** on each icon file. It does **not** resolve **`` / ``** references or bake **`transform`** on those instances into standalone path data. - **`transform` on paths/groups** is experimental upstream; **``** is a separate limitation — the referenced geometry is never inlined. - Design tools and browsers render `` + `transform` correctly; the font pipeline does not. ### Steps to try to resolve 1. **Flatten before webfont** — in Illustrator, Inkscape, or Figma export: **Expand / Flatten / Outline stroke**, or export **plain SVG** without symbols/instances. 2. **SVGO** (in your build): plugins such as **`convertShapeToPath`** and **`cleanupIds`** help; use a preset that inlines or removes `` where possible. webfont’s **`--optimize-svg`** does **not** inline `` — run SVGO in `glyphContentTransformFn` if you need that step. 3. **Scan sources:** `webfont icons/*.svg --svg-diagnose` warns when `` is detected (**12.1.0+**). 4. **Preprocess in code:** ```js import { webfont } from "webfont"; import { optimize } from "svgo"; await webfont({ files: "src/icons/**/*.svg", glyphContentTransformFn: async (glyph) => optimize(glyph.contents, { plugins: ["preset-default"] }).data, }); ``` Adjust SVGO plugins to your icons; test output glyphs in the HTML preview. See also [`docs/migration/issue-0612-svg-use-transform.md`](./docs/migration/issue-0612-svg-use-transform.md). --- ## `unicode-range` enabled but ligatures stopped working ### What error appeared There is often **no error**. Icons via **class + codepoint** (`.icon-phone::before`) still work. Typing **ligature names** (`phone_call`) shows fallback text or blank glyphs after you turned on [`unicodeRange`](./packages/webfont/docs/configuration.md#unicoderange) ([#322](https://github.com/itgalaxy/webfont/issues/322)). ### Why it usually happens - **`unicode-range` is opt-in** (default off). When enabled, webfont emits a range from **private-use code points only** (for example `U+EA01-EA1D`). Ligature identifier strings are ASCII and are **not** included in that range. - The browser applies the icon font only for code points inside `unicode-range`. For `phone_call`, letters `p`, `h`, `o`, … use a system font, so OpenType **`liga` cannot substitute** the icon glyph. - This is expected CSS behavior — `unicode-range` optimizes multi-font pages; ligature-by-name needs the icon font (or a second `@font-face` without range) for ASCII input. ### Steps to try to resolve 1. **Use class + codepoint icons** in production when `unicode-range` is enabled (recommended with multiple icon fonts on one page). 2. **Disable `unicode-range`** if you rely on ligature names: omit `--unicode-range` / `unicodeRange: true` (default), or set `unicodeRange: false` in config. 3. **HTML preview:** built-in `html` omits `unicode-range` when both `templateFontLigatures` and `unicodeRange` are enabled so the Ligature section can still preview names. Generated `css` / `scss` templates do not — enable `unicodeRange` only when you accept the ligature trade-off. 4. See also [Ligature names show text but no icon](#ligature-names-show-text-but-no-icon-html-preview). --- ## Ligature names show text but no icon (HTML preview) ### What error appeared There is often **no error**. The built-in **`html`** preview lists icon names under **Ligature**, but the large icon above each name is **blank** or shows **fallback serif text** (for example `bleach`, `drip_dry`) while the **Class** section (`.font-icon-name::before`) shows the glyph. ### Why it usually happens - **Ligature mode** types the icon name (for example `phone_call`) as normal text. The browser must apply OpenType **`liga`** so that character sequence maps to the icon glyph in the font. - Icon fonts typically **do not** include regular Latin letters — without `liga`, the browser looks up `p`, `h`, `o`, … in the icon font, finds no glyphs, and renders nothing. - From webfont **12.x** onward, the built-in `html` template adds `font-feature-settings: "liga" 1` on `#icon-ligatures` by default (`templateFontLigatures`, on by default). `unicode-range` is **off by default**; if you enable it, ligature preview may break unless the HTML template strips the rule (see above). - **`unicode-range` on `@font-face` limits which characters use the icon font.** When enabled, the computed range covers only private-use code points. Ligature names are ASCII, so the browser keeps a fallback font for those letters and **`liga` never runs**. ### Steps to try to resolve 1. **Regenerate** with a current webfont version (`liga` preview CSS on by default; `unicode-range` off unless you opt in). 2. **If you enabled `unicodeRange`**, see [`unicode-range` enabled but ligatures stopped working](#unicode-range-enabled-but-ligatures-stopped-working). 3. **In your own CSS or template**, enable ligatures where you type icon names: ```css .my-icon-font { font-family: "my-icon-font"; font-feature-settings: "liga" 1; font-variant-ligatures: common-ligatures; } ``` 4. **Prefer class + codepoint** for production (works without `liga` and with `unicode-range`): ```html ``` 5. **Disable preview CSS** if you manage ligatures yourself: `templateFontLigatures: false` or `--no-template-font-ligatures`. 6. Ensure [`ligatures`](./packages/webfont/docs/configuration.md#ligatures) is enabled when using ligature names (`--ligatures` adds ligature glyphs to the font). --- ## Browser hangs or extreme slowdown (large icon webfont, Firefox on Windows) ### What error appeared There is often **no build error**. After upgrading `webfont` (for example v9 → v11+) and shipping a **large** icon font (thousands of glyphs — Material Design Icons, Tabler, etc.), users report: - Firefox on **Windows** freezing for seconds when opening a tab or switching tabs - High CPU in **DirectWrite** / `LigatureSubstitution` during layout reflow - Chrome slower than before; macOS/Linux often less affected See [#558](https://github.com/itgalaxy/webfont/issues/558), [MaterialDesign#6519](https://github.com/Templarian/MaterialDesign/issues/6519), and [tabler-icons#1327](https://github.com/tabler/tabler-icons/issues/1327). ### Why it usually happens - From webfont **12.x**, **`ligatures` default to `false`** (performance on large fonts). **webfont 10.x–11.x** enabled ligatures by default — upgrading from those versions without regenerating fonts is a common trigger for this issue. - When **`ligatures: true`** (or `--ligatures`), each icon name is also encoded as an OpenType ligature (for example `phone_call` → glyph). - **Thousands of ligatures** produce a very large GSUB table. Windows Firefox uses DirectWrite to process ligature lookups during layout — reflow can take seconds per tab switch. - **WOFF2 is not the root cause** — the same ligature-heavy TTF/WOFF triggers the behavior. Fonts built without ligatures perform normally. ### Steps to try to resolve 1. **Keep ligatures disabled** (default on webfont 12.x). On older releases use `--no-ligatures` / `ligatures: false`: ```shell webfont "icons/*.svg" -d dist/fonts ``` ```js await webfont({ files: "icons/**/*.svg" }); ``` Only enable ligatures if you need ligature-by-name text and accept the performance cost: ```shell webfont "icons/*.svg" -d dist/fonts --ligatures ``` 2. **Use class + codepoint CSS** in the app (`.mdi-phone::before { content: "\\f001"; }`) — not ligature-by-name text. 3. **In CSS**, if you cannot regenerate the font yet, try `font-variant-ligatures: none` on icon classes — this helps only when the page does not rely on `liga` for icons; regenerating without ligatures is more reliable. 4. **webfont warns** when glyph count exceeds **2000** with ligatures enabled (stdout). Do not pass `--ligatures` on MDI-scale sets. 5. Prefer **`@mdi/svg` / SVG or JS icon packages** for web apps when the upstream project documents webfont alternatives. --- ## Homebrew: formula not found or wrong source ### What error appeared `brew install webfont` fails with: ```text Warning: No available formula with the name "webfont". Did you mean webfs? ``` Or `brew info webfont` shows `Tap: itgalaxy/webfont` when you expected homebrew-core. ### Why it usually happens - **Stale Homebrew index** — especially right after a new formula lands in homebrew-core. Run `brew update` before installing. - **Monorepo tap still tapped** — if you ran `brew tap itgalaxy/webfont`, Homebrew installs from the tap first. That still works, but it is not the same as the core formula. - **Tap removed but index not refreshed** — after `brew untap itgalaxy/webfont`, some setups need `brew update` before `brew install webfont` resolves homebrew-core. ### Steps to try to resolve 1. **Refresh and install from core:** ```shell brew update brew install webfont ``` 2. **Confirm which formula is installed:** ```shell brew info webfont webfont --version ``` Core shows `From: https://github.com/Homebrew/homebrew-core/...`. The tap shows `Tap: itgalaxy/webfont`. 3. **Switch from tap to core:** ```shell brew uninstall webfont brew untap itgalaxy/webfont brew update brew install webfont ``` 4. **Smoke-test the CLI** (same as the Homebrew formula test): ```shell webfont path/to/icon.svg -d /tmp/webfont-test -f woff2 --dest-create ls /tmp/webfont-test/webfont.woff2 ``` 5. **Optional tap** — for the `webfonts` alias or pre-core formula bumps, see [Install — Homebrew](./packages/webfont/install.md#homebrew-macos--linux-cli). ### Related - [homebrew-core PR #291610](https://github.com/Homebrew/homebrew-core/pull/291610) — merged formula - [#785](https://github.com/itgalaxy/webfont/issues/785) — tracking issue --- ## Can't resolve `fs` (webpack / React / Vite client bundle) ### What error appeared Bundlers fail while building client code with errors such as: ```text Module not found: Error: Can't resolve 'fs' ``` Or at **runtime** in the browser (often with older `webfont` versions bundled into the app): ```text TypeError: Cannot read property 'dirname' of undefined at .../node_modules/glob-parent/index.js ``` The stack trace often includes `globby`, `fast-glob`, `glob-parent`, `cosmiconfig`, or `webfont/dist/standalone.js` ([#198](https://github.com/itgalaxy/webfont/issues/198), [#496](https://github.com/itgalaxy/webfont/issues/496)). ### Why it usually happens - **`webfont` is Node.js-only.** It reads SVG files from disk, runs `globby`, and uses other Node built-ins. It is meant for **build time**, not inside browser/React component bundles. - **The package was imported from client code** (for example `import webfont from "webfont"` in `App.js`). Webpack/Vite tries to bundle the full Node implementation. Without Node’s `path` module, dependencies such as `glob-parent` throw **`Cannot read property 'dirname' of undefined`** ([#496](https://github.com/itgalaxy/webfont/issues/496)). - **From webfont 12.x**, the package `browser` / `exports` default entry resolves to a small stub that rejects with a clear message instead of pulling in `globby` — but you must still **not** call `webfont()` from client code; generate fonts in Node and ship the output files. ### Steps to try to resolve 1. **Do not import `webfont` from client-side code.** Generate fonts in an npm script, a Node build step, or CI: ```shell webfont "src/icons/*.svg" -d public/fonts -t css ``` 2. **Use the [webfont-webpack-plugin](https://github.com/itgalaxy/webfont-webpack-plugin)** (or an equivalent build hook) so generation runs in Node during compilation — not in the browser bundle. 3. **Programmatic API — Node only:** ```js import { webfont } from "webfont"; await webfont({ files: "src/icons/*.svg", dest: "public/fonts" }); ``` Run this from a `.mjs` script, `vite.config.ts`, or webpack config — never from React components loaded in the browser. 4. **If a dependency still pulls `webfont` into the client bundle**, mark it external or move the import to a Node-only file. With **webfont 12.x** and webpack 5+ / modern Vite, the package `browser` field and `exports` default resolve to `dist/browser.js`, which throws a clear error instead of bundling `fs` / `glob-parent`. On **11.x** or webpack 4 without `browser` resolution, you may see `dirname of undefined` until you stop importing `webfont` from client code. 5. **Browser-based generation** is not supported on the current release; see [#708](https://github.com/itgalaxy/webfont/issues/708) for a possible future Web Worker spike. --- ## Cannot find package `@vitest/coverage-v8` after a Vitest bump ### What error appeared `npm test` (or Lefthook pre-push) fails while Vitest starts with coverage enabled: ```text Error: Cannot find package '@vitest/coverage-v8' imported from .../node_modules/vitest/dist/chunks/coverage....js ``` `npm ls @vitest/coverage-v8` may still show the package under `packages/webfont/node_modules/@vitest/coverage-v8`. ### Why it usually happens - **`@vitest/coverage-v8` and `vitest` versions drifted** (for example Dependabot bumped only coverage to `4.1.10` while `vitest` stayed on `4.1.9`). Coverage declares a peer on the matching Vitest release. - **npm nested the coverage package under the workspace** while `vitest` stayed at the monorepo root. Vitest resolves `@vitest/coverage-v8` from *its* install location, so a nested coverage install is invisible. ### Steps to try to resolve 1. Keep **`vitest` and `@vitest/coverage-v8` on the same version** in every workspace that uses them (today `packages/webfont` and `packages/webfont-mcp`). 2. Regenerate the lockfile with a clean install so coverage hoists next to root `vitest`: ```shell rm -rf node_modules packages/*/node_modules npm install ls node_modules/@vitest/coverage-v8 ``` 3. Confirm resolve from Vitest’s location: ```shell node -e 'import { createRequire } from "module"; console.log(createRequire("./node_modules/vitest/package.json").resolve("@vitest/coverage-v8/package.json"))' ``` 4. Re-run `npm test`. --- ## TypeScript declaration emit uses `tsc`, not `vite-plugin-dts` ### What changed Declaration emit no longer goes through `vite-plugin-dts` / `unplugin-dts`. The build runs TypeScript 7: ```shell tsc -p tsconfig.build.json --emitDeclarationOnly ``` then `packages/webfont/scripts/emit-mts-types.mjs` for `.d.mts` (see [ADR 0016](docs/adr/0016-tsc-declarations-typescript7.md)). ### Why the old `@typescript/typescript6` shim is gone TypeScript 7 removed the JavaScript Compiler API that `unplugin-dts` needed. The previous workaround installed `@typescript/typescript6` beside `typescript@7`. That shim is **removed** so the workspace does not pull a TypeScript 6 compiler for builds. ### Steps if declarations are missing after build 1. Confirm `packages/webfont/package.json` `build` still includes `tsc -p tsconfig.build.json --emitDeclarationOnly` before `emit-mts-types.mjs`. 2. Confirm `tsconfig.build.json` has `declaration: true` and `emitDeclarationOnly: true`. 3. Re-run `npm run build -w webfont` and check `dist/src/index.d.ts` / `dist/src/index.d.mts`. 4. Run `npm run test:package` (publint + attw + pack-smoke). ## Knip reports unused exports or types ### What error appeared `npm run depcheck` (Knip) fails with `Unused exports` / `Unused exported types` (or a Knip upgrade starts failing on export-surface findings). ### Why it usually happens Usually one of: 1. A symbol is `export`ed but only used inside the same module (should be module-private). 2. A barrel re-exports something nothing imports. 3. A public entry file is missing from `packages/webfont/knip.json` `entry` (Knip then treats intentional public exports as unused). Per [#822](https://github.com/itgalaxy/webfont/issues/822) / [ADR 0008](docs/adr/0008-knip-instead-of-depcheck.md), we **keep** export/type checks. Do not exclude those issue classes or paper over them with a large `ignoreIssues` map. ### Steps to try to resolve 1. Prefer **removing the `export`** (or the dead re-export) when the symbol is not part of the public API. 2. If it is public API, ensure the file is listed under `entry` in `packages/webfont/knip.json` (for example `src/index.ts`, `src/cli/index.ts`). 3. Use `ignoreDependencies` only for dynamic tooling packages Knip cannot see; document why in TROUBLESHOOTING (do not reintroduce `@typescript/typescript6` — see [ADR 0016](docs/adr/0016-tsc-declarations-typescript7.md)). 4. Re-run `npm run depcheck` and confirm pre-push Lefthook still passes.