- 前端
- 后端
- Web框架
【免费下载链接】modern.js
A progressive web framework based on React and Rsbuild.
本文围绕 Modern.js 源码构建(source build)场景下「被引用子项目必须如何配置」这一核心问题展开。你将完整掌握experiments.sourceBuild开启后,子项目tsconfig.json与package.json中每个必配项的含义与取值依据,并能结合 Modern.js 仓库自带的集成测试工程,验证源码引用与热更新(HMR)在 monorepo 中真实生效的全过程。
一、什么是源码构建模式,为什么子项目必须满足特定配置
源码构建模式用于 monorepo 开发场景:它允许当前应用直接引用 monorepo 中其他子项目的源代码(而非构建产物)参与编译,从而省去「先构建 lib、再等 HMR 链路传导」的过程。在 Modern.js 中通过实验性配置开启:
// modern.config.ts export default { experiments: { sourceBuild: true, }, };从源码实现看,该配置在 parseCommonConfig.ts 中被解析:当experiments.sourceBuild为真值时,Modern.js 会动态加载并注册 Rsbuild 的源码构建插件;其类型定义 types.ts 表明,该配置除boolean外还支持传入对象形式的插件选项(如sourceField、resolvePriority)。插件在 builder 的 package.json 中声明为@rsbuild/plugin-source-build依赖,版本为 1.0.7。
官方文档对原理的描述可参考仓库内文档 源码构建模式指南 与 experiments.sourceBuild 配置说明:开启后,Modern.js 构建时会优先读取子项目 package.json 中source字段对应的源码文件进行编译。
正因为「当前项目要直接编译子项目的源码 + 在 IDE 与类型系统中直接解析子项目的类型」,子项目就必须满足一组硬性配置要求。这正是本文核心文档 readme.md 记录的内容,下面逐条展开。
二、核心要求:子项目 tsconfig.json 的四项必配项
readme.md 明确规定了子项目tsconfig.json的四项要求:
compilerOptions中必须配置"rootDir": "./src";- 必须配置
"composite": true; compilerOptions中必须配置"declaration": true;compilerOptions中必须配置"declarationDir": "./dist/types"。
这四项并非孤立的要求,它们共同服务于 TypeScript 的Project Reference机制,各项的作用如下:
rootDir: "./src":划定子项目的源码根边界。它约束了outDir下产物的目录结构(例如src/card/index.tsx只会生成到dist/card/index.js,而不是dist/src/card/index.js),同时让package.json中exports、source等入口声明与实际编译产物路径保持一一对应,避免引用时路径错位。composite: true:启用「复合项目」,这是 TypeScript Project Reference 的硬性前置条件——只有被引用项目声明为 composite,引用方才能在references中指向它。开启后,TypeScript 可以无需构建子项目就识别其类型,并支持增量编译(代价是会产生*.tsbuildinfo临时文件,文档建议将其加入.gitignore)。declaration: true:要求 TypeScript 在编译子项目时同步产出.d.ts类型声明文件。这样消费方(app)的 TS 语言服务可以直接读取声明文件完成类型提示与跳转,而不需要自己重新编译子项目源码。declarationDir: "./dist/types":把声明文件集中输出到固定目录./dist/types,与 JS 产物目录解耦,为package.json的types字段提供稳定的指向目标。
仓库集成测试工程中的三个子项目都严格按此模板落地。以 common/tsconfig.json 为例:
{ "extends": "@modern-js/tsconfig/base", "compilerOptions": { "composite": true, "declaration": true, "declarationDir": "./dist/types", "jsx": "react-jsx", "baseUrl": "./", "paths": { "@/*": ["./src/*"] }, "rootDir": "src", "outDir": "./dist" }, "include": ["src"] }components/tsconfig.json 与 utils/tsconfig.json 结构完全一致,区别仅在于各自的paths别名(如 components 多了@card/*、utils 多了@common/*),且 utils 额外开启了declarationMap: true。所有子项目均继承自共享基础配置 base.json(含strict、moduleResolution: node等),再叠加上述四项必配项。
三、核心要求:package.json 中 types 与 declarationDir 保持一致
readme.md 的另一条要求是:
package.json的"types"字段必须与tsconfig.json的declarationDir保持一致。
其目的是让消费方(app)的 TypeScript 能沿types找到子项目由declaration生成的声明文件,从而在没有源码编译产物的情况下完成类型检查。仓库中 components/package.json 正是这一要求的落地:
{ "name": "@source-code-build/components", "main": "./dist/index.js", "source": "./src/index.tsx", "exports": { ".": { "source": "./src/index.tsx", "default": "./dist/index.js" }, "./card": { "source": "./src/card/index.tsx", "default": "./dist/card/index.js" } }, "types": "./dist/types/index.d.ts", "typesVersions": { "*": { ".": ["./dist/types/index.d.ts"], "card": ["./dist/types/card/index.d.ts"] } } }可以看到:types指向declarationDir(./dist/types)下生成的index.d.ts;typesVersions则按导出子路径(card)逐一映射到对应的声明文件,保证多入口包中每个子路径都能被正确解析类型。
source 字段:源码构建模式的「入口开关」
除types外,子项目生效源码引用的关键在于source字段。utils/package.json 展示了完整形态:
{ "name": "@source-code-build/utils", "main": "./dist/index.js", "types": "./dist/types/index.d.ts", "exports": { ".": { "source": "./src/index.ts", "default": "./dist/index.js" }, "./common": { "source": "./src/common/index.ts", "default": "./dist/common/index.js" } } }注意source同时出现在顶层字段和每个exports子路径中——这与 源码构建模式指南 的说明一致:当子项目使用了exports配置时,必须在每个入口中增加source条件,Rsbuild 的源码构建插件在解析阶段会优先命中source条件,把解析结果指向.ts/.tsx源文件,交由 app 的构建管线统一编译;而default条件则保留为构建产物路径,供未开启源码构建的场景(如发布后的 npm 消费)使用。
另有一个值得注意的对照样本:common/package.json 的types并未指向declarationDir,而是直接指向源码入口"types": "./src/index.ts"(typesVersions同样指向源码)。这说明 readme 中「types 等于 declarationDir」是标准落地形态;当包同时声明了source字段时,也可以让类型直接落在源码上,两种做法在源码构建链路下都能让消费方拿到类型,选型时应按团队规范保持统一。
四、Project Reference:把整条引用链串起来
仅有子项目自身的配置还不够,引用方向也必须显式声明。本测试工程构成了一条两级引用链:app → components → utils(@source-code-build/common为独立的公共类型包)。
- app 的 tsconfig.json 通过
references指向 components:
{ "references": [ { "path": "../components/tsconfig.json" } ] }- components 的 tsconfig.json 又通过
references指向 utils:
{ "references": [ { "path": "../utils" } ] }Project Reference 为这条链路带来三块能力(见 源码构建模式指南):TypeScript 无需构建子项目即可识别其类型;IDE 内代码跳转可直接进入子项目源码文件;Modern.js 还会读取 project reference 配置,自动识别子项目的tsconfig.compilerOptions.paths,使子项目内部的别名(如 utils 中的@common/*,即 utils/src/index.ts 中import { toLowerCase, toUpperCase } from '@common/index')在跨包编译时也能正确解析。
源码侧的实际依赖关系可验证这一链路:App.tsx 从@source-code-build/components导入Card;而 card/index.tsx 又跨包导入@source-code-build/utils的strAdd工具函数,并引入.less样式。整条链路在sourceBuild: true下全程以源码参与编译。
五、集成测试如何验证:源码修改后热更新生效
仓库为该能力提供了完整的端到端测试 index.test.ts,其验证思路恰好覆盖了源码构建模式的核心承诺:
- 启动即生效:测试通过
launchApp启动 app(配置中开启experiments: { sourceBuild: true }),用 puppeteer 访问页面,断言根节点渲染出Card-Comp Title: App与CARD-COMP CONTENT:hello world——即 components 与 utils 的源码(而非 dist 产物)被正确编译并运行。 - 热更新验证:测试直接改写磁盘上 components 的源文件 card/index.tsx(将
Card Comp替换为Card-Comp),sleep 2 秒后重新访问页面,断言新文案出现在渲染结果中,最后在finally中还原文件。这一步证明了「修改子项目源码 → app 自动重新编译并热更新」的链路无需手动执行子项目构建。
测试基座方面,applyBaseConfig.ts 为集成测试统一关闭了 polyfill 与 TS checker 以加快启动速度,源码构建相关行为由上面的断言本身保证。
六、落地检查清单与注意事项
结合核心文档 readme.md 与测试工程的实际配置,monorepo 子项目接入源码构建时的检查清单如下:
| 配置位置 | 必配项 | 取值/指向 | 作用 |
|---|---|---|---|
tsconfig.json | rootDir | "./src" | 划定源码根,保证产物目录结构稳定 |
tsconfig.json | composite | true | Project Reference 的前置条件 |
tsconfig.json | declaration | true | 编译时产出.d.ts声明 |
tsconfig.json | declarationDir | "./dist/types" | 声明文件统一输出目录 |
package.json | types | 与declarationDir保持一致 | 消费方免构建即可解析类型 |
package.json | source/exports.source | 指向源码文件 | 源码构建插件的解析入口 |
引用方tsconfig.json | references | 指向被引用子项目 | 建立 Project Reference 链路 |
另需注意 源码构建模式指南 中的几条限制:
- 当前项目必须能编译子项目用到的全部语法与样式语言(如子项目用了 Less/装饰器,app 侧需具备对应编译能力);
- 当前项目与子项目需使用一致的代码语法特性版本(如装饰器语法版本);
- 遇到兼容性问题时,可临时移除子项目
package.json中的source字段,回退到基于构建产物调试; - 开启
composite: true后会产生*.tsbuildinfo临时文件,建议加入.gitignore; - 子项目数量增多时构建时间可能变长,因为当前项目需要编译更多的代码——这是用构建时间换取开发期 HMR 效率的权衡。
以上要求均可在仓库 tests/integration/source-code-build/ 目录下的 app、components、utils、common 四个真实工程中逐一对照验证,是接入 Modern.js 源码构建模式时最贴近实战的参照实现。
- 前端
- 后端
- Web框架
【免费下载链接】modern.js
A progressive web framework based on React and Rsbuild.
相关推荐
Knip 集成式 Monorepo(Integrated Monorepos)配置实战:单一 package.json 下的多项目分析
Knip 集成式 Monorepo(Integrated Monorepos)配置实战:单一 package.json 下的多项目分析 导读 本文讲解如何在"集
代码质量静态分析CLIProject AIRI 中的 tsdown unbundle 模式实战:如何保留源码目录结构构建 monorepo 库
Project AIRI 中的 tsdown unbundle 模式实战:如何保留源码目录结构构建 monorepo 库 导读 在构建组件库、多语言包这类"由多
AI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染pnpm 官方 TypeScript 配置包 @pnpm/tsconfig 深度解析:从 extends 继承到 monorepo 构建实战
pnpm 官方 TypeScript 配置包 @pnpm/tsconfig 深度解析:从 extends 继承到 monorepo 构建实战 导读 @pnpm/
包管理器开发工具CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考