Lerna独立模式下如何优雅的发包

4,844 阅读3分钟

1、Lerna简要介绍

Lerna 是一个用来管理monorepo项目的工具,针对使用gitnpm管理多软件包代码仓库的工作流程进行优化,主要解决了下面的问题:

将大型代码仓库分割成多个独立版本化的 软件包(package)对于代码共享来说非常有用。但是,如果某些更改 跨越了多个代码仓库的话将变得很 麻烦 并且难以跟踪,并且,跨越多个代码仓库的测试将迅速变得非常复杂。

为了解决这些(以及许多其它)问题,某些项目会将代码仓库分割成多个软件包(package),并将每个软件包存放到独立的代码仓库中。但是,例如 Babel、 React、Angular、Ember、Meteor、Jest等项目以及许多其他项目则是在 一个代码仓库中包含了多个软件包(package)并进行开发。

Lerna发包有两种模式,本文主要讲独立模式下如何优雅的发包

2、Lerna发包的两种模式

Lerna 允许使用两种模式来管理项目:固定模式(Fixed)或独立模式(Independent)。

lerna.json是配置文件,其中"packages"字段代表需要发的包,version表示以什么形式发包

固定模式(默认)

固定模式的 Lerna 项目以单一版本来操作,所有的包都是一个版本。版本信息在lerna.json文件的version处。当执行lerna publish时,如果自上次发布以来模块已经更新,那么它将被更新为您正在发布的新版本。通常来说固定模式下"packages"下所需发的包也是固定不变的,如下

// lerna.json
{
  "packages": [
    "packages/study-lerna-package1", // 包study-lerna-package1
    "packages/study-lerna-package2" // study-lerna-package2
  ],
  "version": "1.0.1"
}

独立模式

独立模式允许具体维护每个包的版本,包的版本有每个包的package.json的version字段维护,每次发布时,都会收到一个提示,用以说明每个更改过的包是补丁(patch)、小更改(minor)、大更改(major)还是自定义更改(custom change),通常来说没次发布的包是不固定的,需要手动维护lerna.json的packages字段

lerna.json中的version设置为independent可以运行独立模式。

3、独立模式下如何优雅的发包

解决方案:通过脚本以命令行的形式,交互式选择要发布的包,动态修改lerna.json配置文件

// scripts/publish.js
const globby = require('globby')
const inquirer = require('inquirer')
const fs = require('fs-extra')
const handlebars = require('handlebars')
const execa = require('execa')
const cwd = process.cwd()

const getPackagePath = () => {
  const packagePaths = globby.sync('../packages', {
    cwd: __dirname,
    onlyDirectories: true,
    deep: 1,
  })
  return packagePaths.map((item) => item.replace('../', ''))
}

const choosePackage = async (packages) => {
  const answer = await inquirer.prompt({
    type: 'checkbox',
    name: 'packages',
    message: '选择你要发布的包',
    choices: [...packages],
  })
  return answer
}

const reWriteLerna = (packages) => {
  const jsonContent = fs.readFileSync(`${cwd}/lerna-template.txt`, 'utf-8')
  const jsonResult = handlebars.compile(jsonContent)(packages)
  fs.writeFileSync(`${cwd}/lerna.json`, jsonResult)
}

const publish = async () => {
  const packages = getPackagePath()
  const publishPackages = await choosePackage(packages)
  if(publishPackages.packages.length !== 0){
    reWriteLerna(publishPackages)
    execa.commandSync('lerna publish', {
      stdio: 'inherit',
      cwd,
    })
  } else {
    console.log("没有选择包")
  }
}

publish()
// package.json
  "scripts": {
    "publish:independent": "node scripts/publish.js",
    "publish": "lerna publish"
  },
// lerna-template.txt
{
  "packages": [
    {{#each packages}}
      {{#if @last}}
    "{{this}}"
      {{else}}
    "{{this}}",
      {{/if}}
    {{/each}}
  ],
  "version": "independent"
}

执行npm run publish:independent,选择要发布的包,这里两个都选

image.png 选择每个包要发布的版本 image.png 最终发布成功,看到lerna.json文件修改为如下

// lerna.json
{
  "packages": [
    "packages/study-lerna-package1",
    "packages/study-lerna-package2"
  ],
  "version": "independent"
}

注意:需要将lerna.json的添加到.gitignore文件中,因为当文件修改未提交时,lerna是不允许发包的,必须提交之后才行,这里之所以不提交lerna.json是因为变化的是package字段没必要每次提交维护,造成历史记录混乱

发包成功后可以看到子包的package.json中version字段也相应的改变了,并且git log可以看到lerna已经帮你提交了,不要要手动提交

image.png