广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >编程式安装依赖install-pkg源码解析
  • 338
分享到

编程式安装依赖install-pkg源码解析

编程式安装依赖install-pkginstall pkg 2022-12-25 12:12:27 338人浏览 八月长安
摘要

目录正文使用源码分析总结正文 通常安装依赖都是通过命令式的方式来安装,有没有想过可以通过编程式的方式来安装依赖呢? install-pkg是一个用于安装依赖的工具,它可以在不同的环境

正文

通常安装依赖都是通过命令式的方式来安装,有没有想过可以通过编程式的方式来安装依赖呢?

install-pkg是一个用于安装依赖的工具,它可以在不同的环境下安装依赖,比如 npm、yarn、pnpm 等。

源码地址:GitHub.com/antfu/insta…

使用

install-pkg的使用非常简单,根据README的说明,就通过下面的代码就可以安装依赖了:

import { install } from 'install-pkg'
await installPackage('vite', { silent: true })

源码分析

install-pkg的源码非常简单,只有 100 行左右,我们来看看它的实现原理。

根据README的说明,我们可以通过installPackage方法来安装依赖,那么我们先来看看installPackage方法的实现:

installPackage方法在src/index.ts文件中,转成 js 代码如下:

import execa from 'execa'
import { detectPackageManager } from '.'
export async function installPackage(names, options = {}) {
  const detectedAgent = options.packageManager || await detectPackageManager(options.cwd) || 'npm'
  const [agent] = detectedAgent.split('@')
  if (!Array.isArray(names))
    names = [names]
  const args = options.additionalArgs || []
  if (options.preferOffline) {
    // yarn berry uses --cached option instead of --prefer-offline
    if (detectedAgent === 'yarn@berry')
      args.unshift('--cached')
    else
      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,
    },
  )
}

可以看到是一个异步方法,它接收两个参数,第一个参数是要安装的依赖名称,第二个参数是配置项。

在方法内部,首先通过传入的配置项options来获取packageManager,如果没有传入packageManager,则通过detectPackageManager方法来获取packageManager,如果detectPackageManager方法也没有获取到packageManager,则默认使用npm

来看看detectPackageManager方法的实现:

import fs from 'fs'
import path from 'path'
import findUp from 'find-up'
const AGENTS = ['pnpm', 'yarn', 'npm', 'pnpm@6', 'yarn@berry', 'bun']
const LOCKS = {
  'bun.lockb': 'bun',
  'pnpm-lock.yaml': 'pnpm',
  'yarn.lock': 'yarn',
  'package-lock.JSON': 'npm',
  'npm-shrinkwrap.json': 'npm',
}
export async function detectPackageManager(cwd = process.cwd()) {
  let agent = null
  const lockPath = await findUp(Object.keys(LOCKS), { cwd })
  let packageJsonPath
  if (lockPath)
    packageJsonPath = path.resolve(lockPath, '../package.json')
  else
    packageJsonPath = await findUp('package.json', { cwd })
  if (packageJsonPath && fs.existsSync(packageJsonPath)) {
    try {
      const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
      if (typeof pkg.packageManager === 'string') {
        const [name, version] = pkg.packageManager.split('@')
        if (name === 'yarn' && parseInt(version) > 1)
          agent = 'yarn@berry'
        else if (name === 'pnpm' && parseInt(version) < 7)
          agent = 'pnpm@6'
        else if (name in AGENTS)
          agent = name
        else
          console.warn('[ni] Unknown packageManager:', pkg.packageManager)
      }
    }
    catch {}
  }
  // detect based on lock
  if (!agent && lockPath)
    agent = LOCKS[path.basename(lockPath)]
  return agent
}

findUp是一个用于查找文件的工具,它可以从当前目录向上查找文件,直到找到为止。

我们来逐行分析:

const lockPath = await findUp(Object.keys(LOCKS), {cwd})
let packageJsonPath
if (lockPath)
    packageJsonPath = path.resolve(lockPath, '../package.json')
else
    packageJsonPath = await findUp('package.json', {cwd})

最开始是获取package-lock.jsonyarn.lockpnpm-lock.yaml等文件的路径;

如果找到就好办了,直接在这个文件目录下找package.json文件即可;

如果没找到就继续使用findUp方法来查找package.json文件。

if (packageJsonPath &amp;&amp; fs.existsSync(packageJsonPath)) {
    try {
        const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
        // ...
    } catch {
    }
}

如果找到了package.json文件,就读取文件内容,然后解析成 JSON 对象。

