53

Lerna初探 | ¥ЯႭ1I0

 4 years ago
source link: https://yrq110.me/post/tool/study-on-lerna/?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Lerna初探

2018年12月27日

| tool

lerna是一个用于管理包含多个package结构的代码仓库的工具,优化工作流。新版的vue-clinuxtbabel均使用lerna进行自身的package管理。

package可理解为功能模块或子项目。

本文使用的lerna版本: version 3.8.0

  • 当存在一个含有多个package的monorepo
  • 管理这些package的版本与发布时
  • 管理package共用的代码规范等配置时
  • 管理package共用的依赖时

lerna的主要功能可以分为:版本控制发布,需要与npm(或yarn)和git一同使用。

  • fixed/locked(默认)

    固定模式。该模式为单版本号,在根目录中的lerna.json中设置。当使用lerna publish时,如果自从上次发布后有模块改动,那么将会更新到新发布的版本。

    这也是目前Babel用的模式,当你想要自动整合不同包的版本时使用这个模式。它的特点是任何package的major change均会导致所有包都会进行major version的更新。

  • independent

    lerna init --independent
    

    独立模式。该模式中允许开发者独立管理多个包的版本更新。每次发布时,会得到针对每个包改动(patch, minor, major custom change)的提示。lerna会配合git,检查文件变动,只发布有改动的package。

    独立模式允许开发者更新指定package的版本。将lerna.json中的version键设为independent来启用独立模式。

在项目根目录的lerna.json中设置lerna的相关配置。

{
  "version": "1.0.0",
  "npmClient": "yarn",
  "command": {
    "publish": {
      "ignoreChanges": ["ignored-file", "*.md"]
    }
  },
  "packages": [
    "packages/*"
  ]
}

常用的字段:

key value
version 当前仓库版本,当设为independent时开启独立模式
npmClient 执行命令的client,默认为npm,可以设为yarn
command.publish.ignoreChanges 设置不会包含进lerna change/publish操作的文件路径,使用它来避免一些非重要改动时的版本更新,比如更新README.md中的拼写错误
packages 用于定位package的文件路径

yarn workspace

lerna与yarn的workspace特性很好的融合在了一起,前者负责版本管理与发布,后者负责依赖管理

workspace的特点:在所有workspaces所匹配的项目路径下会执行统一的yarn命令,包含测试、安装依赖或执行脚本。

在lerna中启用workspace:

lerna.json中lerna的设置

{
    ...
    "npmClient": "yarn",
    "useWorkspaces": true,
    ...
}

lerna与yarn workspace有很好的相性,设置useWorkspaces等价于使用bootstrap命令的--use-workspaces选项,详情见bootstrap

根目录下的package.json

{
    ...
    "private": true,
    "workspaces": [
    "packages/*"
    ],
    ...
}

"private": true是必须的,workspaces为工作空间中所包含的项目路径,详见workspace

需要注意的是,若开启了workspace功能,则lerna会将package.jsonworkspaces中所设置的项目路径作为lerna packages的路径,而不会使用lerna.json中的packages值。相关源码:

get packageConfigs() {
    if (this.config.useWorkspaces) {
      const workspaces = this.manifest.get("workspaces");
        ...
      return workspaces.packages || workspaces;
    }
    return this.config.packages || [Project.PACKAGE_GLOB];
  }

也就是说,如果使用workspace时未开启useWorkspaces,则yarnlerna会分别管理对应的项目路径。

vue-cli为例,它的lerna.json配置:

{
  "npmClient": "yarn",
  "useWorkspaces": false,
  "version": "3.2.1",
  "packages": [
    "packages/@vue/babel-preset-app",
    "packages/@vue/cli*"
  ]
}

根目录下的package.json:

{
  "private": true,
  "workspaces": [
    "packages/@vue/*",
    "packages/test/*",
    "packages/vue-cli-version-marker"
  ],
  ...
}

它将useWorkspaces设为了false,那么意味着使用yarn管理的是package.jsonworkspaces所对应的项目路径下的依赖,有@vue下的所有项目,test中的测试文件和vue-cli-version-marker。而leran管理的是lerna.jsonpackages所对应的@vue/babel-preset-app@vue/cli*的版本与发布。

而在nuxt中则是lernayarn workspace均采用了相同的package路径。

依赖管理与npm script

下面所操作的lerna项目默认开启了useWorkspaces

安装lerna与初始化lerna项目

```shell
yarn global add lerna
mkdir monorepo && cd monorepo
lerna init
```

创建package

```shell
cd packages
mkdir module-a && cd module-a
yarn init
```

将package的`name`设置成统一的`@repo/module`的格式,在这里就是`@monorepo/module-a`

依赖的安装与移除

