写点什么

从 vue3 和 vite 源码中,我学到了一行代码统一规范团队包管理器的神器

作者:若川
  • 2022 年 5 月 27 日
  • 本文字数:3670 字

    阅读完需:约 12 分钟

1. 前言

大家好,我是若川为了能帮助到更多对源码感兴趣、想学会看源码、提升自己前端技术能力的同学。我倾力最近组织了源码共读活动,已进行三个月,大家一起交流学习,共同进步,很多人都表示收获颇丰。


想学源码,极力推荐之前我写的《学习源码整体架构系列》 包含jQueryunderscorelodashvuexsentryaxiosreduxkoavue-devtoolsvuex4koa-composevue 3.2 发布vue-thiscreate-vue玩具vite等 10 余篇源码文章。


本文仓库 only-allow-analysis,求个star^_^


最近组织了源码共读活动,每周大家一起学习 200 行左右的源码。每周一期,已进行到 14 期。于是搜寻各种值得我们学习,且代码行数不多的源码。


阅读本文,你将学到:


1. 如何学习调试源码2. 学会 npm 钩子3. 学会 "preinstall": "npx only-allow pnpm" 一行代码统一规范包管理器4. 学到 only-allow 原理5. 等等
复制代码

2. 场景

我们项目开发时,常需要安装依赖,虽说一般用文档可以说明。但不是比较强制的约束。是人就容易犯错或者疏忽,假如规定是用的npm,而团队里有人某一天不小心使用了其他包管理器安装了的其他依赖,上传了代码,严重时可能导致线上问题。所以我们需要借助工具(代码)来强制约束。


源码共读第12期中,我们学习了尤雨溪推荐神器 ni ,能替代 npm/yarn/pnpm ?简单好用!源码揭秘!根据锁文件自动匹配相应的包管理器,运行相应的命令。


源码共读第3期中,我们学习了Vue 3.2 发布了,那尤雨溪是怎么发布 Vue.js 的?


其中 Vue3 源码用了 npm 的 preinstall 钩子 约束,只能使用 pnpm 安装依赖。我们接着来看其实现。

3. Vue3 源码 && npm 命令钩子

// vue-next/package.json{  "private": true,  "version": "3.2.22",  "scripts": {    "preinstall": "node ./scripts/preinstall.js",  }}
复制代码


依次执行# install 之前执行这个脚本preinstall# 执行 install 脚本install# install 之后执行这个脚本postinstall
复制代码


当然也支持自定义的命令。


更多可以查看官方文档钩子


接着我们来看 preinstall 源码。


// vue-next/scripts/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 执行脚本则报错,退出进程。


关于 process 对象可以查看 阮一峰老师 process 对象


process.argv 属性返回一个数组,由命令行执行脚本时的各个参数组成。它的第一个成员总是 node,第二个成员是脚本文件名,其余成员是脚本文件的参数。


这段代码能解决文章开头场景提出的问题,但是总不能每个项目都复制粘贴这段代码吧。我们是不是可以封装成 npm 包使用。当时我也没想太多,也没有封装 npm 包。直到我翻看 vite 源码发现了 only-allow 这个包。一行代码统一规范包管理器


{  "scripts": {    "preinstall": "npx only-allow pnpm -y"  }}
复制代码


当时看到这段代码时,我就在想:他们咋知道这个的。当时依旧也没想太多。直到有一天,发现 pnpm 文档 Only allow pnpm 文档 上就有这个。好吧,吃了没看文档的亏。那时我打算分析下这个only-allow 包的源码,打开一看惊喜万分,才 36 行,写它,于是写了这篇文章。


按照惯例,看源码前先准备环境。

4. 环境准备

先克隆代码。

4.1 克隆代码

# 推荐克隆我的源码库git clone https://github.com/lxchuan12/only-allow-analysis.gitcd only-allow-analysis/only-allow# npm i -g pnpmpnpm i
# 或者克隆官方仓库git clone https://github.com/pnpm/only-allow.gitcd only-allow# npm i -g pnpmpnpm i
复制代码


