pnpm-workspace

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

PNPM Workspaces (Constructive Standard)

PNPM 工作区(Constructive 标准)

Create and manage PNPM monorepo workspaces following Constructive conventions. This covers pure TypeScript/JavaScript workspaces (not pgpm workspaces for SQL modules).
遵循Constructive规范创建和管理PNPM monorepo工作区,本文档仅覆盖纯TypeScript/JavaScript工作区(不包含用于SQL模块的pgpm工作区)。

When to Apply

适用场景

Use this skill when:
  • Creating a new TypeScript/JavaScript monorepo
  • Setting up a pnpm workspace structure
  • Configuring lerna for versioning and publishing
  • Managing internal package dependencies
当出现以下情况时可以使用本指南:
  • 创建全新的TypeScript/JavaScript monorepo
  • 搭建pnpm工作区结构
  • 配置lerna进行版本管理和发布
  • 管理内部包依赖

Workspace Structure

工作区结构

A Constructive-standard pnpm workspace:
text
my-workspace/
├── .eslintrc.json
├── .gitignore
├── .prettierrc.json
├── lerna.json
├── package.json
├── packages/
│   ├── package-a/
│   │   ├── package.json
│   │   ├── src/
│   │   └── tsconfig.json
│   └── package-b/
│       ├── package.json
│       ├── src/
│       └── tsconfig.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── tsconfig.json
符合Constructive标准的pnpm工作区结构如下:
text
my-workspace/
├── .eslintrc.json
├── .gitignore
├── .prettierrc.json
├── lerna.json
├── package.json
├── 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 Files

核心配置文件

pnpm-workspace.yaml

pnpm-workspace.yaml

Defines which directories contain packages:
yaml
packages:
  - 'packages/*'
For larger projects with multiple package directories:
yaml
packages:
  - 'packages/*'
  - 'apps/*'
  - 'libs/*'
用于定义哪些目录包含包:
yaml
packages:
  - 'packages/*'
对于包含多个包目录的大型项目,可配置为:
yaml
packages:
  - 'packages/*'
  - 'apps/*'
  - 'libs/*'

Root package.json

根目录 package.json

json
{
  "name": "my-workspace",
  "version": "0.0.1",
  "private": true,
  "repository": {
    "type": "git",
    "url": "https://github.com/org/my-workspace"
  },
  "license": "MIT",
  "publishConfig": {
    "access": "restricted"
  },
  "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"
  },
  "devDependencies": {
    "@types/jest": "^30.0.0",
    "@types/node": "^22.10.2",
    "@typescript-eslint/eslint-plugin": "^8.53.1",
    "@typescript-eslint/parser": "^8.53.1",
    "eslint": "^9.39.2",
    "eslint-config-prettier": "^10.1.8",
    "jest": "^30.2.0",
    "lerna": "^8.2.4",
    "prettier": "^3.8.0",
    "ts-jest": "^29.4.6",
    "ts-node": "^10.9.2",
    "typescript": "^5.6.3"
  }
}
Key points:
  • Root package is
    private: true
    (never published)
  • Scripts use
    pnpm -r
    to run recursively across packages
  • deps
    script for interactive dependency updates
json
{
  "name": "my-workspace",
  "version": "0.0.1",
  "private": true,
  "repository": {
    "type": "git",
    "url": "https://github.com/org/my-workspace"
  },
  "license": "MIT",
  "publishConfig": {
    "access": "restricted"
  },
  "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"
  },
  "devDependencies": {
    "@types/jest": "^30.0.0",
    "@types/node": "^22.10.2",
    "@typescript-eslint/eslint-plugin": "^8.53.1",
    "@typescript-eslint/parser": "^8.53.1",
    "eslint": "^9.39.2",
    "eslint-config-prettier": "^10.1.8",
    "jest": "^30.2.0",
    "lerna": "^8.2.4",
    "prettier": "^3.8.0",
    "ts-jest": "^29.4.6",
    "ts-node": "^10.9.2",
    "typescript": "^5.6.3"
  }
}
核心要点:
  • 根目录包配置为
    private: true
    (永远不会被发布)
  • 脚本使用
    pnpm -r
    在所有包中递归执行
  • deps
    脚本用于交互式更新依赖

lerna.json

lerna.json

json
{
  "$schema": "node_modules/lerna/schemas/lerna-schema.json",
  "version": "independent",
  "npmClient": "pnpm",
  "registry": "https://registry.npmjs.org",
  "command": {
    "create": {
      "homepage": "https://github.com/org/my-workspace",
      "license": "MIT",
      "access": "restricted"
    },
    "publish": {
      "allowBranch": "main",
      "message": "chore(release): publish",
      "conventionalCommits": true
    }
  }
}
Versioning modes:
  • "version": "independent"
    — Each package versioned separately (recommended for utility libraries)
  • "version": "0.0.1"
    — Fixed versioning, all packages share same version (recommended for tightly coupled packages)
json
{
  "$schema": "node_modules/lerna/schemas/lerna-schema.json",
  "version": "independent",
  "npmClient": "pnpm",
  "registry": "https://registry.npmjs.org",
  "command": {
    "create": {
      "homepage": "https://github.com/org/my-workspace",
      "license": "MIT",
      "access": "restricted"
    },
    "publish": {
      "allowBranch": "main",
      "message": "chore(release): publish",
      "conventionalCommits": true
    }
  }
}
版本管理模式:
  • "version": "independent"
    — 每个包单独管理版本(推荐用于工具类库)
  • "version": "0.0.1"
    — 固定版本,所有包共用同一个版本号(推荐用于耦合度高的包)

Package Configuration

包配置

Individual package.json

单个包的 package.json

