尤雨溪推荐神器 ni ,能替代 npm/yarn/pnpm ?简单好用!源码揭秘!

1. 前言

大家好,我是若川。最近组织了源码共读活动,感兴趣的可以加我微信 ruochuan12

想学源码,极力推荐之前我写的《学习源码整体架构系列》jQueryunderscorelodashvuexsentryaxiosreduxkoavue-devtoolsvuex4koa-composevue-next-releasevue-thiscreate-vue等十余篇源码文章。

本文仓库 ni-analysis,求个star^_^[1]

最近组织了源码共读活动

之前写了 Vue3 相关的两篇文章。

  • 初学者也能看懂的 Vue3 源码中那些实用的基础工具函数

  • Vue 3.2 发布了,那尤雨溪是怎么发布 Vue.js 的?

文章里都是写的使用 yarn 。参加源码共读的小伙伴按照我的文章,却拉取的最新仓库代码,发现 yarn install 安装不了依赖,向我反馈报错。于是我去 github仓库 一看,发现尤雨溪把 Vue3仓库yarn 换成了 `pnpm`[2]。贡献文档[3]中有一句话。

We also recommend installing ni[4] to help switching between repos using different package managers. ni also provides the handy nr command which running npm scripts easier.

我们还建议安装 ni[5] 以帮助使用不同的包管理器在 repos 之间切换。ni 还提供了方便的 nr 命令,可以更轻松地运行 npm 脚本。

这个 ni 项目源码虽然是 ts,没用过 ts 小伙伴也是很好理解的,而且主文件其实不到 100行,非常适合我们学习。

阅读本文,你将学到:

1. 学会 ni 使用和理解其原理
2. 学会调试学习源码
3. 可以在日常工作中也使用 ni
4. 等等

2. 原理

github 仓库 ni#how[6]

ni 假设您使用锁文件(并且您应该)

在它运行之前,它会检测你的 yarn.lock / pnpm-lock.yaml / package-lock.json 以了解当前的包管理器,并运行相应的命令。

单从这句话中可能有些不好理解,还是不知道它是个什么。我解释一下。

使用 `ni` 在项目中安装依赖时:假设你的项目中有锁文件 `yarn.lock`,那么它最终会执行 `yarn install` 命令。假设你的项目中有锁文件 `pnpm-lock.yaml`,那么它最终会执行 `pnpm i` 命令。假设你的项目中有锁文件 `package-lock.json`,那么它最终会执行 `npm i` 命令。使用 `ni -g vue-cli` 安装全局依赖时默认使用 `npm i -g vue-cli`当然不只有 `ni` 安装依赖。还有 `nr` - run`nx` - execute`nu` - upgrade`nci` - clean install`nrm` - remove

我看源码发现:ni相关的命令,都可以在末尾追加\?,表示只打印,不是真正执行

所以全局安装 ni 后,可以尽情测试,比如 ni \?nr dev --port=3000 \?,因为打印,所以可以在各种目录下执行,有助于理解 ni 源码。我测试了如下图所示:

9c89958ecfc5bbebec34a199c4dd876e.png
命令测试图示

假设项目目录下没有锁文件,默认就会让用户从npm、yarn、pnpm选择,然后执行相应的命令。但如果在~/.nirc文件中,设置了全局默认的配置,则使用默认配置执行对应命令。

Config

; ~/.nirc; fallback when no lock found
defaultAgent=npm # default "prompt"; for global installs
globalAgent=npm

因此,我们可以得知这个工具必然要做三件事

1. 根据锁文件猜测用哪个包管理器 npm/yarn/pnpm 
2. 抹平不同的包管理器的命令差异
3. 最终运行相应的脚本

接着继续看看 README 其他命令的使用,就会好理解。

3. 使用

看 ni github文档[7]

npm i in a yarn project, again? F**k!

ni - use the right package manager

全局安装。

npm i -g @antfu/ni

如果全局安装遭遇冲突,我们可以加上 --force 参数强制安装。

举几个常用的例子。

3.1 ni - install

ni# npm install
# yarn install
# pnpm install
ni axios# npm i axios
# yarn add axios
# pnpm i axios

3.2 nr - run

nr dev --port=3000# npm run dev -- --port=3000
# yarn run dev --port=3000
# pnpm run dev -- --port=3000
nr
# 交互式选择命令去执行
# interactively select the script to run
# supports https://www.npmjs.com/package/npm-scripts-info convention
nr -# 重新执行最后一次执行的命令
# rerun the last command

