pnpm Workspace Configuration
March 6, 2026 · View on GitHub
This Nx monorepo uses pnpm as the package manager with a specific configuration to optimize for workspace management.
Understanding node_modules in Packages
Why do packages have node_modules directories?
The node_modules directories in individual packages (like packages/core/node_modules) are not full dependency installations. They are lightweight symlinks/junctions created by pnpm for proper Node.js module resolution.
What's Actually Inside
# Check what's in a package's node_modules
Get-ChildItem packages/core/node_modules | Select-Object Name, LinkType, Target
# Output shows they are junctions/symlinks:
# Name LinkType Target
# ---- -------- ------
# tslib Junction C:\...\node_modules\.pnpm\tslib@2.8.1...
# uuid Junction C:\...\node_modules\.pnpm\uuid@13.0.0...
These are pointer files that take up minimal disk space (~0-13 KB total per package).
How pnpm Works
- All actual packages are stored once in
node_modules/.pnpm/(content-addressable store) - Root node_modules contains symlinks to the store
- Package node_modules contains symlinks to the root store for their specific dependencies
- This enables:
- ✅ Fast installations
- ✅ Disk space savings
- ✅ Proper dependency isolation
- ✅ Correct Node.js module resolution
Configuration Files
.npmrc
# Registry configuration
registry=https://registry.npmjs.org/
@acontplus:registry=https://registry.npmjs.org/
access=public
save-exact=true
# Hoist everything to root to minimize package node_modules
shamefully-hoist=true
public-hoist-pattern[]=*
# Workspace package linking
link-workspace-packages=deep
prefer-workspace-packages=true
Key Settings:
shamefully-hoist=true: Hoists dependencies to the root node_modules (similar to npm/yarn behavior)public-hoist-pattern[]=*: Hoists all packages to be accessible from rootlink-workspace-packages=deep: Deep links workspace packagesprefer-workspace-packages=true: Prefers workspace versions over registry versions
.gitignore
# Ignore node_modules in all packages and apps
packages/**/node_modules/
apps/**/node_modules/
libs/**/node_modules/
projects/**/node_modules/
These directories are ignored because:
- They're just symlinks (not real dependencies)
- They're regenerated by
pnpm install - They shouldn't be committed to version control
.npmignore (per package)
Each publishable package has a .npmignore file that ensures:
node_modules/is not published to npm- Test files and configs are excluded
- Only production build artifacts are published
Package Structure
Dependencies Configuration
Production Packages use only peerDependencies:
{
"name": "@acontplus/core",
"peerDependencies": {
"@acontplus/utils": "^1.0.10",
"tslib": "^2.8.1",
"uuid": "^13.0.0"
}
}
Why peerDependencies?
- The consuming application provides the actual dependencies
- Prevents duplicate installations
- Ensures version compatibility across the monorepo
- Standard pattern for library packages
Workspace Configuration
pnpm-workspace.yaml:
packages:
- 'apps/*'
- 'packages/*'
This tells pnpm which directories contain workspace packages.
Common Tasks
Install Dependencies
pnpm install
Clean Install
# Remove all node_modules and reinstall
Remove-Item -Recurse -Force node_modules, packages/*/node_modules, apps/*/node_modules
pnpm install
Check Package node_modules
# See which packages have node_modules (they all will, and that's OK)
Get-ChildItem -Path packages -Directory | ForEach-Object {
$nodeModules = Join-Path $_.FullName "node_modules"
if (Test-Path $nodeModules) {
$size = (Get-ChildItem $nodeModules -Recurse -File -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum).Sum / 1KB
Write-Host "$($_.Name): $([math]::Round($size, 2)) KB"
}
}
Verify Symlinks
# Check if node_modules contents are symlinks
Get-ChildItem packages/core/node_modules | Where-Object { $_.LinkType }
Troubleshooting
"Why do my packages still have node_modules?"
This is normal and expected. pnpm creates these symlink directories for module resolution. They are:
- ✅ Lightweight (< 15 KB per package)
- ✅ Just pointers to the root store
- ✅ Required for Node.js to resolve dependencies
- ✅ Automatically ignored by git
- ✅ Excluded from npm publish
"Can I delete them?"
You can, but they'll come back after pnpm install. They're necessary for the build tools to work properly.
"How is this different from npm/yarn?"
| Feature | pnpm | npm/yarn |
|---|---|---|
| Storage | Content-addressable store | Flat node_modules |
| Disk Usage | ~50% less | More duplication |
| Install Speed | Faster | Slower |
| Strictness | Stricter (only listed deps) | More lenient |
| Workspace Links | Symlinks in packages | Hoisted to root |
Best Practices
- Don't commit
node_modules- Already configured in.gitignore - Use
peerDependenciesin libraries - Prevents dependency duplication - Run
pnpm installafter pulling - Regenerates symlinks - Use
nx resetto clear caches - When experiencing build issues - Check actual disk usage - Symlinks take minimal space