json
{
  "name": "my-package",
  "version": "0.1.0",
  "description": "Package description",
  "author": "Constructive <developers@constructive.io>",
  "main": "index.js",
  "module": "esm/index.js",
  "types": "index.d.ts",
  "homepage": "https://github.com/org/my-workspace",
  "license": "MIT",
  "publishConfig": {
    "access": "public",
    "directory": "dist"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/org/my-workspace"
  },
  "scripts": {
    "clean": "makage clean",
    "build": "makage build",
    "lint": "eslint . --fix",
    "test": "jest"
  },
  "devDependencies": {
    "makage": "0.1.10"
  }
}
Key patterns:
  • publishConfig.directory: "dist"
    — Publish from dist folder (prevents tree-shaking into weird paths)
  • main
    and
    module
    point to built files (not src)
  • Uses makage for build tooling
json
{
  "name": "my-package",
  "version": "0.1.0",
  "description": "Package description",
  "author": "Constructive <developers@constructive.io>",
  "main": "index.js",
  "module": "esm/index.js",
  "types": "index.d.ts",
  "homepage": "https://github.com/org/my-workspace",
  "license": "MIT",
  "publishConfig": {
    "access": "public",
    "directory": "dist"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/org/my-workspace"
  },
  "scripts": {
    "clean": "makage clean",
    "build": "makage build",
    "lint": "eslint . --fix",
    "test": "jest"
  },
  "devDependencies": {
    "makage": "0.1.10"
  }
}
核心配置规则:
  • publishConfig.directory: "dist"
    — 从dist文件夹发布(避免出现奇怪的tree-shaking路径问题)
  • main
    module
    指向构建后的文件(而非src目录下的源码)
  • 使用makage作为构建工具

Internal Dependencies

内部依赖

Reference workspace packages using
workspace:*
:
json
{
  "dependencies": {
    "my-other-package": "workspace:*"
  }
}
When published,
workspace:*
is replaced with the actual version number.
使用
workspace:*
引用工作区内的其他包:
json
{
  "dependencies": {
    "my-other-package": "workspace:*"
  }
}
发布时,
workspace:*
会自动替换为实际的版本号。

Common Commands

常用命令

CommandDescription
pnpm install
Install all dependencies
pnpm -r run build
Build all packages
pnpm -r run test
Test all packages
pnpm --filter <pkg> run build
Build specific package
pnpm up -r -i -L
Interactive dependency update
pnpm lerna version
Version packages
pnpm lerna publish
Publish packages
命令描述
pnpm install
安装所有依赖
pnpm -r run build
构建所有包
pnpm -r run test
测试所有包
pnpm --filter <pkg> run build
构建指定的包
pnpm up -r -i -L
交互式更新依赖
pnpm lerna version
为包生成版本号
pnpm lerna publish
发布包

Creating a New Package

创建新包

bash
undefined
bash
undefined

Create package directory

创建包目录

mkdir -p packages/my-new-package/src
mkdir -p packages/my-new-package/src

Initialize package.json

初始化package.json

cd packages/my-new-package pnpm init

Then configure package.json following the pattern above.
cd packages/my-new-package pnpm init

然后按照上面的配置规则配置package.json即可。

TypeScript Configuration

TypeScript配置

Root tsconfig.json

根目录 tsconfig.json

json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "declaration": true,
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true
  }
}
json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "declaration": true,
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true
  }
}

Package tsconfig.json

单个包的 tsconfig.json

json
{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}
json
{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}

PNPM vs PGPM Workspaces

PNPM 与 PGPM 工作区对比

AspectPNPM WorkspacePGPM Workspace
PurposeTypeScript/JS packagesSQL database modules
Configpnpm-workspace.yamlpnpm-workspace.yaml + pgpm.json
Buildmakage buildpgpm package
Outputdist/ folderSQL bundles
Registrynpmnpm (for @pgpm/* packages)
Some repos (like constructive) are hybrid — they have both pnpm packages and pgpm modules.
对比项PNPM 工作区PGPM 工作区
用途TypeScript/JS包SQL数据库模块
配置pnpm-workspace.yamlpnpm-workspace.yaml + pgpm.json
构建命令makage buildpgpm package
输出产物dist/ 文件夹SQL构建包
镜像源npmnpm(用于@pgpm/* 包)
部分代码库(比如constructive)是混合模式——同时包含pnpm包和pgpm模块。

Best Practices

最佳实践

  1. Keep root private: Never publish the root package
  2. Use workspace protocol: Always use
    workspace:*
    for internal deps
  3. Consistent structure: All packages follow same directory layout
  4. Shared config: Extend root tsconfig.json in packages
  5. Independent versioning: Use for utility libraries with different release cycles
  6. Fixed versioning: Use for tightly coupled packages that should release together
  1. 保持根目录私有:永远不要发布根目录的包
  2. 使用工作区协议:内部依赖始终使用
    workspace:*
    引用
  3. 结构一致:所有包遵循相同的目录布局
  4. 配置共享:包内的tsconfig.json继承根目录的配置
  5. 独立版本管理:适用于发布周期不同的工具类库
  6. 固定版本管理:适用于需要一起发布的高耦合包

References

参考资料

  • Related skill:
    pnpm-publishing
    for publishing workflow with makage
  • Related skill:
    pgpm-workspace
    for SQL module workspaces
  • Related skill:
    pgpm-publishing
    for publishing pgpm modules
  • 相关技能:
    pnpm-publishing
    了解配合makage的发布流程
  • 相关技能:
    pgpm-workspace
    了解SQL模块工作区配置
  • 相关技能:
    pgpm-publishing
    了解pgpm模块的发布方法