缘由
之前的几次尝试均为手动下载覆盖或者安装 C++ 依赖,一直有网络因素和环境依赖的困扰,这次打算翻翻源码,看一下如何从根源解决自定义预编译二进制地址的问题
结论
在npmrc中添加
ELECTRON_MIRROR=https://registry.npmmirror.com/-/binary/electron/
ELECTRON_BUILDER_BINARIES_MIRROR=https://registry.npmmirror.com/-/binary/electron-builder-binaries/
better_sqlite3_binary_host=https://registry.npmmirror.com/-/binary/better-sqlite3
- 如果用的是
Bun,请在项目根目录下添加.env文件,将上述配置中的better_sqlite3_binary_host替换成npm_config_better_sqlite3_binary_host并写入,但是 1.1.3 存在 node 兼容问题,不推荐
原理
三者均支持通过读取环境变量从自定义URL下载预编译二进制
npmmirror 的二进制文件下载地址的前缀为 https://registry.npmmirror.com/-/binary/
npmrc 中的环境变量在运行时环境中会自动加上前缀 npm_config_
electron
electron 通过 @electron/get 执行 postInstall 脚本下载二进制,读取 .npmrc、package.json的config 和 环境变量中ELECTRON_MIRROR 获取下载地址
node_modules/@electron/get/dist/cjs/artifact-utils.js的 23-38 行
function mirrorVar(name, options, defaultValue) {
// Convert camelCase to camel_case for env var reading
const snakeName = name.replace(/([a-z])([A-Z])/g, (_, a, b) => `${a}_${b}`).toLowerCase();
return (
// .npmrc
process.env[`npm_config_electron_${name.toLowerCase()}`] ||
process.env[`NPM_CONFIG_ELECTRON_${snakeName.toUpperCase()}`] ||
process.env[`npm_config_electron_${snakeName}`] ||
// package.json
process.env[`npm_package_config_electron_${name}`] ||
process.env[`npm_package_config_electron_${snakeName.toLowerCase()}`] ||
// env
process.env[`ELECTRON_${snakeName.toUpperCase()}`] ||
options[name] ||
defaultValue);
}
electron-builder
electron-builder 是通过 app-builder-lib 下载二进制,读取 .npmrc、package.json的config 和 环境变量中ELECTRON_BUILDER_BINARIES_MIRROR 获取下载地址
node_modules\app-builder-lib\out\binDownload.js 的 19-40 行
function getBinFromUrl(name, version, checksum) {
const dirName = `${name}-${version}`;
let url;
if (process.env.ELECTRON_BUILDER_BINARIES_DOWNLOAD_OVERRIDE_URL) {
url = process.env.ELECTRON_BUILDER_BINARIES_DOWNLOAD_OVERRIDE_URL + "/" + dirName + ".7z";
}
else {
const baseUrl = process.env.NPM_CONFIG_ELECTRON_BUILDER_BINARIES_MIRROR ||
process.env.npm_config_electron_builder_binaries_mirror ||
process.env.npm_package_config_electron_builder_binaries_mirror ||
process.env.ELECTRON_BUILDER_BINARIES_MIRROR ||
"https://github.com/electron-userland/electron-builder-binaries/releases/download/";
const middleUrl = process.env.NPM_CONFIG_ELECTRON_BUILDER_BINARIES_CUSTOM_DIR ||
process.env.npm_config_electron_builder_binaries_custom_dir ||
process.env.npm_package_config_electron_builder_binaries_custom_dir ||
process.env.ELECTRON_BUILDER_BINARIES_CUSTOM_DIR ||
dirName;
const urlSuffix = dirName + ".7z";
url = `${baseUrl}${middleUrl}/${urlSuffix}`;
}
return getBin(dirName, url, checksum);
}
better-sqlite3
使用 prebuild-install 发布预编译二进制。根据其文档,其读取的是 better_sqlite3_binary_host 变量
但是根据源码,其实读取的是 npm_config_better_sqlite3_binary_host
同时,路径结尾不能包含 /,否则会报错
node_modules/prebuild-install/util.js 的 7-36 行
function urlTemplate (opts) {
if (typeof opts.download === 'string') {
return opts.download
}
const packageName = '{name}-v{version}-{runtime}-v{abi}-{platform}{libc}-{arch}.tar.gz'
const hostMirrorUrl = getHostMirrorUrl(opts)
if (hostMirrorUrl) {
return hostMirrorUrl + '/{tag_prefix}{version}/' + packageName
}
if (opts.pkg.binary && opts.pkg.binary.host) {
return [
opts.pkg.binary.host,
opts.pkg.binary.remote_path,
opts.pkg.binary.package_name || packageName
].map(function (path) {
return trimSlashes(path)
}).filter(Boolean).join('/')
}
return github(opts.pkg) + '/releases/download/{tag_prefix}{version}/' + packageName
}
function getEnvPrefix (pkgName) {
return 'npm_config_' + (pkgName || '').replace(/[^a-zA-Z0-9]/g, '_').replace(/^_/, '')
}
function getHostMirrorUrl (opts) {
const propName = getEnvPrefix(opts.pkg.name) + '_binary_host'
return process.env[propName] || process.env[propName + '_mirror']
}