news 2026/6/10 2:50:28

十条经过实战检验的 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/6/9 20:11:02

自动操作浏览器,又一款 Chrome 神器,开源了!

大家最近在折腾 AI Agent 时&#xff0c;有没有遇到过这种尴尬的情况&#xff1a;让 AI 去某个网站查资料、抓数据&#xff0c;它在后台吭哧吭哧跑了半天&#xff0c;最后告诉你&#xff1a;“对不起&#xff0c;报错了”。大多时候&#xff0c;你会发现这些问题&#xff0c;主…

作者头像 李华
网站建设 2026/6/10 0:35:29

域控十年演进

下面这份内容&#xff0c;不是 ECU 合并史&#xff0c;也不是“几域几控”的产品路线图&#xff0c;而是站在**“域控作为智能汽车/机器人系统中枢”的高度&#xff0c;对未来十年的一次结构性演进判断**。&#x1f9e0;&#x1f697; 域控十年演进&#xff08;2025–2035&…

作者头像 李华
网站建设 2026/6/9 21:23:58

导师推荐8个AI论文工具,继续教育学生轻松搞定论文写作!

导师推荐8个AI论文工具&#xff0c;继续教育学生轻松搞定论文写作&#xff01; AI 工具如何助力论文写作&#xff0c;让学术之路更轻松 在当今快速发展的学术环境中&#xff0c;继续教育学生面临着越来越多的挑战&#xff0c;尤其是在论文写作方面。传统的写作方式不仅耗时耗力…

作者头像 李华
网站建设 2026/6/9 21:02:25

vue3怎么实现网页端的文件夹上传?

武汉码农の大文件上传奇遇记&#xff1a;在长江边写信创代码 各位好&#xff0c;我是小王&#xff0c;武汉光谷某软件公司"防脱发小组"组长。最近接了个政府项目&#xff0c;要求在信创环境下上传4G文件&#xff0c;还必须开源可审查——这就像让我用热干面调料写火…

作者头像 李华
网站建设 2026/6/9 23:35:47

基于BS架构的高校超市自助售卖系统的设计与实现

摘 要 随着医药行业快速发展与居民健康意识提升&#xff0c;传统超市销售模式效率低、管理滞后等问题凸显。同时&#xff0c;政策对商品流通监管趋严&#xff0c;对信息化管理提出更高要求。消费者购买商品习惯逐渐向便捷化、个性化转变&#xff0c;急需借助数字化技术优化购买…

作者头像 李华
网站建设 2026/6/9 1:34:41

我们的系统出现找不到d3dx9_25.dll如何解决? 免费下载方法分享

在使用电脑系统时经常会出现丢失找不到某些文件的情况&#xff0c;由于很多常用软件都是采用 Microsoft Visual Studio 编写的&#xff0c;所以这类软件的运行需要依赖微软Visual C运行库&#xff0c;比如像 QQ、迅雷、Adobe 软件等等&#xff0c;如果没有安装VC运行库或者安装…

作者头像 李华