在 monorepo 中引用依赖时直接指向源码

本文最后更新于:2022年1月13日 下午

场景

在 monorepo 项目中,我们可能有几十个 lib 模块,而 lib 模块如果需要发布到 monorepo 外,则必须打包为 js,并且将 main/module 指向打包后的 js 文件,以便所有人都能使用。

例如下面这样一个简单的 lib 模块

  • lib-a
    • src
    • README.md
    • package.json
    • tsconfig.json

原先不需要发布时 package.json 中可能直接指向源码

这种 monorepo 中 lib 无构建的优化方式在 如何减少 monorepo 中 lib 的初始化时间 提到过

1
2
3
4
5
6
{
"name": "lib-a",
"main": "src/index.ts",
"module": "src/index.ts",
"types": "src/index.ts"
}

而当发布后,则需要修改为

1
2
3
4
5
6
{
"name": "lib-a",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"types": "src/index.ts"
}

这就导致我们至少需要添加一个 setup 脚本,以供第一次拉取项目的人批量执行所有模块的初始化动作。例如 pnpm 的命令可能是 pnpm --filter . run setup

1
2
3
4
5
{
"scripts": {
"setup": "npm run build"
}
}

如果模块只有一两个,那么可能不会花太多时间。但如果模块有几十个(我们的生产项目中,大约有 37 个),就算一个模块的初始化构建仅需要几秒钟,那么累积起来也需要几分钟。目前有许多做法

由于吾辈大部分 web 项目均基于 vite 开发,所以便考虑使用创建一个 vite/rollup 插件来重写 module resolve,将 import 的模块直接重写指向源码而非 dist/index.js,即便这样会增加每个模块开发时的时间,但平均到每个模块中依赖的其他 lib 不超过 10 个,额外增加的时间几乎是微不足道的(主要是在一个 nodejs 进程并且使用 esbuild 编译)。

实现

在自行实现之前,吾辈也检索过现有的插件,例如 @rollup/plugin-alias,但它的配置是静态的,例如吾辈需要配置 @liuli-util/* 全部指向 @liuli-util/*/src/index.ts,这需要为每个模块单独配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { defineConfig } from 'rollup'
import alias from '@rollup/plugin-alias'

export default defineConfig({
plugins: [
alias({
entries: [
{
find: '@liuli-util/async',
replacement: '@liuli-util/async/src/index.ts',
},
{
find: '@liuli-util/array',
replacement: '@liuli-util/array/src/index.ts',
},
// 可能还有更多
],
}),
],
})

而吾辈期望专注于做这件事情,所以便单独开发了一个插件 rollup-plugin-ts-alias

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { Plugin, ResolveIdResult } from 'rollup'
import { pathExists } from 'fs-extra'

export function tsAlias(
includes: (string | RegExp)[],
excludes: (string | RegExp)[] = [],
): Plugin & { enforce: 'pre' | 'post' } {
return {
name: 'rollup-plugin-ts-alias',
enforce: 'pre',
async resolveId(source: string): Promise<ResolveIdResult> {
excludes.push(/\/.*\//)
const predicate = (item: string | RegExp) =>
typeof item === 'string' ? source.startsWith(item) : item.test(source)
if (includes.some(predicate) && !excludes.some(predicate)) {
let res: string
try {
res = require.resolve(source + '/src/index.ts')
} catch (e) {
return null
}
if (!(await pathExists(res))) {
console.warn('path not exists: ', res)
return null
}
console.log('rewrite: ', res)
return res
}
return null
},
}
}

使用

该插件已发布至 npm @liuli-util/rollup-plugin-ts-alias

安装

1
pnpm i -D @liuli-util/rollup-plugin-ts-alias

配置

1
2
3
4
5
6
// vite.config.ts
import { tsAlias } from '@liuli-util/rollup-plugin-ts-alias'

export default defineConfig({
plugins: [tsAlias(['@liuli-util/'])],
})

之后,在 monorepo 直接修改 lib 的源码便能热更新,不需要启动额外的 terminal,也不再需要添加 setup 命令供全部初始化。下面可以看到,依赖的 lib @liuli-util/react-router 已经被指向源码了

1642080421952

问题


在 monorepo 中引用依赖时直接指向源码
https://blog.rxliuli.com/p/cf2eba92f2c74b38a150ee9ed32779a8/
作者
rxliuli
发布于
2022年1月13日
许可协议