編程式安裝依賴install-pkg源碼解析
正文
通常安裝依賴都是通過命令式的方式來安裝,有沒有想過可以通過編程式的方式來安裝依賴呢?
install-pkg
是一個用于安裝依賴的工具,它可以在不同的環(huán)境下安裝依賴,比如 npm、yarn、pnpm 等。
使用
install-pkg
的使用非常簡單,根據(jù)README
的說明,就通過下面的代碼就可以安裝依賴了:
import { install } from 'install-pkg' await installPackage('vite', { silent: true })
源碼分析
install-pkg
的源碼非常簡單,只有 100 行左右,我們來看看它的實現(xiàn)原理。
根據(jù)README
的說明,我們可以通過installPackage
方法來安裝依賴,那么我們先來看看installPackage
方法的實現(xiàn):
installPackage
方法在src/index.ts
文件中,轉(zhuǎn)成 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, }, ) }
可以看到是一個異步方法,它接收兩個參數(shù),第一個參數(shù)是要安裝的依賴名稱,第二個參數(shù)是配置項。
在方法內(nèi)部,首先通過傳入的配置項options
來獲取packageManager
,如果沒有傳入packageManager
,則通過detectPackageManager
方法來獲取packageManager
,如果detectPackageManager
方法也沒有獲取到packageManager
,則默認使用npm
。
來看看detectPackageManager
方法的實現(xiàn):
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.json
、yarn.lock
、pnpm-lock.yaml
等文件的路徑;
如果找到就好辦了,直接在這個文件目錄下找package.json
文件即可;
如果沒找到就繼續(xù)使用findUp
方法來查找package.json
文件。
if (packageJsonPath && fs.existsSync(packageJsonPath)) { try { const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) // ... } catch { } }
如果找到了package.json
文件,就讀取文件內(nèi)容,然后解析成 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
來判斷使用哪個包管理器;
- 如果
packageManager
是yarn
,并且版本號大于1
,則使用yarn@berry
; - 如果
packageManager
是pnpm
,并且版本號小于7
,則使用pnpm@6
; - 如果
packageManager
是yarn
、pnpm
、npm
、bun
中的一個,則直接使用; - 否則就打印一個警告。
// detect based on lock if (!agent && lockPath) agent = LOCKS[path.basename(lockPath)]
如果沒有通過package.json
來獲取packageManager
,則通過lockPath
來獲取packageManager
。
這個方法的核心就是通過兩個方式來獲取packageManager
:
- 通過
package.json
中的packageManager
字段; - 通過
lock
文件來獲取。
可以說是非常巧妙。
我們繼續(xù)看installPackage
方法:
const detectedAgent = options.packageManager || await detectPackageManager(options.cwd) || 'npm' const [agent] = detectedAgent.split('@')
這里是第一行,就是獲取packageManager
,和上面講的方法相輔相成,繼續(xù)往下看:
if (!Array.isArray(names)) names = [names]
這里是將name
統(tǒng)一變成數(shù)組,方便后面處理。
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
參數(shù),如果設置了這個參數(shù),就會在args
中添加--prefer-offline
或者--cached
參數(shù),因為yarn@berry
和npm
的參數(shù)不一樣。
return execa( agent, [ agent === 'yarn' ? 'add' : 'install', options.dev ? '-D' : '', ...args, ...names, ].filter(Boolean), { stdio: options.silent ? 'ignore' : 'inherit', cwd: options.cwd, }, )
這里的命令是根據(jù)packageManager
來拼接的,yarn
和npm
的命令不一樣,所以需要判斷一下。
最后就是執(zhí)行安裝命令了,這里使用了execa
來執(zhí)行命令,這個庫的用法和child_process
差不多,但是更加方便,參考:execa。
總結(jié)
通過學習這個庫,我們可以學到很多東西,比如:
- 如何判斷用戶使用的包管理器;
- 如何查找文件;
- 如何使用
execa
來執(zhí)行命令。
同時這里面還穿插著很多node
的知識和包管理器的知識,比如:
node
的path.basename
方法;- 包管理器的
lock
文件; - 包管理器的參數(shù)和命令。
以上就是編程式安裝依賴install-pkg源碼解析的詳細內(nèi)容,更多關于編程式安裝依賴install-pkg的資料請關注腳本之家其它相關文章!
相關文章
JavaScript知識:構(gòu)造函數(shù)也是函數(shù)
構(gòu)造函數(shù)就是初始化一個實例對象,對象的prototype屬性是繼承一個實例對象。本文給大家分享javascript構(gòu)造函數(shù)詳解,對js構(gòu)造函數(shù)相關知識感興趣的朋友一起學習吧2021-08-08微信小程序動態(tài)的加載數(shù)據(jù)實例代碼
這篇文章主要介紹了 微信小程序動態(tài)的加載數(shù)據(jù)實例代碼的相關資料,需要的朋友可以參考下2017-04-04JavaScript選擇器函數(shù)querySelector和querySelectorAll
這篇文章主要介紹了?JavaScript選擇器函數(shù)querySelector和querySelectorAll,下面文章圍繞querySelector和querySelectorAll的相關資料展開詳細內(nèi)容,需要的朋友可以參考一下2021-11-11Three.js添加陰影和簡單后期處理實現(xiàn)示例詳解
這篇文章主要為大家介紹了Three.js添加陰影和簡單后期處理實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04JavaScript中MutationObServer監(jiān)聽DOM元素詳情
這篇文章主要給大家分享的是?JavaScript中MutationObServer監(jiān)聽DOM元素詳情,DOM的MutationObServer接口,可以在DOM被修改時異步執(zhí)行回調(diào)函數(shù),我的理解就是可以監(jiān)聽DOM修改。下面來看看文章的詳細內(nèi)容,需要的朋友可以參考一下2021-11-11Web?Animations?API實現(xiàn)一個精確計時的時鐘示例
這篇文章主要為大家介紹了Web?Animations?API實現(xiàn)一個精確計時的時鐘示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07