| .. | ||
| README.md | ||
Monorepo 基础设施与工具
本文档旨在提供 monorepo 基础设施的大纲,并解释我们做出相关决策的理由。
任务编排
我们的代码库大量使用了基于 PNPM --filter 语法的并行化。这有助于最大限度地减少开发者等待构建等任务完成的时间。monorepo 中的每个项目(如果适用)将包含以下脚本:
{
"scripts": {
"build": "pnpm --if-present --workspace-concurrency=Infinity --stream --filter=\"$npm_package_name...\" '/^build:project:.*$/'",
"build:project": "pnpm --if-present '/^build:project:.*$/'",
"lint": "pnpm --if-present '/^lint:lang:.*$/'",
"lint:fix": "pnpm --if-present '/^lint:fix:lang:.*$/'",
"watch:build": "pnpm --if-present --workspace-concurrency=Infinity --filter=\"$npm_package_name...\" --parallel '/^watch:build:project:.*$/'",
"watch:build:project": "pnpm --if-present run '/^watch:build:project:.*$/'"
}
}
这些脚本概述了所使用的命名方案,以便于使用正则表达式进行任务并行化。为确保整个 monorepo 的一致性,不应编辑这些脚本。 新脚本应使用上述正则表达式概述的命名方案添加,例如,build:project:bundle 可能是运行像 webpack 这样的工具的脚本。我们还利用了许多 PNPM 选项来确保良好的开发者体验:
--if-present:确保如果脚本未找到,PNPM 不会报错。--workspace-concurrency=Infinity:尽可能多地并行运行任务。--stream:将所有脚本的输出放入单个流中,使输出清晰易读。--filter="$npm_package_name...":此过滤器告诉 PNPM,我们希望针对当前项目及其依赖图中的所有依赖项(先处理依赖项)运行脚本。
为了进一步缩短构建时间,我们使用了两种额外的技术:
- 对于
build脚本,我们提供了包含依赖项构建(build)和不包含依赖项构建(build:project)两种方式。 - 使用基于
wireit的任务输出缓存(详情见下文)。
任务输出缓存
我们的仓库使用 wireit 来提供任务输出缓存。特别是,这允许我们缓存 build 脚本的输出,以避免不必要的运行。
目标是最大限度地减少开发者等待项目构建的时间。
{
"scripts": {
"build": "pnpm --if-present --workspace-concurrency=Infinity --stream --filter=\"$npm_package_name...\" '/^build:project:.*$/'",
"build:project": "pnpm --if-present '/^build:project:.*$/'",
"build:project:bundle": "wireit"
},
"wireit": {
"build:project:bundle": {
"command": "webpack",
"files": [
"... - 包资源作为输入"
],
"output": [
"... - 包资源作为输出"
],
"dependencies": [
"dependencyOutputs"
]
},
"dependencyOutputs": {
"files": [
"... - 依赖资源作为输入",
"... - 由集成到 `pnpm install` 的 monorepo 工具自动更新",
"... - 详情请参阅 `.pnpmfile.cjs` 文件和 https://pnpm.io/pnpmfile"
]
}
}
}
在上面的示例中,build:project:bundle 调用 wireit,后者根据资源状态有条件地执行 webpack。
对 wireit 的简化理解如下:
- 如果输入源发生更改或输出源尚未生成,
wireit将执行webpack命令并缓存输出源 - 如果输入源未更改,
wireit将从其缓存版本创建输出源