DEV Community

rxliuli
rxliuli

Posted on

Directly point to source code when referencing dependencies in monorepo

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"
}
Enter fullscreen mode Exit fullscreen mode

When published, it needs to be modified to

{
  "name": "lib-a",
  "main": "dist/index.js",
  "module": "dist/index.esm.js",
  "types": "src/index.ts"
}
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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

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
      ],
    }),
  ],
})
Enter fullscreen mode Exit fullscreen mode

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
    },
  }
}
Enter fullscreen mode Exit fullscreen mode

use

The plugin has been published to npm @liuli-util/rollup-plugin-ts-alias

Install

pnpm i -D @liuli-util/rollup-plugin-ts-alias
Enter fullscreen mode Exit fullscreen mode

configure

// vite.config.ts
import { tsAlias } from '@liuli-util/rollup-plugin-ts-alias'

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

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

1642080421952

problem

Top comments (0)