lerna 的体验
为什么使用 lerna
lerna 是基于git+npm的多package项目管理工具
工程化思想的两个维度:1、大幅减少重复操作 2、提升操作的标准化。以效能为核心。
实现原理
1、通过import-local预先调用本地lerna命令
2、通过yargs生成脚手架,先注册全局属性,在注册命令,最后通过parse方法解析参数
3、lerna命令注册时需要传入builder和handler两个方法,builder方法用户注册命令专属的options,handler用来处理命令的业务逻辑
5、lerna通过配置npm本地依赖的方法来进行本地开发,具体写法是在package.json的依赖中写入:file:your-local-module-path,在lerna publish时会自动将该路径替换
脚手架项目初始化
1、 初始化npm项目
mkdir fe-cli && cd fe-cli
npm init -y
2、 安装lenrna
3、 lerna init 初始化项目
将会在该文件夹下生成 packages文件夹、lerna.json
创建 package
1、lerna create 创建 package
会在packages下创建名为core的npm包
2、lerna add 安装依赖
lerna add react 将会把 react 安装进 packages 下所有的包中
lerna add react packages/core 将会把 react 安装进指定的 packages/包中
https://github.com/lerna/lerna/tree/main/commands/add#readme
3、lerna link 链接依赖
完成内部文件的相互依赖,会为你生成软链
lrwxr-xr-x 1 liuyan staff 14B 7 6 16:09 utils -> ../../../utils
脚手架开发和测试
1、lerna exec 执行 shell 脚本
可以为你批量执行shell脚本(也可以指定scope)
lerna exec – rm -rf node_modules/
2、lerna run 执行 npm 命令
lerna run test 批量执行packages中的test命令
3、lerna clean 清空依赖
清除 packages 下所有的 node_modules
4、lerna bootstrap 重装依赖
将原本 package.json 中记录的依赖重新安装
脚手架的发布上线
1、lerna version
2、lerna changed
查看哪些包有了改动
3、lerna diff
commit之间的diff对比
4、lerna publish
lerna 源码学习
1、入口文件
#!/usr/bin/env node
"use strict";
/* eslint-disable import/no-dynamic-require, global-require */
const importLocal = require("import-local");
if (importLocal(__filename)) {
require("npmlog").info("cli", "using local version of lerna");
} else {
require(".")(process.argv.slice(2));
// require(".") 中的 . 等价于 ./ 又等价与 ./index.js
}
2、进入 require(“.”) 对应的index.js
"use strict";
// 代码的运行是一行一行往下的,会一条一条解析require文件
const cli = require("@lerna/cli");
const addCmd = require("@lerna/add/command");
const publishCmd = require("@lerna/publish/command");
const versionCmd = require("@lerna/version/command");
const pkg = require("./package.json");
module.exports = main;
function main(argv) {
const context = {
lernaVersion: pkg.version,
};
// 可以看出这是使用设计模式:构造者。对一个对象不断调用自身方法,并且在返回自身,不断壮大自己。
return cli()
.command(addCmd)
.command(publishCmd)
.command(versionCmd)
.parse(argv, context);
}
3、若是在packages内部的包相互使用,更轻松的安装方式
//在某个包中使用另一个分包
"dependencies": {
"@lerna/command": "file:../../core/command"
}
但是你想想,file多对应的文件是怎么发布上线的?
看到 publish 命令中的代码
resolveLocalDependencyLinks() {
// resolve relative file: links to their actual version range
const updatesWithLocalLinks = this.updates.filter((node) =>
Array.from(node.localDependencies.values()).some((resolved) => resolved.type === "directory")
);
return pMap(updatesWithLocalLinks, (node) => {
for (const [depName, resolved] of node.localDependencies) {
// regardless of where the version comes from, we can't publish "file:../sibling-pkg" specs
const depVersion = this.updatesVersions.get(depName) || this.packageGraph.get(depName).pkg.version;
// it no longer matters if we mutate the shared Package instance
node.pkg.updateLocalDependency(resolved, depVersion, this.savePrefix);
}
// writing changes to disk handled in serializeChanges()
});
}
Comments