本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
本期学习的源码是由vue团队核心成员antful小哥开发的小工具install-pkg, 用于检查包管理器,自动安装。源码核心代码不到40行却非常值得学习!
1.学习内容
https://www.npmjs.com/package/@antfu/install-pkg
https://github.com/antfu/install-pkg.git
川哥文章: https://juejin.cn/post/7039494476913442824
2.学习过程
是什么?
以编程方式安装包。自动检测包管理器。
怎么用?
npm i @antfu/install-pkg
import { installPackage } from '@antfu/install-pkg'
await installPackage('vite', { silent: true })
读源码:
{
"name": "@antfu/install-pkg",
"version": "0.1.0",
"scripts": {
"start": "esno src/index.ts"
},
}
又一个值得注意的依赖:esno:
https://www.npmjs.com/package/esno
TypeScript / ESNext node runtime powered by esbuild
index.ts源码:
// src/index.ts
export * from './detect'
export * from './install'
逐个看对应的文件,首先是detect.ts:
//// src/detect.ts
import path from 'path'
import findUp from 'find-up'
export type PackageManager = 'pnpm' | 'yarn' | 'npm'
const LOCKS: Record<string, PackageManager> = {
'pnpm-lock.yaml': 'pnpm',
'yarn.lock': 'yarn',
'package-lock.json': 'npm',
}
// 在有pnpm-lock.yaml文件的项目中,detectPackageManager 函数最终返回的是 pnpmx
export async function detectPackageManager(cwd = process.cwd()) {
//利用findUp去找项目下有没有pnpm-lock.yaml这一类的文件
const result = await findUp(Object.keys(LOCKS), { cwd })
//找到了就返回相应的包名 pnpm
//path.basename('/Users/install-pkg/pnpm-lock.yaml') 则是 pnpm-lock.yaml
const agent = (result ? LOCKS[path.basename(result)] : null)
return agent
}
detect.ts用于检查项目使用的是什么包管理器,核心原理就是使用find-up找pnpm-lock.yaml或者yarn.lock或者package-lock.json这些文件的路径,有路径说明有对应的文件,就能确定使用的是什么包管理工具。
下面看install.ts:
// src/install.ts
import execa from 'execa'
import { detectPackageManager } from '.'
export interface InstallPackageOptions {
// 路径
cwd?: string
// 开发还是生产
dev?: boolean
silent?: boolean
// 指定包管理
packageManager?: string
preferOffline?: boolean
additionalArgs?: string[]
}
export async function installPackage(names: string | string[], options: InstallPackageOptions = {}) {
// 如果指定了则使用指定的,否则调用detectPackageManager检测
const agent = options.packageManager || await detectPackageManager(options.cwd) || 'npm'
if (!Array.isArray(names))
names = [names]
const args = options.additionalArgs || []
if (options.preferOffline)
args.unshift('--prefer-offline')
return execa(
agent,
[
agent === 'yarn'
? 'add'
: 'install',
options.dev ? '-D' : '',
...args,
...names,
].filter(Boolean),
{
stdio: options.silent ? 'ignore' : 'inherit',
cwd: options.cwd,
},
)
}
install.ts主要是根据检测包管理器的结果进行依赖安装,使用execa来执行脚本。
3.总结
总结:通过锁文件自动检测使用何种包管理器(npm、yarn、pnpm),最终用 execa 执行依赖安装命令。
学完本期源码,您可以思考如下问题:
1.node.js中如何检查是否存在目录或者文件,谈谈你的解决方案?
2.如何以编程的方式而不是命令的方式执行脚本?
3.如何判断项目使用的是何种包管理器?
4.node.js中path.basename是什么含义?
5.简述install-pkg的核心流程?