if (typeof pkg.packageManager === 'string') {
    const [name, version] = pkg.packageManager.split('@')
    if (name === 'yarn' && parseInt(version) > 1)
        agent = 'yarn@berry'
    else if (name === 'pnpm' && parseInt(version) < 7)
        agent = 'pnpm@6'
    else if (name in AGENTS)
        agent = name
    else
        console.warn('[ni] Unknown packageManager:', pkg.packageManager)
}

这里是用过packageManager来判断使用哪个包管理器;

  • 如果packageManageryarn,并且版本号大于1,则使用yarn@berry
  • 如果packageManagerpnpm,并且版本号小于7,则使用pnpm@6
  • 如果packageManageryarnpnpmnpmbun中的一个,则直接使用;
  • 否则就打印一个警告。
// detect based on lock
if (!agent && lockPath)
    agent = LOCKS[path.basename(lockPath)]

如果没有通过package.json来获取packageManager,则通过lockPath来获取packageManager

这个方法的核心就是通过两个方式来获取packageManager

  • 通过package.json中的packageManager字段;
  • 通过lock文件来获取。

可以说是非常巧妙。

我们继续看installPackage方法:

const detectedAgent = options.packageManager || await detectPackageManager(options.cwd) || 'npm'
const [agent] = detectedAgent.split('@')

这里是第一行,就是获取packageManager,和上面讲的方法相辅相成,继续往下看:

if (!Array.isArray(names))
    names = [names]

这里是将name统一变成数组,方便后面处理。

const args = options.additionalArgs || []
if (options.preferOffline) {
    // yarn berry uses --cached option instead of --prefer-offline
    if (detectedAgent === 'yarn@berry')
        args.unshift('--cached')
    else
        args.unshift('--prefer-offline')
}

这里是处理preferOffline参数,如果设置了这个参数,就会在args中添加--prefer-offline或者--cached参数,因为yarn@berrynpm的参数不一样。

return execa(
    agent,
    [
      agent === 'yarn'
        ? 'add'
        : 'install',
      options.dev ? '-D' : '',
      ...args,
      ...names,
    ].filter(Boolean),
    {
      stdio: options.silent ? 'ignore' : 'inherit',
      cwd: options.cwd,
    },
  )

这里的命令是根据packageManager来拼接的,yarnnpm的命令不一样,所以需要判断一下。

最后就是执行安装命令了,这里使用了execa来执行命令,这个库的用法和child_process差不多,但是更加方便,参考:execa。

总结

通过学习这个库,我们可以学到很多东西,比如:

  • 如何判断用户使用的包管理器;
  • 如何查找文件;
  • 如何使用execa来执行命令。

同时这里面还穿插着很多node的知识和包管理器的知识,比如:

  • nodepath.basename方法;
  • 包管理器的lock文件;
  • 包管理器的参数和命令。

以上就是编程式安装依赖install-pkg源码解析的详细内容,更多关于编程式安装依赖install-pkg的资料请关注编程网其它相关文章!

--结束END--

本文标题: 编程式安装依赖install-pkg源码解析

本文链接: https://www.lsjlt.com/news/175764.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 编程式安装依赖install-pkg源码解析
    目录正文使用源码分析总结正文 通常安装依赖都是通过命令式的方式来安装,有没有想过可以通过编程式的方式来安装依赖呢? install-pkg是一个用于安装依赖的工具,它可以在不同的环境...
    99+
    2022-12-25
    编程式安装依赖install-pkg install pkg
  • Spring源码解析之循环依赖的实现流程
    目录前言循环依赖实现流程前言 上篇文章中我们分析完了Spring中Bean的实例化过程,但是没有对循环依赖的问题进行分析,这篇文章中我们来看一下spring是如何解决循环依赖的实现。...
    99+
    2022-11-13
  • Spring源码解析之编程式事务
    目录一、前言二、编程式事务解析三、编程式事务示例四、TransactionCallback五、TransactionCallbackWithoutResult一、前言 在Sprin...
    99+
    2022-11-12
  • Hadoop源码分析二安装配置过程详解
    目录1、 创建用户2、 安装jdk3、 修改hosts4、 配置ssh免密登录5、 安装zookeeper解压:修改配置文件修改内容如下:配置环境变量启动6、 安装hadoop对于三...
    99+
    2022-11-12
  • Spring源码解析之编程式事务的示例分析
    这篇文章主要为大家展示了“Spring源码解析之编程式事务的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Spring源码解析之编程式事务的示例分析”这篇文章吧。一、前言在Spring中...
    99+
    2023-06-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作