mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-25 08:32:11 +03:00
fix(cli): resolve tray runtime import to a file:// URL so --tray works on Windows (#11332)
Merged via consolidated batch validation. Fixes omniroute server --tray on Windows: absolute paths passed to dynamic import() are parsed as URLs, and a Windows drive letter (C:) isn't a supported URL scheme. Resolves via pathToFileURL. Own regression test passes.
This commit is contained in:
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, writeFileSync, chmodSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { homedir } from "node:os";
|
||||
import { execSync } from "node:child_process";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const RUNTIME_DIR = join(homedir(), ".omniroute", "runtime");
|
||||
// systray2 is a maintained fork with prebuilt binaries — installed lazily at runtime,
|
||||
@@ -16,6 +17,16 @@ export const SYSTRAY_PACKAGE = "systray2";
|
||||
export const SYSTRAY_VERSION = "2.1.4";
|
||||
const SYSTRAY_SPEC = `${SYSTRAY_PACKAGE}@${SYSTRAY_VERSION}`;
|
||||
|
||||
// Dynamic `import()` resolves its specifier as a URL, not a filesystem path.
|
||||
// On Windows the lazily-installed systray2 lives at an absolute path whose
|
||||
// leading drive letter the ESM loader parses as an unsupported URL scheme
|
||||
// (e.g. `c:`) and rejects. Build a file:// URL so the tray import works on
|
||||
// Windows too. Same defect fixed for the CLI db-fallback imports in #11238,
|
||||
// missed at this call site.
|
||||
export function systrayModuleSpecifier(runtimeDir: string): string {
|
||||
return pathToFileURL(join(runtimeDir, "node_modules", SYSTRAY_PACKAGE)).href;
|
||||
}
|
||||
|
||||
export function resolveSystrayBinName(platform: NodeJS.Platform): string | null {
|
||||
if (platform === "win32") return "tray_windows_release.exe";
|
||||
if (platform === "darwin") return "tray_darwin_release";
|
||||
@@ -60,8 +71,7 @@ export async function loadSystray(): Promise<(new (...args: unknown[]) => unknow
|
||||
// drop the +x bit on extraction (observed on macOS).
|
||||
chmodSystrayBinAt(RUNTIME_DIR, process.platform);
|
||||
try {
|
||||
const modPath = join(RUNTIME_DIR, "node_modules", SYSTRAY_PACKAGE);
|
||||
const mod = await import(modPath);
|
||||
const mod = await import(systrayModuleSpecifier(RUNTIME_DIR));
|
||||
return (mod.default ?? mod.SysTray ?? mod) as (new (...args: unknown[]) => unknown) | null;
|
||||
} catch (err) {
|
||||
console.warn(`[omniroute] tray runtime import failed: ${(err as Error).message}`);
|
||||
|
||||
48
tests/unit/cli/tray-runtime-windows-esm-import.test.ts
Normal file
48
tests/unit/cli/tray-runtime-windows-esm-import.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import path from "node:path";
|
||||
import os from "node:os";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import {
|
||||
systrayModuleSpecifier,
|
||||
SYSTRAY_PACKAGE,
|
||||
} from "../../../bin/cli/runtime/trayRuntime.ts";
|
||||
|
||||
// Regression guard for the Windows-only ESM loader failure at the lazy tray
|
||||
// import in bin/cli/runtime/trayRuntime.ts (loadSystray):
|
||||
//
|
||||
// Error: Only URLs with a scheme in: file, data, and node are supported by
|
||||
// the default ESM loader. On Windows, absolute paths must be valid file://
|
||||
// URLs. Received protocol 'c:'
|
||||
//
|
||||
// `import()` resolves its specifier as a URL. A POSIX absolute path like
|
||||
// /home/x/.omniroute/runtime/node_modules/systray2 doubles as a valid relative
|
||||
// URL, so passing it works by accident on Linux/macOS (and CI stays green). A
|
||||
// Windows absolute path is C:\Users\x\.omniroute\runtime\node_modules\systray2,
|
||||
// whose leading drive letter the loader parses as the URL scheme `c:` and
|
||||
// rejects — so `omniroute server --tray` never loads the tray on Windows.
|
||||
// This is the same defect as #11238 (CLI db-fallback imports), which missed
|
||||
// this call site. The specifier must be a file:// URL.
|
||||
|
||||
test("systrayModuleSpecifier returns a file:// URL, not a raw absolute path", () => {
|
||||
const runtimeDir = path.join(os.homedir(), ".omniroute", "runtime");
|
||||
const spec = systrayModuleSpecifier(runtimeDir);
|
||||
|
||||
assert.match(
|
||||
spec,
|
||||
/^file:\/\//,
|
||||
"dynamic import() of a raw absolute path fails on Windows (drive letter " +
|
||||
"parsed as a URL scheme); wrap the path in pathToFileURL(...).href",
|
||||
);
|
||||
assert.ok(spec.includes(SYSTRAY_PACKAGE), "specifier must target the systray2 package");
|
||||
// A file:// URL is a loader-acceptable specifier on every platform.
|
||||
assert.doesNotThrow(() => new URL(spec));
|
||||
});
|
||||
|
||||
test("systrayModuleSpecifier matches pathToFileURL of the module directory", () => {
|
||||
const runtimeDir = path.join(os.tmpdir(), "omniroute-tray-spec-test");
|
||||
const expected = pathToFileURL(
|
||||
path.join(runtimeDir, "node_modules", SYSTRAY_PACKAGE),
|
||||
).href;
|
||||
assert.equal(systrayModuleSpecifier(runtimeDir), expected);
|
||||
});
|
||||
Reference in New Issue
Block a user