如何基于客户端OS分发不同二进制文件至npm包并通过npx调用?
解决方案:基于操作系统分发npm包二进制文件
一、二进制文件放置位置(支持npx调用)
要让二进制文件能被npx直接调用,需满足两个核心要求:
- 将下载后的二进制文件存放到npm包的
bin目录中 - 在
package.json的bin字段里配置命令与二进制文件的映射关系
示例package.json配置:
{ "bin": { "my-tool": "./bin/my-tool" }, "scripts": { "postinstall": "node ./scripts/download-binary.js" } }
注:Windows系统下npm会自动识别.exe后缀,bin字段无需区分系统后缀。
二、跳过重复下载(二进制版本无变化时)
通过记录已下载二进制的版本号,与目标版本对比实现跳过逻辑:
- 从你的
.version文件的label字段中读取当前要下载的二进制版本 - 将该版本号保存到
bin/.binary-version标记文件中 - 每次执行
postinstall时,先读取标记文件的版本:- 版本一致则直接跳过下载
- 版本不一致或标记文件不存在时,执行下载流程
三、修改后的下载脚本示例
import { createWriteStream, readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; import { get } from 'https'; import { platform } from 'os'; import { exit } from 'process'; interface Asset { url: string; id: number; name: 'Windows.exe' | 'Darwin' | 'Linux'; label: string; content_type: string; state: 'uploaded'; size: number; download_count: number; created_at: string; updated_at: string; browser_download_url: string; } // 确保bin目录存在,避免下载时出错 const binDir = './bin'; if (!existsSync(binDir)) { mkdirSync(binDir); } const versionFile = './.version'; const binaryVersionMarker = `${binDir}/.binary-version`; // 解析版本映射表 const assets = JSON.parse(readFileSync(versionFile).toString()) as Asset[]; const versionFromNames = Object.fromEntries(assets.map(dist => [dist.name, dist])); // 匹配当前系统对应的二进制资源 let targetAsset: Asset | null = null; let binaryName: string; switch (platform()) { case 'win32': { binaryName = 'my-tool.exe'; targetAsset = versionFromNames['Windows.exe']; break; } case 'darwin': { binaryName = 'my-tool'; targetAsset = versionFromNames['Darwin']; break; } case 'linux': { binaryName = 'my-tool'; targetAsset = versionFromNames['Linux']; break; } default: { console.warn(`暂不支持该操作系统: ${platform()}`); exit(0); } } if (!targetAsset) { console.error(`未找到当前系统对应的二进制文件`); exit(1); } // 检查是否需要更新 const currentVersion = existsSync(binaryVersionMarker) ? readFileSync(binaryVersionMarker, 'utf8').trim() : ''; if (currentVersion === targetAsset.label) { console.log(`二进制文件已是最新版本 ${targetAsset.label},跳过下载`); exit(0); } // 下载二进制文件到bin目录 const outputPath = `${binDir}/${binaryName}`; const file = createWriteStream(outputPath); console.log(`开始下载版本 ${targetAsset.label} 的二进制文件...`); get(targetAsset.browser_download_url, function (resp) { resp.pipe(file); file.on('finish', () => { file.close(); // 给非Windows系统的二进制添加可执行权限 if (platform() !== 'win32') { import('fs/promises').then(fs => fs.chmod(outputPath, 0o755)); } // 保存版本标记 writeFileSync(binaryVersionMarker, targetAsset.label); console.log(`二进制文件下载完成,已保存到 ${outputPath}`); }); }).on('error', (err) => { console.error(`下载失败: ${err.message}`); exit(1); });
四、额外注意事项
- 权限处理:非Windows系统下,下载后的二进制文件需要添加可执行权限,脚本中已通过
chmod实现 - npm缓存兼容:即使使用
npm ci或缓存安装,postinstall仍会执行,但版本检查逻辑会自动跳过重复下载 - Linux适配:脚本补充了Linux系统的处理逻辑,可根据你实际的二进制URL调整
内容的提问来源于stack exchange,提问作者Rahul A Ranger
相关产品推荐
相关产品推荐

