Skip to content

About this page

On this page

Project ​

The project section is a package's canonical metadata. It is the single place to declare who a package is, and it does three jobs:

  1. Defines package.json fields. On dbx sync, the fields the section defines are written into the project's package.json (everything else is left untouched).
  2. Feeds the replacements. Every template (biome.json, workflows, tsconfigs, READMEs, ...) reads these values.
  3. Drives scaffolding. dbx project:init* uses the section as its defaults (and project.type picks the kind of project to generate).
dbx.config.ts
ts
export default defineConfig({
    toolchain: {
        project: {
            type: 'monorepo-root',
            name: 'acme',
            scope: '@acme',
            author: 'Acme Inc. <hello@acme.dev>',
            license: 'MIT',
            homepage: 'https://acme.dev',
            repo: 'https://github.com/acme/acme',
            description: 'The acme monorepo',
            nodeVersion: '22',
            packageManager: 'yarn',
            packageManagerVersion: '4.17.1',
        },
    },
});

package.json mapping ​

A sync writes only the fields the config defines; a field you omit is never touched. The mapping:

project fieldpackage.json fieldNotes
name + scopename@acme/app when a scope is set, else app.
descriptiondescription
authorauthor
licenselicense
homepagehomepage
reporepository{ type: 'git', url } (+ directory, see below).
packageManagerpackageManageryarn@4.17.1 when packageManagerVersion is set.
type—Project kind for detection/scaffolding.
nodeVersion—Node version for CI/templates (not engines).

nodeVersion is intentionally not written to engines: it is the Node version the generated workflows run on (node-version: ''), which is a different concern from the published engines.node range.

Workspace inheritance ​

A non-root dbx.config.* is merged over the nearest ancestor config (see Configuration → Nested configs). For project specifically:

  • Identity fields are never inherited: name, scope, description and type are per-project. A workspace keeps its own values, falling back to its package.json for name/scope/description, and is still detected from its own files — a root project never renames or rescopes a workspace. The scope therefore comes from the workspace's own package.json name (eg. @acme/web); an unscoped name stays unscoped unless the workspace config sets scope.
  • Shared metadata is inherited (author, license, homepage, repo, packageManager, ...), so those package.json values and replacement tokens stay shared.
  • inherit: false opts out entirely: the project keeps only its own project and ignores the parent's (useful for an unrelated package inside the tree).
packages/web/dbx.config.ts
ts
export default defineConfig({
    toolchain: {
        // `name`/`scope` are identity; here the scope comes from the package's
        // `@acme/web` name. `author`/`license`/`repo` still inherit from the root.
        project: { name: 'web' },
        // ...
    },
});

repo resolves to the workspace path ​

For a non-root project, repo also resolves to the workspace's location in the repository, written as repository.directory:

package.json
json
{
    "repository": {
        "type": "git",
        "url": "https://github.com/acme/acme",
        "directory": "packages/web"
    }
}

The directory is computed relative to the repository root (the nearest ancestor with a .git), exactly like npm expects for a monorepo workspace.

When the section is absent ​

If project is not defined, nothing is written to package.json — and the replacement tokens fall back to the project's own package.json values (name/scope from name, plus description, author, license, homepage, repository, engines.node, packageManager). So a repo with no project section still generates correct tokens from its existing metadata.

Scaffolding ​

dbx project:init, dbx project:init:monorepo and dbx project:init:workspace read project as their defaults, and:

  • name is required (from --name or project.name) — a scaffold fails with a clear error when it is missing. A workspace always needs an explicit --name (the root's name is not the workspace name).
  • --type must be valid (project, monorepo-root or monorepo-workspace) when passed explicitly; an invalid value is rejected. A project.type in the config still drives dbx sync detection, but scaffolding deliberately ignores it (a scaffold launched from a monorepo root must not tag a workspace with the root's type).
sh
dbx project:init --name my-app --scope @acme --type project --out ./my-app
dbx project:init:monorepo --name acme --scope @acme --out .
dbx project:init:monorepo --name acme --scope @acme --example --out .
dbx project:init:workspace --name web --root . --dir packages/web

The scaffolded dbx.config.ts spells the resolved project fields out, so a generated project is self-describing from day one. Existing files in the output directory are never overwritten (pass --force to replace them), and a scaffold refuses to write outside its output directory.

A nested project:init below a root that already has .oxfmtrc.json / .oxlintrc.json skips its own Oxc files and fmt.oxc / lint.oxc opt-ins, so it uses the root formatting/linting setup.

Pass --package-manager <pm> (yarn default, or pnpm / npm / bun) to choose the manager the scaffold targets.

Package manager and catalogs ​

The scaffold and dbx sync write a catalog (package -> version range) into the file the package manager reads it from, so dependencies can be declared with catalog::

ManagerCatalog fileNotes
yarn.yarnrc.ymlcatalog: / catalogs: (default + named).
pnpmpnpm-workspace.yamlcatalog: / catalogs:.
bunpackage.jsonworkspaces.catalog.
npm—No catalog support; dependencies are pinned.

Scaffolding resolves the manager from the packageManager input, writes the catalog of the packages the project depends on, and opts into it in the generated dbx.config.*, so dbx sync owns the file. To manage catalogs in an existing project, add a catalogs section (see Package catalogs):

dbx.config.ts
ts
export default defineConfig({
    toolchain: {
        project: { packageManager: 'yarn' },
        catalogs: {
            packages: { react: '^19.0.0', lodash: '^4.17.21' },
            named: { legacy: { react: '^18.0.0' } },
        },
    },
});

dbx sync writes (and keeps in sync) catalog: / catalogs: in .yarnrc.yml (Yarn) or pnpm-workspace.yaml (pnpm). A catalog file is only written when it is absent or already toolchain-managed (it carries a marker comment); a hand-written one is left untouched. Bun catalogs live in package.json and npm has no catalog support — both emit a warning.

Programmatic API ​

ts
import { projectFromPackageJson, projectToPackageJson, projectSyncTask } from '@deltabox/toolchain';

// Seed a config from an existing package.json.
const project = projectFromPackageJson(require('./package.json'));

// Map the defined fields onto package.json fields.
const pkgFields = projectToPackageJson(project, { directory: 'packages/web' });

// The sync task itself.
projectSyncTask();
ExportDescription
projectFromPackageJson(pkg)Derive project metadata from a package.json.
projectToPackageJson(p, o?)Map defined project fields onto package.json fields.
projectSyncTask()The sync task that applies the project section to package.json.