开源项目一般先看README.md


Force a specific package manager to be used on a project


强制在项目上使用特定的包管理器


Usage


Add a preinstall script to your project's package.json.


If you want to force yarn, add:


{  "scripts": {    "preinstall": "npx only-allow yarn"  }}
复制代码


同理可得:强制使用 npmpnpm也是类似设置。

4.2 调试源码

我们通过查看 package.json 文件。


// only-allow/package.json{  "bin": "bin.js",}
复制代码


确定主入口文件为 only-allow/bin.js


在最新版的 VSCode 中,auto attach 功能,默认支持智能调试,如果发现不支持,可以通过快捷键 ctrl + shift + p 查看是否启用。


于是我们在 only-allow/package.json 文件中,添加如下命令。


// only-allow/package.json{  "scripts": {    "preinstall": "node bin.js pnpm"  },}
复制代码


可以提前在 only-allow/bin.js 文件打上断点 const usedPM = whichPMRuns()


按快捷键 ctrl + ` 快捷键打开终端。输入如下 yarn add release-it -D 命令,即可调试 only-allow/bin.js



最终调试完会在终端报错提示使用 pnpm install


如下图所示:



更多调试细节可以看我的这篇文章:新手向:前端程序员必学基本技能——调试JS代码


接着我们按调试来看源码主流程。

5. only-allow 源码

// only-allow/bin.js#!/usr/bin/env nodeconst whichPMRuns = require('which-pm-runs')// 输出边框盒子const boxen = require('boxen')
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)}// 第一个参数则是 用户传入的希望使用的包管理器// 比如 npx only-allow pnpm // 这里调试是 node bin.js pnpmconst wantedPM = argv[0]// npm pnpm yarn 都不是,则报错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()// 希望使用的包管理器 不相等,则报错。// - npm 提示使用 npm install// - pnpm 提示使用 pnpm install// - yarn 提示使用 yarn install// 最后退出进程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)}
复制代码


跟着断点,我们可以查看到 which-pm-runs

6. which-pm-runs 当前运行的是哪一个包管理器

最终返回包管理器和版本号。


根据调试可知,process.env.npm_config_user_agent 是类似这样的字符串。


"yarn/1.22.10 npm/? node/v14.16.0 linux x64"


'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) }}
复制代码

6.1 String.prototype.substr 截取字符串

顺带提下。我之前在 vue-next 源码看到的 pull request => chore: remove deprecated String.prototype.substr


String.prototype.substr is deprecated.


也就是说不推荐使用 substr。推荐使用 slice


ecma 规范

7. 总结

我们通过从团队需要规范统一包管理器的实际场景出发,讲了 vue3 源码中 preinstall 钩子 约束只能使用 pnpm 。同时通过查看 vite 源码和 pnpm 文档,了解到 only-allow 这个包。可以做到一行代码统一规范包管理器"preinstall": "npx only-allow pnpm"


也学习了其原理。only-allow 期待的包管理器和运行的包管理器对比。匹配失败,则报错。而 which-pm-runs 通过获取 process.env.npm_config_user_agent 变量获取到当前运行脚本的包管理器和版本号。


我们通过文档和沟通约束,不如用工具(代码)约束。


文章写到这里,让我想起我 2018 年写的文章参加有赞前端技术开放日所感所想


当时演讲的大佬说过一句话。无比赞同。


技术(开源)项目本质上是:理念、套路、规范的工具化。


同时给我们的启发也是要多看官方文档和规范。


建议读者克隆我的仓库动手实践调试源码学习。


最后可以持续关注我 @若川。欢迎与我交流,参与 源码共读 活动,每周大家一起学习 200 行左右的源码,共同进步。

发布于: 刚刚阅读数: 5
用户头像

若川

关注

还未添加个人签名 2018.09.11 加入

https://lxchuan12.gitee.io

评论

发布
暂无评论
从 vue3 和 vite 源码中,我学到了一行代码统一规范团队包管理器的神器_JavaScript_若川_InfoQ写作社区