安装Electron失败,Electron failed to install correctly

2023年 9月 7日 132.5k 0

背景

在更新项目的时候,重新安装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,执行通过。

启动项目,成功~

相关文章

服务器端口转发,带你了解服务器端口转发
服务器开放端口,服务器开放端口的步骤
产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
如何使用 WinGet 下载 Microsoft Store 应用
百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

发布评论