Trovella Wiki

Adding Dependencies

Step-by-step guide for adding a new dependency to the monorepo -- catalog vs. non-catalog, SDK boundary rules, and Renovate group configuration.

Decision: Catalog or Non-Catalog?

Before adding a dependency, decide where its version should live:

ConditionVersion LocationSpecifier in package.json
Used by 2+ workspace packagespnpm-workspace.yaml catalog"catalog:"
Must stay in lockstep with a related package already in the catalogpnpm-workspace.yaml catalog"catalog:"
Used by a single package, no cross-package constraintThat package's package.jsonDirect range (e.g., "^2.1.0")

When in doubt, start non-cataloged. Promote to the catalog when a second package needs the same dependency.

Adding a Catalog Dependency

Step 1: Add to the catalog

Add the package and version range to the catalog: section of pnpm-workspace.yaml, in the appropriate domain group:

catalog:
  # Search
  typesense: ^3.0.4
  my-new-package: ^1.0.0 # <-- add here

Step 2: Reference from package.json

In each workspace package that needs the dependency, add it with "catalog:" as the version:

{
  "dependencies": {
    "my-new-package": "catalog:",
  },
}

Or use the pnpm CLI, then manually replace the resolved version with "catalog:":

pnpm add my-new-package --filter @repo/api
# Then edit packages/api/package.json: change "^1.0.0" to "catalog:"

Step 3: Install

pnpm install

Step 4: Verify

pnpm install --frozen-lockfile   # Must pass -- confirms lockfile consistency
pnpm ci:check                    # Full quality gate suite

Adding a Non-Catalog Dependency

# Add directly to the target package
pnpm add some-ui-lib --filter @repo/web

# Verify
pnpm ci:check

The version is written directly into the package's package.json. No catalog entry needed.

External SDK Boundary Check

If the dependency is an external service SDK (a client library for an external API), it must be wrapped in a dedicated @repo/* package. This is an architecture rule enforced by ESLint no-restricted-imports and dependency-cruiser.

Examples of SDK boundaries already in place:

SDKWrapper Package
ioredis@repo/cache
@anthropic-ai/sdk@repo/ai
@google/genai@repo/ai
typesense@repo/search
better-auth@repo/auth
inngestImported in @repo/web (background job definitions)

If your new dependency is an external SDK:

  1. Create or identify the wrapper package (e.g., @repo/cache wraps ioredis).
  2. Add the SDK to SDK_BOUNDARIES in packages/config-eslint/restrictions.js.
  3. Run pnpm lint to verify zero violations.
  4. Run pnpm dep-cruise to verify the dependency graph.

See the Package Boundaries section in the root CLAUDE.md for the full layer hierarchy and rules.

Renovate Group Check

After adding a new dependency, check if it belongs to an existing Renovate group in renovate.json. The groups use matchPackagePatterns with regex:

GroupPatternWould match...
drizzle^drizzledrizzle-orm, drizzle-kit, drizzle-zod
trpc^@trpc/@trpc/client, @trpc/server
eslint^eslint, ^@eslint/, ^typescript-eslinteslint-plugin-*, @eslint/js
tailwindcss^tailwindcss, ^@tailwindcss/tailwindcss, @tailwindcss/forms
sentry^@sentry/@sentry/nextjs, @sentry/node

If your package matches an existing pattern, it is automatically grouped. If it does not match any pattern and is the start of a new vendor cluster, create a new group:

{
  "description": "Group My Vendor packages",
  "groupName": "my-vendor",
  "matchPackagePatterns": ["^@my-vendor/"],
}

Complete Checklist

  1. Decide: catalog or non-catalog?
  2. If catalog: add version to pnpm-workspace.yaml, use "catalog:" in package.json
  3. If non-catalog: add directly with pnpm add --filter
  4. If external SDK: add to SDK_BOUNDARIES in restrictions.js, verify with pnpm lint and pnpm dep-cruise
  5. Check if the package fits an existing Renovate group; create a new group if needed
  6. Run pnpm install (never pnpm update)
  7. Run pnpm ci:check to validate
  8. Commit the changes: pnpm-workspace.yaml (if catalog), package.json, pnpm-lock.yaml, and renovate.json (if group added)

On this page