# Dependency Management Best practices for managing dependencies in a Turborepo monorepo. ## Core Principle: Install Where Used Dependencies belong in the package that uses them, not the root. ```bash # Good: Install in specific package pnpm add react --filter=@repo/ui pnpm add next --filter=web # Avoid: Installing in root pnpm add react -w # Only for repo-level tools! ``` ## Benefits of Local Installation ### 1. Clarity Each package's `package.json` lists exactly what it needs: ```json // packages/ui/package.json { "dependencies": { "react": "^18.0.0", "class-variance-authority": "^0.7.0" } } ``` ### 2. Flexibility Different packages can use different versions when needed: ```json // packages/legacy-ui/package.json { "dependencies": { "react": "^17.0.0" } } // packages/ui/package.json { "dependencies": { "react": "^18.0.0" } } ``` ### 3. Better Caching Installing in root changes workspace lockfile, invalidating all caches. ### 4. Pruning Support `turbo prune` can remove unused dependencies for Docker images. ## What Belongs in Root Only repository-level tools: ```json // Root package.json { "devDependencies": { "turbo": "latest", "husky": "^8.0.0", "lint-staged": "^15.0.0" } } ``` **NOT** application dependencies: - react, next, express - lodash, axios, zod - Testing libraries (unless truly repo-wide) ## Installing Dependencies ### Single Package ```bash # pnpm pnpm add lodash --filter=@repo/utils # npm npm install lodash --workspace=@repo/utils # yarn yarn workspace @repo/utils add lodash # bun cd packages/utils && bun add lodash ``` ### Multiple Packages ```bash # pnpm pnpm add jest --save-dev --filter=web --filter=@repo/ui # npm npm install jest --save-dev --workspace=web --workspace=@repo/ui # yarn (v2+) yarn workspaces foreach -R --from '{web,@repo/ui}' add jest --dev ``` ### Internal Packages ```bash # pnpm pnpm add @repo/ui --filter=web # This updates package.json: { "dependencies": { "@repo/ui": "workspace:*" } } ``` ## Keeping Versions in Sync ### Option 1: Tooling ```bash # syncpack - Check and fix version mismatches npx syncpack list-mismatches npx syncpack fix-mismatches # manypkg - Similar functionality npx @manypkg/cli check npx @manypkg/cli fix # sherif - Rust-based, very fast npx sherif ``` ### Option 2: Package Manager Commands ```bash # pnpm - Update everywhere pnpm up --recursive typescript@latest # npm - Update in all workspaces npm install typescript@latest --workspaces ``` ### Option 3: pnpm Catalogs (pnpm 9.5+) ```yaml # pnpm-workspace.yaml packages: - "apps/*" - "packages/*" catalog: react: ^18.2.0 typescript: ^5.3.0 ``` ```json // Any package.json { "dependencies": { "react": "catalog:" // Uses version from catalog } } ``` ## Internal vs External Dependencies ### Internal (Workspace) ```json // pnpm/bun { "@repo/ui": "workspace:*" } // npm/yarn { "@repo/ui": "*" } ``` Turborepo understands these relationships and orders builds accordingly. ### External (npm Registry) ```json { "lodash": "^4.17.21" } ``` Standard semver versioning from npm. ## Peer Dependencies For library packages that expect the consumer to provide dependencies: ```json // packages/ui/package.json { "peerDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" }, "devDependencies": { "react": "^18.0.0", // For development/testing "react-dom": "^18.0.0" } } ``` ## Common Issues ### "Module not found" 1. Check the dependency is installed in the right package 2. Run `pnpm install` / `npm install` to update lockfile 3. Check exports are defined in the package ### Version Conflicts Packages can use different versions - this is a feature, not a bug. But if you need consistency: 1. Use tooling (syncpack, manypkg) 2. Use pnpm catalogs 3. Create a lint rule ### Hoisting Issues Some tools expect dependencies in specific locations. Use package manager config: ```yaml # .npmrc (pnpm) public-hoist-pattern[]=*eslint* public-hoist-pattern[]=*prettier* ``` ## Lockfile **Required** for: - Reproducible builds - Turborepo dependency analysis - Cache correctness ```bash # Commit your lockfile! git add pnpm-lock.yaml # or package-lock.json, yarn.lock ```