<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
通常安裝依賴都是通過命令式的方式來安裝,有沒有想過可以通過程式設計式的方式來安裝依賴呢?
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.json
、yarn.lock
、pnpm-lock.yaml
等檔案的路徑;
如果找到就好辦了,直接在這個檔案目錄下找package.json
檔案即可;
如果沒找到就繼續使用findUp
方法來查詢package.json
檔案。
if (packageJsonPath && 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
來判斷使用哪個包管理器;
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
檔案來獲取。可以說是非常巧妙。
我們繼續看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@berry
和npm
的引數不一樣。
return execa( agent, [ agent === 'yarn' ? 'add' : 'install', options.dev ? '-D' : '', ...args, ...names, ].filter(Boolean), { stdio: options.silent ? 'ignore' : 'inherit', cwd: options.cwd, }, )
這裡的命令是根據packageManager
來拼接的,yarn
和npm
的命令不一樣,所以需要判斷一下。
最後就是執行安裝命令了,這裡使用了execa
來執行命令,這個庫的用法和child_process
差不多,但是更加方便,參考:execa。
通過學習這個庫,我們可以學到很多東西,比如:
execa
來執行命令。同時這裡面還穿插著很多node
的知識和包管理器的知識,比如:
node
的path.basename
方法;lock
檔案;以上就是程式設計式安裝依賴install-pkg原始碼解析的詳細內容,更多關於程式設計式安裝依賴install-pkg的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45