读39行代码的小工具 install-pkg并解析其实现

install-pkg是一个用于编程安装npm包的工具,它能自动检测并使用正确的包管理器。本文深入解析其仓库、package.json、依赖和代码实现,包括ni自动检测、tsup编译工具、execa命令执行库以及find-up文件查找工具的使用。同时,文章还分析了关键代码逻辑,如检测包管理器、stdi配置及过滤逻辑。

install-pkg

以编程方式安装程序包。并且自动检测包装管理器(npm、yarn和pnpm)

npm i @antfu/install-pkg

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

也就是通过编程方式去安装某个包到本地中,它让我想起了14期remote-git-tags中的实现 , 本质上是通过代码执行去调用 命令安装

仓库

这里推荐使用川哥整理好的同步文章仓库

//川
git clone https://github.com/lxchuan12/install-pkg-analysis.git
cd install-pkg-analysis/install-pkg && pnpm i

//官方项目
git clone https://github.com/antfu/install-pkg.git
cd install-pkg && pnpm i 

package.json 解析

ni

自动检测包管理工具, 使用正确的包管理器。 它可以根据你的锁文件去推断你当前的包管理器, 根据相应的命令 去做转化

相关的script:

 "prepublishOnly": "nr build","dev": "nr build --watch","start": "esno src/index.ts","build": "tsup src/index.ts --format cjs,esm --dts --no-splitting","release": "bumpp --commit --push --tag && pnpm publish","lint": "eslint \"{src,test}/**/*.ts\"","lint:fix": "nr lint -- --fix" 
prepublishOnly

npm钩子, 在执行 npm publish 之前会执行这个命令

nr

ni 下 可执行命令, 对应的 是 npm run /yarn run / pnpm run

nr build , 如果你使用npm包管理工具,对应的就是 npm run build

esno

具有自动CJS/ESM模式和缓存的ts编译工具, 本质就是一个tsx

tsup

使用Esbuild,无需配置即可打包你的TS库

tsup src/index.ts --format cjs,esm --dts --no-splitting

bumpp

更好的进行版本号提升的工具,基于version-bump-prompt改造

生产依赖

execa

命令执行相关的库, 基于 child_process 进行了封装,使用更加友好

import {execa} from 'execa';
const {stdout} = await execa('echo', ['unicorns']);
console.log(stdout); 

find-up

通过查找父目录查找文件或目录 , 字面意思 就是找到指定文件路径的工具

import path from 'node:path';
import {findUp, pathExists} from 'find-up';

console.log(await findUp('unicorn.png'));
//=> '/Users/sindresorhus/unicorn.png' 

代码

index.ts

export * from './detect'
export * from './install' 

统一出口,将detect 和 install的导出 从index统一导出

install.ts

import execa from 'execa'
import { detectPackageManager } from '.'

export interface InstallPackageOptions {cwd?: stringdev?: booleansilent?: booleanpackageManager?: stringpreferOffline?: booleanadditionalArgs?: string[]
}

export async function installPackage(names: string | string[], options: InstallPackageOptions = {}) {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,},)
} 

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',
}

export async function detectPackageManager(cwd = process.cwd()) {const result = await findUp(Object.keys(LOCKS), { cwd })const agent = (result ? LOCKS[path.basename(result)] : null)return agent
} 

逐行分析

const agent = options.packageManager || await detectPackageManager(options.cwd) || 'npm'if (!Array.isArray(names))names = [names] 

agent 从默认的配置对象中以及 detectPackageManager 函数去读取我们使用的是什么包管理工具,不成立的情况下默认为npm, 并将第一个传入的非数组参数包装了为数组类型

import findUp from 'find-up'
export async function detectPackageManager(cwd = process.cwd()) {const result = await findUp(Object.keys(LOCKS), { cwd })const agent = (result ? LOCKS[path.basename(result)] : null)return agent
} 

detectPackageManager 函数 通过当前执行命令的目录,使用 find-up去查找 锁文件(LOCKS),得到对应的 包管理工具, 否则为null。 知道findUp 函数是干啥的这里就很好理解了

 const args = options.additionalArgs || []if (options.preferOffline)args.unshift('--prefer-offline') 

args变量 即 执行执行时的 附加参数

一条没见过的新命令: –prefer-offline

--prefer-offline将使npm跳过任何条件请求(304检查)直接使用缓存数据,只有在缓存无法匹配到的时候,才去访问网络。这样我们将依赖包添加到项目的过程就会快很多,可以理解为 优先使用本地或者全局的缓存 ,没有才进行联网安装

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

在读这一块代码时,产生了一个疑问, Boolean这干啥的,第一眼想起了装箱类型, 即它本身是一个function, 所以它被作为callback 给了filter使用。 接着我就去研究这个函数的作用,它会根据传入的参数是否有效 决定返回值。 即 1 返回 true , 0 返回false

Boolean(1)true
Boolean(0)false 

此时就明白了, 它被作为了filter的回调函数使用, filter将数组成员依次传给Boolean, 再根据结果过滤数据! 也就是数组内的参数 如果是空字符, 或者 判断得到的空字符成员 最终被会移除

stdio

将决定输入输出流的显示,当指定silent: true , 安装包时的信息会在控制台回显, 默认是ignore 不显示。

最后execa将结果对象通过Promise返回

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值