背景
在更新项目的时候,重新安装Electron,目标版本:v26.1.0,执行npm i electron@26.1.0
,报错:
Electron failed to install correctly, please delete node_modules/electron and try installing again
原因排查
以前遇到过类似的问题,就是因为不可抗力的原因无法访问npm镜像,下载Electron包失败。
在项目中添加.npmrc文件:
registry=https://registry.npmmirror.com
设置成淘宝镜像
重新执行npm i electron@26.1.0
新的错误出现:
electron@26.1.0 postinstall /Users/xxxx/project/xxxxxxx/node_modules/electron
node install.js
RequestError: connect ETIMEDOUT 185.199.108.133:443
首先看下这个ip,是请求到github去了,为什么请求这个ip,答案在上面的的install.js
中
看下Electron的代码结构
在下载npm包后会执行install.js,这个文件主要就是判断下对应的Electron版本是否已经下载,如果没有的话会调用@electron/get
中的方法去下载Electron的包。
我们接着往下走,在@electron/get
中找到downloadArtifact
的代码:
/**
* Downloads an artifact from an Electron release and returns an absolute path
* to the downloaded file.
*
* @param artifactDetails - The information required to download the artifact
*/
async function downloadArtifact(_artifactDetails) {
const artifactDetails = Object.assign({}, _artifactDetails);
if (!_artifactDetails.isGeneric) {
const platformArtifactDetails = artifactDetails;
if (!platformArtifactDetails.platform) {
d('No platform found, defaulting to the host platform');
platformArtifactDetails.platform = process.platform;
}
if (platformArtifactDetails.arch) {
platformArtifactDetails.arch = utils_1.getNodeArch(platformArtifactDetails.arch);
}
else {
d('No arch found, defaulting to the host arch');
platformArtifactDetails.arch = utils_1.getHostArch();
}
}
utils_1.ensureIsTruthyString(artifactDetails, 'version');
artifactDetails.version = artifact_utils_1.getArtifactVersion(artifactDetails);
const fileName = artifact_utils_1.getArtifactFileName(artifactDetails);
const url = await artifact_utils_1.getArtifactRemoteURL(artifactDetails);
const cache = new Cache_1.Cache(artifactDetails.cacheRoot);
// Do not check if the file exists in the cache when force === true
if (!artifactDetails.force) {
d(`Checking the cache (${artifactDetails.cacheRoot}) for ${fileName} (${url})`);
const cachedPath = await cache.getPathForFileInCache(url, fileName);
if (cachedPath === null) {
d('Cache miss');
}
else {
d('Cache hit');
try {
await validateArtifact(artifactDetails, cachedPath, downloadArtifact);
return cachedPath;
}
catch (err) {
d("Artifact in cache didn't match checksums", err);
d('falling back to re-download');
}
}
}
if (!artifactDetails.isGeneric &&
utils_1.isOfficialLinuxIA32Download(artifactDetails.platform, artifactDetails.arch, artifactDetails.version, artifactDetails.mirrorOptions)) {
console.warn('Official Linux/ia32 support is deprecated.');
console.warn('For more info: https://electronjs.org/blog/linux-32bit-support');
}
return await utils_1.withTempDirectoryIn(artifactDetails.tempDirectory, async (tempFolder) => {
const tempDownloadPath = path.resolve(tempFolder, artifact_utils_1.getArtifactFileName(artifactDetails));
const downloader = artifactDetails.downloader || (await downloader_resolver_1.getDownloaderForSystem());
d(`Downloading ${url} to ${tempDownloadPath} with options: ${JSON.stringify(artifactDetails.downloadOptions)}`);
await downloader.download(url, tempDownloadPath, artifactDetails.downloadOptions);
await validateArtifact(artifactDetails, tempDownloadPath, downloadArtifact);
return await cache.putFileInCache(url, tempDownloadPath, fileName);
});
}
其他代码可以不管,我们关注两行代码:
await downloader.download(url, tempDownloadPath, artifactDetails.downloadOptions);
这个url就是要下载包的地址,看下这个url是哪来的
const url = await artifact_utils_1.getArtifactRemoteURL(artifactDetails);
artifact_utils_1来自artifact-utils.js
看下这个文件:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("./utils");
const BASE_URL = 'https://github.com/electron/electron/releases/download/';
const NIGHTLY_BASE_URL = 'https://github.com/electron/nightlies/releases/download/';
function getArtifactFileName(details) {
utils_1.ensureIsTruthyString(details, 'artifactName');
if (details.isGeneric) {
return details.artifactName;
}
utils_1.ensureIsTruthyString(details, 'arch');
utils_1.ensureIsTruthyString(details, 'platform');
utils_1.ensureIsTruthyString(details, 'version');
return `${[
details.artifactName,
details.version,
details.platform,
details.arch,
...(details.artifactSuffix ? [details.artifactSuffix] : []),
].join('-')}.zip`;
}
exports.getArtifactFileName = getArtifactFileName;
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);
}
async function getArtifactRemoteURL(details) {
const opts = details.mirrorOptions || {};
let base = mirrorVar('mirror', opts, BASE_URL);
if (details.version.includes('nightly')) {
const nightlyDeprecated = mirrorVar('nightly_mirror', opts, '');
if (nightlyDeprecated) {
base = nightlyDeprecated;
console.warn(`nightly_mirror is deprecated, please use nightlyMirror`);
}
else {
base = mirrorVar('nightlyMirror', opts, NIGHTLY_BASE_URL);
}
}
const path = mirrorVar('customDir', opts, details.version).replace('{{ version }}', details.version.replace(/^v/, ''));
const file = mirrorVar('customFilename', opts, getArtifactFileName(details));
// Allow customized download URL resolution.
if (opts.resolveAssetURL) {
const url = await opts.resolveAssetURL(details);
console.log('url', url);
return url;
}
return `${base}${path}/${file}`;
}
exports.getArtifactRemoteURL = getArtifactRemoteURL;
function getArtifactVersion(details) {
return utils_1.normalizeVersion(mirrorVar('customVersion', details.mirrorOptions || {}, details.version));
}
exports.getArtifactVersion = getArtifactVersion;
这里面最坑爹的点就是:
const BASE_URL = 'https://github.com/electron/electron/releases/download/';
const NIGHTLY_BASE_URL = 'https://github.com/electron/nightlies/releases/download/';
是的,它写死了下载的地址,只从github下载,所以不管怎么修改镜像地址也是没用的。可以加两个console.log把获取到的链接出来看看
下载链接如下:github.com/electron/el…
到此,原因已经找到了,因为要去github下载指定的包,结果就请求超时了。
解决方法
科学上网
简单点开个科学上网,把proxy打开就行了,我用的mac,这个方案比较坑爹,开了Clash还要改.bash_profile,下载完还要改回去,很麻烦。
手动下载
先看看install.js里面的一段代码:
手动执行下install.js,可以看到electronPath其实就是/node_modules/electron/dist/Electron.app/Contents/MacOS/Electron
我们试试把github.com/electron/electron/releases/download/v26.1.0/electron-v26.1.0-darwin-x64.zip下载下来,放进去看看。
尝试执行install.js:node node_modules/electron/install.js
还是会执行下载。
主要的是少了一个path.txt
,判断是否安装的方法有个判断path.txt的内容是否与platformPath
一致。
加上path.txt
Electron.app/Contents/MacOS/Electron
重新执行install.js,执行通过。
启动项目,成功~