Fix CLI routing: single classifier, open-project for cold & hot start
- Add classify.ts as the single source of truth for CLI invocation routing (discriminated union, derives known commands from Commander) - Remove all hardcoded command lists and duplicate path detection logic - Simplify shell wrappers to dumb pipes (zero classification) - Fix hot-start: use `open -n -g -a` (VS Code pattern) so second-instance event fires when app is already running - Fix cold-start race: pull-based IPC (getPendingOpenProject) so renderer fetches the pending path after React mounts, instead of push event that arrived before the listener existed - Fix asar read corruption: unpack node-entrypoint-runner.js from asar (Node.js v24 in Electron 41 can't parse package.json inside asar) - Strip ELECTRON_RUN_AS_NODE from env when spawning desktop app from CLI
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import { existsSync, statSync } from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
export type CliInvocation =
|
||||
| { kind: "cli"; argv: string[] }
|
||||
| { kind: "open-project"; resolvedPath: string };
|
||||
|
||||
export function isPathLikeArg(arg: string): boolean {
|
||||
return (
|
||||
arg === "." ||
|
||||
arg === ".." ||
|
||||
arg.startsWith("./") ||
|
||||
arg.startsWith("../") ||
|
||||
arg.startsWith("/") ||
|
||||
arg === "~" ||
|
||||
arg.startsWith("~/") ||
|
||||
/^[A-Za-z]:[\\/]/.test(arg)
|
||||
);
|
||||
}
|
||||
|
||||
export function expandUserPath(inputPath: string): string {
|
||||
if (inputPath === "~") {
|
||||
return homedir();
|
||||
}
|
||||
|
||||
if (inputPath.startsWith("~/")) {
|
||||
return path.join(homedir(), inputPath.slice(2));
|
||||
}
|
||||
|
||||
return inputPath;
|
||||
}
|
||||
|
||||
export function isExistingDirectory(input: { pathArg: string; cwd: string }): boolean {
|
||||
const resolvedPath = path.resolve(input.cwd, expandUserPath(input.pathArg));
|
||||
|
||||
if (!existsSync(resolvedPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return statSync(resolvedPath).isDirectory();
|
||||
}
|
||||
|
||||
export function classifyInvocation(input: {
|
||||
argv: string[];
|
||||
knownCommands: ReadonlySet<string>;
|
||||
cwd: string;
|
||||
}): CliInvocation {
|
||||
const [firstArg] = input.argv;
|
||||
if (!firstArg) {
|
||||
return { kind: "cli", argv: input.argv };
|
||||
}
|
||||
|
||||
if (firstArg.startsWith("-")) {
|
||||
return { kind: "cli", argv: input.argv };
|
||||
}
|
||||
|
||||
if (input.knownCommands.has(firstArg)) {
|
||||
return { kind: "cli", argv: input.argv };
|
||||
}
|
||||
|
||||
if (isExistingDirectory({ pathArg: firstArg, cwd: input.cwd })) {
|
||||
return {
|
||||
kind: "open-project",
|
||||
resolvedPath: path.resolve(input.cwd, expandUserPath(firstArg)),
|
||||
};
|
||||
}
|
||||
|
||||
return { kind: "cli", argv: input.argv };
|
||||
}
|
||||
Reference in New Issue
Block a user