3.3 nx - execute

nx jest# npx jest
# yarn dlx jest
# pnpm dlx jest

4. 阅读源码前的准备工作

4.1 克隆

# 推荐克隆我的仓库(我的保证对应文章版本)
git clone https://github.com/lxchuan12/ni-analysis.git
cd ni-analysis/ni
# npm i -g pnpm
# 安装依赖
pnpm i
# 当然也可以直接用 ni# 或者克隆官方仓库
git clone https://github.com/vuejs/ni.git
cd ni
# npm i -g pnpm
# 安装依赖
pnpm i
# 当然也可以直接用 ni

众所周知,看一个开源项目,先从 package.json 文件开始看起。

4.2 package.json 文件

{"name": "@antfu/ni","version": "0.10.0","description": "Use the right package manager",// 暴露了六个命令"bin": {"ni": "bin/ni.js","nci": "bin/nci.js","nr": "bin/nr.js","nu": "bin/nu.js","nx": "bin/nx.js","nrm": "bin/nrm.js"},"scripts": {// 省略了其他的命令 用 esno 执行 ts 文件// 可以加上 ? 便于调试,也可以不加// 或者是终端 npm run dev \?"dev": "esno src/ni.ts ?"},
}

根据 dev 命令,我们找到主入口文件 src/ni.ts

4.3 从源码主入口开始调试

// ni/src/ni.ts
import { parseNi } from './commands'
import { runCli } from './runner'// 我们可以在这里断点
runCli(parseNi)

找到 ni/package.jsonscripts,把鼠标移动到 dev 命令上,会出现运行脚本调试脚本命令。如下图所示,选择调试脚本。

a151e585e3d38d30b334847b5fc42e21.png
VSCode 调试
762edb58d8eee43cda0626fbcbaaade0.png
VSCode 调试 Node.js 说明

5. 主流程 runner - runCli 函数

这个函数就是对终端传入的命令行参数做一次解析。最终还是执行的 run 函数。

对于 process 不了解的读者,可以看阮一峰老师写的 process 对象[8]

// ni/src/runner.ts
export async function runCli(fn: Runner, options: DetectOptions = {}) {// process.argv:返回一个数组,成员是当前进程的所有命令行参数。// 其中 process.argv 的第一和第二个元素是Node可执行文件和被执行JavaScript文件的完全限定的文件系统路径,无论你是否这样输入他们。const args = process.argv.slice(2).filter(Boolean)try {await run(fn, args, options)}catch (error) {// process.exit方法用来退出当前进程。它可以接受一个数值参数,如果参数大于0,表示执行失败;如果等于0表示执行成功。process.exit(1)}
}

我们接着来看,run 函数。

6. 主流程 runner - run 主函数

这个函数主要做了三件事

1. 根据锁文件猜测用哪个包管理器 npm/yarn/pnpm - detect 函数
2. 抹平不同的包管理器的命令差异 - parseNi 函数
3. 最终运行相应的脚本 - execa 工具
// ni/src/runner.ts
// 源码有删减
import execa from 'execa'
const DEBUG_SIGN = '?'
export async function run(fn: Runner, args: string[], options: DetectOptions = {}) {// 命令参数包含 问号? 则是调试模式,不执行脚本const debug = args.includes(DEBUG_SIGN)if (debug)// 调试模式下,删除这个问号remove(args, DEBUG_SIGN)// cwd 方法返回进程的当前目录(绝对路径)let cwd = process.cwd()let command// 支持指定 文件目录// ni -C packages/foo vite// nr -C playground devif (args[0] === '-C') {cwd = resolve(cwd, args[1])// 删掉这两个参数 -C packages/fooargs.splice(0, 2)}// 如果是全局安装,那么实用全局的包管理器const isGlobal = args.includes('-g')if (isGlobal) {command = await fn(getGlobalAgent(), args)}else {let agent = await detect({ ...options, cwd }) || getDefaultAgent()// 猜测使用哪个包管理器,如果没有发现锁文件,会返回 null,则调用 getDefaultAgent 函数,默认返回是让用户选择 promptif (agent === 'prompt') {agent = (await prompts({name: 'agent',type: 'select',message: 'Choose the agent',choices: agents.map(value => ({ title: value, value })),})).agentif (!agent)return}// 这里的 fn 是 传入解析代码的函数command = await fn(agent as Agent, args, {hasLock: Boolean(agent),cwd,})}// 如果没有命令,直接返回,上一个 runCli 函数报错,退出进程if (!command)return// 如果是调试模式,那么直接打印出命令。调试非常有用。if (debug) {// eslint-disable-next-line no-consoleconsole.log(command)return}// 最终用 execa 执行命令,比如 npm i// https://github.com/sindresorhus/execa// 介绍:Process execution for humansawait execa.command(command, { stdio: 'inherit', encoding: 'utf-8', cwd })
}

