import-local执行流程解析
import-local 概述
当本地和全局同时存在两个脚手架命令时,使用 import-local 可以优先加载本地脚手架命令
const importLocal = require("import-local");
if (importLocal(__filename)) {
require("npmlog").info("cli", "正在使用 jinhui-cli 本地版本");
} else {
require(".")(process.argv.slice(2));
}
以上述代码为例:执行 jinhui 命令时实际执行的应该是
node C:\Program Files\nodejs\jinhui-cli\cli.js
所以将调试程序定位到全局下的 cli 文件,进入 import-local 源码
// __filename格式化 normalizedFilename => c:\\nvm\\v14.18.0\\node_modules\\jinhui-cli\\cli.js
const normalizedFilename = filename.startsWith("file://") ? fileURLToPath(filename) : filename;
// 向上依次查找package.json文件 如果目录存在该文件则判定全局主目录 => c:\\nvm\\v14.18.0\\node_modules\\jinhui-cli
const globalDir = pkgDir.sync(path.dirname(normalizedFilename));
// pkgDir内部调用
findUp.sync("package.json", { cwd: path.dirname(normalizedFilename) });
// 全局主目录相对于执行文件的相对路径 => cli.js
const relativePath = path.relative(globalDir, normalizedFilename);
// 获取 package.json 内容
const pkg = require(path.join(globalDir, "package.json"));
// 当前目录是否存在执行文件
const localFile = resolveCwd.silent(path.join(pkg.name, relativePath));
// 本地 node_modules 绝对路径
const localNodeModules = path.join(process.cwd(), "node_modules");
// 执行的文件在当前的node_modules下
// 执行文件与当前目录如果以..开头则不是同一目录下 取反则判定是同一目录
// 下列判定为:执行文件在当前目录下 且 执行文件根目录与当前目录根目录相同
const filenameInLocalNodeModules =
!path.relative(localNodeModules, normalizedFilename).startsWith("..") &&
path.parse(localNodeModules).root === path.parse(normalizedFilename).root;
// 执行文件不在当前node_modules下 且 当前node_modules下存在该执行文件 直接执行当前node_modules下的JS文件
return (
!filenameInLocalNodeModules && localFile && path.relative(localFile, normalizedFilename) !== "" && require(localFile)
);
findUp 逐级向上查找
module.exports.sync = (name, options = {}) => {
// 相对路径转绝对路径
let directory = path.resolve(options.cwd || "");
// 获取目录跟路径
const { root } = path.parse(directory);
// 所有要查找的文件名
const filenames = [].concat(name);
while (true) {
// locatePath 遍历数组中的文件名在指定目录下是否存在 如果存在则将查找到的第一个文件名返回
const file = locatePath.sync(filenames, { cwd: directory });
if (file) {
return path.join(directory, name);
}
if (directory === root) {
return null;
}
// 每次遍历逐级递减目录
directory = path.dirname(directory);
}
};