Vite+
The whole Vite+ config lives in toolchain.vite. Every key is passed through verbatim to Vite+, so dbx.config.* stays the only hand-authored root config:
export default defineConfig({
toolchain: {
vite: {
test: { include: ['src/**/*.test.ts'], environment: 'node' },
lint: { ignorePatterns: ['dist/**'] },
fmt: { semi: true, singleQuote: true },
},
},
});dbx sync writes a thin root vite.config.ts that resolves that config at load time, so the generated file never duplicates (or drifts from) dbx.config.*:
import { defineConfig, type ViteUserConfig } from 'vite-plus';
// Generated by @deltabox/toolchain — edit the vite section in dbx.config and run `dbx sync`.
import { resolveVite } from '@deltabox/toolchain/vite';
export default defineConfig(resolveVite<ViteUserConfig>({ cwd: import.meta.dirname }));The shim imports defineConfig from whichever Vite package the project installs (vite or vite-plus, detected from package.json), so a plain vite project gets import { defineConfig, type UserConfig } from 'vite' and resolveVite<UserConfig>(...).
resolveVite(options?) resolves the same-directory dbx.config.* (so a nested vite.config.ts uses the nested config it sits next to), or the toolchain you pass, then returns toolchain.vite. Pass vite inline to merge overrides over the loaded section (resolveVite({ vite: { ... } })). It is exported from @deltabox/toolchain/vite for programmatic use too.
The single toolchain opt-out is config. Set vite: { config: false } to leave a hand-written vite.config.ts alone; set config to a string (relative to the project, or absolute) to write the managed shim at a different path instead. Everything else under vite is Vite+ config (plugins, test, lint, fmt, run.tasks, ...), so toolchain.vite is the full Vite+ configuration. The section is typed as Vite+'s own config (ToolchainViteConfig = Vite's UserConfig as augmented by Vite+), so the whole Vite+ surface autocompletes in dbx.config.ts.
Static shims
By default the shim calls resolveVite at load time (shim: 'runtime'). Set vite: { shim: 'static' } to serialize the resolved config into the shim instead:
import { defineConfig, type ViteUserConfig } from 'vite-plus';
export default defineConfig<ViteUserConfig>({
resolve: { tsconfigPaths: true },
run: { tasks: {/* ... */} },
});A static shim imports only defineConfig, so Vite+ can load it without a built @deltabox/toolchain (useful for source-first monorepos and bootstrapping). It must stay serializable: functions, regexps and class instances are rejected at sync time (plugins still work with shim: 'runtime').
Runtime shims import @deltabox/toolchain/vite by default. Set vite.shimImport to change that specifier, for example to a local wrapper that re-exports resolveVite. Relative values are resolved from the repository root and rewritten relative to each generated config:
export default defineConfig({
toolchain: {
vite: {
shimImport: './scripts/local-vite-resolver.ts',
},
},
});This only affects shim: 'runtime' (the default); static shims have no toolchain import.
Vite 8 resolves TypeScript paths through resolve.tsconfigPaths. Set it in toolchain.vite.resolve (and in Vitest configs) to reuse the tsconfig aliases:
export default defineConfig({
toolchain: {
vite: { resolve: { tsconfigPaths: true } },
},
});For explicit aliases (or Vite versions without built-in paths support), use resolveTsconfig() from @deltabox/toolchain/tsconfig (see the tsconfig guide).
Add the shim to vite.fmt.ignorePatterns so Oxfmt leaves the generated file alone - without it vp fmt would rewrite the shim and dbx sync --check would report drift. In a monorepo put **/vite.config.ts on the root config: workspace fmt settings are inherited, but a workspace's ignorePatterns array replaces the root's list. The default fmt ignores already include **/vite.config.ts; dbx migrate --from vite adds /vite.config.ts to vite.fmt only when the folded config already owns fmt.
Format and lint defaults
Opt in with vite: { fmt: true } / vite: { lint: true } to use the toolchain defaults (DEFAULT_FMT_RULES / DEFAULT_LINT_RULES, exported from @deltabox/toolchain). An options object is merged over those defaults, so fmt: { semi: false } keeps the shared ignores and print width and only changes semi. Shared ignore lists live in DEFAULT_FMT_IGNORE_PATTERNS / DEFAULT_LINT_IGNORE_PATTERNS, both layered over DEFAULT_IGNORE_PATTERNS. All three are exported (readonly) from @deltabox/toolchain and @deltabox/toolchain/shared, so you can compose your own list instead of repeating the defaults:
import { DEFAULT_FMT_IGNORE_PATTERNS, defineConfig } from '@deltabox/toolchain';
export default defineConfig({
toolchain: {
vite: {
fmt: {
ignorePatterns: [...DEFAULT_FMT_IGNORE_PATTERNS, '**/fixtures/**'],
},
},
},
});Import sorting is enabled by default too. sortImports uses the usual groups (builtin, external, internal/subpath, relatives, styles) plus a workspace group whose patterns come from every dbx.config.* project in the repository, so consumer packages sort together without a hand-written customGroups entry. Override sortImports: false to disable it, or set your own groups — the resolved workspace group is still appended (and listed in groups) unless you disable sorting.
To reuse the workspace group in your own sortImports, import the resolver:
import { resolveFmtCustomGroups } from '@deltabox/toolchain';
const customGroups = await resolveFmtCustomGroups();
// [{ groupName: 'workspace', elementNamePattern: ['@acme/**', 'app', 'app/**', ...] }]resolveFmtCustomGroups() reads the project graph, so it is safe to call from inside dbx.config.*. Pass { config: 'path/to/dbx.config.ts' } to resolve a specific config instead of the discovered one, or an inline project to add a project the graph does not know about.
In VS Code, set deltabox.toolchain.enableFormatImportGroupDefaults to false (user or workspace settings) to stop the extension from adding the workspace group.
Oxc config files
Vite+ reads fmt/lint from vite.config.ts, but the Oxc VS Code extension reads .oxfmtrc.json / .oxlintrc.json. When vite.fmt / vite.lint are defined, dbx sync writes those files as managed files so the editor picks up the same configuration. Only the root gets them by default; a nested dbx.config.* gets its own pair only when it defines vite.fmt / vite.lint itself (an inherited root config does not create per-workspace files).
To adopt an existing hand-written vite.config.ts, fold it in with the migration (see Migrations):
dbx migrate --from vite --write # folds the object into toolchain.vite (routes fmt/lint)
dbx sync # writes the managed vite.config.ts shim