Scenes
In a monorepo project, we may have dozens of lib modules, and if the lib module needs to be published outside the monorepo, it must be packaged as js, and main/module
points to the packaged js file, so that everyone can use it .
For example a simple lib module like the following
- lib-a
- src
- README.md
- package.json
- tsconfig.json
The source code may be directly pointed to in package.json when there is no need to publish it originally
This optimization of lib buildless in monorepo is mentioned in How to reduce the initialization time of lib in monorepo
{
"name": "lib-a",
"main": "src/index.ts",
"module": "src/index.ts",
"types": "src/index.ts"
}
When published, it needs to be modified to
{
"name": "lib-a",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"types": "src/index.ts"
}
This leads us to add at least one setup script for the first time pulling the project to perform the initialization of all modules in batches. For example the command for pnpm might be pnpm --filter .run setup
{
"scripts": {
"setup": "npm run build"
}
}
If there are only one or two modules, it probably won't take much time. But if there are dozens of modules (in our production project, about 37), even if the initial build of a module only takes a few seconds, it will take minutes cumulatively. There are many practices
- All initialized only for the first time, and subsequent updates are rebuilt by the modules used by the user. For example antv series of open source projects
- Use typescript's project-references to point directly to the ts source files. For example open source project Maskbook
- Cache built files to avoid rebuilds. For example nx.js, yarn-plugin-change, ultra-runner
- Speed ​​up the build of each module. I tried to use esbuild to refactor @liuli-util/cli
Since most of our web projects are developed based on vite, we consider creating a vite/rollup plugin to rewrite module resolve, and rewrite the imported module directly to the source code instead of dist/index.js, even if this will increase each The time for module development, but on average no more than 10 other libs depend on each module, the extra time is almost insignificant (mainly in a nodejs process and compiled with esbuild).
accomplish
Before implementing it myself, I also retrieved existing plugins, such as @rollup/plugin-alias, but its configuration is static For example, we need to configure @liuli-util/*
to all point to @liuli-util/*/src/index.ts
, which needs to be configured separately for each module.
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',
},
// maybe more
],
}),
],
})
And I expect to focus on doing this, so I developed a separate plugin rollup-plugin-ts-alias
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
},
}
}
use
The plugin has been published to npm @liuli-util/rollup-plugin-ts-alias
Install
pnpm i -D @liuli-util/rollup-plugin-ts-alias
configure
// vite.config.ts
import { tsAlias } from '@liuli-util/rollup-plugin-ts-alias'
export default defineConfig({
plugins: [tsAlias(['@liuli-util/'])],
})
After that, the source code of lib can be hot-updated by directly modifying the source code of lib in monorepo, no need to start additional terminal, and no need to add setup
command for full initialization. As you can see below, the dependent lib @liuli-util/react-router has been pointed to the source code
problem
- How should a large monorepo solve performance and collaboration issues?
- Or do we not need to use a monorepo?
Top comments (0)