为什么需要统一包管理器
目前前端并没有统一的包管理器,整个管理器圈子是比较混乱的,对于npm,yarn,pnpm等都有各自维护依赖的方式,如果一个大型项目同时使用多种pacakge manager将很难维护,且容易导致线上问题
为了解决这个问题需要限制一个项目中的包管理器种类,但是npm并没有这样的能力,因此目前大部分有需求的项目使用only-allow完成限制功能
only-alow简单使用
贴一下官方文档里的使用方式
{
"scripts": {
"preinstall": "npx only-allow pnpm"
}
}
only-allow通过命令行入参获取package manager白名单,在preinstall阶段完成package manager的校验,如果校验不通过无法进入install阶段
源码分析
only-allow如何限制包管理呢,下面来看一下源码实现
获取包管理器白名单
const argv = process.argv.slice(2)
if (argv.length === 0) {
console.log('Please specify the wanted package manager: only-allow <npm|cnpm|pnpm|yarn>')
process.exit(1)
}
const wantedPM = argv[0]
在执行npx only-allow pnpm时解析入参即可获取到包管理器名称(wantedPM),如果入参为空会报错
入参合法性检验
if (wantedPM !== 'npm' && wantedPM !== 'cnpm' && wantedPM !== 'pnpm' && wantedPM !== 'yarn') {
console.log(`"${wantedPM}" is not a valid package manager. Available package managers are: npm, cnpm, pnpm, or yarn.`)
process.exit(1)
}
only-allow会对入参进行检验,如果不是npm/pnpm/yarn/cnpm就会报错
这一步校验还是很合理的,如果用户填了一个不存在的pakcage manager,这个项目就没有人可以安装依赖了(考虑很全面,用户体验良好)。
获取当前运行的包管理器
通过which-pm-runs获取用户当前使用的npm包,这个包的实现也比较简单(这个包每周居然有200w+的下载量)
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('/')
const name = pmSpec.substring(0, separatorPos)
return {
name: name === 'npminstall' ? 'cnpm' : name,
version: pmSpec.substring(separatorPos + 1)
}
}
通过process.env.npm_config_user_agent获取当前使用的包管理器,这列对cnpm有额外的判断,看一下就好了
输出提示信息
if (usedPM && usedPM.name !== wantedPM && !isInstalledAsDependency) {
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
// some code
}
process.exit(1)
}
这里只是简单的判断和输出了,boxen是一个在terminal输出方框的库,感兴趣可以看一下
总结一下
only-allow在preinstall阶段完成对package manager的判断,如果不符合配置则抛出error,提示用户无法进入install阶段