Skip to content

About this page

On this page

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:

dbx.config.ts
ts
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.*:

vite.config.ts
ts
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:

vite.config.ts
ts
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:

dbx.config.ts
ts
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:

dbx.config.ts
ts
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:

dbx.config.ts
ts
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:

ts
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):

sh
dbx migrate --from vite --write   # folds the object into toolchain.vite (routes fmt/lint)
dbx sync                          # writes the managed vite.config.ts shim