基于 lerna 的多包项目建设与npm & unpkg 的发布(一)
系列
因为内容太多,文章分开写的,目录如下:
基于 lerna 的多包项目建设与npm & unpkg 的发布(一)
- 主要介绍 lerna 的基本使用及相关命令实践
基于 lerna 的多包项目建设与npm & unpkg 的发布(二)
- 主要介绍如何基于 lerna 进行 npm 多包发布和版本更新
说明
lerna
是一个用来管理和发布基于 git & npm 的多包项目工具,基于 lerna
能够非常方便的管理多包项目,利于协作与本地开发,及 npm 发布等
从包 link 模式上来说, lerna 的部分能力可以简单理解为 npm link
,因为通过 lerna 可以因为某个 npm package 的本地代码。
从项目场景来说,lerna 非常适合多包依赖(并且包之间也存在互相引用)的场景,基于 lerna 能够统一发布项目中的npm 包,而不需要手动发布和修改版本号的。
高级使用中,可以基于 git 提交自动发包或者其他方面。
lerna 需要全局安装,并提供了强大的 cli 命令:
npm i -g lerna
基于 lerna 初始化项目: lerna init
$ lerna init
lerna notice cli v3.22.1
lerna info Creating package.json
lerna info Creating lerna.json
lerna info Creating packages directory
lerna success Initialized Lerna files
通过 init 初始化的 lerna 项目,默认会初始化三个文件:
packages
:包目录package.json
:项目整体的 packagelerna.json
:lerna 配置
lerna create <pkg-scope>/<pkg-name>
:创建一个包
$ lerna create @plui/builder-runtime
info cli using local version of lerna
lerna notice cli v3.22.1
package name: (@plui/builder-runtime)
version: (0.0.1)
description: plui, builder runtime
keywords: plui
homepage:
license: (ISC)
entry point: (lib/builder-runtime.js)
git repository: (https://github.com/xxxxxxxxxxx.git)
上面的命令 lerna create @plui/builder-runtime
是使用 lerna 在 packages 中创建了一个本地的包,这个包将来可能发布到 npm ,并配其他包引用。
可以发现填写的信息和 npm init 差不多,而 git repo 则是根据当前的 git 自动生成的。
@lerna/create
命令属性
可以关注下 --es-module
,直接安装一个初始化转义的 ES Module,导入导出都是 ES Module 风格,不过要编译这个 ES Module 需要编译支持。
创建一个 ES6 模块
一般可以借助 babel 编译一个简单的 ES6 模块,这个过程用到了 lerna run
命令和 @babel/cli
比如上面截图创建的这个包 package.json 核心内容如下:
其中 scripts 是我后面添加的,主要看下 main,他的入口是 dist
,而不是 src,因此如果没有对这个模块进行编译,则无法找到模块。
{
"name": "@plui/component-input",
"main": "dist/component-input.js",
"module": "dist/component-input.module.js",
"directories": {
"lib": "dist",
"test": "__tests__"
},
"scripts": {
"test": "eco \"Error: run tests from root\" && exit 1",
"build": "babel src -d dist",
"prepublish": "yarn build"
}
}
基于 babel 对模块构建后,会在 dist/index.js
构建出 ES5 代码:
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = componentInput;
function componentInput() {// TODO
}
然后就可以直接引用,同时,这个命令在发布的时候也会将 npm 包进行编译。(这块在后面详细讲)
关于包的创建
一般来说,项目中很少直接使用 lerna create
创建,因为依赖项包括一些其他的模块目录配置比较复杂。
基本都会使用模板引擎(yeoman)或者是自己手写 script 接收入参去做一些事情。
lerna ls
or lerna list
这个命令用于查看 packages 下面有哪些包,示例:
$ lerna ls
info cli using local version of lerna
lerna notice cli v3.22.1
@plui/builder-core
@plui/builder-runtime
@plui/builder-utils
@plui/component-button
@plui/component-input
lerna success found 5 packages
lerna ls
的会对包列表进行合法检查,如果 package 存在问题会进行提示:
- 如
package.json
本身不存在或者是name
不存在,会自动忽略这个包 - 如果存在两个 name 相同的 package,会直接报错,比如下面的报错:
lerna ERR! ENAME Package name "@plui/component-input" used in multiple packages:
为什么 lerna ls
看到 package 但是无法引入使用?
第一次使用 lerna 的时候可能会有一个疑问,通过 lerna ls
命令能够查看到某个 package,但是在代码使用的时候,却提示错误,无法使用。
在 packages/*
创建了一个包,只是声明,但在使用的时候,需要显示的挂载到目录中。
其实和
npm link
比较像,类似一种软链实现
lerna bootstrap
:挂载 package
上面提到,在 packages/*
下面创建的包,无法直接引入,构建工具会报错,需要对 package 进行挂载。
这个命令的功能类似于 link
,它可以将 packages/*
下的包映射到全局的 node_modules
中,能够在 packages/*
之外,或者是之内的任意地方通过 node_modules/
的包名引入本地工程的包,而不需要通过相对路径。
基于 lerna bootstrap 挂载后
挂在完之后,在 node_modules 中即可看到我们的包,从而能够使用这个包
--hoist
: 收敛包依赖管理
通常一个包会有多个依赖,多个包会有相同依赖,如果在每个 package 里面安装,一方面会增加多个 node_modules,安装好几次同一个依赖,也不方便统一管理依赖版本号。
通过 --hoist
能够很好的管理不同的包的全局依赖,如果某些包需要安装依赖,则通过 lerna bootstrap --hoist
去安装即可。
--hoist
还有一个很棒的能力,是将所装依赖的 sh
命令工具安装到各自的 node_modules/.bin
中,这样每个包都可以非常方便的执行 sh
命令或者脚本。
关于 hoist:
实际项目中,我很少使用这个属性,因为它和 yarn workspace
是冲突的,如果你的 npmClient=yarn
,则无法使用 hoist
。
他们的作用差不多,yarn 通过 workspace 也能非常友好的管理不同 package 的依赖,并且都将依赖安装在全局根目录的 node_modules 中。
使用 yarn workspace
而不是 --hoist
关于 yarn workspaces
:
在 lerna.json
中可以配置:
{
"npmClient": "yarn",
"useWorkspaces": true,
"packages": ["packages/*"]
}
声明 npmClient 使用 yarn,并且 workspace 是通过 packages/
目录下各个 package 名称作为每一个 workspace。
查看项目中所有 workspaces
$ yarn workspaces info
yarn workspaces v1.22.10
{
"@plui/builder-core": {
"location": "packages/builder-core",
"workspaceDependencies": [],
"mismatchedWorkspaceDependencies": []
}
}
在某个 workspace 安装依赖
比如下面安装三个 babel 依赖,每个包用来进行构建的时候会使用到
yarn workspace @plui/component-button add -D @babel/cli @babel/core @babel/preset-env
安装完之后,会自动执行 yarn build
如果存在,完成可用产物构建,比如 ES6 ==> ES5
在某个 workspace 执行 npm script
命令格式:yarn workspace <workspace_name> run <scripts>
$ yarn workspace @plui/component-button run build
yarn workspace v1.22.10
yarn run v1.22.10
warning package.json: No license field
$ babel src -d dist
Successfully compiled 1 file with Babel (790ms).
Done in 1.23s.
Done in 1.64s.
yarn workspaces 与 lerna --hoist 对比
从整体使用体验上来说,workspaces 要比 --hoist 使用顺畅和方便很多,毕竟有 yarn 在背书,整体实现及流畅度体验都比较友好。
lerna run <xxx>
:每个 package 运行 scripts 命令
关于 lerna run
命令的各种命令行属性如下:
通过 lerna run xx
可以按照顺序依次执行各个 package 的 scripts 命令,一般在统一构建或者是运行测试脚本的时候非常有用。
示例:下面代码本质上执行的是 babel src --out-dir dist
,这个命令是在 @plui/builder-core
定义的简单使用 babel 编译 ES6 到 ES5 的代码
$ lerna run build
info cli using local version of lerna
lerna notice cli v3.22.1
lerna info Executing command in 1 package: "yarn run build"
lerna info run Ran npm script 'build' in '@plui/builder-core' in 1.4s:
yarn run v1.22.10
$ babel src --out-dir dist
Successfully compiled 1 file with Babel (761ms).
Done in 1.18s.
lerna success run Ran npm script 'build' in 1 package in 1.4s:
lerna success - @plui/builder-core
运行完脚本之后:
lerna exec -- <command>
:每个 package 运行 shell 命令
和 lerna run
不同的是,lerna run
入参是一个定义在每个 package 的 package.json
的 scripts
命令
而 lerna exec
则是在每一个 package 的目录下执行一条 shell 命令,比如:
lerna exec -- echo "x" > readme.md
是给每个 package 创建一个 readme 文件
lerna add <package>[@version] [--dev] [--peer]
:为每个 package 安装依赖
相关入参:https://github.com/lerna/lerna/tree/main/commands/add#readme
上面介绍了通过 lerna exec
和 yarn workspace
对不同的 package 可以进行不同的动作或者统一的动作。
而 lerna add
则专注于给每个 package 安装统一的依赖,可以理解为 lerna add
是 lerna exec
的上层封装。
这里主要说下两个入参:
--dev
--peer
需要注意的是,通过
lerna add
一次只能安装一个包
--dev
:devDenpendencies
安装依赖的时候如果是安装到开发依赖,则可以通过 --dev
控制,比如要给每一个 package 安装 babel 相关的依赖。
执行:lerna add @babel/core --dev
$ lerna add @babel/core --dev
info cli using local version of lerna
lerna notice cli v3.22.1
lerna info Adding @babel/core in 2 packages
lerna info bootstrap root only
lerna clearn
:清理所有 package 的 node_modules
通过 lerna clearn
可以清理每个 package 下面的 node_modules 目录
lerna import
:基于 git commit 引入包到当前项目仓库
https://github.com/lerna/lerna/tree/main/commands/import#readme
# Getting started with Lerna
$ git init lerna-repo && cd lerna-repo
$ npx lerna init
$ npm install
# Adding a commit
$ git add .
$ git commit -m "Initial lerna commit" # Without a commit, import command would fail
# Importing other repository
$ npx lerna import <path-to-external-repository>
使用场景:
有些场景中,大家一开始可能不在一个项目仓库开发,但是后面需要移动到统一的项目仓库(lerna管理的项目目录).
在原来项目模块开发完成后,可以基于 git commit 将 package 引入到当前 lerna 下面的 packages ,成为当前项目目录的一个包,然后统一管理。
示例:
在 test/ 目录下开发一个模块:
在 lerna 项目中引入这个模块:
$ lerna import ../test/
info cli using local version of lerna
lerna notice cli v3.22.1
lerna info About to import 1 commits from ../test/ into packages\test
? Are you sure you want to import these commits onto the current branch? Yes
lerna info f057af0
lerna success import finished
lerna link
:所有互相引用的 packages 建立软链
和 npn link 差不多,实际项目中我使用的其实比较少
文章版权:Postbird-There I am , in the world more exciting!
本文链接:http://www.ptbird.cn/lerna-app-npm.html
转载请注明文章原始出处 !