SoFunction
Updated on 2025-03-02

Programmatic installation dependency install-pkg source code analysis

text

Usually, installation dependencies are installed in imperative ways. Have you ever thought that you can install dependencies programmatically?

install-pkgIt is a tool for installing dependencies. It can install dependencies in different environments, such as npm, yarn, pnpm, etc.

Source code address:/antfu/insta…

use

install-pkgIt is very simple to use, according toREADMEThe following code can be used to install the dependencies:

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

Source code analysis

install-pkgThe source code of the 100 lines is very simple, so let's take a look at its implementation principle.

according toREADMEWe can use the instructionsinstallPackageHow to install dependencies, let's take a look firstinstallPackageMethod implementation:

installPackageMethod insrc/In the file, the code to convert it to js is as follows:

import execa from 'execa'
import { detectPackageManager } from '.'
export async function installPackage(names, options = {}) {
  const detectedAgent =  || await detectPackageManager() || 'npm'
  const [agent] = ('@')
  if (!(names))
    names = [names]
  const args =  || []
  if () {
    // yarn berry uses --cached option instead of --prefer-offline
    if (detectedAgent === 'yarn@berry')
      ('--cached')
    else
      ('--prefer-offline')
  }
  return execa(
    agent,
    [
      agent === 'yarn'
        ? 'add'
        : 'install',
       ? '-D' : '',
      ...args,
      ...names,
    ].filter(Boolean),
    {
      stdio:  ? 'ignore' : 'inherit',
      cwd: ,
    },
  )
}

It can be seen that it is an asynchronous method, which receives two parameters, the first parameter is the dependency name to be installed, and the second parameter is the configuration item.

Inside the method, first pass the incoming configuration itemoptionsCome and get itpackageManager, if not transmittedpackageManager, thendetectPackageManagerMethod to obtainpackageManager,ifdetectPackageManagerThe method was not obtainedpackageManager, then default to usenpm

Come and have a lookdetectPackageManagerMethod implementation:

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',
  '': 'pnpm',
  '': 'yarn',
  '': 'npm',
  '': 'npm',
}
export async function detectPackageManager(cwd = ()) {
  let agent = null
  const lockPath = await findUp((LOCKS), { cwd })
  let packageJsonPath
  if (lockPath)
    packageJsonPath = (lockPath, '../')
  else
    packageJsonPath = await findUp('', { cwd })
  if (packageJsonPath && (packageJsonPath)) {
    try {
      const pkg = ((packageJsonPath, 'utf8'))
      if (typeof  === 'string') {
        const [name, version] = ('@')
        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
          ('[ni] Unknown packageManager:', )
      }
    }
    catch {}
  }
  // detect based on lock
  if (!agent && lockPath)
    agent = LOCKS[(lockPath)]
  return agent
}

findUpis a tool for finding files that can search for files from the current directory until they are found.

Let's analyze it step by step:

const lockPath = await findUp((LOCKS), {cwd})
let packageJsonPath
if (lockPath)
    packageJsonPath = (lockPath, '../')
else
    packageJsonPath = await findUp('', {cwd})

At the beginning it is to obtainpath to the file;

If you find it, it will be easy. Just look for it in this file directoryJust file;

If not found, continue to usefindUpMethod to finddocument.

if (packageJsonPath &amp;&amp; (packageJsonPath)) {
    try {
        const pkg = ((packageJsonPath, 'utf8'))
        // ...
    } catch {
    }
}

If foundThe file is read and then parsed into a JSON object.

if (typeof  === 'string') {
    const [name, version] = ('@')
    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
        ('[ni] Unknown packageManager:', )
}

Used herepackageManagerTo determine which package manager to use;

  • ifpackageManageryesyarn, and the version number is greater than1, then useyarn@berry
  • ifpackageManageryespnpmand the version number is less than7, then usepnpm@6
  • ifpackageManageryesyarnpnpmnpmbunOne of them will be used directly;
  • Otherwise, print a warning.
// detect based on lock
if (!agent && lockPath)
    agent = LOCKS[(lockPath)]

If not passedCome and get itpackageManager, thenlockPathCome and get itpackageManager

The core of this method is to obtain it through two wayspackageManager

  • passIn-housepackageManagerField;
  • passlockFile to get.

It can be said to be very clever.

Let's continue watchinginstallPackagemethod:

const detectedAgent =  || await detectPackageManager() || 'npm'
const [agent] = ('@')

Here is the first line, which is to obtainpackageManager, complement the above method, continue reading:

if (!(names))
    names = [names]

Here isnameIt is unified into an array for easy subsequent processing.

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

Here is the processingpreferOfflineParameters, if this parameter is set, it will beargsAdded in--prefer-offlineor--cachedParameters, becauseyarn@berryandnpmThe parameters are different.

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

The command here is based onpackageManagerTo splice,yarnandnpmThe commands are different, so you need to make a judgment.

Finally, execute the installation command, which is used hereexecato execute commands, the usage of this library andchild_processAlmost the same, but more convenient, refer to:execa

Summarize

By learning this library, we can learn a lot, such as:

  • How to determine the package manager used by the user;
  • How to find files;
  • How to useexecato execute the command.

At the same time, there are many interspersed herenodeknowledge and knowledge of package manager, such as:

  • nodeofmethod;
  • Package Managerlockdocument;
  • Package manager parameters and commands.

The above is the detailed content of the source code analysis of program installation dependency install-pkg. For more information about program installation dependency install-pkg, please pay attention to my other related articles!