Files
gelectron/cli/gelectron.js
T
miles 1689adffc6 feat: enhance native binary resolution and add bundled Node.js runtime
- Refactored gelectron.js to improve native binary discovery with additional paths and environment variable support.
- Implemented a function to find the Node.js runtime, allowing bundled Node.js to be used when available.
- Updated package.json to include gelectron-packager as a CLI command.
- Enhanced gelectron-packager.js to locate installed gelectron runtime and added compatibility for bundled Node.js.
- Modified install-release.sh to download and install a private Node.js runtime alongside the gelectron binary.
- Updated NSIS installer script to include bundled Node.js runtime.
- Enhanced AppImage and DMG packaging scripts to include Node.js runtime.
- Added clipboard, nativeTheme, and screen modules for Electron compatibility.
- Implemented native bridge request handling for clipboard operations.
2026-09-05 22:23:37 -05:00

183 lines
6.3 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Gelectron CLI - Run Electron apps with the Servo rendering engine.
*
* Usage:
* gelectron <path-to-app> Run an Electron app
* gelectron --version Print version
* gelectron --help Show help
*/
'use strict';
const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
const VERSION = require('../package.json').version;
const args = process.argv.slice(2);
if (args.includes('--version') || args.includes('-v')) {
console.log(`gelectron v${VERSION}`);
process.exit(0);
}
if (args.includes('--help') || args.includes('-h') || args.length === 0) {
console.log(`
gelectron v${VERSION} - Firefox-engine Electron alternative
Usage:
gelectron <path-to-app> Run an Electron app
gelectron <file.js> Run a main process script directly
gelectron --version Print version
gelectron --help Show this help
Examples:
gelectron . Run the app in the current directory
gelectron ./my-app Run a specific app
gelectron main.js Run a main process script directly
Environment Variables:
GELECTRON_DEV=1 Enable development mode
GELECTRON_LOG=1 Enable verbose logging
VITE_DEV_SERVER_URL=<url> Connect to a Vite dev server
`.trim());
process.exit(0);
}
const appPath = path.resolve(args[0]);
if (!fs.existsSync(appPath)) {
console.error(`gelectron: error: path not found: ${appPath}`);
process.exit(1);
}
let mainScript;
let isPackaged = false;
if (fs.statSync(appPath).isDirectory()) {
const pkgPath = path.join(appPath, 'package.json');
if (!fs.existsSync(pkgPath)) {
console.error(`gelectron: error: no package.json found in ${appPath}`);
process.exit(1);
}
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
mainScript = path.join(appPath, pkg.main || 'index.js');
isPackaged = true;
} else {
mainScript = appPath;
}
if (!fs.existsSync(mainScript)) {
console.error(`gelectron: error: main script not found: ${mainScript}`);
process.exit(1);
}
const nativeDir = path.join(__dirname, '..', 'crates', 'gelectron-core');
const platformSuffix = process.platform === 'win32' ? 'win32' : process.platform === 'darwin' ? 'darwin' : 'linux';
const archSuffix = process.arch === 'arm64' ? 'arm64' : 'x64';
const ext = process.platform === 'win32' ? 'dll' : process.platform === 'darwin' ? 'dylib' : 'so';
const napiPlatform = process.platform === 'win32' ? `${platformSuffix}-${archSuffix}-msvc` : process.platform === 'darwin' ? `${platformSuffix}-${archSuffix}` : `${platformSuffix}-${archSuffix}-gnu`;
const candidates = [
path.join(nativeDir, `gelectron_core.${napiPlatform}.node`),
path.join(__dirname, '..', `gelectron_core.${napiPlatform}.node`),
path.join(__dirname, '..', 'npm', napiPlatform, `gelectron_core.${napiPlatform}.node`),
path.join(nativeDir, 'release', `gelectron_core.${ext}`),
path.join(nativeDir, 'debug', `gelectron_core.${ext}`),
path.join(__dirname, '..', `gelectron_core.${ext}`),
path.join(__dirname, '..', 'napi-dist', process.platform, process.arch, `gelectron_core.${ext}`),
];
let nativeAddonPath = null;
for (const candidate of candidates) {
if (fs.existsSync(candidate)) {
nativeAddonPath = candidate;
break;
}
}
const env = {
...process.env,
GELECTRON_MAIN_SCRIPT: mainScript,
GELECTRON_APP_PATH: isPackaged ? appPath : path.dirname(mainScript),
GELECTRON_NATIVE_ADDON: nativeAddonPath || '',
GELECTRON_VERSION: VERSION,
GELECTRON_DEV: process.env.GELECTRON_DEV || '',
};
if (process.env.GELECTRON_LOG) {
console.log(`[gelectron] main script: ${mainScript}`);
console.log(`[gelectron] app path: ${env.GELECTRON_APP_PATH}`);
console.log(`[gelectron] native addon: ${nativeAddonPath || 'not found (using build target)'}`);
}
const rustBin = path.join(__dirname, '..', 'target', 'release', 'gelectron');
const rustBinDebug = path.join(__dirname, '..', 'target', 'debug', 'gelectron');
function findNativeBinary() {
const candidates = [];
if (process.env.GELECTRON_BINARY) candidates.push(process.env.GELECTRON_BINARY);
candidates.push(rustBin, rustBinDebug);
// npm-installed layout: the binary ships alongside the CLI
candidates.push(path.join(__dirname, '..', 'bin', 'gelectron'));
const platform = process.platform;
if (platform === 'win32') {
for (const pf of [process.env.PROGRAMFILES, process.env['PROGRAMFILES(X86)'], 'C:\\Program Files']) {
if (pf) candidates.push(path.join(pf, 'Gelectron', 'gelectron.exe'));
}
candidates.push(path.join(process.env.LOCALAPPDATA || '', 'gelectron', 'gelectron.exe'));
} else {
candidates.push('/usr/local/bin/gelectron');
candidates.push('/usr/local/lib/gelectron/gelectron');
candidates.push('/usr/bin/gelectron');
candidates.push('/opt/homebrew/bin/gelectron');
candidates.push(path.join(process.env.HOME || '', '.local', 'bin', 'gelectron'));
}
const exeSuffix = platform === 'win32' ? '.exe' : '';
for (const c of candidates) {
for (const candidate of [c, c + exeSuffix]) {
if (candidate && fs.existsSync(candidate)) return candidate;
}
}
// PATH lookup (which / where)
try {
const { execSync } = require('child_process');
const cmd = platform === 'win32' ? 'where gelectron' : 'which gelectron';
const out = execSync(cmd, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).toString();
const first = out.split(/\r?\n/).map((s) => s.trim()).find((s) => s.length > 0);
if (first && fs.existsSync(first)) return first;
} catch (e) {
// Not on PATH
}
return null;
}
let executable = findNativeBinary();
if (executable && process.platform !== 'win32') {
try {
fs.accessSync(executable, fs.constants.X_OK);
} catch (e) {
fs.chmodSync(executable, 0o755);
}
}
if (executable) {
if (process.env.GELECTRON_LOG) console.log(`[gelectron] Using native binary: ${executable}`);
const child = spawn(executable, args, {
env,
stdio: 'inherit',
cwd: process.cwd(),
});
child.on('exit', (code) => process.exit(code || 0));
} else {
console.log(`[gelectron] Native runtime not found. Install it or run: cargo build --release -p gelectron`);
console.log(`[gelectron] Falling back to the Node.js compatibility layer (no native window support)...\n`);
require('../src/electron/runtime.js').run(mainScript, env);
}