我们学习完主流程,接着来看两个重要的函数:detect 函数、parseNi 函数。

根据入口我们可以知道。

runCli(parseNi)run(fn)这里 fn 则是 parseNi

6.1 根据锁文件猜测用哪个包管理器(npm/yarn/pnpm) - detect 函数

代码相对不多,我就全部放出来了。

主要就做了三件事情1. 找到项目根路径下的锁文件。返回对应的包管理器 `npm/yarn/pnpm`。
2. 如果没找到,那就返回 `null`。
3. 如果找到了,但是用户电脑没有这个命令,则询问用户是否自动安装。
// ni/src/agents.ts
export const LOCKS: Record<string, Agent> = {'pnpm-lock.yaml': 'pnpm','yarn.lock': 'yarn','package-lock.json': 'npm',
}
// ni/src/detect.ts
export async function detect({ autoInstall, cwd }: DetectOptions) {const result = await findUp(Object.keys(LOCKS), { cwd })const agent = (result ? LOCKS[path.basename(result)] : null)if (agent && !cmdExists(agent)) {if (!autoInstall) {console.warn(`Detected ${agent} but it doesn't seem to be installed.\n`)if (process.env.CI)process.exit(1)const link = terminalLink(agent, INSTALL_PAGE[agent])const { tryInstall } = await prompts({name: 'tryInstall',type: 'confirm',message: `Would you like to globally install ${link}?`,})if (!tryInstall)process.exit(1)}await execa.command(`npm i -g ${agent}`, { stdio: 'inherit', cwd })}return agent
}

接着我们来看 parseNi 函数。

6.2 抹平不同的包管理器的命令差异 - parseNi 函数

// ni/src/commands.ts
export const parseNi = <Runner>((agent, args, ctx) => {// ni -v 输出版本号if (args.length === 1 && args[0] === '-v') {// eslint-disable-next-line no-consoleconsole.log(`@antfu/ni v${version}`)process.exit(0)}if (args.length === 0)return getCommand(agent, 'install')// 省略一些代码
})

通过 getCommand 获取命令。

// ni/src/agents.ts
// 有删减
// 一份配置,写个这三种包管理器中的命令。export const AGENTS = {npm: {'install': 'npm i'},yarn: {'install': 'yarn install'},pnpm: {'install': 'pnpm i'},
}
// ni/src/commands.ts
export function getCommand(agent: Agent,command: Command,args: string[] = [],
) {// 包管理器不在 AGENTS 中则报错// 比如 npm 不在if (!(agent in AGENTS))throw new Error(`Unsupported agent "${agent}"`)// 获取命令 安装则对应 npm installconst c = AGENTS[agent][command]// 如果是函数,则执行函数。if (typeof c === 'function')return c(args)// 命令 没找到,则报错if (!c)throw new Error(`Command "${command}" is not support by agent "${agent}"`)// 最终拼接成命令字符串return c.replace('{0}', args.join(' ')).trim()
}

6.3 最终运行相应的脚本

得到相应的命令,比如是 npm i,最终用这个工具 execa[9] 执行最终得到的相应的脚本。

await execa.command(command, { stdio: 'inherit', encoding: 'utf-8', cwd })

7. 总结

我们看完源码,可以知道这个神器 ni 主要做了三件事

1. 根据锁文件猜测用哪个包管理器 npm/yarn/pnpm - detect 函数
2. 抹平不同的包管理器的命令差异 - parseNi 函数
3. 最终运行相应的脚本 - execa 工具

我们日常开发中,可能容易 npmyarnpnpm 混用。有了 ni 后,可以用于日常开发使用。Vue 核心成员 Anthony Fu[10] 发现问题,最终开发了一个工具 ni[11] 解决问题。而这种发现问题、解决问题的能力正是我们前端开发工程师所需要的。

