作用:当前项目中的 node_modules 中存在一个脚手架命令,和全局的 node 环境中也存在一个脚手架命令的时候,它会优先选用 node_modules 中的本地版本。
通常情况,全局安装一个脚手架后,本地是不需要安装脚手架的。但是当我们本地安装脚手架的时候,意味着我们项目里用到了这个脚手架。当与全局冲突的时候,比如全局和本地都有这个脚手架,但是版本不同,那么我们应该使用本地的脚手架。这就是import-local的作用。
18060-057a5d168f339963.jpg

1、源码

  1. #!/usr/bin/env node
  2. "use strict";
  3. /* eslint-disable import/no-dynamic-require, global-require */
  4. const importLocal = require("import-local");
  5. //__filename 表示当前的文件路径 为true 则用输出使用本地版本的log
  6. //判断本地`node_modules` 中是否存在脚手架
  7. if (importLocal(__filename)) {
  8. require("npmlog").info("cli", "using local version of lerna");
  9. } else {
  10. //这个模块返回了一个 main 方法 并把 process.argv.slice(2) 作为参数执行
  11. require(".")(process.argv.slice(2));
  12. }
  1. 'use strict';
  2. const path = require('path');
  3. const {fileURLToPath} = require('url');
  4. const resolveCwd = require('resolve-cwd');
  5. const pkgDir = require('pkg-dir');
  6. module.exports = filename => {
  7. //得到文件具体路径
  8. const normalizedFilename = filename.startsWith('file://') ? fileURLToPath(filename) : filename;
  9. //获取当前脚手架所在的全局目录,包括package.json目录 返回 path 的目录名
  10. //path.dirname 去掉文件名,返回目录 //globalDir 全局的路径 cwd当前node命令执行时所在的文件夹目录
  11. const globalDir = pkgDir.sync(path.dirname(normalizedFilename));
  12. //将 globalDir 和 filename 进行相对路径比较 ,得到当前文件名
  13. //path.relative 根据当前工作目录返回从 from 到 to 的相对路径,相同返回''
  14. const relativePath = path.relative(globalDir, normalizedFilename);
  15. // 将 globalDir 和 package.json 的路径进行合并,得到package.json的信息
  16. const pkg = require(path.join(globalDir, 'package.json'));
  17. //将 pkg.name 和 relativePath 进行了合并 得到lerna/cli.js
  18. const _r=path.join(pkg.name, relativePath)
  19. // 判断当前本地是否有这个文件,有则返回路径
  20. const localFile = resolveCwd.silent(_r);
  21. //得到本地的node_modules
  22. const localNodeModules = path.join(process.cwd(), 'node_modules');
  23. const _l= path.relative(localNodeModules, normalizedFilename)
  24. // //返回的对象方法会返回一个对象,其属性表示 path 的有效元素,属性包括dir、root、base、name、ext
  25. const _lr=path.parse(localNodeModules).root
  26. const _nr=path.parse(normalizedFilename).root
  27. const filenameInLocalNodeModules = !_l.startsWith('..') &&
  28. _lr === _nr;
  29. const _ln= path.relative(localFile, normalizedFilename)
  30. const flag=!filenameInLocalNodeModules && localFile && _ln !== ''
  31. return flag && require(localFile);
  32. };

本项目中的结果:
image.png
全局安装的结果(全局安装后 会先加载进入全局的import-lacal,在进入core/cli界面,【暂不清楚原因】)
image.png
返回true,执行log运行后执行微任务,打印其他信息
image.png

2、结论

import-local 模块就是一个函数,当条件满足的时候,会先执行函数内部的 require(localFile).
那为什么 using local version of lerna 会在最前面打印出来呢?
脚手架 command 内部的实现是利用了微任务队列,虽然先执行了 require(localFile),但是真正的执行逻辑都放在微任务队列里了,而 using local version of lerna 是宏任务最后的内容,所以会在执行宏任务的时候输出 log,然后再执行微任务队列里脚手架实际的业务逻辑代码。