workspace-audit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Workspace Audit (pnpm 10+)

工作区审计(pnpm 10+)

Purpose

审计目的

Analyze pnpm monorepo workspace configuration to:
  • Optimize dependency management
  • Check workspace protocol and catalog usage
  • Audit build hook and dependency rule configuration
  • Identify cargo-culted or outdated settings
分析pnpm单体仓库工作区配置,以:
  • 优化依赖管理
  • 检查workspace协议和目录的使用情况
  • 审计构建钩子和依赖规则配置
  • 识别盲目照搬或过时的设置

When to Use This Skill

适用场景

Use when the user asks to:
  • "Audit my monorepo"
  • "Check workspace configuration"
  • "Optimize pnpm workspace"
  • "Review monorepo setup"
Trigger phrases: "workspace audit", "monorepo", "pnpm workspace", "workspaces"
当用户提出以下需求时使用:
  • "审计我的单体仓库"
  • "检查工作区配置"
  • "优化pnpm工作区"
  • "审核单体仓库设置"
触发关键词:"workspace audit"、"monorepo"、"pnpm workspace"、"workspaces"

Workflow

工作流程

Step 1: Identify Workspace Type

步骤1:识别工作区类型

bash
cat pnpm-workspace.yaml 2>/dev/null
cat package.json | jq '.packageManager, .engines'
ls -la nx.json turbo.json 2>/dev/null
Check the
packageManager
field — it tells you the exact pnpm version. Audit advice below assumes pnpm 10+; call out version-gated settings when the project is on an older minor.
bash
cat pnpm-workspace.yaml 2>/dev/null
cat package.json | jq '.packageManager, .engines'
ls -la nx.json turbo.json 2>/dev/null
检查
packageManager
字段——它会显示确切的pnpm版本。以下审计建议基于pnpm 10+;如果项目使用旧版本,需标注受版本限制的设置。

Step 2: Analyze Workspace Structure

步骤2:分析工作区结构

yaml
undefined
yaml
undefined

Good: Explicit patterns

推荐:明确的匹配模式

packages:
  • 'packages/*'
  • 'apps/*'
  • 'tools/*'
packages:
  • 'packages/*'
  • 'apps/*'
  • 'tools/*'

Avoid: Too broad

不推荐:范围过宽

packages:
  • '**'

**Config-only workspace:** A `pnpm-workspace.yaml` without a `packages:` field is valid for single-package projects that still want catalog, overrides, or build hook config.
packages:
  • '**'

**仅配置型工作区:** 不含`packages:`字段的`pnpm-workspace.yaml`对仍需使用目录、覆盖或构建钩子配置的单包项目有效。

Step 3: Check Workspace Protocol Usage

步骤3:检查workspace协议使用

Good:
json
{
  "dependencies": {
    "@myorg/shared": "workspace:*",
    "@myorg/utils": "workspace:^"
  }
}
Audit:
  • Flag hardcoded versions (
    "^1.0.0"
    ) for internal packages — should use
    workspace:*
bash
undefined
规范用法:
json
{
  "dependencies": {
    "@myorg/shared": "workspace:*",
    "@myorg/utils": "workspace:^"
  }
}
审计要点:
  • 标记内部包的硬编码版本(如
    "^1.0.0"
    )——应使用
    workspace:*
bash
undefined

Find all package.json files and check for org-scoped internal refs

查找所有package.json文件并检查组织范围内的内部引用

fd -t f 'package.json' packages apps | xargs grep -l '@myorg/'
undefined
fd -t f 'package.json' packages apps | xargs grep -l '@myorg/'
undefined

Step 4: Check Dependency Hoisting

步骤4:检查依赖提升

Pnpm uses isolated
node_modules
by default — no hoisting. Check
.npmrc
for overrides:
ini
hoist=true            # enables hoisting to .pnpm/node_modules
shamefully-hoist=true # makes node_modules flat like npm — last resort
Audit:
shamefully-hoist=true
is a red flag. It bypasses pnpm's isolation model. Should only be present if a specific tool requires it, with a comment explaining why.
bash
pnpm dedupe --check
Pnpm默认使用隔离的
node_modules
——无依赖提升。检查
.npmrc
中的覆盖设置:
ini
hoist=true            # 启用依赖提升至.pnpm/node_modules
shamefully-hoist=true # 将node_modules设置为类似npm的扁平结构——最后手段
审计要点:
shamefully-hoist=true
是危险信号。它会绕过pnpm的隔离模型。仅当特定工具需要时才可设置,并需添加注释说明原因。
bash
pnpm dedupe --check

Step 5: Check Catalog Configuration

步骤5:检查目录配置

Catalog definition

目录定义

yaml
undefined
yaml
undefined

pnpm-workspace.yaml

pnpm-workspace.yaml

