Use Cases (Mis à jour: 02/06/2026)

Monorepo pratique avec Claude Code et pnpm workspace

Construisez un petit monorepo avec Claude Code et pnpm workspace: dépendances, filter, CI et pièges courants.

Monorepo pratique avec Claude Code et pnpm workspace

pnpm workspace évite de copier le même code partout

Je suis Masa, l’opérateur de claudecode-lab.com.

Même un petit produit se divise vite en plusieurs morceaux: application publique, tableau d’administration, UI partagée, configuration, scripts de contenu, jobs email et outils de test. Le problème n’est pas d’avoir plusieurs dossiers. Le problème commence quand le même helper, la même URL publique ou le même schema de validation est copié dans trois endroits.

pnpm workspace permet de gérer plusieurs packages dans un seul dépôt Git. La documentation officielle pnpm Workspace explique qu’un workspace regroupe plusieurs projets et nécessite un fichier pnpm-workspace.yaml à la racine.

Claude Code est surtout utile comme relecteur. Il peut repérer un workspace:* manquant, une dépendance dans le mauvais sens, une CI qui reconstruit tout inutilement, ou un package partagé qui reçoit de la logique métier. Ce sont ces petites dérives qui rendent un monorepo coûteux.

Ce guide utilise pnpm 11.5.0. Pour le contexte général, consultez aussi la gestion de monorepo avec Claude Code et la gestion des dépendances.


Structure cible: quatre packages pour bien commencer

Inutile de créer une grande plateforme dès le départ. Deux apps et deux packages partagés suffisent souvent.

flowchart LR
  web["apps/web\n@acme/web"] --> ui["packages/ui\n@acme/ui"]
  web --> config["packages/config\n@acme/config"]
  admin["apps/admin\n@acme/admin"] --> ui
  admin --> config
acme-workspace/
  apps/web/src/main.ts
  apps/web/package.json
  apps/admin/src/main.ts
  apps/admin/package.json
  packages/config/src/index.ts
  packages/config/package.json
  packages/ui/src/index.ts
  packages/ui/package.json
  pnpm-workspace.yaml
  package.json
  .npmrc
  CLAUDE.md

Trois usages sont immédiats. D’abord, packages/ui peut contenir de petits helpers de présentation utilisés par l’app web et l’admin. Ensuite, packages/config centralise les URL publiques, les noms de feature flags et les constantes. Enfin, vous pouvez ajouter plus tard packages/contracts pour partager des types d’API ou des schemas Zod entre frontend et backend.

Le piège consiste à créer packages/common pour tout ce qui ne trouve pas sa place. Un package partagé doit avoir le même sens pour tous ses consommateurs. Avec Claude Code, formulez la demande ainsi: “extrais seulement le helper UI dupliqué; laisse la logique de facturation dans l’application”.


Configuration minimale à copier

Créez d’abord pnpm-workspace.yaml. La page officielle pnpm-workspace.yaml montre comment inclure ou exclure des dossiers.

packages:
  - "apps/*"
  - "packages/*"

catalog:
  typescript: ^5.8.3

Le package.json racine orchestre les commandes.

{
  "name": "acme-workspace",
  "private": true,
  "packageManager": "pnpm@11.5.0",
  "scripts": {
    "check:web": "pnpm --filter @acme/web build",
    "build": "pnpm -r --sort --if-present build",
    "test": "pnpm -r --if-present test",
    "changed:test": "pnpm --filter \"...[origin/main]\" --if-present test"
  },
  "devDependencies": {
    "typescript": "catalog:"
  }
}

tsconfig.base.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "skipLibCheck": true,
    "noEmit": true
  }
}

Dans .npmrc, rendez la résolution locale explicite:

link-workspace-packages=false
save-workspace-protocol=rolling
shared-workspace-lockfile=true
strict-peer-dependencies=true
auto-install-peers=false

L’élément important est workspace:*. pnpm documente que le protocole workspace: refuse de résoudre une dépendance en dehors du workspace local. Cela évite de récupérer par erreur un package du registry portant le même nom.

packages/config/package.json:

{
  "name": "@acme/config",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "exports": {
    ".": {
      "types": "./src/index.ts",
      "import": "./src/index.ts"
    }
  }
}
export const appConfig = {
  productName: "Acme Workspace",
  supportEmail: "support@example.com",
  publicSiteUrl: "https://example.com"
} as const;

packages/ui dépend de config via le workspace:

{
  "name": "@acme/ui",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "dependencies": {
    "@acme/config": "workspace:*"
  },
  "exports": {
    ".": {
      "types": "./src/index.ts",
      "import": "./src/index.ts"
    }
  }
}
import { appConfig } from "@acme/config";

export function renderPrimaryButton(label: string): string {
  return `[${appConfig.productName}] ${label}`;
}

L’application web déclare seulement ce qu’elle utilise:

{
  "name": "@acme/web",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "scripts": {
    "build": "tsc -p tsconfig.json"
  },
  "dependencies": {
    "@acme/config": "workspace:*",
    "@acme/ui": "workspace:*"
  }
}

apps/web/tsconfig.json:

{
  "extends": "../../tsconfig.base.json",
  "include": ["src"]
}
import { appConfig } from "@acme/config";
import { renderPrimaryButton } from "@acme/ui";

console.log(appConfig.publicSiteUrl);
console.log(renderPrimaryButton("Start trial"));

Lancement:

corepack pnpm install
corepack pnpm --filter @acme/web build
corepack pnpm -r --sort --if-present build

Donnez d’abord les frontières à Claude Code

Claude Code propose un guide officiel pour les monorepos and large codebases. La leçon principale est de réduire le contexte inutile. Dans un pnpm workspace, un lancement depuis la racine doit être accompagné d’une portée claire.

Le CLAUDE.md racine reste global:

# Acme Workspace

This repository is a pnpm workspace.

Packages:
- apps/web: customer-facing TypeScript app
- apps/admin: internal admin app
- packages/ui: shared UI helpers
- packages/config: shared runtime constants

Rules:
- Use pnpm, not npm or yarn.
- Add internal dependencies with workspace:*.
- Run focused commands with pnpm --filter before full workspace commands.
- Do not move business logic into packages/ui.

Les règles locales appartiennent au dossier concerné. La documentation memory de Claude Code explique que CLAUDE.md fournit des instructions persistantes. Il faut donc éviter les longs textes génériques.

claude -p "
Inspect this pnpm workspace. Do not edit files yet.
List the package graph, scripts, and risky dependency directions.
Then propose the smallest change needed to share UI helpers between apps/web and apps/admin.
"

Cette demande force une inspection avant toute modification.


filter rend le travail quotidien et la CI plus légers

pnpm Filtering restreint une commande à un sous-ensemble de packages.

pnpm --filter @acme/web build
pnpm --filter @acme/web... build
pnpm --filter ...@acme/ui test
pnpm --filter "...[origin/main]" --if-present test

Le piège est le sens des .... @acme/web... sélectionne web et ses dépendances. ...@acme/ui sélectionne ui et les packages qui dépendent de ui. Après un changement UI, exécuter seulement @acme/ui... peut oublier les tests web/admin.

Une CI ciblée peut ressembler à ceci:

name: workspace-check

on:
  pull_request:

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm
      - run: corepack enable
      - run: corepack prepare pnpm@11.5.0 --activate
      - run: pnpm install --frozen-lockfile
      - run: pnpm --filter "...[origin/main]" --if-present test
      - run: pnpm --filter "...[origin/main]" --if-present build

Pièges fréquents et corrections concrètes

Premier piège: déclarer une dépendance interne comme une version semver normale.

{
  "dependencies": {
    "@acme/ui": "^0.1.0"
  }
}

Dans le workspace, écrivez plutôt:

{
  "dependencies": {
    "@acme/ui": "workspace:*"
  }
}

Deuxième piège: mettre de la logique métier dans un package partagé. packages/ui ne doit pas appeler d’API ni décider du plan de facturation. Troisième piège: les dépendances circulaires. Si packages/ui importe depuis apps/web, la frontière est cassée.

Je demande régulièrement cette vérification:

claude -p "
Check this workspace for circular dependencies and misplaced imports.
Focus on packages/* importing from apps/*, duplicated config values,
and dependencies that should be workspace:*.
Return findings with file paths and minimal fixes.
"

Si vous publiez des packages, ajoutez Changesets:

pnpm add -Dw @changesets/cli
pnpm changeset init
pnpm changeset
pnpm changeset version
pnpm -r publish --access public

La documentation pnpm indique que le versioning de packages workspace s’appuie généralement sur Changesets ou Rush. Les applications apps/* restent normalement private: true.


Résumé et résultat vérifié

pnpm workspace n’est pas une couche lourde. C’est une base simple pour traiter l’UI, la configuration, les types et les tests partagés comme de vraies dépendances. Claude Code est utile pour revoir le graphe, limiter les changements et analyser les échecs de CI.

Ensuite, améliorez vos instructions avec CLAUDE.md best practices et stabilisez les vérifications avec Claude Code testing strategies. Pour un déploiement en équipe, utilisez la page Claude Code training.

J’ai vérifié l’exemple avec Windows, Node.js 22, Corepack et pnpm 11.5.0. En pratique, les deux erreurs les plus fréquentes sont l’oubli de workspace:* et le mauvais sens de filter. Faire afficher le package graph par Claude Code avant toute édition aide à repérer les abstractions inutiles et les dépendances circulaires.

#claude-code #pnpm #workspace #monorepo #typescript #ci
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.