import type { PluginOption } from 'vite' import * as path from 'path' import * as fs from 'fs-extra' export interface ElectronBytecodeOptions { entry?: string keepSource?: boolean } function shouldSkipBytecode(): boolean { const flag = String(process.env.SKIP_ELECTRON_BYTECODE ?? '').trim().toLowerCase() return flag === '1' || flag === 'true' || flag === 'yes' } function resolveElectronExecutable(): string | null { try { const electronPath = require('electron') if (typeof electronPath === 'string' && fs.pathExistsSync(electronPath)) { return electronPath } } catch (error) { console.warn('[vite-plugin-electron-encrypt] Skipping bytecode compilation because Electron is unavailable:', error) return null } console.warn('[vite-plugin-electron-encrypt] Skipping bytecode compilation because the Electron executable could not be resolved.') return null } export default function electronBytecode(options?: ElectronBytecodeOptions): PluginOption { return { name: 'vite-plugin-electron-encrypt', apply: 'build', async closeBundle() { if (process.env.NODE_ENV !== 'production') return if (shouldSkipBytecode()) { console.warn('[vite-plugin-electron-encrypt] Skipping bytecode compilation because SKIP_ELECTRON_BYTECODE is enabled.') return } const entryPath = path.resolve(process.cwd(), options?.entry || 'dist-electron/main/main.js'); if (!fs.pathExistsSync(entryPath)) return const electronPath = resolveElectronExecutable() if (!electronPath) return const outputPath = entryPath.replace(/\.js$/, '.jsc') const backupPath = `${entryPath}.bak` const tempScriptPath = path.join(path.dirname(entryPath), '_compile.js') const { execFileSync } = require('child_process') fs.copyFileSync(entryPath, backupPath) try { // We must compile the bytecode using the ELECTRON executable, not the local Node.js. // Node.js and Electron use different V8 versions. // Run a small script inside Electron to compile the file const compileScript = ` const bytenode = require('bytenode'); bytenode.compileFile({ filename: ${JSON.stringify(entryPath)}, output: ${JSON.stringify(outputPath)} }).then(() => process.exit(0)).catch(e => { console.error(e); process.exit(1); }); `; fs.writeFileSync(tempScriptPath, compileScript); execFileSync(electronPath, [tempScriptPath], { env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' } }); } catch (err) { console.error('Failed to compile bytecode with Electron:', err); fs.copyFileSync(backupPath, entryPath) throw err; } finally { if (fs.pathExistsSync(tempScriptPath)) { fs.unlinkSync(tempScriptPath) } } const stub = [ "require('bytenode')", `require('./${path.basename(outputPath)}')` ].join("\n") fs.writeFileSync(entryPath, stub) } } }