@puppeteer/browsers CLI 类深入解析:在命令行与代码中管理 Chrome/Firefox 浏览器生命周期
【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer
导读
@puppeteer/browsers是 Puppeteer 官方拆分出的浏览器管理库,用于"下载、缓存、列出与启动"浏览器及驱动。而CLI类正是这套能力的命令行门户:它把所有程序化 API 包装成install、launch、clear、list等子命令,同时也允许你通过构造参数自定义命令名、缓存目录、前缀子命令与固定版本(pinned)策略,从而被npx @puppeteer/browsers和npx puppeteer browsers两条命令复用。读完本文,你将掌握CLI类的全部构造参数与run()执行模型、每个内置子命令的完整用法与输出格式,以及 Puppeteer 主包如何通过CLI暴露browsers子命令。
本文以 API 文档 browsers.cli.md 为骨架,结合 CLI.ts 源码、README、index.md 与相关测试展开。
一、CLI类在项目中的定位
在 Puppeteer 仓库中,@puppeteer/browsers位于 packages/browsers,其描述为 "Download and launch browsers",CLI类是它的核心公共类(export declare class CLI)。它的两个消费入口说明了它的地位:
- 独立包入口:main-cli.ts 中直接执行
void new CLI().run(process.argv),对应bin字段lib/main-cli.js,即npx @puppeteer/browsers ...背后的实现。 - Puppeteer 主包入口:packages/puppeteer/src/node/cli.ts 以高度定制的参数
new CLI({...}).run(process.argv)复用同一个类,向用户暴露npx puppeteer browsers ...。
也就是说:同一个类,通过构造参数即可派生出两套风格完全不同的 CLI。这正是理解CLI类价值的关键——它是一台"可配置的命令行生成器"。
二、类签名与整体结构
export declare class CLICLI类的全部公开表面只有两部分,正如 browsers.cli.md 所示:
| 成员 | 签名 | 说明 |
|---|---|---|
| Constructor | (opts?, rl?) | 构造 CLI 实例,配置命令名、版本、缓存目录等 |
| Method | run(argv: string[]): Promise<void> | 解析并执行命令行参数,返回结束信号 |
从源码看,该类内部(CLI.ts)维护着一组私有状态:
#cachePath:默认缓存/安装根目录,缺省为process.cwd();#scriptName:显示的脚本名(影响--help中的$0),默认@puppeteer/browsers;#version:版本号,默认取包内常量packageVersion(当前源码为3.2.1,见 package.json);#rl:可注入的readline.Interface,用于clear命令的交互确认;#pinnedBrowsers/#prefixCommand/#allowCachePathOverride:见下文构造参数详解。
run()则基于yargs动态注册命令集(细节见第四、五节),因此--help、<command> --help均是自动生成、天然可用的内建文档。
三、构造参数详解(opts 与 rl)
构造函数完整签名(见 browsers.cli.constructor.md):
constructor( opts?: | string | { cachePath?: string; scriptName?: string; version?: string; prefixCommand?: { cmd: string; description: string; }; allowCachePathOverride?: boolean; pinnedBrowsers?: Partial< Record< Browser, { buildId: string; skipDownload: boolean; } > >; }, rl?: readline.Interface, );1.opts传字符串的快捷语义
若opts直接传入字符串,会被等价转换为{cachePath: opts}(CLI.ts)。因此在测试与二次开发中常写new CLI(tmpDir),等价于把浏览器安装目录指向tmpDir。
2.cachePath?: string
浏览器下载与安装的根目录。未提供时默认process.cwd()(当前工作目录)。从源码结构看,真正的目录布局与Cache类兼容,即与 Puppeteer 运行时缓存结构一致。构造后缓存路径也决定clear/list默认作用范围。值得注意的是,#cachePath在逻辑上仅作为"回退值"——只要allowCachePathOverride开启,命令行上的--path会优先覆盖它。
3.scriptName?: string与version?: string
二者分别定制 CLI 自称的名字与版本号,直接注入 yargs 的.scriptName()与.version()(CLI.ts)。效果体现在帮助文本:默认scriptName为@puppeteer/browsers,所以帮助里的示例显示$0 install chrome;Puppeteer 主包则把它改为puppeteer,于是同一份示例渲染成puppeteer install ...。
4.prefixCommand?: {cmd: string; description: string}
当传入该字段时,CLI不会把install/launch/clear/list注册在顶层,而是先注册一条前缀命令cmd,再把浏览器子命令挂在其下(CLI.ts)。Puppeteer 主包正是用{cmd: 'browsers', description: 'Manage browsers of this Puppeteer installation'}实现了npx puppeteer browsers install ...的交互形态(见 cli.ts)。
5.allowCachePathOverride?: boolean
是否允许用户在命令行上用--path覆盖缓存目录,默认true。设置为false时,yargs 中--path选项会被移除(CLI.ts),从而把安装位置强制锁定在构造时指定的cachePath。Puppeteer 主包为避免用户绕过其配置管理而设false,并将其指向configuration().cacheDirectory。
6.pinnedBrowsers?: Partial<Record<Browser, {buildId; skipDownload}>>
固定版本映射表,语义复杂但非常实用,涉及三处联动:
- 位置参数变为可选:设置
pinnedBrowsers后,install的子命令签名从install <browser>变成install [browser],允许直接运行$0 install以批量安装全部 pinned 浏览器(CLI.ts)。 - 批量安装互不阻塞:不带 browser 参数安装时使用
Promise.allSettled并行执行,避免某个浏览器先失败导致其余安装进入异常状态;全部结束后若存在失败项才统一抛出(CLI.ts)。 @pinned/默认 buildId 解析:存在 pinned 表时,未显式写@buildId的浏览器其 buildId 记为pinned,再由#resolvePinnedBrowserIfNeeded查表替换为真实版本(CLI.ts);skipDownload: true的条目会跳过安装。
Puppeteer 主包将 Chrome / Firefox / chrome-headless-shell 三者绑定到其发布时锁定的PUPPETEER_REVISIONS版本,并可从puppeteer.config.js覆盖(见 cli.ts)。
7.rl?: readline.Interface
可注入的 readline 接口。仅在clear命令做危险删除确认时使用(默认从stdin/stdout创建)。测试通过注入模拟应答实现自动化,例如 CLI.test.ts 与 chrome/cli.test.ts 中的new CLI(tmpDir, createMockedReadlineInterface('yes'))——yes代表确认清空,no代表取消,覆盖了两条分支。
四、run():统一入口与执行模型
class CLI { run(argv: string[]): Promise<void>; }run()只做一件事:把传入的 argv(通常即process.argv)交给 yargs 解析执行。实现要点(CLI.ts):
- 通过
yargs(hideBin(argv))去掉node与脚本路径两个前置参数; - 注入
scriptName、version; - 若配置了
prefixCommand,先注册前缀命令,其子命令构建逻辑与原逻辑完全一致(复用同一个#build()); - 最终
demandCommand(1)保证必须给出至少一个子命令,再.help()生成帮助文本。
run的返回Promise<void>表示整个命令执行完成(成功或抛错);测试、二次封装均可await cli.run([...])。注意它返回后不会自动退出进程——进程退出由顶层入口(main-cli.ts中的void ...run())自行负责。
五、内置子命令全景
run()内部通过#build()注册了 5 个子命令(CLI.ts),分别对应 index.md 帮助文档中的 4 个常用命令外加一个实验命令:
1.install <browser>:下载并安装
核心用法(帮助文本示例汇总于源码 CLI.ts):
# 安装 Stable 频道最新版 Chrome for Testing npx @puppeteer/browsers install chrome@stable # 安装指定完整版本 npx @puppeteer/browsers install chrome@116.0.5793.0 # 安装指定 milestone(大版本号) 的最新版 npx @puppeteer/browsers install chrome@117 # 安装 chrome-headless-shell 的 Beta 频道版本 npx @puppeteer/browsers install chrome-headless-shell@beta # 按 Chromium 修订号安装 npx @puppeteer/browsers install chromium@1083080 # Firefox 支持 stable/beta/devedition/esr/nightly 及精确版本 npx @puppeteer/browsers install firefox@stable npx @puppeteer/browsers install firefox@stable_111.0.1 # ChromeDriver 也可按频道或版本安装 npx @puppeteer/browsers install chromedriver@canary npx @puppeteer/browsers install chromedriver@115.0.5790 # 最新 patch 版本关键参数:
| 参数 | 类型/默认值 | 说明 |
|---|---|---|
--platform | 自动探测 | 指定目标平台,取值来自BrowserPlatform枚举;源码用detectBrowserPlatform()做默认值(CLI.ts) |
--path | 当前工作目录 | 下载/安装根目录;相对路径相对 cwd 解析,目录结构兼容 Puppeteer 缓存 |
--base-url | 默认官方源 | 自定义下载源(内网镜像等场景) |
--install-deps | false | 是否尝试安装系统依赖;仅 Linux 且仅对 Chrome 生效,需要 root 权限 |
--format | {{browser}}@{{buildId}} {{path}} | 输出模板,支持{{browser}}、{{buildId}}、{{path}}、{{platform}}占位符(CLI.ts) |
install的内部流程是:先用resolveBuildId()把stable/canary/117等别名解析为真实 buildId,再调用底层install()API 下载解压;若遇到半途失败(IncompleteInstallationError),会提示用clear清空缓存后重试(CLI.ts)。成功后会按--format输出实际 buildId 与可执行文件的绝对路径,方便脚本解析。
2.launch <browser>:启动浏览器
# 启动缓存中的指定版本 npx @puppeteer/browsers launch chrome@115.0.5790.170 npx @puppeteer/browsers launch firefox@112.0a1 # 启动后分离子进程 npx @puppeteer/browsers launch chrome@115.0.5790.170 --detached # 启动系统里安装的 Chrome(Canary 频道) npx @puppeteer/browsers launch chrome@canary --system # 把额外参数透传给浏览器二进制 npx @puppeteer/browsers launch chrome@115.0.5790.170 -- --version三个布尔选项:--detached(分离子进程)、--system(改为在系统安装位置查找,而非缓存目录)、--dumpio(转发浏览器的 stdout/stderr)。实现上launch分支依据--system选择computeSystemExecutablePath或computeExecutablePath得出可执行文件路径,再调用底层launch()(CLI.ts)。透传参数依赖 yargs 的populate--配置,即--之后的内容原样交给浏览器。
已知限制:系统浏览器启动仅适用于 Chrome/Chromium(见 index.md)。
3.clear:清空缓存
移除指定缓存目录下的全部已安装浏览器。删除前会弹出交互确认Do you want to permanently and recursively delete the content of <dir> (yes/No)?,仅当输入y或yes才执行(CLI.ts)。自动化脚本可用注入的rl回答yes(测试中正是如此)。若allowCachePathOverride=false,则固定清理构造时指定的目录。
4.list:列出已安装浏览器
输出每行的格式为浏览器@buildId (platform) executablePath(CLI.ts):
npx @puppeteer/browsers list # 自定义缓存目录 npx @puppeteer/browsers list --path /tmp/my-browser-cache对应测试 list.test.ts 覆盖了空目录、正常列表与非法目录等场景。
5.bisect <path>(实验性)
针对 Chrome for Testing 的二分定位工具:<path>可以是.mjs/.cjs/.js脚本,也可以是 npm script 名;配合--good <v>、--bad <v>给出"最后正常/最先异常"的两个版本,用-g/-b作短别名;--cft默认开启。它会自动下载bisect-builds.py(保存在~目录下)并以python3执行,借助脚本与测试命令找出首个引入问题的构建(CLI.ts)。
六、派生 CLI 的真实样例:puppeteer browsers
CLI的"可配置 + 复用"设计最佳例证在 packages/puppeteer/src/node/cli.ts:Puppeteer 主包用下列参数实例化:
new CLI({ cachePath: config.cacheDirectory!, scriptName: 'puppeteer', version: packageVersion, prefixCommand: {cmd: 'browsers', description: 'Manage browsers of this Puppeteer installation'}, allowCachePathOverride: false, pinnedBrowsers: { [Browser.CHROME]: {buildId: ..., skipDownload: ...}, [Browser.FIREFOX]: {buildId: ..., skipDownload: true}, [Browser.CHROMEHEADLESSSHELL]: {buildId: ..., skipDownload: ...}, }, }).run(process.argv);由此可以得到一个完全不同的用户界面:
npx puppeteer browsers --help npx puppeteer browsers install # 安装全部 pinned 浏览器(依据当前配置/发布版本) npx puppeteer browsers install chrome --install-deps # 额外安装系统依赖其中prefixCommand让用户先输入browsers,pinnedBrowsers让不带 browser 参数的install变成"按 Puppeteer 锁定的版本安装",而allowCachePathOverride:false则强制所有安装落在 Puppeteer 的配置缓存目录中。同一条命令语义通过不同构造参数完成切换,正是CLI类设计价值的直接体现。
七、测试与验证:行为即契约
packages/browsers/test/src下存在成体系的 CLI 测试,可作为使用与预期行为的参照:
- CLI.test.ts:核心行为,覆盖非法频道报错(
Invalid Chrome channel)、--透传参数、clear确认/取消、输出格式等; - chrome/cli.test.ts、chromedriver/cli.test.ts、firefox/cli.test.ts:验证各浏览器子包的 install/launch 行为与 mock readline;
- list.test.ts:列表输出与缓存状态一致性。
测试统一采用new CLI(tmpDir, createMockedReadlineInterface('yes')).run([...])的编程式调用,说明CLI不仅面向终端,也能作为库被可靠地驱动(这得益于rl参数的可注入性)。
八、运行环境与排障提示
根据 index.md 与 package.json,使用前请确认环境满足:
- Node 版本符合
engines(当前为>=22.12.0); - 解压工具:Firefox 在 Linux 需要
xz/bzip2、macOS 需要hdiutil;Chrome 在 Linux/macOS 需要unzip、Windows 需要tar.exe; - 若处于代理网络:CLI 尊重
HTTP_PROXY/HTTPS_PROXY/NO_PROXY,但需额外安装可选依赖npm install proxy-agent; - 需要详尽日志时启用 Node 内置调试通道:
env NODE_DEBUG="puppeteer:browsers:*" npx @puppeteer/browsers install chrome@stable。可用通道包括puppeteer:browsers:cache(缓存操作)、:fileUtil(解压等文件工具)、:install(下载安装进度)、:launcher(启动参数与进程状态)。
更多命令行形态(含npx @puppeteer/browsers@<version>版本锁定、npx --yes自动确认安装等)可参考 index.md 的 CLI 章节;程序化安装/启动 API 的对应说明见 install.md、launch.md 与 process.md。
【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考