【若川视野 x 源码共读】第20期 | Vue团队核心成员开发的39行小工具 install-pkg 安装包

584 阅读2分钟

本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。

本期学习的源码是由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的核心流程?