作用:当前项目中的 node_modules 中存在一个脚手架命令,和全局的 node 环境中也存在一个脚手架命令的时候,它会优先选用 node_modules 中的本地版本。
通常情况,全局安装一个脚手架后,本地是不需要安装脚手架的。但是当我们本地安装脚手架的时候,意味着我们项目里用到了这个脚手架。当与全局冲突的时候,比如全局和本地都有这个脚手架,但是版本不同,那么我们应该使用本地的脚手架。这就是import-local的作用。
1、源码
#!/usr/bin/env node
"use strict";
/* eslint-disable import/no-dynamic-require, global-require */
const importLocal = require("import-local");
//__filename 表示当前的文件路径 为true 则用输出使用本地版本的log
//判断本地`node_modules` 中是否存在脚手架
if (importLocal(__filename)) {
require("npmlog").info("cli", "using local version of lerna");
} else {
//这个模块返回了一个 main 方法 并把 process.argv.slice(2) 作为参数执行
require(".")(process.argv.slice(2));
}
'use strict';
const path = require('path');
const {fileURLToPath} = require('url');
const resolveCwd = require('resolve-cwd');
const pkgDir = require('pkg-dir');
module.exports = filename => {
//得到文件具体路径
const normalizedFilename = filename.startsWith('file://') ? fileURLToPath(filename) : filename;
//获取当前脚手架所在的全局目录,包括package.json目录 返回 path 的目录名
//path.dirname 去掉文件名,返回目录 //globalDir 全局的路径 cwd当前node命令执行时所在的文件夹目录
const globalDir = pkgDir.sync(path.dirname(normalizedFilename));
//将 globalDir 和 filename 进行相对路径比较 ,得到当前文件名
//path.relative 根据当前工作目录返回从 from 到 to 的相对路径,相同返回''
const relativePath = path.relative(globalDir, normalizedFilename);
// 将 globalDir 和 package.json 的路径进行合并,得到package.json的信息
const pkg = require(path.join(globalDir, 'package.json'));
//将 pkg.name 和 relativePath 进行了合并 得到lerna/cli.js
const _r=path.join(pkg.name, relativePath)
// 判断当前本地是否有这个文件,有则返回路径
const localFile = resolveCwd.silent(_r);
//得到本地的node_modules
const localNodeModules = path.join(process.cwd(), 'node_modules');
const _l= path.relative(localNodeModules, normalizedFilename)
// //返回的对象方法会返回一个对象,其属性表示 path 的有效元素,属性包括dir、root、base、name、ext
const _lr=path.parse(localNodeModules).root
const _nr=path.parse(normalizedFilename).root
const filenameInLocalNodeModules = !_l.startsWith('..') &&
_lr === _nr;
const _ln= path.relative(localFile, normalizedFilename)
const flag=!filenameInLocalNodeModules && localFile && _ln !== ''
return flag && require(localFile);
};
本项目中的结果:
全局安装的结果(全局安装后 会先加载进入全局的import-lacal,在进入core/cli界面,【暂不清楚原因】)
返回true,执行log运行后执行微任务,打印其他信息
2、结论
import-local 模块就是一个函数,当条件满足的时候,会先执行函数内部的 require(localFile).
那为什么 using local version of lerna 会在最前面打印出来呢?
脚手架 command 内部的实现是利用了微任务队列,虽然先执行了 require(localFile),但是真正的执行逻辑都放在微任务队列里了,而 using local version of lerna 是宏任务最后的内容,所以会在执行宏任务的时候输出 log,然后再执行微任务队列里脚手架实际的业务逻辑代码。