【源码共读】| 一行代码统一规范 包管理器

981 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。

【若川视野 x 源码共读】第16期 | 一行代码统一规范 包管理器 点击了解本期详情一起参与

今天阅读的库是:only-allow

image-20221007105640737

这个库是用来限制项目包管理器,从而实现统一包管理器规范

使用场景

vue的源码中,我们知道vue使用的是pnpm来做包管理器,那么他是怎么统一整个项目都使用这个包管理器呢,我们看下源码

在他的package.json中有一段代码

// package.json
/* ...省略 */
"scripts": {
    /* ...省略 */
    "preinstall": "node ./scripts/preinstall.js"
 }
// 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执行脚本就会抛出异常

那么,有没有一种方法,不用每次创建项目都需要创建一个文件来对包管理器做规范化呢

only-allow的出现解决了这个问题

如果使用pnpm,则可以编写如下代码:

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

源码分析

我们来看下这个库是怎么实现的,从package.json中得知,入口为bin.js

image-20221007140944440

源码中引用了这两个依赖


// 获取当前包管理器
const whichPMRuns = require('which-pm-runs')
// 装饰控制台输出
const boxen = require('boxen')

image-20221007114605404

image-20221007114554328

#!/usr/bin/env node

// 获取当前包管理器
const whichPMRuns = require('which-pm-runs')
// 装饰控制台输出
const boxen = require('boxen')

const argv = process.argv.slice(2)
// 检测用户输入的包管理器名称  比如:npx only-allow pnpm
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]
// 校验是否是 【npm,cnpm,pnpm,yarn】中的一种
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)
}
// 获取使用的包管理器
const usedPM = whichPMRuns()
const cwd = process.env.INIT_CWD || process.cwd()
const isInstalledAsDependency = cwd.includes('node_modules')
// 如果当前使用的包管理器与设定的包管理器不同,则抛出异常
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
    case 'cnpm':
      console.log(boxen('Use "cnpm 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)
}

代码比较精简,就是获取当前node环境中的包管理名称和设定的名称做对比,如果不一致则抛出异常

接下来,我们来看下which-pm-runs的实现 源码地址

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

传入当前环境的包管理信息,做字符串分割,返回一个对象

{name: "pnpm", version: "7.9.5"}

总结

通过vueonly-allow库的研究,使用一些工具和手段来约束团队,从而实现项目包管理器的规范化

而这些使用这些工具,能够帮助我们更加便捷的实现规范化流程,使得项目流程上更加统一和工程化。