catalog: react: ^19.0.0 react-dom: ^19.0.0 typescript: ^5.0.0 vite: ^6.0.0 vitest: ^3.0.0

npm: prefix aliases a name to a different package implementation

vite: npm:@org/custom-vite-fork@^1.0.0

undefined
catalog: react: ^19.0.0 react-dom: ^19.0.0 typescript: ^5.0.0 vite: ^6.0.0 vitest: ^3.0.0

npm: 前缀将名称别名到不同的包实现

vite: npm:@org/custom-vite-fork@^1.0.0

undefined

Usage in packages

包中的用法

json
{
  "dependencies": { "react": "catalog:", "react-dom": "catalog:" },
  "devDependencies": { "vite": "catalog:", "vitest": "catalog:", "typescript": "catalog:" }
}
json
{
  "dependencies": { "react": "catalog:", "react-dom": "catalog:" },
  "devDependencies": { "vite": "catalog:", "vitest": "catalog:", "typescript": "catalog:" }
}

Audit checks

审计检查

  • All shared deps should use
    catalog:
    , not hardcoded versions in individual packages
  • Avoid
    @latest
    in catalog entries — defeats reproducibility and conflicts with
    minimumReleaseAge
  • npm:
    aliases must be intentional and version-pinned (not
    @latest
    )
  • catalogMode: force
    (10.12.1+) — enforces that all packages use
    catalog:
    for any dep in the catalog; flag if shared deps exist but this is not enabled
  • cleanupUnusedCatalogs: true
    (10.15+) — automatically removes stale catalog entries on install; flag if catalog has grown large and this is not set
  • 所有共享依赖应使用
    catalog:
    ,而非在单个包中硬编码版本
  • 避免在目录条目中使用
    @latest
    ——这会破坏可复现性,并与
    minimumReleaseAge
    冲突
  • npm:
    别名必须是有意设置的,且版本固定(不能是
    @latest
  • catalogMode: force
    (10.12.1+)——强制所有包对目录中的任何依赖使用
    catalog:
    ;如果存在共享依赖但未启用该设置,需标记
  • cleanupUnusedCatalogs: true
    (10.15+)——安装时自动删除过时的目录条目;如果目录已变大但未设置该值,需标记

Step 6: Check Build Order

步骤6:检查构建顺序

Verify cross-package dependencies are declared:
json
{
  "name": "@myorg/app",
  "dependencies": {
    "@myorg/ui": "workspace:*",
    "@myorg/utils": "workspace:*"
  }
}
bash
pnpm -r run build        # respects topological order
turbo run build --dry-run  # if Turbo is used
验证跨包依赖已声明:
json
{
  "name": "@myorg/app",
  "dependencies": {
    "@myorg/ui": "workspace:*",
    "@myorg/utils": "workspace:*"
  }
}
bash
pnpm -r run build        # 遵循拓扑顺序
turbo run build --dry-run  # 如果使用Turbo

Step 7: Check Build Hook Configuration

步骤7:检查构建钩子配置

Build hook config belongs in
pnpm-workspace.yaml
, not
package.json
.
构建钩子配置应放在
pnpm-workspace.yaml
中,而非
package.json

pnpm 10.0–10.25

pnpm 10.0–10.25

yaml
undefined
yaml
undefined

Blacklist: skip post-install scripts for these

黑名单:跳过这些包的安装后脚本

ignoredBuiltDependencies:
  • unrs-resolver
  • sharp
ignoredBuiltDependencies:
  • unrs-resolver
  • sharp

Whitelist: only these packages may run post-install scripts

白名单:仅这些包可运行安装后脚本

onlyBuiltDependencies:
  • esbuild

Don't use both together — pick the model that fits the project's security posture.
onlyBuiltDependencies:
  • esbuild

不要同时使用两者——选择符合项目安全策略的模型。

pnpm 10.26+ —
allowBuilds

pnpm 10.26+ —
allowBuilds

Replaces
ignoredBuiltDependencies
and
onlyBuiltDependencies
with a single explicit map:
yaml
allowBuilds:
  esbuild: true
  unrs-resolver: false
  sharp: false
用单个显式映射替代
ignoredBuiltDependencies
onlyBuiltDependencies
yaml
allowBuilds:
  esbuild: true
  unrs-resolver: false
  sharp: false

strictDepBuilds
(10.3+)

strictDepBuilds
(10.3+)

Fails the install if any dependency tries to run a build script that isn't covered by the allow/ignore config:
yaml
strictDepBuilds: true
Audit: If neither
onlyBuiltDependencies
/
allowBuilds
nor
strictDepBuilds
is set, build scripts run unchecked — flag as a supply-chain risk.
如果任何依赖尝试运行未被允许/忽略配置覆盖的构建脚本,安装将失败:
yaml
strictDepBuilds: true
审计要点: 如果未设置
onlyBuiltDependencies
/
allowBuilds
strictDepBuilds
,构建脚本将不受检查——标记为供应链风险。

Step 8: Check Dependency Rules

步骤8:检查依赖规则

All of these belong in
pnpm-workspace.yaml
.
所有这些规则都应放在
pnpm-workspace.yaml
中。

minimumReleaseAge (10.16+) / minimumReleaseAgeExclude (10.17+)

minimumReleaseAge (10.16+) / minimumReleaseAgeExclude (10.17+)

Prevents installing packages published less than N minutes ago:
yaml
minimumReleaseAge: 1440  # 24 hours

minimumReleaseAgeExclude:
  - '@typescript/native-preview'  # bleeding-edge, exempt by design
Audit: missing entirely is a risk signal.
1440
(24h) is a reasonable default.
防止安装发布时间不足N分钟的包:
yaml
minimumReleaseAge: 1440  # 24小时

minimumReleaseAgeExclude:
  - '@typescript/native-preview'  # 前沿版本,特意豁免
审计要点:完全未设置该值是风险信号。
1440
(24小时)是合理的默认值。

trustPolicy (10.21+)

trustPolicy (10.21+)

Enforces publisher trust levels — complements
minimumReleaseAge
with a signature/provenance check:
yaml
trustPolicy: audit       # audit | warn | off

trustPolicyExclude:      # (10.22+) exempt specific packages
  - '@myorg/internal'

trustPolicyIgnoreAfter: 525600  # (10.27+) ignore trust for packages older than 1 year
Audit: flag if
trustPolicy
is absent and the project has
minimumReleaseAge
set — both are supply-chain controls that complement each other.
强制实施发布者信任级别——通过签名/来源检查补充
minimumReleaseAge
yaml
trustPolicy: audit       # audit | warn | off

trustPolicyExclude:      # (10.22+) 豁免特定包
  - '@myorg/internal'

trustPolicyIgnoreAfter: 525600  # (10.27+) 忽略发布超过1年的包的信任检查
审计要点:如果项目已设置
minimumReleaseAge
但未设置
trustPolicy
,需标记——两者都是互补的供应链控制措施。

blockExoticSubdeps (10.26+)

blockExoticSubdeps (10.26+)

Restricts git, file, and URL dependencies to direct dependencies only — prevents transitive exotic sources:
yaml
blockExoticSubdeps: true
Audit: flag if git or file deps appear in the dependency tree and this is not enabled.
限制git、文件和URL依赖仅为直接依赖——防止传递性的非常规来源:
yaml
blockExoticSubdeps: true
审计要点:如果依赖树中出现git或文件依赖但未启用该设置,需标记。

overrides

overrides

Pin or replace transitive dependency versions, including
catalog:
references:
yaml
overrides:
  vite: 'catalog:'    # force transitive consumers to use the catalog version
  vitest: 'catalog:'
  lodash: '^4.17.21' # pin vulnerable transitive dep
Audit: check for outdated pinned versions, or missing overrides where catalog versions are inconsistent across the dep tree.
固定或替换传递性依赖版本,包括
catalog:
引用:
yaml
overrides:
  vite: 'catalog:'    # 强制传递性消费者使用目录版本
  vitest: 'catalog:'
  lodash: '^4.17.21' # 固定有漏洞的传递性依赖
审计要点:检查过时的固定版本,或目录版本在依赖树中不一致时是否缺少覆盖设置。

peerDependencyRules

peerDependencyRules

Suppress spurious peer dep warnings — common with custom toolchain forks:
yaml
peerDependencyRules:
  allowAny:
    - vite
    - vitest
  allowedVersions:
    vite: '*'
    vitest: '*'
Audit: flag
allowAny: ['*']
— that's too broad. Specific package names are fine.
抑制虚假的peer依赖警告——常见于自定义工具链分支:
yaml
peerDependencyRules:
  allowAny:
    - vite
    - vitest
  allowedVersions:
    vite: '*'
    vitest: '*'
审计要点:标记
allowAny: ['*']
——范围过宽。指定具体包名称是可行的。

Step 9: Check .npmrc

步骤9:检查.npmrc

Most
.npmrc
settings commonly copy-pasted into projects are redundant defaults or belong elsewhere:
SettingIssue
auto-install-peers=true
Default in pnpm 9+ — redundant
prefer-frozen-lockfile=true
Default in pnpm 10 — redundant; use
--frozen-lockfile
CLI flag in CI for hard-fail behavior
prefer-workspace-packages=true
Superseded by
workspace:
protocol
strict-peer-dependencies=true
Not default; use only if you want hard failures on peer mismatches — evaluate per-project
Audit: flag
.npmrc
entries that are no-ops or have better homes.
Valid reasons to use
.npmrc
:
ini
undefined
大多数被复制粘贴到项目中的
.npmrc
设置是冗余的默认值,或应放在其他位置:
设置问题
auto-install-peers=true
pnpm 9+中的默认值——冗余
prefer-frozen-lockfile=true
pnpm 10中的默认值——冗余;在CI中使用
--frozen-lockfile
CLI标志实现强制失败行为
prefer-workspace-packages=true
已被
workspace:
协议取代
strict-peer-dependencies=true
非默认值;仅当希望peer依赖不匹配时强制失败才使用——需按项目评估
审计要点:标记无作用或有更合适存放位置的.npmrc条目。
使用.npmrc的合理场景:
ini
undefined

Private registry for scoped packages

组织范围内包的私有仓库

@myorg:registry=https://npm.myorg.com/
@myorg:registry=https://npm.myorg.com/

Windows cross-platform script compatibility

Windows跨平台脚本兼容性

shell-emulator=true
undefined
shell-emulator=true
undefined

Step 10: Check for Common Issues

步骤10:检查常见问题

Inconsistent dep versions (not in catalog)

不一致的依赖版本(未在目录中)

bash
npx syncpack list-mismatches
bash
npx syncpack list-mismatches

Circular dependencies

循环依赖

bash
npx madge --circular packages/*/src
bash
npx madge --circular packages/*/src

Stale catalog entries

过时的目录条目

Set
cleanupUnusedCatalogs: true
in
pnpm-workspace.yaml
, or run:
bash
pnpm install  # removes stale entries if cleanupUnusedCatalogs is enabled
pnpm-workspace.yaml
中设置
cleanupUnusedCatalogs: true
,或运行:
bash
pnpm install  # 如果启用cleanupUnusedCatalogs,会删除过时条目

Step 11: Generate Report

步骤11:生成报告

markdown
undefined
markdown
undefined

Workspace Audit Report

工作区审计报告

Structure

结构

  • pnpm version: 10.x
  • Packages: 5 (3 apps, 2 libs)
  • pnpm版本:10.x
  • 包数量:5个(3个应用,2个库)

Workspace Protocol

Workspace协议

  • workspace:* used for all internal deps
  • 2 packages use hardcoded versions for internal deps
  • 所有内部依赖使用workspace:*
  • 2个包对内部依赖使用硬编码版本

Catalog

目录

  • Shared deps pinned in catalog
  • @latest used in 1 catalog entry — defeats reproducibility
  • catalogMode not set — consider force to enforce catalog usage
  • 共享依赖在目录中固定
  • 1个目录条目使用@latest——破坏可复现性
  • 未设置catalogMode——考虑启用force以强制使用目录

Build Hooks

构建钩子

  • onlyBuiltDependencies configured in pnpm-workspace.yaml
  • Build hook config found in package.json — move to pnpm-workspace.yaml
  • strictDepBuilds not set — unchecked build scripts
  • 在pnpm-workspace.yaml中配置了onlyBuiltDependencies
  • 在package.json中发现构建钩子配置——需移至pnpm-workspace.yaml
  • 未设置strictDepBuilds——构建脚本不受检查

Dependency Rules

依赖规则

  • minimumReleaseAge not set (supply-chain risk)
  • trustPolicy not set (complements minimumReleaseAge)
  • overrides pin transitive deps to catalog versions
  • blockExoticSubdeps not set — exotic transitive sources unchecked
  • 未设置minimumReleaseAge(供应链风险)
  • 未设置trustPolicy(补充minimumReleaseAge)
  • 覆盖设置将传递性依赖固定为目录版本
  • 未设置blockExoticSubdeps——传递性非常规来源不受检查

Configuration

配置

  • .npmrc is minimal — no cargo-culted settings
  • prefer-frozen-lockfile=true in .npmrc — already the default, remove it
  • .npmrc配置简洁——无盲目照搬的设置
  • .npmrc中存在prefer-frozen-lockfile=true——已是默认值,需删除

Recommendations

建议

  1. Set minimumReleaseAge: 1440 in pnpm-workspace.yaml
  2. Set trustPolicy: audit alongside minimumReleaseAge
  3. Move build hook config from package.json to pnpm-workspace.yaml
  4. Enable strictDepBuilds: true
  5. Replace hardcoded internal dep versions with workspace:*

See `references/workspace-template.md` for an optimized pnpm-workspace.yaml template.
  1. 在pnpm-workspace.yaml中设置minimumReleaseAge: 1440
  2. 配合minimumReleaseAge设置trustPolicy: audit
  3. 将构建钩子配置从package.json移至pnpm-workspace.yaml
  4. 启用strictDepBuilds: true
  5. 将内部依赖的硬编码版本替换为workspace:*

查看`references/workspace-template.md`获取优化后的pnpm-workspace.yaml模板。