├── .gitignore ├── index.js ├── test └── package.json ├── README.md ├── package.json ├── LICENSE └── bin └── core.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Dependency directories 10 | node_modules/ 11 | 12 | # Optional npm cache directory 13 | .npm 14 | 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | const { resolve } = require('path'); 3 | const program = require('commander'); 4 | const { fixElectron } = require('./bin/core'); 5 | const data = require(resolve('./', 'package.json')); 6 | 7 | program 8 | .command('start') 9 | .alias('s') 10 | .description('fix electron') 11 | .action(() => { 12 | data.PWD = './'; 13 | fixElectron(data); 14 | }); 15 | 16 | program.parse(process.argv); -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-fix-test", 3 | "version": "1.0.0", 4 | "description": "electron-fix test", 5 | "scripts": { 6 | "fix": "electron-fix start", 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "dependencies": { 10 | "electron": "^7.1.7" 11 | }, 12 | "devDependencies": { 13 | "electron-fix": "file:.." 14 | }, 15 | "author": "pangxieju", 16 | "license": "MIT" 17 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # electron-fix 2 | [![npm version](https://badge.fury.io/js/electron-fix.svg)](https://badge.fury.io/js/electron-fix) 3 | 4 | > Specific area "throw new Error('Electron failed to install correctly, please delete node_modules/electron and try installing again')" 5 | 6 | ## Installing 7 | 8 | Using npm: 9 | 10 | ```bash 11 | $ npm install electron-fix -g 12 | ``` 13 | 14 | Start: 15 | 16 | ```bash 17 | $ electron-fix start 18 | ``` 19 | 20 | or `package.json` add: 21 | ``` 22 | "scripts": { 23 | "fix": "electron-fix start" 24 | } 25 | ``` 26 | ```bash 27 | $ npm run fix 28 | ``` 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-fix", 3 | "version": "1.1.6", 4 | "description": "Special area error fixed.", 5 | "main": "index.js", 6 | "bin": { 7 | "electron-fix": "index.js" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/pangxieju/electron-fix" 15 | }, 16 | "keywords": [ 17 | "electron", 18 | "electron-helper", 19 | "electron-install", 20 | "electron-fix" 21 | ], 22 | "author": "pangxieju", 23 | "license": "MIT", 24 | "dependencies": { 25 | "axios": "^1.10.0", 26 | "chalk": "^2.4.2", 27 | "commander": "^2.20.0", 28 | "ora": "^4.0.3" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/pangxieju/electron-fix/issues/new" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 pangxieju 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bin/core.js: -------------------------------------------------------------------------------- 1 | const { writeFile, accessSync, constants, createWriteStream } = require('fs'); 2 | const { platform, arch, tmpdir } = require('os'); 3 | const { resolve } = require('path'); 4 | const { exec, execSync } = require('child_process'); 5 | const axios = require('axios'); 6 | const chalk = require('chalk'); 7 | const ora = require('ora'); 8 | 9 | const ORIGIN = 'https://npmmirror.com/mirrors/electron/'; 10 | const TMPDIR = tmpdir(); 11 | const OUTDIR = '/node_modules/electron/'; 12 | const PATH_TXT = { 13 | 'darwin': 'Electron.app/Contents/MacOS/Electron', 14 | 'win32': 'electron.exe' 15 | }; 16 | 17 | 18 | /** 19 | * fsExistsSync 20 | * @param {String} filePath 21 | * @return {Boolean} 22 | */ 23 | const fsExistsSync = (filePath) => { 24 | try{ 25 | accessSync(resolve(filePath), constants.F_OK); 26 | }catch(e){ 27 | return false; 28 | } 29 | return true; 30 | } 31 | 32 | 33 | /** 34 | * setFileName 35 | * @param {Object / String} version symbols 36 | * @return {String} 37 | */ 38 | const setFileName = (data) => { 39 | const version = getVersion(data); 40 | if (!data || !version) throw 'version is undefined'; 41 | 42 | if (typeof data === 'string') return data; 43 | 44 | let name = [ 45 | 'electron', 46 | `v${version}`, 47 | `${platform()}`, 48 | `${arch()}` 49 | ]; 50 | 51 | if (data.symbols) name.push('symbols'); 52 | 53 | return name.join('-'); 54 | }; 55 | 56 | 57 | /** 58 | * downloadElectron 59 | * @param {String} url 60 | * @param {String} downloadDir 61 | * @return {String} 62 | */ 63 | const downloadElectron = async (url, downloadDir) => { 64 | const response = await axios({ 65 | method: 'GET', 66 | url: url, 67 | responseType: 'stream' 68 | }); 69 | 70 | response.data.pipe(createWriteStream(downloadDir)); 71 | 72 | return new Promise((resolve, reject) => { 73 | response.data.on('end', () => { 74 | resolve() 75 | }) 76 | response.data.on('error', () => { 77 | reject() 78 | }) 79 | }) 80 | }; 81 | 82 | 83 | /** 84 | * unzip 85 | * @param {String} entry 86 | * @param {String} output 87 | */ 88 | const unzip = (entry, output) => { 89 | if (!fsExistsSync(entry)) { 90 | throw 'File does not exist!'; 91 | } 92 | 93 | return new Promise((resolve, reject) => { 94 | const command = process.platform === 'win32' 95 | ? `powershell -Command "Expand-Archive -Path '${entry}' -DestinationPath '${output}' -Force"` 96 | : `unzip -o ${entry} -d ${output}`; 97 | 98 | exec(command, (error) => { 99 | if (error) { 100 | console.error(`exec error: ${error}`); 101 | reject(error); 102 | } else { 103 | resolve(); 104 | } 105 | }); 106 | }); 107 | }; 108 | 109 | 110 | /** 111 | * isInstallElectron 112 | * @param {Object} package 113 | * @param {String} electronPackagePath 114 | * @return {Boolean} 115 | */ 116 | const isInstallElectron = (package, electronPackagePath) => { 117 | if (package.PWD && !fsExistsSync(electronPackagePath)) { 118 | return false; 119 | } 120 | 121 | const { dependencies, devDependencies } = package; 122 | if ( 123 | (dependencies && dependencies.electron) || 124 | (devDependencies && devDependencies.electron) 125 | ) { 126 | return true; 127 | } 128 | return false; 129 | }; 130 | 131 | 132 | /** 133 | * getVersion 134 | * @param {Object} data 135 | * @return {String} result string 136 | */ 137 | const getVersion = (data) => { 138 | const { dependencies, devDependencies } = data; 139 | let version = ''; 140 | 141 | // Extract declared version from either dependencies or devDependencies 142 | if (dependencies?.electron) { 143 | version = dependencies.electron; 144 | } else if (devDependencies?.electron) { 145 | version = devDependencies.electron; 146 | } 147 | 148 | // Handle "catalog:" format (monorepo specific) 149 | if (version === 'catalog:' || version.startsWith('catalog:')) { 150 | try { 151 | // Execute pnpm command to get installed Electron details 152 | const output = execSync('pnpm list electron --depth Infinity --json', { 153 | encoding: 'utf-8', 154 | stdio: ['pipe', 'pipe', 'ignore'] // Suppress error output 155 | }); 156 | 157 | const packages = JSON.parse(output); 158 | 159 | // Find Electron version in dependency tree 160 | for (const pkg of packages) { 161 | if (pkg.devDependencies?.electron) { 162 | return pkg.devDependencies.electron.version; 163 | } 164 | if (pkg.dependencies?.electron) { 165 | return pkg.dependencies.electron.version; 166 | } 167 | } 168 | } catch (error) { 169 | console.warn('Failed to retrieve Electron version via pnpm:', error.message); 170 | } 171 | 172 | // Fallback to parsing catalog format if pnpm method fails 173 | if (version.startsWith('catalog:')) { 174 | return version.split(':')[1] || ''; 175 | } 176 | } 177 | 178 | // Remove version prefix characters (^/~) if present 179 | if (/^[\^~]/.test(version)) { 180 | return version.substring(1); 181 | } 182 | 183 | return version; 184 | }; 185 | 186 | /** 187 | * writeConfig 188 | * @param {Object} PWD pathTxt 189 | */ 190 | const writeConfig = (output, data) => { 191 | let outData = ''; 192 | 193 | if (data && data[platform()]) { 194 | outData = data[platform()]; 195 | } 196 | return new Promise((resolve, reject) => { 197 | writeFile(output, outData, error => { 198 | if (error) { 199 | reject(); 200 | } else { 201 | resolve(); 202 | } 203 | }); 204 | }); 205 | }; 206 | 207 | 208 | /** 209 | * fixElectron 210 | * @param {Object} 211 | * required: 212 | * PWD[string] 213 | * version[string] 214 | * other: 215 | * pathTxt[Object] 216 | * symbols[string] 217 | * origin[string] 218 | */ 219 | const fixElectron = (data) => { 220 | const version = getVersion(data); 221 | const fileName = setFileName(data) + '.zip'; 222 | const outDir = data.PWD + OUTDIR; 223 | const electronPackagePath = resolve(outDir, 'package.json'); 224 | 225 | console.log(chalk.default.bold('Electron version:', version)); 226 | const loading = ora(chalk.yellow('Loading...')).start(); 227 | 228 | if (isInstallElectron(data, electronPackagePath)) { 229 | const downloadUrl = (data.origin || ORIGIN) + version + '/' + fileName; 230 | const downloadDir = resolve(data.entry || TMPDIR, fileName); 231 | 232 | loading.text = 'Download Electron...'; 233 | 234 | downloadElectron(downloadUrl, downloadDir).then(() => { 235 | loading.succeed(chalk.green('Download Electron successful!')); 236 | 237 | const zipEntry = resolve(TMPDIR, fileName); 238 | const zipOutput = resolve(outDir, 'dist'); 239 | const configOutput = resolve(outDir, 'path.txt'); 240 | const configData = Object.assign({}, PATH_TXT, data.pathTxt || {}); 241 | 242 | Promise.all([ 243 | unzip(zipEntry, zipOutput).then(() => { 244 | loading.succeed(chalk.green('Unzip Electron successful!')); 245 | }), 246 | writeConfig(configOutput, configData).then(() => { 247 | loading.succeed(chalk.green('Write configuration succeeded!')); 248 | }) 249 | ]).then(() => { 250 | loading.succeed(chalk.green('Success!')); 251 | }); 252 | }).catch(error => { 253 | console.error(error); 254 | }); 255 | 256 | } else { 257 | loading.fail(`You didn't install electron!`); 258 | console.log( 259 | chalk.yellow.bold( 260 | `Try it 'yarn add electron' or 'npm install electron -D'.` 261 | ) 262 | ); 263 | } 264 | }; 265 | 266 | 267 | module.exports = { 268 | setFileName, 269 | downloadElectron, 270 | isInstallElectron, 271 | getVersion, 272 | writeConfig, 273 | fixElectron 274 | }; 275 | --------------------------------------------------------------------------------