一文读懂 pnpm Workspace 的 Package Map

7 阅读3分钟

一、最核心的问题先回答

为什么我只在 pnpm-workspace.yaml 里写了 apps/*
pnpm 就能知道 apps/webapps/api 这些包,并把它们用起来?

答案只有一句话:

因为 pnpm 在启动时,
先用 Glob 规则找目录 → 再识别哪些是真正的包 → 在内存里记住它们的 name 和路径


二、什么是 Glob?(一定要先懂这个)

1️⃣ Glob 是什么

Glob 是一种“用模式匹配文件路径”的规则

你每天其实都在用,比如:

ls *.js
ls apps/*

这里的 * 就是 Glob。


2️⃣ Glob 的“全称”是啥?

  • 没有严格官方全称
  • 约定俗成理解为:Global Pattern Matching
  • 起源于 Unix Shell

👉 它不是 pnpm 发明的,是整个操作系统层面的东西。


3️⃣ Glob 能干什么?

Glob 只做一件事

👉 在磁盘上找出“路径长得像”的文件或目录

⚠️ 重要:

  • Glob 不懂什么是包
  • Glob 不看 package.json
  • 它只认路径形状

三、apps/* 到底是什么意思?

packages:
  - apps/*

这句话的真实含义是:

“请在项目根目录下,
找出所有路径形状像 apps/某个名字 的目录。”

比如磁盘上有:

apps/web
apps/api
apps/docs

Glob 匹配结果就是这三个。


四、pnpm 是怎么一步步工作的?(重点)

第一步:pnpm 判断是不是 Workspace

pnpm 启动时先看:

有没有 pnpm-workspace.yaml

  • 有 → Workspace 模式
  • 没有 → 单包模式

⚠️ pnpm 只认这个文件


第二步:Glob 展开(找目录)

pnpm 读取:

packages:
  - apps/*

然后:

  • 使用 Glob 规则
  • 遍历文件系统
  • 找到所有匹配的目录:
apps/web
apps/api
apps/docs

⚠️ 此时 pnpm 还不知道谁是包


第三步:识别“真正的包”

pnpm 接下来会逐个检查:

  • apps/web → 有 package.json
  • apps/api → 有 package.json
  • apps/docs → 没有 ❌

只有package.json 的目录才算 workspace 包。


第四步:建立 Package Map(最关键)

pnpm 会读取每个包的 package.json 里的:

{
  "name": "@my-org/web",
  "version": "1.0.0"
}

然后在内存中建立一张表

@my-org/web  -> apps/web
@my-org/api  -> apps/api
@my-org/ui   -> packages/ui

👉 这一步非常重要:

  • pnpm 认的是 name
  • 不是目录名
  • 不是路径

五、pnpm 是什么时候把包“连起来”的?

❌ 不是扫描时

✅ 是安装依赖时

比如 apps/web/package.json

{
  "dependencies": {
    "@my-org/ui": "^1.0.0"
  }
}

pnpm 在安装时会想:

  1. 我要找 @my-org/ui
  2. Workspace 里有没有同名包?
  3. 有 → 用本地的
  4. 没有 → 去 npm 仓库下载

👉 所谓“连起来”,本质是:

把依赖指向本地 workspace 包,而不是远程包


六、如果没有 pnpm-workspace.yaml 会怎样?

pnpm 会:

  • ❌ 不扫描其他目录
  • ❌ 不建立包映射表
  • ❌ 不知道本地还有同名包

结果就是:

即使你本地有 packages/ui
pnpm 也会去 npm 仓库下载一个同名包。


七、mac 上怎么自己“看到” Glob 在干嘛?

macOS 默认用的是 zsh,它天生支持 Glob。

你可以直接在终端试:

# 看 Glob 匹配了哪些目录
ls apps/*

再试:

# 看哪些是真正的包
ls apps/*/package.json

👉 这两步,和 pnpm 内部做的事情几乎一模一样。


八、最容易踩的坑(小白必看)

❌ 错误写法

packages:
  - apps

只会尝试:

apps/package.json

✅ 正确写法

packages:
  - apps/*

九、终极一句话总结(背下来就够了)

Glob 只是用来找目录的规则。
pnpm 用 Glob 找到目录后,再通过 package.json 判断哪些是包,
并把它们的 name 和路径记在内存里。
真正把包“连起来”的,是后面的依赖解析,而不是 Glob 本身。


pnpm Workspace 全流程图

┌────────────────────────────┐
│ 你运行 pnpm install        │
└─────────────┬──────────────┘
              │
              ▼
┌────────────────────────────┐
│ ① 是否存在 pnpm-workspace   │
│    .yaml ?                 │
└─────────────┬──────────────┘
      有      │        没有
      ▼       │         ▼
┌──────────────────┐   ┌──────────────────┐
│ Workspace 模式    │   │ 单包模式          │
└────────┬─────────┘   │(不扫描别的包)     │
         │             └──────────────────┘
         ▼
┌────────────────────────────┐
│ ② 读取 pnpm-workspace.yaml │
│    packages:               │
│    - apps/*                │
│    - packages/*            │
└─────────────┬──────────────┘
              ▼
┌────────────────────────────┐
│ ③ Glob 展开(找目录)        │
│ apps/* →                   │
│   apps/web                 │
│   apps/api                 │
│   apps/docs                │
└─────────────┬──────────────┘
              ▼
┌────────────────────────────┐
│ ④ 判断是否是包              │
│   有没有 package.json ?     │
│   web  ✅                  │
│   api  ✅                  │
│   docs ❌                  │
└─────────────┬──────────────┘
              ▼
┌────────────────────────────┐
│ ⑤ 读取 package.json        │
│   name / version           │
└─────────────┬──────────────┘
              ▼
┌────────────────────────────┐
│ ⑥ 建立 Package Map(内存)   │
│   @my-org/web → apps/web   │
│   @my-org/api → apps/api   │
└─────────────┬──────────────┘
              ▼
┌────────────────────────────┐
│ ⑦ 依赖解析(install 阶段)   │
│ web 依赖 @my-org/ui ?       │
│ → workspace 里有吗?         │
│ → 有 → 用本地包              │
│ → 没有 → 去 npm 仓库         │
└────────────────────────────┘