前言
项目开发的过程中,统一的包管理器能减少很多隐藏的问题,一致的开发环境,可以提升代码可靠性和团队协作效率。除了人的约束,我们还可以在代码层面也做一层限制。
only-allow
pnpm
的作者写了一个库 only-allow github,这个包的功能很简单,强制在项目上使用特定的包管理器
使用方法就更简单了
// package.json
{
"scripts": {
"preinstall": "npx only-allow pnpm"
}
}
preinstall
是 npm的一个生命周期钩子,在 install
之前执行,
问题
上述方法很好,就是存在一个问题,拦不住高版本的npm,具体原因是 npm 的 preinstall
钩子是在依赖安装之后执行的(详情 github)
也就是我们想在安装之前判断是什么包管理器,但是npm有些版本有bug,顺序反过来了
不过也找到了一个比较有意思的 解决办法 freecodecamp,通过 engines
来限制包管理器
源码
which-pm-runs
这个库是 only-allow
的依赖库,用来判断当前的包管理工具和包管理工具的版本
'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('/')
const name = pmSpec.substring(0, separatorPos)
return {
name: name === 'npminstall' ? 'cnpm' : name,
version: pmSpec.substring(separatorPos + 1)
}
}
process.env.npm_config_user_agent
是一个在 Node.js 环境中可用的环境变量,它存储了有关使用的 npm(Node Package Manager)或相关 CLI(命令行界面)工具的信息。具体来说,这个环境变量包含了当前环境中运行 npm 或其它兼容工具时使用的用户代理(User Agent)字符串。
only-allow
#!/usr/bin/env node
const whichPMRuns = require('which-pm-runs')
const availablePMList = ['npm', 'cnpm', 'pnpm', 'yarn', 'bun']
// 美化输出
function box(s) {
const lines = s.trim().split("\n")
const width = lines.reduce((a, b) => Math.max(a, b.length), 0)
const surround = x => '║ \x1b[0m' + x.padEnd(width) + '\x1b[31m ║'
const bar = '═'.repeat(width)
const top = '\x1b[31m╔═══' + bar + '═══╗'
const pad = surround('')
const bottom = '╚═══' + bar + '═══╝\x1b[0m'
return [top, pad, ...lines.map(surround), pad, bottom].join('\n')
}
// 获取输入的参数
const argv = process.argv.slice(2)
if (argv.length === 0) {
console.log(`Please specify the wanted package manager: only-allow <${availablePMList.join('|')}>`)
process.exit(1)
}
// 输入的内容不在指定的包管理器范围内
const wantedPM = argv[0]
if (!availablePMList.includes(wantedPM)) {
const pmStr = `${availablePMList.slice(0, -1).join(', ')} or ${availablePMList[availablePMList.length - 1]}`
console.log(`"${wantedPM}" is not a valid package manager. Available package managers are: ${pmStr}.`)
process.exit(1)
}
const usedPM = whichPMRuns()
const cwd = process.env.INIT_CWD || process.cwd()
const isInstalledAsDependency = cwd.includes('node_modules')
// 判断是不是指定的包管理器
if (usedPM && usedPM.name !== wantedPM && !isInstalledAsDependency) {
switch (wantedPM) {
case 'npm':
console.log(box('Use "npm install" for installation in this project'))
break
case 'cnpm':
console.log(box('Use "cnpm install" for installation in this project'))
break
case 'pnpm':
console.log(box(`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.io/`))
break
case 'yarn':
console.log(box(`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/`))
break
case 'bun':
console.log(box(`Use "bun install" for installation in this project.
If you don't have Bun, go to https://bun.sh/docs/installation and find installation method that suits your environment.`))
break
}
process.exit(1)
}
中断程序执行用的是 process.exit(1)
(详情 node)
结语
如有错误,想法或建议,欢迎留言。