- 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第20期,链接
@antfu/install-pkg
-
编程式安装包,自动检测包管理器(npm,yarn,pnpm)
-
通过lock文件检测包管理器,然后通过
execa
执行安装命令
Usage
npm i @antfu/install-pkg
import { installPackage } from '@antfu/install-pkg'
await installPackage('vite', { silent: true })
源码
- 入口
index.ts
// index.ts
export * from './detect'
export * from './install'
- 自动检测包管理器
detectPackageManager
// 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',
}
// process.cwd() = /User/duzit/code
export async function detectPackageManager(cwd = process.cwd()) {
const result = await findUp(Object.keys(LOCKS), { cwd })
const agent = (result ? LOCKS[path.basename(result)] : null)
return agent
}
detectPackageManager(cwd)
默认传入当前目录,如process.cwd() = /User/duzit/code
find-up
在cwd
下索引包管理器的lock文件,返回lock文件目录,再通过path.basename()
获取对应的lock文件名
// /User/duzit/code/package-lock.json
cwd = '/User/duzit/code'
const result = await findUp(Object.keys(LOCKS), cwd)
// result = /User/duzit/code/package-lock.json
const agent = (result ? LOCKS[path.basename(result)] : null)
// path.basename(result) = package-lock.json
// agent = 'npm'
- 编程式安装
execa
// 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 = {}) {
// 默认 npm
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', // http://nodejs.cn/api-v12/child_process/options_stdio.html 配置在父进程和子进程之间建立的管道
cwd: options.cwd,
},
)
}
installPackage('vite', {
dev: true,
silent: true,
packageManager: 'npm',
preferOffline: true,
})
// npm install -D --prefer-offline vite
filter(Boolean)
去除无效值
依赖
{
"dependencies": {
"execa": "^5.1.1",
"find-up": "^5.0.0"
}
}
收获
-
了解
find-up
execa
使用 -
path.basename()
-
options.stdio