前言
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 的使用者,就无法达到预期效果了。
暂时没找到解决方式,有时间还会去看下解决的方式,如果各位大佬有好的方式也可以留言。
总结
- 理解了npm命令钩子触发的时机
- 理解process的使用
- 可以通过npm钩子规范特定的包管理器
结尾
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎点赞收藏关注三连,对作者也是一种鼓励。