继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

浅谈源码之only-allow

旧巷老友
关注TA
已关注
手记 15
粉丝 87
获赞 596

前言

only-allow是什么

我个人的理解就是强制在项目中使用特定的包管理器,有时候我们项目开发时,需要安装依赖,虽说一般用文档可以说明。但不是比较强制的约束。是人就容易犯错或者疏忽,假如规定是用的npm,而团队里有人某一天不小心使用了其他包管理器安装了的其他依赖,上传了代码,严重时可能导致线上问题。所以我们需要借助工具(代码)来强制约束。了解了only-allow可以用来之后我们还需要了解这个插件什么时候触发的,这就涉及到npm的钩子函数了,下边咱们就带着问题一点一点的去分析。

Vue3使用的包管理器和如何去强制使用这个包管理器

我们查看vue3中的package.json发现它使用的是pnpm下载的依赖。
pnmp优点如下图(来自官方网站说明pnpm

vue3源码怎么使用去强制使用这个包管理器的呢?其实到Vue3 源码用了 npm 的 preinstall [钩子] 约束,只能使用 pnpm 安装依赖。

自己也亲自尝试并且去运行了

第一次尝试使用yarn安装Vue3的依赖,给出以下错误

第二次使用npm安装Vue3的依赖给出以下错误

根据官网下载pnpm命令

// windows 请根据官网说明下载
curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm

最后运行,项目依赖下载完毕

pnpm i

Vue3中package.json的npm钩子

源码中实现方式如下:

"preinstall": "node ./scripts/preinstall.js",

preinstall这个钩子运行在本地npm install上不带任何参数时,执行后边配置的命令。

postinstall这个钩子是下载结束之后执行配置的命令

生命周期示范

// 感兴趣的话可以去查看npm的钩子
{
  "scripts": {
    "precompress": "{{ executes BEFORE the `compress` script }}", // 执行某个命令之前
    "compress": "{{ run command to compress files }}", // 执行的命令
    "postcompress": "{{ executes AFTER `compress` script }}" // 运行某个命令之后
  }
}

查看preinstall.js源码

// 如果获取到npm命令不是pnpm安装或者为空字符串给出提示并退出执行,process是Node的一个全局对象
// 感兴趣的话可以去了解一下
if (!/pnpm/.test(process.env.npm_execpath || '')) { 
  console.warn(
    `\u001b[33mThis repository requires using pnpm as the package manager ` +
      ` for scripts to work properly.\u001b[39m\n`
  )
  process.exit(1)
}

现在我们就可以使用preinstall解决依赖包不同的安装方案问题,统一依赖包的安装,但是每个项目使用上边代码粘贴又不太方便,是否可以把上边的代码发布成一个依赖包,发布完成之后就可以直接引入,有更多的时间去划水摸鱼,之后就有了only-allow这个包,强制使用依赖包使用yarn | npm | pnpm安装,自己可以修改配置,vite里使用的也是这个包。

// vite使用pnpm
"preinstall": "npx only-allow pnpm"
// npm🌰
"preinstall": "npx only-allow npm"
// yarn🌰
"preinstall": "npx only-allow yarn"

话不多说咱们就看下这个包怎么实现的

only-allow解析

克隆代码

git clone https://github.com/pnpm/only-allow.git  
cd only-allow

查看官方md

大概知道了使用方法和用来干什么,强制在项目中使用特定的包管理器,首先在项目的package.json添加一个预安装脚本,输入命令,

// 使用npm安装
{
  "scripts": {
    "preinstall": "npx only-allow npm"
  }
}

开始学习源码

查看package.json文件发现主文件在bin.js中, 我们先大概看下bin.js干了什么,然后使用vs code断点调试源码(如何使用自行百度),下载only-allow需要的依赖包

npm install

添加script命令开始调试

"preinstall": "node bin.js npm"

添加完成之后在bin.js文件打个断点,并且vs code支持断点调试,输入以下命令

yarn add axios -D

可以进入断点并查看源代码

通过读源码可以发现,主要是通过process.argv来判断参数获取到想要使用的依赖包的安装方式,然后通过which-pm-runs这个依赖包货获取到使用什么方式下载的依赖包,然后通过wantedPM和usedPM的对比是否值一样,如果不一样输出一些错误信息并且通过process.exit(1)停止命令的执行如图

only-allow源码解析

#!/usr/bin/env node
/**
 * 使用到的依赖包
 * which-pm-runs可以获取到node运行使用的命令,通过npm包上传地址查看代码比较老好久没更新了,自行打印值
 * boxen控制面板输出的值的样式,使用方式console.log(boxen('unicorn', {padding: 1}));
 */
const whichPMRuns = require('which-pm-runs') 
const boxen = require('boxen') 

// 获取到执行的命令,之后携带的参数,
// 例如:node bin.js npm打印出['/usr/local/bin/node', '/Users/文件地址/only-allow/bin.js', 'npm']
const argv = process.argv.slice(2) 

if (argv.length === 0) { // 进行判断没获取到抛出错误
  console.log('Please specify the wanted package manager: only-allow <npm|pnpm|yarn>')
  process.exit(1) // 停止执行命令,可以给出code的值
}

// 取出想要执行命令的值类例如node bin.js npm中的npm
const wantedPM = argv[0]
if (wantedPM !== 'npm' && wantedPM !== 'pnpm' && wantedPM !== 'yarn') { 
  console.log(`"${wantedPM}" is not a valid package manager. Available package managers are: npm, pnpm, or yarn.`)
  process.exit(1)
}

// 可以获取到执行命令用的那种方式和版本号,例如name:'yarn' version:'1.22.17'
const usedPM = whichPMRuns()

// 对比想要使用的安装方式和正在用的安装方式是否一致,不一致给出警告并停止执行
if (usedPM && usedPM.name !== wantedPM) { 
  const boxenOpts = { borderColor: 'red', borderStyle: 'double', padding: 1 }
  switch (wantedPM) {
    case 'npm':
      console.log(boxen('Use "npm install" for installation in this project', boxenOpts))
      break
    case 'pnpm':
      console.log(boxen(`Use "pnpm install" for installation in this project.

If you don't have pnpm, install it via "npm i -g pnpm".
For more details, go to https://pnpm.js.org/`, boxenOpts))
      break
    case 'yarn':
      console.log(boxen(`Use "yarn" for installation in this project.

If you don't have Yarn, install it via "npm i -g yarn".
For more details, go to https://yarnpkg.com/`, boxenOpts))
      break
  }
  process.exit(1)
}

使用钩子函数的坑

经过验证,还存在一些问题:npm 和 yarn 对待 preinstall 的调用时机不一致。npm 仅会在当前项目执行安装(即 npm install)时会触发该钩子调用,单独安装某个模块(即 npm install )时并不会触发;而 yarn 则在这两种情况下都会触发该钩子命令。这样一来,如果想通过该钩子命令去限制 npm 的使用者,就无法达到预期效果了。

暂时没找到解决方式,有时间还会去看下解决的方式,如果各位大佬有好的方式也可以留言。

总结

  1. 理解了npm命令钩子触发的时机
  2. 理解process的使用
  3. 可以通过npm钩子规范特定的包管理器

结尾

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎点赞收藏关注三连,对作者也是一种鼓励。

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP