☰
Modern.js 源码构建模式:monorepo 子项目 tsconfig 与 package.json 的必配项及实战
2026/10/13 9:27:22 网站建设 项目流程
  • 前端
  • 后端
  • Web框架

【免费下载链接】modern.js

A progressive web framework based on React and Rsbuild.

项目地址:https://gitcode.com/gh_mirrors/mo/modern.js
点击查看免费下载

本文围绕 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的四项要求:

  1. compilerOptions中必须配置"rootDir": "./src";
  2. 必须配置"composite": true;
  3. compilerOptions中必须配置"declaration": true;
  4. 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,其验证思路恰好覆盖了源码构建模式的核心承诺:

  1. 启动即生效:测试通过launchApp启动 app(配置中开启experiments: { sourceBuild: true }),用 puppeteer 访问页面,断言根节点渲染出Card-Comp Title: App与CARD-COMP CONTENT:hello world——即 components 与 utils 的源码(而非 dist 产物)被正确编译并运行。
  2. 热更新验证:测试直接改写磁盘上 components 的源文件 card/index.tsx(将Card Comp替换为Card-Comp),sleep 2 秒后重新访问页面,断言新文案出现在渲染结果中,最后在finally中还原文件。这一步证明了「修改子项目源码 → app 自动重新编译并热更新」的链路无需手动执行子项目构建。

测试基座方面,applyBaseConfig.ts 为集成测试统一关闭了 polyfill 与 TS checker 以加快启动速度,源码构建相关行为由上面的断言本身保证。

六、落地检查清单与注意事项

结合核心文档 readme.md 与测试工程的实际配置,monorepo 子项目接入源码构建时的检查清单如下:

配置位置必配项取值/指向作用
tsconfig.jsonrootDir"./src"划定源码根,保证产物目录结构稳定
tsconfig.jsoncompositetrueProject Reference 的前置条件
tsconfig.jsondeclarationtrue编译时产出.d.ts声明
tsconfig.jsondeclarationDir"./dist/types"声明文件统一输出目录
package.jsontypes与declarationDir保持一致消费方免构建即可解析类型
package.jsonsource/exports.source指向源码文件源码构建插件的解析入口
引用方tsconfig.jsonreferences指向被引用子项目建立 Project Reference 链路

另需注意 源码构建模式指南 中的几条限制:

  1. 当前项目必须能编译子项目用到的全部语法与样式语言(如子项目用了 Less/装饰器,app 侧需具备对应编译能力);
  2. 当前项目与子项目需使用一致的代码语法特性版本(如装饰器语法版本);
  3. 遇到兼容性问题时,可临时移除子项目package.json中的source字段,回退到基于构建产物调试;
  4. 开启composite: true后会产生*.tsbuildinfo临时文件,建议加入.gitignore;
  5. 子项目数量增多时构建时间可能变长,因为当前项目需要编译更多的代码——这是用构建时间换取开发期 HMR 效率的权衡。

以上要求均可在仓库 tests/integration/source-code-build/ 目录下的 app、components、utils、common 四个真实工程中逐一对照验证,是接入 Modern.js 源码构建模式时最贴近实战的参照实现。

  • 前端
  • 后端
  • Web框架

【免费下载链接】modern.js

A progressive web framework based on React and Rsbuild.

项目地址:https://gitcode.com/gh_mirrors/mo/modern.js
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询