mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-29 13:05:18 +02:00
94 lines
2.9 KiB
JavaScript
94 lines
2.9 KiB
JavaScript
#!/usr/bin/env node
|
|
'use strict';
|
|
|
|
/**
|
|
* Shared cross-platform browser launcher.
|
|
*
|
|
* Extracted from scripts/plan-canvas.js (which had a working but error-silent
|
|
* tri-platform branch) and scripts/control-pane.js (which had a darwin-only
|
|
* branch that silently no-op'd on Windows/Linux). This helper:
|
|
*
|
|
* 1. Dispatches `open` / `cmd /c start` / `xdg-open` based on process.platform
|
|
* 2. Handles the child's 'error' event so a missing launcher does not cause
|
|
* an unhandled error after a detached spawn
|
|
* 3. Returns a structured { opened, reason } result for the launch request.
|
|
* Later asynchronous errors cannot change the returned result; success
|
|
* does not prove a browser opened.
|
|
*
|
|
* The signature is intentionally small (single function, no class) so callers
|
|
* can import without picking up the rest of scripts/lib.
|
|
*
|
|
* Tests live at tests/lib/platform-launch.test.js.
|
|
*/
|
|
|
|
const { spawn } = require('child_process');
|
|
|
|
/**
|
|
* Pick the platform-appropriate opener command + args.
|
|
* Returns [cmd, args] suitable for child_process.spawn.
|
|
*
|
|
* @param {NodeJS.Platform} platform
|
|
* @param {string} url
|
|
* @returns {[string, string[]]}
|
|
*/
|
|
function openerCommandFor(platform, url) {
|
|
if (platform === 'darwin') return ['open', [url]];
|
|
if (platform === 'win32') return ['cmd', ['/c', 'start', '', url]];
|
|
return ['xdg-open', [url]];
|
|
}
|
|
|
|
/**
|
|
* Open a URL in the user's default browser, dispatching per-platform.
|
|
*
|
|
* Returns the synchronous launch-request result. Asynchronous child errors
|
|
* are handled, but are not an acknowledgment that a browser opened.
|
|
*
|
|
* @param {string} url
|
|
* @param {NodeJS.Platform} [platform] - injectable for tests; defaults to process.platform
|
|
* @param {typeof spawn} [spawnProcess] - injectable process launcher for tests
|
|
* @returns {{ opened: boolean, reason: string }}
|
|
*/
|
|
function openBrowser(url, platform = process.platform, spawnProcess = spawn) {
|
|
if (typeof url !== 'string' || url.length === 0) {
|
|
return { opened: false, reason: 'invalid-url' };
|
|
}
|
|
|
|
const [cmd, args] = openerCommandFor(platform, url);
|
|
let child;
|
|
try {
|
|
child = spawnProcess(cmd, args, {
|
|
detached: true,
|
|
stdio: 'ignore',
|
|
});
|
|
} catch (err) {
|
|
return {
|
|
opened: false,
|
|
reason: `spawn-threw:${err && err.code ? err.code : 'unknown'}`,
|
|
};
|
|
}
|
|
|
|
// Listen for ENOENT/EACCES/etc that would otherwise be silently swallowed
|
|
// when the user has no `open` / `xdg-open` / `start` available.
|
|
let capturedError = null;
|
|
child.on('error', (err) => {
|
|
capturedError = err && err.code ? err.code : 'spawn-error';
|
|
});
|
|
|
|
// Best-effort: detach so we don't keep the parent alive on the launcher.
|
|
try {
|
|
child.unref();
|
|
} catch {
|
|
/* unref may throw on some platforms; safe to ignore */
|
|
}
|
|
|
|
if (capturedError) {
|
|
return { opened: false, reason: `child-error:${capturedError}` };
|
|
}
|
|
return { opened: true, reason: 'spawned' };
|
|
}
|
|
|
|
module.exports = {
|
|
openBrowser,
|
|
openerCommandFor,
|
|
};
|