另外,我发现 Vue 生态很多基本都切换成了使用 pnpm[12]

因为文章不宜过长,所以未全面展开讲述源码中所有细节。非常建议读者朋友按照文中方法使用VSCode调试 ni 源码。学会调试源码后,源码并没有想象中的那么难

最后可以持续关注我@若川。欢迎加我微信 ruochuan12源码共读 活动,大家一起学习源码,共同进步。

参考资料

[1]

本文仓库 ni-analysis,求个star^_^: https://github.com/lxchuan12/ni-analysis.git

[2]

pnpm: https://github.com/vuejs/vue-next/pull/4766/files

[3]

贡献文档: https://github.com/vuejs/vue-next/blob/master/.github/contributing.md#development-setup

[4]

ni: https://github.com/antfu/ni

[5]

ni: https://github.com/antfu/ni

[6]

github 仓库 ni#how: https://github.com/antfu/ni#how

[7]

ni github文档: https://github.com/antfu/ni

[8]

阮一峰老师写的 process 对象: http://javascript.ruanyifeng.com/nodejs/process.html

[9]

execa: https://github.com/sindresorhus/execa

[10]

Anthony Fu: https://antfu.me

[11]

ni: https://github.com/antfu/ni

[12]

pnpm: https://pnpm.io

最近组建了一个江西人的前端交流群,如果你是江西人可以加我微信 ruochuan12 私信 江西 拉你进群。

推荐阅读

1个月,200+人,一起读了4周源码
我历时3年才写了10余篇源码文章,但收获了100w+阅读

老姚浅谈:怎么学JavaScript?

我在阿里招前端,该怎么帮你(可进面试群)

2aa7959937dbeeaa4ba40dd60f32a501.gif

················· 若川简介 ·················

你好,我是若川,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列
从2014年起,每年都会写一篇年度总结,已经写了7篇,点击查看年度总结。
同时,最近组织了源码共读活动

c7e3b0740f7ebca75947beff38b3ce11.png

识别方二维码加我微信、拉你进源码共读

今日话题

略。欢迎分享、收藏、点赞、在看我的公众号文章~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275273.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何了解自己的认知偏差_了解吸引力偏差

如何了解自己的认知偏差Let me introduce you the attractiveness bias theory known as cognitive bias.让我向您介绍称为认知偏差的吸引力偏差理论。 Think about a person with outstanding fashion. It will draw our attention, and maybe encourage us to interact with…

隐马尔可夫模型(HMM)及Viterbi算法

HMM简介 对于算法爱好者来说&#xff0c;隐马尔可夫模型的大名那是如雷贯耳。那么&#xff0c;这个模型到底长什么样&#xff1f;具体的原理又是什么呢&#xff1f;有什么具体的应用场景呢&#xff1f;本文将会解答这些疑惑。  本文将通过具体形象的例子来引入该模型&#xf…

尤大直播分享:vue3生态进展和展望

大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以加我微信 ruochuan12前言10月23日&#xff0c;参加了前端早早聊组织的【vue生态专场】&#xff0c;准备写一波分享方便大家学习。早上有4个话题&#xff1a;volar开发&#xff0c;搭建平台组件开发…

利用Python查看微信共同好友

思路 首先通过itchat这个微信个人号接口扫码登录个人微信网页版&#xff0c;获取可以识别好友身份的数据。这里是需要分别登录两人微信的&#xff0c;拿到两人各自的好友信息存到列表中。 这样一来&#xff0c;查共同好友就转化成了查两个列表中相同元素的问题。获取到共同好友…

女生适合学ux吗_UX设计色彩心理学,理论与可访问性

女生适合学ux吗Colour is an interesting topic, which I feel is often overlooked and sometimes under-appreciated. One of the first things I was taught was the power of colour, how it can have an impact on human emotion, and that there should be purpose behin…

初学者也能看懂的 Vue2 源码中那些实用的基础工具函数

1. 前言大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以加我微信 ruochuan12想学源码&#xff0c;极力推荐之前我写的《学习源码整体架构系列》jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4、koa-compose、…

清除浮动mini版

1&#xff09; 清除浮动mini版(简约而不简单).clr:after { content:"";display:table;clear:both;}.clr{zoom:1;} 转载于:https://www.cnblogs.com/jinbiao/archive/2011/09/26/2191170.html

