Building a monorepo project based on pnpm

Get into the habit of writing together! This is the first day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event .

Influenced by some classmates ( @dongdong acridine ), I feel that it is necessary to record some of the things I have done, whether it is a good experience to step on the pit, maybe some people seem to be running accounts, but there will always be some people who find it a little useful, or You can give me some advice, I think it will be helpful.
I'm @Laffery , a front-end novice just getting started. If you think this article is helpful to you, please give it a thumbs up and swipe away.

background

Back to the topic, when I was developing my own project recently, as the scale of the project got bigger and bigger, and I wanted to develop an additional library, the original project structure was no longer applicable.

For example, the original project structure is like this

# app
├── lib
│   ├── pkg1
│   │   └── package.json
│   ├── pkg2
│   │   └── package.json
│   └── pkg3
│       └── package.json
└── package.json
复制代码

The main pain point I'm experiencing right now is this

  • Why do I have these packages in my project?

    • These packages are general (for myself) packages that I gradually settled down during the development process, and the main body of the project apphas dependencies on these packages

    • I want to develop them together in this project, because there is a lot of code that can be reused

  • Imagine that pkg1, pkg2, pkg3and the dependencies of our main project appoverlap. I believe that the friends who have been tortured by node_modules know that this means

    • slow download
    • Project size is too large
  • Each pkg has a variety of npm scripts, of course, the main ones are dev, build, test, and lintthe like. If you have such a workflow - after each PR, all packages are lint or tested in CI before they are allowed to be merged, then you will be troubled by the need to execute scripts in each package directory in turn.

What is a monorepo?

简单来说就是把多个项目放在同一个代码仓库里,区别于multirepo(每个项目对应一个代码仓库)。它的核心优势就是便于代码复用版本管理

这里不多做介绍,如果有不清楚的小伙伴,可以自行搜索。

pnpm

大家都知道,npm是下载nodejs自带的包管理工具,它当然十分优秀,但是有时候下载龟速,为人诟病。

而我近来一直在使用的yarn,很好的解决了一些下载慢的问题,但是这和这篇文章的主题没有关系了。(其实yarn也能做monorepo,但是为什么我用pnpm呢?因为我在上一家公司见到的就是pnpm)

谈谈这篇文章的主角,pnpm

节约磁盘空间并提升安装速度

pnpm有一个概念,叫做workspace,就是将一个仓库分成不同的工作空间,一般是一个package对应一个workspace。

pnpm在执行pnpm install(根目录下)时,做的事情就是将所有workspace的所有的依赖,下载到node_modules/.pnpm目录下,然后再根据各workspace的依赖,在其目录下通过软链接的方式将这些依赖添加进来。

盗用一下官方的图

image.png

相信看到这里大家能意识到,所有依赖只需要下载一次,那么下载不仅快,而且占用的磁盘体积也小。

对原有项目进行monorepo改造

我们重新调整下项目的结构

# app
├── packages
│   ├── pkg1
│   │   ├── package.json
│   │   └── pnpm-lock.yaml
│   ├── pkg2
│   │   ├── package.json
│   │   └── pnpm-lock.yaml
│   ├── pkg3
│   │   ├── package.json
│   │   └── pnpm-lock.yaml
│   └── app
│       ├── package.json
│       └── pnpm-lock.yaml
├── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml
复制代码

是不是感觉清爽很多[旺柴]。

我们在pnpm-workspace.yaml中对workspace进行配置,好让pnpm知道都有哪些workspace。

# ./pnpm-workspace.yaml
packages:
  # root directory
  - "."
  # all packages in subdirs of packages/
  - "packages/**"
  # exclude packages that are inside test/ directories
  - "!**/test/**" # '!' means exclude
复制代码

现在动手执行pnpm install,和前面说的一样,所有设定好的workspace的依赖同时被下载下来了。

执行各workspace的脚本

现在我们回到开头提到的一个场景。

