Skip to content

Package management and dependency rules

npm / pnpm workspaces

The monorepo uses pnpm with workspaces defined in pnpm-workspace.yaml:

  • themes/*
  • plugins/*
  • packages/*
  • tools/*

Root package.json also lists workspaces for compatibility with tools that read it.

Hoisting

pnpm installs dependencies into:

  • A root node_modules with symlinks into workspace packages
  • Per-package node_modules where needed for peer dependencies or isolation

Expectation: Hoisting reduces duplication, but you must not rely on implicit hoisting — a package must declare every dependency it imports in its own package.json. CI and fresh installs must succeed without “it worked because another package pulled it in.”

Rules

  1. Declare own dependencies — If plugins/my-plugin/src/index.js imports a package, that package must appear in plugins/my-plugin/package.json (dependencies or devDependencies as appropriate).
  2. Workspace protocol — Internal packages use workspace:* when published through the workspace (for example shared npm packages under packages/).
  3. Single install — Run pnpm install once at the monorepo root; do not ask contributors to pnpm install inside each plugin unless troubleshooting.
  4. Composer — Run root composer install for monorepo PHP tooling; run pnpm composer-install (or nx run <project>:composer-install) for per-project vendors as needed.

Composer path repositories

Plugins and themes often reference monorepo libraries via a path repository in composer.json:

json
"repositories": [
  {
    "type": "path",
    "url": "../../packages/phpunit-config",
    "options": { "symlink": false }
  }
]

Keep relative paths correct for the package’s depth under plugins/ or themes/.

Troubleshooting

SymptomThings to check
Module not found after adding an importAdd the dependency to that package’s package.json and run pnpm install from root.
Wrong version of a transitive depAdd a direct devDependency or use pnpm overrides at root (sparingly, with team agreement).
Peer dependency warningsInstall the peer into the package that consumes it or align versions with @wordpress/* packages.
Duplicate React or block editor issuesEnsure a single version of @wordpress/* across the workspace; check root and package package.json alignment.
Composer path not foundVerify url in composer.json relative to the package directory.

Lockfiles

  • pnpm-lock.yaml is committed at the root — keep it authoritative for JS.
  • Per-package composer.lock — follow team policy; many migrations remove per-package locks in favor of reproducible installs from composer.json constraints. Confirm with maintainers before deleting.