前言
大家好,我是作曲家种太阳
lerna是一个多包管理工具,可以帮助我们多个package统一管理维护,极大提升了开发效率,常用于脚手架开发,组件库开发. babel也是基于lerna开发的. lerna本身是集成了yargs,其实自身也是可以注册命令,
看完本文,你的收获
-
- 学会调试源码技巧
-
-
搞懂lerna如何执行一条命令的思路,更有利于你搞懂脚手架的开发流程
-
1.使用流程
建议先去官网了解下使用流程与基本命令,使用环节本文不会占太多篇幅
画张图你就明白lerna的基本命令使用了~:
2. 安装lerna
npm install lerna --save
复制代码
3.webstorm 调试源码前准备
(1).打开webstorm的prefences面板,勾选node包,内置库会高亮可调式
(2).取消下面两个勾选,让debug模式可以在node_mudules下面运行
(3).在node_modules下面找到lerna和@lerna 这两个文件包
在node_modules/lerna/cli.js中开启debug模式,并且加入参数
加入参数init
(4).开始测试
给cli.js的12行打上断点,开始测试
4.开始调试lerna init命令
我们刚才配置好了调试参数,也配置好了命令,我们看看在lerna init 这一句命令是怎么执行的 \
(1).先来看看lerna/package.json中的bin
"bin": {
"lerna": "cli.js"
},
复制代码
ps: bin 中 key是lerna ,value是cli.js,这个细节很重要,也是脚手架比较重要的细节,当有了bin以后,安装过后,lerna可以直接执行
(2).首先 在cli.js中
#!/usr/bin/env node
const importLocal = require("import-local")
if (importLocal(__filename)) {
require("npmlog").info("cli", "using local version of lerna");
} else {
console.log("test start")
require(".")(process.argv.slice(2));
}
复制代码
ps:
-
- process.argv.slice(2) 是截取了lerna后的参数,这里截取的具体指是 init
-
- require(“.”) 看起来很奇怪,但其实加载的./index.js文件,并把参数传递过去了
-
-
开头 #!/usr/bin/env node 是执行命令时候告诉系统用node环境运行,这也是脚手架的重要的一环
-
(3).进入到lerna/index.js中
进入到这里你会发现导入了很多来自于@lerna的包,并有一个main函数
function main(argv) {
const context = {
lernaVersion: pkg.version,
};
return cli()
.command(addCmd)
.command(bootstrapCmd)
.command(changedCmd)
.command(cleanCmd)
.command(createCmd)
.command(diffCmd)
.command(execCmd)
.command(importCmd)
.command(infoCmd)
.command(initCmd)
.command(linkCmd)
.command(listCmd)
.command(publishCmd)
.command(runCmd)
.command(versionCmd)
.parse(argv, context);
}
复制代码
ps:
-
- main函数的位置参数argv 就是 cli.js传递过来的命令参数(init)
-
- 返回了一个cli()函数,并注册了很命
-
- 不比深究,我们继续往下看cli() 这个函数是干什么
5.调试@lerna/cli
进入到@lerna/cli/index.js文件
进入后你会恍然大悟,原来cli()函数是一个yargs的实例对象,并配置了一些参数
这里有个有意思的是:
- 1.摘要
const cli = yargs(argv, cwd); .wrap(cli.terminalWidth()) 复制代码
ps:这里是生成一个yargs实例,并且终端设置成满屏
结合4.3步骤,你应该就清楚了,原来cli().command() 是在注册命令
根据不同的命令参数,执行不同的模块,所以我们下一步去@lerna/init模块当中
6.调试 @lerna/init
(1)进入到 @lerna/init/command 当中(是参考@lerna/cli.js的initCmd路径)
- 这里是在cli()函数(yargs实例)中注册命令与handle执行函数
- handle执行函数并传递了参数argv 里调用了@lerna/init/index
(2)进入@lerna/init/index
首先进入眼帘的是:
factory
InitCommand
这两个函数
ps发现:
- 1.factory返回了InitCommand的实例
- 2.InitCommand继承了commder类
- 3.根据以上两点argv参数其实是传递到了commder类中
- 4.commder类做了对argv参数的校验,后续逻辑先不关注
- 5.有一段代码引起了我的好奇
execute() {
let chain = Promise.resolve();
chain = chain.then(() => this.ensurePackageJSON());
chain = chain.then(() => this.ensureLernaConfig());
chain = chain.then(() => this.ensurePackagesDir());
return chain.then(() => {
this.logger.success("", "Initialized Lerna files");
});
}
复制代码
这种链式调用不正是控制异步执行顺序的妙招么,有兴趣的同学可以研究下event loop和promise规范!!
- 6.ensurePackageJSON,ensureLernaConfig,ensurePackagesDir逻辑 分别创建了json文件,config文件,还有一个packages文件夹,我们不比深究逻辑,有意思的是ensurePackageJSON使用了write-json-file这个npm包
课外加餐:介绍 write-json-file 包
这里不是调试lerna源码的逻辑主干,只是觉得这个包很方便,创建一个json文件
安装: npm install write-json-file
使用:
const writeJsonFile = require('write-json-file');
const log = require("npmlog");
//创建json文件,indent为缩进
writeJsonFile('foo.json', {test: 123},{indent:1} );
复制代码
总结
从头到尾我带大家调试lerna的源码,并且一步步的带你走完命令怎么执行的流程,
明白了lerna执行命令的逻辑
相信你对类似于脚手架的业务又有了新的了解,有兴趣的同学可以继续往下深入研究其他包的源码