Fiddler 十分钟最全使用介绍

Wireshark 、HTTPWatch、Fiddler的介绍 Firebug虽然可以抓包&#xff0c;但是对于分析http请求的详细信息&#xff0c;不够强大。模拟http请求的功能也不够&#xff0c;且firebug常常是需要“无刷新修改”&#xff0c;如果刷新了页面&#xff0c;所有的修改都不会保存。Wiresha…

视觉测试_视觉设计流行测验

视觉测试重点 (Top highlight)I often discuss the topic of improving visual design skills with junior and mid-level designers. While there are a number of design principles the designers should learn and practice, one important skill that is not often consid…

如何给开源项目提过 PR 呢?其实很简单

大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以加我微信 ruochuan12源码共读群里有小伙伴聊到如何给开源项目提PR&#xff0c;所以今天分享这篇文章。你有给开源的库或者框架提过 PR 吗&#xff1f;如果没有&#xff0c;那么今天的文章会教你怎么…

一次回母校教前端的经历

大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以加我微信 ruochuan12已进行了三个月&#xff0c;很多小伙伴都表示收获颇丰。分享一篇武大毕业的耀耀大佬的文章。有些时候会受限于环境影响&#xff0c;特别是在校大学生。所以要融入到积极上进的环…

设计插画工具_5个强大的设计师插画工具

设计插画工具As Product Designers, most likely, we have come across illustrative work. Visual design is one important element in enhancing the user experience. As many gravitate toward attractive looking products, designers are also adapting to the changing…

如何才能更合理地分配项目奖金?

项目奖金通常情况下都是属于基本工资之外的一种绩效奖励&#xff0c;也就是说&#xff0c;它在员工的薪酬中&#xff0c;是属于浮动的那一部分收入&#xff0c;而不是一种固定收入。基于这样一种认知&#xff0c;跟大家讨论下如何才能更合理地进行项目奖金的分配&#xff1f; 首…

Codeforces 741 D - Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

D - Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths 思路&#xff1a; 树上启发式合并 从根节点出发到每个位置的每个字符的奇偶性记为每个位置的状态&#xff0c;每次统计一下每个状态的最大深度 为了保证链经过当前节点u&#xff0c;我们先计算每个子树的答案…

figma下载_切换到Figma并在其中工作不必是火箭科学,这就是为什么

figma下载We have seen Elon Musk and SpaceX making Rocket Science look like a child’s play. In the same spirit, should design tools be rocket science that is too hard to master? Not at all.我们已经看到埃隆马斯克(Elon Musk)和SpaceX使Rocket Science看起来像是…

npm、yarn、cnpm、pnpm 使用操作都在这了

大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以加我微信 ruochuan12有时候想查个命令&#xff0c;或者换个镜像找了几篇文章才找到&#xff0c;最近闲着没事干&#xff0c;干脆整理一篇文档&#xff0c;以后就不用在网上瞎搜有的还写不全。Usage…

CAN控制器的选择

在进行CAN总线开发前&#xff0c;首先要选择好CAN总线控制器。下面就比较一些控制器的特点。 一些主要的CAN总线器件产品 制造商 产品型号 器件功能及特点 Intel 82526 82527 8XC196CA/CB CAN通信控制器&#xff0c;符合CAN2.0A CAN通信控制器&#xff0c;符合CAN2.0B 扩展…

洛谷 4115 Qtree4——链分治

题目&#xff1a;https://www.luogu.org/problemnew/show/P4115 论文&#xff1a;https://wenku.baidu.com/view/1bc2e4ea172ded630b1cb602.html 重链剖分&#xff0c;分别用线段树维护每条重链。线段树叶子的信息是该点轻孩子的信息&#xff1b;线段树区间的信息是考虑重链的一…

每次启动项目的服务,电脑竟然乖乖的帮我打开了浏览器,100行源码揭秘!

1. 前言大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以加我微信 ruochuan12 参与&#xff0c;已进行三个月了&#xff0c;大家一起交流学习&#xff0c;共同进步。想学源码&#xff0c;极力推荐之前我写的《学习源码整体架构系列》 包含jQuery、…

初级爬虫师_初级设计师的4条视觉原则

初级爬虫师重点 (Top highlight)Like many UXers, I got into the industry from a non-visual background (in my case it was Business and later on Human Cognition). Even though I found great benefits coming from those backgrounds, it also meant I had no UI/Visua…