Constructive PNPM
PNPM monorepo workspaces, publishing with makage, and large-scale monorepo management following Constructive conventions. This skill covers the full lifecycle of TypeScript/JavaScript package development in PNPM workspaces.
When to Apply
Use this skill when:
- Creating workspaces: Setting up new PNPM monorepos, configuring workspace structure
- Managing packages: Internal dependencies, filtering, selective builds, dependency updates
- Publishing: Building with makage, versioning with lerna, publishing to npm
- Scaling: Organizing large codebases, CI/CD patterns, build optimization
Quick Start
# Create workspace
mkdir my-workspace && cd my-workspace
pnpm init
# Configure workspace
cat > pnpm-workspace.yaml << 'EOF'
packages:
- 'packages/*'
EOF
# Create a package
mkdir -p packages/my-lib/src
cd packages/my-lib && pnpm init
# Install dependencies
cd ../.. && pnpm install
# Build all packages
pnpm -r run build
Workspace Structure
my-workspace/
├── .eslintrc.json
├── .gitignore
├── .prettierrc.json
├── lerna.json
├── package.json # private: true, pnpm -r scripts
├── packages/
│ ├── package-a/
│ │ ├── package.json
│ │ ├── src/
│ │ └── tsconfig.json
│ └── package-b/
│ ├── package.json
│ ├── src/
│ └── tsconfig.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── tsconfig.json
Core Configuration
Root package.json
{
"name": "my-workspace",
"version": "0.0.1",
"private": true,
"scripts": {
"build": "pnpm -r run build",
"clean": "pnpm -r run clean",
"test": "pnpm -r run test",
"lint": "pnpm -r run lint",
"deps": "pnpm up -r -i -L"
}
}
Key: root is always private: true, scripts use pnpm -r for recursive execution.
Internal Dependencies
{
"dependencies": {
"my-other-package": "workspace:*"
}
}
When published, workspace:* is replaced with the actual version number.
Publishing with Makage
Constructive publishes from dist/ folder using makage to prevent tree-shaking into weird import paths.
Package Configuration
{
"name": "my-package",
"version": "0.1.0",
"main": "index.js",
"module": "esm/index.js",
"types": "index.d.ts",
"publishConfig": {
"access": "public",
"directory": "dist"
},
"scripts": {
"clean": "makage clean",
"build": "makage build",
"prepublishOnly": "npm run build"
},
"devDependencies": {
"makage": "0.1.10"
}
}
Anti-Pattern: ESM-Only with Exports Map
NEVER use the exports map pattern — it breaks CommonJS consumers, exposes dist/ in import paths, and is incompatible with dist-folder publishing.
Anti-Pattern: Manual Build Scripts
NEVER use manual build scripts with rimraf/copyfiles — makage handles all of this automatically.
Deep Nested Imports
Deep nested imports via file path are fully supported and recommended for tree-shaking:
import { OrmClient } from '@my-org/sdk/api';
import { AdminClient } from '@my-org/sdk/admin';
Publishing Workflow
# 1. Build and test
pnpm install && pnpm -r run build && pnpm -r run test
# 2. Version (interactive)
pnpm lerna version
# 3. Publish
pnpm lerna publish from-package
Key Commands
| Command | Description |
|---|---|
pnpm install | Install all dependencies |
pnpm -r run build | Build all packages |
pnpm -r run test | Test all packages |
pnpm --filter <pkg> run <cmd> | Run in specific package |
pnpm --filter <pkg>... run <cmd> | Run in package and dependencies |
pnpm add <dep> --filter <pkg> | Add dependency to package |
pnpm add <dep> -w | Add dependency to root |
pnpm up -r -i -L | Interactive dependency update |
pnpm lerna version | Version packages |
pnpm lerna publish from-package | Publish packages |
Filtering and Selective Builds
pnpm --filter my-app run build # Single package
pnpm --filter my-app... run build # Package + dependencies
pnpm --filter "...[origin/main]" run build # Changed since main
pnpm --filter "!my-legacy" run build # Exclude package
Troubleshooting Quick Reference
| Issue | Quick Fix |
|---|---|
| Package not found after publish | Ensure publishConfig.directory is "dist" |
| Types not found | Ensure types points to declaration file |
| ESM import errors | Ensure module points to ESM build |
| Dependency resolution issues | pnpm store prune && rm -rf node_modules && pnpm install |
| Workspace link issues | pnpm why <package-name> |
Reference Guide
Consult these reference files for detailed documentation on specific topics:
| Reference | Topic | Consult When |
|---|---|---|
| references/workspace.md | Workspace creation and configuration | Setting up a new monorepo, configuring pnpm-workspace.yaml, TypeScript config |
| references/publishing.md | Publishing with makage and lerna | Building packages, dist-folder pattern, versioning, npm publishing |
| references/monorepo-management.md | Large monorepo management | Filtering, selective builds, dependency management, CI/CD patterns, hybrid workspaces |
Cross-References
Related skills (separate from this skill):
pgpm(references/workspace.md) — SQL module workspaces (PGPM uses pnpm under the hood)pgpm(references/publishing.md) — Publishing pgpm modules to npmconstructive-boilerplate-pgpm-init— Workspace initialization templates