本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
这篇文章将会学到如何限定使用的包管理器, 以及这个限定包是如何实现的.
由于现在的包管理器为了限定包的版本, 设计使用了lock文件来锁定版本. 不同的包管理器会可能会和导致集成工具不一致. 从而导致版本问题. 所以要限定包管理器.
这个功能用到了npm钩子中的preinstall.
"scripts": {
"preinstall": "npx only-allow pnpm"
},
刚看到代码时我想为什么加上npx, 不用加也可以啊, 但测试发现, 如果第一次安装依赖时是没有only-allow的,会导致出错.
加上 npx 就不会报错了, 会提示先安装这个依赖, 然后这个限制包的功能就起效了.
从only-allow的源码来看, 它的script里是这样配置的:
// only-allow/package.json
{
"scripts": {
"preinstall": "node bin.js pnpm"
},
}
意思很明确, 安装前执行bin.js的代码.下面对bin.js代码段注释解析:
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)
}
获取第二个之后的参数赋给argv数组.如果argv长度为零.说明使用有误.提示并退出.
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)
}
获取配置的目标包管理器,不合格就提示退出.
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)
}
通过whichPMRuns获取到正在使用的包管理器.不合配置就提示退出.下面看下whichPMRuns的实现
//node_modules\which-pm-runs\index.js
'use strict'
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)
}
}
最关键的就是process.env.npm_config_user_agent. 这个变量反映出当前包管理器.
各个用户代理器都具有包含版本信息的用户代理字符串。 npm模块会将这个字符串解析为一个对象,该对象可以可以使用process.env.npm_config_user_agent取得。
这个变量值大致如下:
npm: npm/9.8.0 node/v16.17.1 win32 x64 workspaces/false
yarn: yarn/1.22.19 npm/? node/v16.17.1 win32 x64
pnpm: pnpm/8.6.0 npm/? node/v16.17.1 win32 x64
另外, 还看到了一个不错的命令行工具boxen. 这个工具就是将要强调的文字用各种效果的框框包裹起来.
不推荐使用 substr。推荐使用 slice . substr和substring有可能会废弃!