news 2026/4/18 5:23:11

十条经过实战检验的 TypeScript monorepo 约定

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
十条经过实战检验的 TypeScript monorepo 约定
点击上方 程序员成长指北,关注公众号 回复1,加入高级Node交流群

十条经过实战检验的 TypeScript monorepo 约定 —— 覆盖命名、TS 配置、project references、构建、发布、测试与边界控制 —— 让代码库能够在时间中稳定扩展。

Monorepo 在最初总是让人感觉非常顺滑 —— 但六个月后就会变得一团糟。秘诀不在于炫技的工具链,而在于一小套朴素但持久的约定。下面这十条约定能够帮助团队持续交付,不再出现“谁又把什么弄坏了?”这种戏码。说实话,未来的你一定会感谢现在的你。


1)按业务域命名,而不是按技术层命名

使用业务语言(auth、billing、search),而不是技术层(utils、helpers)。这会促使更清晰的边界划分,也更容易确定归属。

apps/ web/ worker/packages/ auth/ billing/ search/ ui/

为什么能长期有效:业务域可以经得住重构,而技术层不会。


2)统一使用 workspaces + workspace: 协议

选择一个工具(我偏好 pnpm,因为速度快且更严格),并用workspace:*来明确本地依赖,同时避免版本耦合。

// package.json (root){ "name": "@acme/monorepo", "private": true, "packageManager": "pnpm@9.0.0", "workspaces": ["apps/*", "packages/*"], "scripts": { "build": "pnpm -r build", "test": "pnpm -r test" }}// apps/web/package.json{ "name": "@acme/web", "dependencies": { "@acme/auth": "workspace:*", "@acme/ui": "workspace:*" }}

为什么能长期有效:不会意外发布半成品版本,也不会造成同级包之间的 semver 漂移。


3)使用一个严格的 tsconfig.base.json —— 然后所有子包继承它

把严格规则放在最顶层;只有在确有需求时才下放例外。

// tsconfig.base.json at the repo root{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", "lib": ["ES2022", "DOM"], "strict": true, "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, "skipLibCheck": true, "declaration": true, "declarationMap": true, "verbatimModuleSyntax": true, "isolatedModules": true }}

子包配置:

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

为什么能长期有效:统一的基础规则能够避免风格漂移和微妙的类型退化。


4)使用 TypeScript Project References + build mode

这决定了你的 monorepo 究竟是“任何改变都会触发全量构建”,还是“只构建变动部分”。

// packages/ui/tsconfig.json{ "extends": "../../tsconfig.base.json", "compilerOptions": { "composite": true, "outDir": "dist", "rootDir": "src" }, "references": [{ "path": "../auth" }]}

根目录脚本:

tsc -b packages/* # 以依赖图增量构建全部包tsc -b -w # watch 模式下使用 references

为什么能长期有效: 随着依赖图规模扩大,构建依然保持增量而不是变慢。


5)统一库构建工具:库用 tsup,开发用 tsx

不要同时操控多个 bundler。保持工具链简单直观。

// packages/auth/package.json{ "name": "@acme/auth", "type": "module", "main": "./dist/index.cjs", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", "scripts": { "dev": "tsx watch src/index.ts", "build": "tsup src/index.ts --dts --format esm,cjs --clean" }}

为什么能长期有效:团队可能每年都会想换 bundler,但你不需要 —— tsup 和 tsx 足够快且可预期。


6)使用干净的 exports,不要允许 deep imports

只暴露你希望暴露的内容。应用层不应该通过packages/ui/src/button这种路径导入内部实现。

// packages/ui/package.json{ "name": "@acme/ui", "type": "module", "sideEffects": false, "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.cjs" } }, "files": ["dist"]}

为什么能长期有效:包内部的重命名不会影响整个 monorepo。


7)使用 Changesets 发布;两条命令自动化 release

人类可读的变更说明现在写好;自动化 semver 稍后执行。

// .changeset/config.json{ "changelog": "@changesets/cli/changelog", "commit": false, "linked": [], "access": "public", "baseBranch": "main"}// root package.json{ "scripts": { "changeset": "changeset", "version-packages": "changeset version", "release": "pnpm -r build && changeset publish" }}

为什么能长期有效:改动意图清晰,标记一致,不再有 “到底发布了啥?” 的疑问。


8)用 ESLint 强化边界,而不是靠团队默契

明确规定“谁可以 import 谁”。这样能减少争议。

// .eslintrc.cjs (root)module.exports = { root: true, parser: "@typescript-eslint/parser", plugins: ["@typescript-eslint", "import"], extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], rules: { "import/no-restricted-paths": ["error", { "zones": [ { "target": "./packages/auth", "from": "./packages/ui" }, // ui 不能 import auth { "target": "./packages/billing", "from": "./packages/auth"} // auth 不能 import billing ] }], "import/no-cycle": "error" }}

为什么能长期有效:边界设定存活在机器人和工具里,而不是口口相传的默契。


9)一个测试运行器,多项目共用:Vitest workspace

保持测试快速且一致。测试文件与代码邻近;从根目录一次性运行所有测试。

// vitest.workspace.ts at the rootimport { defineWorkspace } from 'vitest/config' export default defineWorkspace([ { test: { include: ['packages/auth/src/**/*.test.ts'] } }, { test: { include: ['packages/ui/src/**/*.test.tsx'] } }, { test: { include: ['apps/web/src/**/*.test.tsx'] } },])

