1. 源码简介

官网简介:Install package programmatically. Detect package managers automatically (npm, yarn and pnpm)
简单理解,就是用于检查包管理器,自动安装依赖。

2. 使用

  1. npm i @antfu/install-pkg
  2. import { installPackage } from '@antfu/install-pkg'
  3. await installPackage('vite', { silent: true })

3. 读源码

从入口 src/index.ts进去看,只对外暴露了两个文件,detect.ts 和 install.ts,意为探测和安装,功能职责划分得很明显。

先看看detect.ts,非常简短的代码,其中 find-up 这个包是用来查找文件路径。
这段代码意思很简单,根据当前目录lock文件,返回它是哪个类型的包管理器(npm | pnpm | yarn)。

  1. import path from 'path'
  2. import findUp from 'find-up'
  3. export type PackageManager = 'pnpm' | 'yarn' | 'npm'
  4. const LOCKS: Record<string, PackageManager> = {
  5. 'pnpm-lock.yaml': 'pnpm',
  6. 'yarn.lock': 'yarn',
  7. 'package-lock.json': 'npm',
  8. }
  9. /**
  10. * @params cwd: current working directory
  11. */
  12. export async function detectPackageManager(cwd = process.cwd()) {
  13. // /Users/install-pkg/pnpm-lock.yaml
  14. const result = await findUp(Object.keys(LOCKS), { cwd })
  15. // pnpm
  16. const agent = (result ? LOCKS[path.basename(result)] : null)
  17. return agent
  18. }

再看看install.ts,最终结果是用到了 execa 这个包用来执行脚本。

  1. import execa from 'execa'
  2. import { detectPackageManager } from '.'
  3. export interface InstallPackageOptions {
  4. // 目录路径
  5. cwd?: string
  6. // 安装包是开发还是生产
  7. dev?: boolean
  8. // stdio
  9. silent?: boolean
  10. // 指定包管理
  11. packageManager?: string
  12. // 离线安装模式, --prefer-offline 优先使用缓存数据
  13. preferOffline?: boolean
  14. // 额外参数
  15. additionalArgs?: string[]
  16. }
  17. export async function installPackage(names: string | string[], options: InstallPackageOptions = {}) {
  18. const agent = options.packageManager || await detectPackageManager(options.cwd) || 'npm'
  19. if (!Array.isArray(names))
  20. names = [names]
  21. const args = options.additionalArgs || []
  22. // npm 提供了离线安装模式,使用 --offline, --prefer-offline, --prefer-online 可以指定离线模式。
  23. if (options.preferOffline)
  24. args.unshift('--prefer-offline')
  25. // 最终执行命令类似于 pnpm install -D --prefer-offine release-it react antd
  26. return execa(
  27. agent,
  28. [
  29. agent === 'yarn'
  30. ? 'add'
  31. : 'install',
  32. options.dev ? '-D' : '',
  33. ...args,
  34. ...names,
  35. ].filter(Boolean),
  36. {
  37. // https://nodejs.org/api/child_process.html#optionsstdio
  38. stdio: options.silent ? 'ignore' : 'inherit',
  39. cwd: options.cwd,
  40. },
  41. )
  42. }

4. 总结流程

简单说,这就是一个通过脚本命令去安装依赖的包。

install-pkg.png