本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
-
场景: 开发项目时,我们需要安装依赖,包管理器有
npm、cnpm、yarn、pnpm,为了项目规范和统一性,减少错误,最好能有一个方法,确定一个包管理器作为唯一的使用包管理器。 -
vue3中使用了npm的
preinstall钩子和preinstall.js脚本来限制使用的包管理器
preinstall.js。检测如果不是用pnpm则退出程序
但是我们总不能每次都复制这个代码,来限制包管理器的使用,这样感觉太麻烦了。下面就引出only-allow的包,来简化操作
- 使用
only-allow来限制包管理器的使用 先来看它的README:
强制在一个项目中使用同一个包管理器
使用也非常简单,在packagejson中添加下面一行代码即可
//强制使用npm
{
"scripts": {
"preinstall": "npx only-allow npm"
}
}
用其他的包管理器也是类似的
- 调试
首先 clone 若川大大提供的 github.com/lxchuan12/o…,使用pnpm安装完依赖后,查看package.json,看到入口文件为bin.js
开启vscode的调试模式,按住Ctrl + shift + p,在输入框输入debug,下面会出现Debug:Toogle Auto Attch,选择Smart模式
切换到bin.js文件,将断点打到const whichPMRuns = require('which-pm-runs')
在终端上输入yarn add release-it -D开始调试
进入which-pm-runs内
'use strict'
// process.env.npm_config_user_agent为yarn/1.22.10 npm/? node/v16.15.0 win32 x64
module.exports = function () {
if (!process.env.npm_config_user_agent) {
return undefined
}
return pmFromUserAgent(process.env.npm_config_user_agent)
}
function pmFromUserAgent (userAgent) {
const pmSpec = userAgent.split(' ')[0]
const separatorPos = pmSpec.lastIndexOf('/')
// 获取包管理器名及其版本
return {
name: pmSpec.substr(0, separatorPos),
version: pmSpec.substr(separatorPos + 1)
}
}
这个函数的主要作用是获取当前使用的包管理器的名字和版本,供bin.js使用
还有一点是值得注意的在which-pm-runs中使用了String.prototype.substr在mdn上有这样一段介绍,我们可以可以使用slice或者subString代替
跳回bin.js,源码和注释如下:
#!/usr/bin/env node
const whichPMRuns = require("which-pm-runs");
const boxen = require("boxen");
// 通过process.argv获取当前进程命令行参数(第一个参数为node,第二个参数为当前运行的脚本名,其余是脚本的参数)
// argv是当前命令的数组
const argv = process.argv.slice(2);
// 当前命令参数为空时,打印报错信息,并退出
// process.exit()传入大于0的参数表示执行失败,传入0表示执行成功
if (argv.length === 0) {
console.log(
"Please specify the wanted package manager: only-allow <npm|pnpm|yarn>"
);
process.exit(1);
}
// 获取需要的包管理器的name
const wantedPM = argv[0];
// 将取出的name与常见的包管理器进行对比,不是其中的任意一项则打印报错信息,并退出
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);
}
// 通过whichPMRuns获取正在使用的包管理器名及版本号对象,当使用的包管理器名与需要的包管理器名的不一致时,
// 根据需要的包管理器名,打印不同的报错信息,并退出。
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钩子+ only-allow;
-
通过
process.argv,可以获取到当前命令行的参数 -
String.prototype.substr已经几乎被废弃了,可以改用slice和subString. -
process.env.npm_config_user_agent可以获取当前使用的包管理器名、版本。以及node的版本信息、平台信息。
-