如果你有这样一个workflow--每次PR后在CI里面把所有包都进行lint或test测试之后才允许合并,那么你一定会为需要依次到各个包目录下执行脚本而烦恼。

在根目录下执行app下的脚本

通常你要执行app包下指令,可能会这样做

cd packages/app
npm run xxx
复制代码

或者你是个省事小能手,在./package.json里面这样写

# ./package.json
{
    // ...
    "scripts": {
        "app:xxx": "cd packages/app && npm run xxx"
    }
    // ...
}
复制代码

这样你就只需要在根目录下执行npm run app:dev就可以了。

这种做法未尝不可,但是假如有一天你心血来潮,把这个包的目录名称改成了webapp,就得忍痛把这些指令全部改成cd packages/webapp && npm run xxx

pnpm提供一个--filter-F)指令,你可以设定选择器,让pnpm在特定包下执行指令

我们假设上面提到的包叫这些名字(package.json里的name)

  • pkg1: @laffery/pkg1
  • pkg2: @laffery/pkg2
  • pkg3: @laffery/pkg3
  • app: @laffery/app

现在你要执行app下的dev指令,只需要

pnpm run --filter @laffery/app dev
复制代码

我们一般很少改包的名字,所以这样改目录名字就不会有问题。

当然了,我们还是一个省事小能手,可以把所有常用的指令都配置在根目录下。

# ./package.json
{
    // ...
    "scripts": {
        "app:dev": "pnpm run --filter @laffery/app dev",
        "app:build": "pnpm run --filter @laffery/app build",
        "app:deploy": "pnpm run --filter @laffery/app deploy"
    }
    // ...
}
复制代码

现在我们所有的命令都可以在根目录下执行了。

批量执行命令

这个--filter既然是过滤器,又不是只选择其中一个,当然是支持筛选多个workspace啦。

现在你想对所有的包进行lint或test,可以这样做

# ./package.json
{
    // ...
    "scripts": {
        "lint": "pnpm run --filter=\"@laffery/*\" lint",
        "test": "pnpm run --filter=\"@laffery/*\" test"
    }
    // ...
}
复制代码

然后在你的.github/workflows/ci.yaml下轻松地写下

# .github/workflows/ci.yaml
jobs:
  check:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - name: Install
        run: npm i -g pnpm && pnpm i
      - name: ESlint
        run: pnpm lint
      - name: Tests
        run: pnpm t
复制代码

大功告成!

Of course, there are some other functions, such as unifying the publish npm package, you can check it out on the official website.

Reuse the code under the same repository

Suppose appdepends on [email protected]and [email protected], and the latter both depend on [email protected]. The normal practice is to use the version on npm directly. But if you want to directly use the one that is currently under development [email protected], but it has not been published on npm, it is difficult to do.

Not at all import xxx from "../../pacakge/pkc3/xxx".

At this time, the workspace comes in handy, you can pkg1write dependencies like this

// packages/pkg1/package.json
{
    "dependencies": {
        "@laffery/pkg3": "workspace:1.5.1",
    }
}
复制代码

For a detailed introduction, see the documentation on the official website .

VSCode Workspace

In addition, if you are a VSCode fan like me, you can consider creating a new xxx.code-workspacefile in the root directory

{
  "folders": [
    {
      "path": "packages/pkg1",
      "name": "Package 1"
    },
    // ...
    {
      "path": "packages/app",
      "name": "Web Application"
    },
  ]
}
复制代码

The editor will pop up a 打开工作区button, click it will have a miraculous effect~, for more information, please refer to .

Summarize

This article introduces some experience in building a monorepo project based on pnpm, you can

  • Save disk space and increase installation speed
  • Convenient unified management, execute a series of instructions such as testing, building, and publishing
  • Reuse the code of other packages under the same repository

This article is simultaneously published on my personal blog, if you are interested, you can click here .

I'm @Laffery , a front-end rookie just hitting the road. If you like my article, or find it useful, please like/follow.

Guess you like

Origin juejin.im/post/7084582387060834340