为什么能长期有效:共用 reporters、快照与覆盖率,不需要为每个包定制配置。


10)集中管理环境变量类型:在 @acme/env 中用 Zod 校验

不要把process.env.FOO散落在代码各处。验证一次,到处复用。

// packages/env/src/index.tsimport { z } from "zod" const schema = z.object({ NODE_ENV: z.enum(["development", "test", "production"]), DATABASE_URL: z.string().url(), PORT: z.coerce.number().int().default(3000)}) export const env = schema.parse(process.env)export type Env = z.infer<typeof schema>

在任意应用中使用:

import { env } from "@acme/env"app.listen(env.PORT)

为什么能长期有效: 环境配置不正确会尽早失败 —— 还带着类型提示,而不是凌晨两点崩在生产上。


一些微小但长期有效的习惯

  • 库中优先使用 named exports,更易重构。

  • 每个包保留 README.md,记录其作用与示例 import。

  • 在 CODEOWNERS 中标注模块负责人,方便分流评审。

  • 添加 prepack 脚本,确保发布前构建正确。


结语

Monorepo 并不会因为某个“大问题”而失败,而是因为无数个小问题不断累积。以上十条约定能减少团队、包和需求增加所带来的摩擦。如果你也有经历战火、价值连城的经验技巧,欢迎分享 —— 我一定会借鉴(当然也会注明出处)。

地址:

https://medium.com/@kaushalsinh73/10-typescript-monorepo-conventions-that-age-well-c1a6841226f5

原文作者: Neurobyte

Node 社群

我组建了一个氛围特别好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你对Node.js学习感兴趣的话(后续有计划也可以),我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。 “分享、点赞、在看” 支持一波👍
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/8 16:27:31

为什么不让程序员直接对接客户,而是通过产品经理?

来源&#xff1a;zhihu.com/question/659588326&#x1f449; 欢迎加入小哈的星球&#xff0c;你将获得: 专属的项目实战&#xff08;多个项目&#xff09; / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论新项目&#xff1a;《Spring AI 项目实战》正在更新中..…

作者头像 李华
网站建设 2026/4/16 7:59:05

Anthropic万字长文:一篇AI Agent评估体系的详细解析!

Datawhale干货 作者&#xff1a;Anthropic团队&#xff0c;来源&#xff1a;PaperAgentAnthropic发布了一篇blog《揭秘AI Agents评估》&#xff0c;细节满满&#xff01;原文略微有点长&#xff0c;整理了一张脑图&#xff0c;要点精髓&#xff1a;良好的评估&#xff08;evalu…

作者头像 李华
网站建设 2026/3/23 13:51:20

苹果终于眨眼了:打电话给谷歌救 Siri(对,真的是谷歌)

在苹果总部的某个角落&#xff0c;Siri 可能又一次礼貌地回了句&#xff1a;“我不太明白你的意思。”然后 Tim Cook 的脸——据说当场红到像煮熟的龙虾。接着&#xff0c;全世界最有钱的科技公司&#xff0c;干了一件很“反苹果”的事&#xff1a;给谷歌打电话求救。是的&…

作者头像 李华
网站建设 2026/4/15 15:19:16

智能驾驶十年演进

下面这份内容&#xff0c;不是厂商路线图&#xff0c;也不是技术名词堆砌&#xff0c;而是站在**“智能驾驶作为长期运行、可规模落地的社会系统”视角&#xff0c;对未来十年的一次结构性演进判断**。&#x1f697; 智能驾驶十年演进&#xff08;2025–2035&#xff09; 一、核…

作者头像 李华
网站建设 2026/4/17 8:33:40

网页如何通过jquery实现文件夹上传?

2023年XX月XX日 外包项目攻坚日志 - 20G级文件传输系统开发实录 &#xff08;关键词&#xff1a;信创环境兼容/海量文件存储/企业级断点续传/简历镀金项目&#xff09; 凌晨3点&#xff1a;需求风暴会议复盘 客户作为省级档案数字化服务商&#xff0c;提出的变态需求&#xff…

作者头像 李华