pnpm-workspace
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePNPM 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.jsonCore 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 (never published)
private: true - Scripts use to run recursively across packages
pnpm -r - script for interactive dependency updates
deps
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:
- — Each package versioned separately (recommended for utility libraries)
"version": "independent" - — Fixed versioning, all packages share same version (recommended for tightly coupled packages)
"version": "0.0.1"
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:
- — Publish from dist folder (prevents tree-shaking into weird paths)
publishConfig.directory: "dist" - and
mainpoint to built files (not src)module - 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"
}
}核心配置规则:
- — 从dist文件夹发布(避免出现奇怪的tree-shaking路径问题)
publishConfig.directory: "dist" - 和
main指向构建后的文件(而非src目录下的源码)module - 使用makage作为构建工具
Internal Dependencies
内部依赖
Reference workspace packages using :
workspace:*json
{
"dependencies": {
"my-other-package": "workspace:*"
}
}When published, is replaced with the actual version number.
workspace:*使用引用工作区内的其他包:
workspace:*json
{
"dependencies": {
"my-other-package": "workspace:*"
}
}发布时,会自动替换为实际的版本号。
workspace:*Common Commands
常用命令
| Command | Description |
|---|---|
| Install all dependencies |
| Build all packages |
| Test all packages |
| Build specific package |
| Interactive dependency update |
| Version packages |
| Publish packages |
| 命令 | 描述 |
|---|---|
| 安装所有依赖 |
| 构建所有包 |
| 测试所有包 |
| 构建指定的包 |
| 交互式更新依赖 |
| 为包生成版本号 |
| 发布包 |
Creating a New Package
创建新包
bash
undefinedbash
undefinedCreate 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 工作区对比
| Aspect | PNPM Workspace | PGPM Workspace |
|---|---|---|
| Purpose | TypeScript/JS packages | SQL database modules |
| Config | pnpm-workspace.yaml | pnpm-workspace.yaml + pgpm.json |
| Build | makage build | pgpm package |
| Output | dist/ folder | SQL bundles |
| Registry | npm | npm (for @pgpm/* packages) |
Some repos (like constructive) are hybrid — they have both pnpm packages and pgpm modules.
| 对比项 | PNPM 工作区 | PGPM 工作区 |
|---|---|---|
| 用途 | TypeScript/JS包 | SQL数据库模块 |
| 配置 | pnpm-workspace.yaml | pnpm-workspace.yaml + pgpm.json |
| 构建命令 | makage build | pgpm package |
| 输出产物 | dist/ 文件夹 | SQL构建包 |
| 镜像源 | npm | npm(用于@pgpm/* 包) |
部分代码库(比如constructive)是混合模式——同时包含pnpm包和pgpm模块。
Best Practices
最佳实践
- Keep root private: Never publish the root package
- Use workspace protocol: Always use for internal deps
workspace:* - Consistent structure: All packages follow same directory layout
- Shared config: Extend root tsconfig.json in packages
- Independent versioning: Use for utility libraries with different release cycles
- Fixed versioning: Use for tightly coupled packages that should release together
- 保持根目录私有:永远不要发布根目录的包
- 使用工作区协议:内部依赖始终使用引用
workspace:* - 结构一致:所有包遵循相同的目录布局
- 配置共享:包内的tsconfig.json继承根目录的配置
- 独立版本管理:适用于发布周期不同的工具类库
- 固定版本管理:适用于需要一起发布的高耦合包
References
参考资料
- Related skill: for publishing workflow with makage
pnpm-publishing - Related skill: for SQL module workspaces
pgpm-workspace - Related skill: for publishing pgpm modules
pgpm-publishing
- 相关技能:了解配合makage的发布流程
pnpm-publishing - 相关技能:了解SQL模块工作区配置
pgpm-workspace - 相关技能:了解pgpm模块的发布方法
pgpm-publishing