添加所有package中的依赖

lerna add dep-name

会将dep-name包安装到packages所包含的package中。

移除所有package中的依赖

lerna exec -- yarn remove dep-name

移除packages所包含的package中的dep-name包。

给指定package中添加依赖

lerna add dep-name --scope module-a

module-apackage中添加的dep-name包,使用--scope命令限定目标package范围。

移除指定package中的依赖

lerna目前并没有remove这种命令,需要在对应package的package.json中删除对应依赖,然后执行lerna bootstrap即可。

在package中引入相邻依赖

目前的项目结构如下:

monorepo/
    packages/
        module-a/
        module-b/

如果想在module-b中引入module-a,执行如下命令即可

lerna add @monorepo/module-a --scope @monorepo/module-b

执行npm script

执行所有package中的scripts命令

使用lerna的run命令就可以在每个package中执行所包含的对应脚本,前提是需要先在package中写好公共的scripts

比如,若每个package均有testscript

"name": "@monorepo/module-a",
"scripts": {
    "test": "jest"
}

则使用如下命令即可在每个package内执行测试:

lerna run test --stream

执行指定package中的scripts命令

需要使用--scope过滤器来限定作用范围

比如,在project-alpha的package.json中:

{
    "name": "project-alpha",
    "version": "1.0.0",
    "main": "index.js",
    "scripts": {
    "dev": "node index.js"
    }
}

运行它的dev命令需用下面的语句:

lerna exec --scope project-alpha -- yarn run dev

采用统一的规范配置

以husky和prettier为例

yarn add --dev husky prettier lint-staged -W

使用-W选项会将依赖安装到workspace的根目录下。

在根目录下正常设置相关配置文件

// .prettierrc
{
    "singleQuote": true,
    "jsxBracketSameLine": true,
    "bracketSpacing": true,
    "semi": true,
    "arrowParens": "always",
    "printWidth": 120
}
// package.json
{
    ...
    "scripts": {
        ...
        "precommit": "lint-staged",
        ...
    },
    "lint-staged": {
        "packages/**/*.{js,jsx}": [
            "prettier --write",
            "git add"
        ]
    },
    ...
    "devDependencies": {
        "husky": "^1.2.1",
        "lerna": "^3.8.0",
        "lint-staged": "^8.1.0",
        "prettier": "^1.15.3"
    }
}
git commit -m "lint test"

这样,每次在根目录下执行git命令时会对里面的所有package进行lint

共用的devDependencies

多数package中共用的devDependencies类型的库都可以提升到项目根目录中,这样做的好处有:

  1. 所有包使用相同版本的依赖,统一管理
  2. 可使用自动化工具让根目录下的依赖保持更新
  3. 减少依赖的安装时间,一次安装,多处使用
  4. 节省存储空间,安装在根目录的node_module

提交与发布

lerna中版本控制及发布相关的概念与工具:

  • Conventional Commits

    约定式提交。一种源于AngularJS commit rules的提交规则。

  • Conventional Changelog

    用于从git元数据中生成CHANGELOG.md的工具,该工具仅当遵循Conventional Commits规则时起作用。

  • Semantic Release

    lerna中内置的一个工具,用于生成版本号、git标签、Conventional Changelog、发布的提交信息以及修改记录。可以在lerna.json中将conventionalCommits标记设为true开启,该工具仅当遵循Conventional Commits规则时起作用。

  • Commitlint

    一个遵循Conventional Commits的commit信息格式化工具

具体的操作与demo可查看这篇文章

command value options
lerna init 创建一个新的lerna项目或将已存在项目改造为lerna项目 --independent/-i
lerna bootstrap 当使用yarn并开启了workspace时等价于在根目录执行yarn install
lerna import <pathToRepo> 将本地路径<pathToRepo>中的包导入到packages/<directory-name>,并提交操作记录
lerna publish 对更新后的包发布新版本;使用新版本号标记;升级所有npm和git中的库 --npm-tag [tagname], --canary/-c, --skip-git, --force-publish [packages]
lerna change 检查自上次发布以来改动的包
lerna diff [package?] 比较自上次发布以来的所有或指定的包
lerna run [script] 在每个包中执行一个npm script
lerna ls 列出当前lerna项目中的public包

用来过滤命令执行时的范围,详见@lerna/filter-options

filter description
--scope <glob> 仅包含glob所匹配到的package
--ignore <glob> 排除glob匹配到的package
--no-private 排除私有package,默认是包含的

注意,如果package想要使用npm script执行本地的可执行文件需要自己单独设置依赖。并且,在package的package.json中,一般还需设置在runtime需要的依赖和一些公共的scripts

  • 跨项目本地开发
  • TypeScript项目

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK