Monorepo (pnpm + Turborepo)

Pathrule3 Rules • 2 Memories • 1 Skill

A pattern bundle for JavaScript and TypeScript monorepos built on pnpm workspaces and Turborepo. It codifies the rules that keep task pipelines deterministic and cacheable, package boundaries explicit, and dependency versions unified. Use it so AI agents and humans both extend the repo without breaking the cache or leaking cross-package imports.

Suggested path map

Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.

/ workspace root
Pin the pnpm version in packageManager and CI
monorepo-pnpm-turborepo-review
turbo.json/
Every Turborepo task declares outputs, inputs, and env
packages/
Import workspace packages only through their exported public entry
Share base tooling configs as internal workspace packages
pnpm-workspace.yaml/
Pin shared dependency versions with pnpm catalogs

Rules

3
Every Turborepo task declares outputs, inputs, and env/turbo.jsonhighstrictA task that omits outputs, inputs, or env entries will cache wrong results or never restore from cache.
1Turborepo only caches and replays a task correctly when its hash covers everything the task reads and produces. Configure each task so the hash is complete.
2 
3- Set `outputs` to every artifact the task produces (for example `[".next/**", "!.next/cache/**"]` or `["dist/**"]`); a missing `outputs` means cache restores nothing.
4- Add build-time `env` entries for every variable that affects output, and set `envMode: "strict"` so undeclared variables cannot silently influence a build and invalidate the hash.
5- Use `dependsOn: ["^build"]` to build upstream packages first; mark `dev`/`watch` tasks `persistent: true` and `cache: false`.
6- Enabling Turborepo Remote Cache (`turbo login && turbo link`) shares hits across CI machines and branches; configure `remoteCache: { enabled: true }` in `turbo.json` and use the Vercel Remote Cache or a self-hosted Turborepo Cache Server.
Import workspace packages only through their exported public entry/packageshighstrictDeep or relative imports across packages bypass the exports map and break Turborepo's dependency graph.
1Internal packages are consumed by their name and `exports` field, never by reaching across folder boundaries. This keeps the dependency graph honest and Turborepo Boundaries valid.
2 
3- Import a workspace package as `@repo/ui` (resolved via its `package.json` `exports`), never as `../../packages/ui/src/...`.
4- Declare every workspace package you use in the consuming package's `package.json` as `"@repo/ui": "workspace:*"`; an undeclared import is an implicit dependency Turborepo cannot track.
5- Keep each package's public surface in its `exports` field and avoid deep subpath imports unless they are explicitly listed there.
6- Run `turbo boundaries` in CI to catch cross-package violations and undeclared dependencies before merge.
Pin the pnpm version in packageManager and CI/roothighstrictA mismatched pnpm version between local and CI silently resolves different lockfile entries and breaks reproducibility.
1The `packageManager` field and the CI install action must agree on the exact pnpm version so the lockfile is interpreted identically everywhere.
2 
3- Set `"packageManager": "[email protected]"` (full semver) in the root `package.json`; this is enforced by Corepack when enabled.
4- Pin the same version in CI: for GitHub Actions, use `pnpm/action-setup` with an explicit `version` matching the `packageManager` field.
5- Commit `pnpm-lock.yaml`; never add it to `.gitignore`.
6- Run `pnpm install --frozen-lockfile` in CI so an out-of-date lockfile fails the build rather than silently resolving.

Memories

2
Pin shared dependency versions with pnpm catalogs/pnpm-workspace.yamlDefine one version per shared dependency in pnpm-workspace.yaml and reference it with catalog: in each package.
1This repo uses pnpm workspace catalogs as the single source of truth for shared dependency versions, so every package stays on the same React, TypeScript, and tooling versions without manual syncing.
2 
3- `pnpm-workspace.yaml` lists workspace globs under `packages:` (for example `apps/*` and `packages/*`) plus a `catalog:` block for the default catalog and optional named `catalogs:` for distinct version sets (for example `catalog:react19`).
4- Packages reference shared deps as `"react": "catalog:"` (default catalog) or `"react": "catalog:react19"` for a named one, instead of hard-coding a range in each package.
5- Bump a version in one place in the catalog and run `pnpm install`; pnpm rewrites the resolved lockfile entries across all packages automatically.
6- The content-addressable store hard-links shared deps, so a single `pnpm-lock.yaml` keeps installs deterministic and fast across all machines.
Share base tooling configs as internal workspace packages/packagesTypeScript, ESLint, and Tailwind base configs live in dedicated packages and are extended per app or package.
1Tooling configuration is published as internal config packages (for example `@repo/typescript-config`, `@repo/eslint-config`) rather than duplicated or pushed to the repo root. Each app and package extends the base it needs.
2 
3- There is intentionally no root `tsconfig.json` for source compilation; each package has its own `tsconfig.json` that does `"extends": "@repo/typescript-config/base.json"`.
4- ESLint flat config and Tailwind/PostCSS presets are exported from config packages and imported so a rule change ships once and busts the cache in all dependents automatically.
5- List the config package as a `devDependency` with `workspace:*` so Turborepo tracks a config change as an input to all consumers.
6- Keep the root `package.json` for workspace-level scripts, the `turbo` binary dep, and `packageManager`; per-package concerns stay inside each package.

Skills

1
monorepo-pnpm-turborepo-review/rootChecklist to review a pnpm + Turborepo monorepo change before merge.
1---
2name: monorepo-pnpm-turborepo-review
3description: Review checklist for changes in a pnpm workspaces and Turborepo monorepo, covering task hashing and caching, package boundaries, catalog-pinned versions, shared configs, and pnpm version pinning. Use before merging any change that touches turbo.json, pnpm-workspace.yaml, package.json files, or cross-package imports.
4---
5 
6# Monorepo (pnpm + Turborepo) review
7 
8## Task pipeline and caching
9 
10- [ ] Every new or changed `turbo.json` task sets `outputs` covering all produced artifacts and excludes cache dirs (e.g. `!.next/cache/**`).
11- [ ] Build-affecting env vars are declared in the task `env` or `globalEnv`; `envMode` is `strict`.
12- [ ] `dependsOn` uses `^build` for upstream packages; `dev`/`watch` tasks are `persistent: true` and `cache: false`.
13- [ ] Remote cache is configured if the team uses one (`turbo login` run, `remoteCache.enabled: true` in `turbo.json`).
14 
15## Package boundaries
16 
17- [ ] New cross-package usage imports by package name (`@repo/*` via `exports`), never by relative or deep `src`/`dist` paths.
18- [ ] Each workspace package used is declared with `workspace:*` in the consuming package's `package.json`.
19- [ ] `turbo boundaries` and the build pass with no implicit dependencies.
20 
21## Dependencies and versions
22 
23- [ ] Shared dependency versions use `catalog:` or a named `catalog:` instead of hard-coded ranges.
24- [ ] `pnpm-lock.yaml` is committed and reflects the current install; only one resolved version of each shared dep.
25- [ ] `packageManager` in the root `package.json` matches the pnpm version pinned in CI (`pnpm/action-setup` `version` field).
26- [ ] CI installs with `--frozen-lockfile`.
27 
28## Shared config
29 
30- [ ] TypeScript, ESLint, and Tailwind extend the shared `@repo/*-config` packages rather than duplicating config.
31- [ ] Config package changes bust the cache in all downstream packages (listed as `devDependency workspace:*`).

Why this pattern

Monorepo task pipelines silently lose cache hits and packages drift across boundaries as the repo grows.

Built for Teams running a JavaScript or TypeScript monorepo on pnpm workspaces and Turborepo..

Keeps your assistant from:

  • Cache misses from unset outputs or env vars left out of a task hash
  • Cross-package imports that bypass the package's public entry and break boundaries
  • Duplicate dependency versions because packages pin their own ranges instead of using the catalog
License
Apache-2.0
Version
1.0.0
Updated
2026-06-09
View source