--- url: https://toolchain.deltabox.studio/guide/formatting.md --- # Formatting & linting `toolchain.fmt` and `toolchain.lint` select a formatter/linter, hold its options, and optionally add project-scoped VS Code defaults: ```ts [dbx.config.ts] export default defineConfig({ toolchain: { fmt: { vscode: true, // add missing VS Code formatter settings (default false) oxc: { printWidth: 100, singleQuote: true }, biome: { formatter: { indentWidth: 4 } }, }, lint: { vscode: true, oxc: { rules: { eqeqeq: 'warn' } }, eslint: { rules: {} }, }, }, }); ``` The supported tools are: * `fmt`: `oxc` (Oxfmt), `biome`, `prettier`, `dprint`. * `lint`: `oxc` (Oxlint), `biome`, `eslint`. Each tool accepts `true` (use the defaults) or `false` (disable that tool), in addition to its options object. The Oxfmt/Oxlint option types are generated from the tools' official JSON schemas (vendored in the package), so editor completion and the generated config reference match the tool. The shared ignore lists are exported from `@deltabox/toolchain` and `@deltabox/toolchain/shared`: `DEFAULT_IGNORE_PATTERNS` (build/cache dirs), and `DEFAULT_FMT_IGNORE_PATTERNS` / `DEFAULT_LINT_IGNORE_PATTERNS` (the Oxc defaults, layered over `DEFAULT_IGNORE_PATTERNS`). Import them to extend the defaults rather than re-listing them. Oxfmt import sorting is enabled in the defaults as well. `sortImports` includes a `workspace` group built from every `dbx.config.*` project's scope/name; `resolveFmtCustomGroups()` returns that group so you can reuse it in a custom `sortImports` config. Set `sortImports: false` to opt out. ## Shorthand opt-in `fmt: true` / `lint: true` (or an options object with only `vscode`) picks a tool automatically. `dbx sync` resolves it in this order: 1. the project's `.vscode/settings.json`; 2. the nearest `package.json` or tooling config file, walking up to the repo root (a workspace-specific tool beats a root-installed one); 3. the VS Code user settings language blocks; 4. the Oxc defaults. Each level prefers a tool's own config file (`.oxfmtrc.json`, `biome.json`, `.prettierrc`, `dprint.json`, `.eslintrc.*`, ...) before its `package.json` dependency, and honours the formatter/linter precedence. If nothing is found, sync warns (`fmt.no-tool` / `lint.no-tool`) and still applies the Oxc defaults. Existing user-owned config files are not overwritten blindly: the detected tool's options are read into the matching `toolchain.fmt.*` / `toolchain.lint.*` entry. Oxc options are merged over the toolchain defaults using the same mapping as [`dbx migrate`](/guide/migrations/). ## Running the active tool `dbx fmt` / `dbx lint` run the tool the config (or discovery) selects, even when `toolchain.fmt` / `toolchain.lint` is omitted: * an explicit `toolchain.fmt.*` / `toolchain.lint.*` tool wins, in precedence order; * otherwise the same discovery `dbx sync` uses picks a tool from the project `.vscode/settings.json`, the nearest config file/`package.json`, or the VS Code user settings; * with nothing configured or installed, they fall back to `vp fmt` / `vp lint` (Oxfmt / Oxlint). Mode flags are translated per tool (`--check`/`--write` for formatters, `--fix` for linters) and other operands are forwarded. Oxc keeps the full Vite+ passthrough, so `dbx vite fmt ...` still accepts every `vp` flag. ## Precedence When more than one formatter/linter is configured, editor defaults use: * **formatters**: `oxc` > `biome` > `prettier` > `dprint`. * **linters**: `oxc` > `biome` > `eslint`. `dbx sync` warns (`fmt.conflict` / `lint.conflict`) when this happens; silence it with [`logger.suppress`](/guide/config/logging). `toolchain.vite.fmt` / `toolchain.vite.lint` are the Oxc config written by Vite+, and **override** `fmt.oxc` / `lint.oxc` with a deep merge (scalars, arrays and booleans in `vite.*` win). With both: ```ts toolchain: { fmt: { oxc: { semi: false, singleQuote: true } }, vite: { fmt: { singleQuote: false } }, } // effective Oxfmt options: { semi: false, singleQuote: false } ``` ## Generated config files * `fmt.oxc` / `lint.oxc` (+ `vite.fmt` / `vite.lint`) write managed `.oxfmtrc.json` / `.oxlintrc.json`. * `fmt.biome` / `lint.biome` merge into the managed `biome.json`. * The deprecated top-level `biome` section still works; prefer `fmt.biome` / `lint.biome`. Oxc files are root-only unless a nested `dbx.config.*` defines `fmt.oxc` / `lint.oxc` / `vite.fmt` / `vite.lint` itself. Biome keeps its existing per-project behaviour. When more than one formatter or linter is configured, sync warns (`fmt.conflict` / `lint.conflict`) because the generated config files and editor defaults may conflict. ## VS Code editor defaults Set `fmt.vscode` and/or `lint.vscode` (both default `false`) to add missing project-scoped settings to `.vscode/settings.json`. The generated defaults are deliberately small: * `editor.defaultFormatter` * language blocks (`[javascript]`, `[typescript]`, ...) * `editor.codeActionsOnSave` (lint only) Use an options object to override or exclude individual keys: ```ts toolchain: { fmt: { oxc: {}, vscode: { overrides: { 'editor.defaultFormatter': 'oxc.oxc-vscode', '[typescript]': { 'editor.defaultFormatter': 'esbenp.prettier-vscode' }, }, exclude: ['[javascript]'], }, }, lint: { oxc: {}, vscode: true }, } ``` These editor defaults are **not tracked** and existing keys are never overridden. (The separate Conventional Commits/Biome VS Code sync may track `.vscode/settings.json` when those sections are enabled.) Remove or change the generated keys freely. Setting `vscode: false` (or omitting it) adds nothing. A nested project scaffolded below a root that already has `.oxfmtrc.json` / `.oxlintrc.json` omits its own Oxc files and `fmt.oxc` / `lint.oxc` opt-ins, so `dbx fmt` / `dbx lint` keep using the root configuration. ## Migrating ```sh dbx migrate --from oxc --write # .oxfmtrc/.oxlintrc -> fmt.oxc/lint.oxc dbx migrate --from biome --from prettier --write dbx migrate --from all --write ``` Unless `--target` is given, the migrated formatter/linter options go to `toolchain.vite.fmt`/`toolchain.vite.lint` when the existing Vite config already defines `fmt`/`lint`; otherwise they go to the top-level `toolchain.fmt.oxc`/`toolchain.lint.oxc`. See [Migrations](/guide/migrations/). ## Biome > **Deprecated:** prefer `toolchain.fmt.biome` / `toolchain.lint.biome` (see > above). The top-level `biome` section still works and still writes > `biome.json`. The `biome` section generates a `biome.json` for consumers that still want a Biome config. The toolchain itself is built on **Oxc** (Oxfmt + Oxlint) through Vite+, but a repo that runs Biome directly can let the toolchain manage its `biome.json` from the same `dbx.config.*`. ```ts [dbx.config.ts] export default defineConfig({ toolchain: { biome: true, // or { overrides: { ... } } }, }); ``` `dbx sync` writes `biome.json`, resolving the `{{name}}` / `{{scope}}` replacement tokens and merging your overrides over the defaults. If any other formatter/linter is also configured, sync still writes both the Biome and Oxc config files, but warns (`fmt.conflict` / `lint.conflict`) that the two tools may conflict. Silencing it is possible with `logger: { suppress: ['fmt.conflict'] }`; prefer one toolchain per repo. ### Options | Option | Type | Default | Description | | ----------- | ------------------------- | -------------------- | -------------------------------------------- | | `enabled` | `boolean` | enabled when defined | Master switch. | | `vscode` | `boolean` | `true` | Write the Biome VS Code settings/extensions. | | `overrides` | `Record` | — | Deep-merged over the generated `biome.json`. | #### `overrides` Overrides are merged into the generated config **recursively**, and **arrays are unioned** (not replaced) — so `files.includes` can be extended: ```ts biome: { overrides: { files: { includes: ['!**/generated/**'] }, formatter: { indentWidth: 2 }, }, } ``` Default array entries can be pruned with a `$remove:` entry (its value is JSON, or a raw string when it is not valid JSON) or replaced wholesale with `$reset`: ```ts import { BIOME_RESET, removeBiomeEntry } from '@deltabox/toolchain/biome'; biome: { overrides: { files: { includes: [ removeBiomeEntry('!**/.generated'), // drop a default '!**/generated/**', ], }, assist: { actions: { source: { organizeImports: { options: { groups: [BIOME_RESET, ['@/lib/**']] }, }, }, }, }, }, } ``` `removeBiomeEntry(entry)` builds the directive, and nested arrays (eg. import groups) are matched deeply. The same directives work in `fmt.biome` / `lint.biome`; sources are applied in order, so later ones see earlier results. #### `vscode` When on (the default), the sync also writes the recommended Biome VS Code settings and the Biome extension recommendation (see [VS Code Extension](/guide/vscode)). Set `biome: { vscode: false }` to skip them. ### Programmatic API ```ts import { BIOME_RESET, biomeConfig, biomeSyncTask, biomeVscodeSettings, mergeBiomeConfig, removeBiomeEntry, renderBiomeConfig, renderBiomeJson, resolveBiomeArray, } from '@deltabox/toolchain/biome'; ``` | Export | Description | | ----------------------------------------- | -------------------------------------------------------- | | `biomeSyncTask()` | The sync task (writes `biome.json`). | | `biomeConfig` / `biomeVscodeSettings` | The generic Biome config / recommended VS Code settings. | | `renderBiomeConfig(tokens, overrides?)` | Resolve tokens + merge overrides into an object. | | `renderBiomeJson(tokens, overrides?)` | Serialize the rendered config. | | `mergeBiomeConfig(base, patch)` | The recursive/array-union merge used above. | | `resolveBiomeArray(base, patch)` | Resolve `$remove:`/`$reset` directives for one array. | | `removeBiomeEntry(entry)` / `BIOME_RESET` | Build the array-pruning / array-reset directives. | > Migrating **from** Biome to Oxc is a different concern — see > [Migrations](/guide/migrations/).