源码学习---一行代码统一规范团队包管理器的神器(only-allow)

场景:团队开发项目时,常常需要安装依赖,虽然一般使用文档可以说明。但不是比较强制的约束。(之前遇到一个项目在同事电脑安装依赖能运行,而我电脑安装依赖却跑不起来。后面发现是由于我node和npm的版本引起的安装依赖和运行项目失败了参考解决链接)。假如规定是用的npm,团队中的某个同学使用其他包管理工具进行依赖的安装,且提交了。那么很大可能会直接影响到同时在该分支工作的其他同学,带来不必要的精力损耗。这时候我们可以借助工具(代码)来强制约束。

其中 Vue3 源码用了 npm 的 preinstall 钩子 约束,只能使用 pnpm 安装依赖。我们接着来看其实现:


Vue3 源码 && npm 命令钩子

1
2
3
4
5
6
7
8
// vue-next/package.json
{
"private": true,
"version": "3.2.22",
"scripts": {
"preinstall": "node ./scripts/preinstall.js",
}
}
1
2
3
4
5
6
7
依次执行
# install 之前执行这个脚本
preinstall
# 执行 install 脚本
install
# install 之后执行这个脚本
postinstall

当然也支持自定义的命令。更多可以查看官方文档钩子

1
2
3
4
5
6
7
8
9
// vue-next/scripts/preinstall.js

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)
}

这段代码也相对简单,校验如果不是 pnpm执行脚本则报错,退出进程。

关于 process 对象可以查看 阮一峰老师 process 对象

process.argv 属性返回一个数组,由命令行执行脚本时的各个参数组成。它的第一个成员总是 node,第二个成员是脚本文件名,其余成员是脚本文件的参数。

参考若川文章

环境准备:克隆代码

1
2
3
4
5
# 或者克隆官方仓库
git clone https://github.com/pnpm/only-allow.git
cd only-allow
# npm i -g pnpm (这行命令也要输入)
pnpm i

Tips:开源项目一般先看README.md
readme

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
only-allow
Force a specific package manager to be used on a project
强制在项目中使用特定的包管理器

Usage
Add a preinstall script to your project's package.json.

If you want to force npm, add:

{
"scripts": {
"preinstall": "npx only-allow npm"
}
}
If you want to force pnpm, add:

{
"scripts": {
"preinstall": "npx only-allow pnpm"
}
}
If you want to force yarn, add:

{
"scripts": {
"preinstall": "npx only-allow yarn"
}
}

目录结构:

项目目录结构

调试源码

查看 package.json 文件。

1
2
3
4
// only-allow/package.json
{
"bin": "bin.js",
}

确定主入口文件为 only-allow/bin.js。

在最新版的 VSCode 中,auto attach 功能,默认支持智能调试,如果发现不支持,可以通过快捷键 ctrl + shift + p 查看是否启用。

于是我们在 only-allow/package.json 文件中,添加如下命令。

1
2
3
4
5
6
// only-allow/package.json
{
"scripts": {
"preinstall": "node bin.js pnpm"
},
}

可以提前在 only-allow/bin.js 文件打上断点 const usedPM = whichPMRuns()

打断点

然后:
输入如下 yarn add release-it -D (或者 npm i) 命令,即可调试 only-allow/bin.js。

安装yarn可直接进入此电脑cmd输入安装命令:(npm install -g yarn)

查看版本:yarn –version
输入命令

only-allow 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// only-allow/bin.js
#!/usr/bin/env node
const whichPMRuns = require('which-pm-runs')
const boxen = require('boxen')

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)
}
// 第一个参数则是 用户传入的希望使用的包管理器
// 比如 npx only-allow pnpm
// 这里调试是 node bin.js pnpm
const wantedPM = argv[0]
// npm pnpm yarn 都不是,则报错
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)
}
// 使用的包管理器
const usedPM = whichPMRuns()
// 希望使用的包管理器 不相等,则报错。
// - npm 提示使用 npm install
// - pnpm 提示使用 pnpm install
// - yarn 提示使用 yarn install
// 最后退出进程
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 的脚本钩子和 process 的简单应用。only-allow 通过获取系统环境运行时的信息,和预设好的参数信息进行对比,来限制预期外包管理器的使用。

参考文章

从 vue3 和 vite 源码中,我学到了一行代码统一规范团队包管理器的神器