196 lines
7.2 KiB
TypeScript
196 lines
7.2 KiB
TypeScript
|
|
import { createRequire } from "node:module";
|
||
|
|
import path from "node:path";
|
||
|
|
import { fileURLToPath } from "node:url";
|
||
|
|
|
||
|
|
import {
|
||
|
|
APP_KEYS,
|
||
|
|
OPEN_DESIGN_SIDECAR_CONTRACT,
|
||
|
|
SIDECAR_ENV,
|
||
|
|
SIDECAR_SOURCES,
|
||
|
|
} from "@open-design/sidecar-proto";
|
||
|
|
import {
|
||
|
|
resolveAppIpcPath,
|
||
|
|
resolveAppRuntimePath,
|
||
|
|
resolveLogFilePath,
|
||
|
|
resolveNamespace,
|
||
|
|
resolveNamespaceRoot,
|
||
|
|
resolveSidecarBase,
|
||
|
|
resolveSourceRuntimeRoot,
|
||
|
|
} from "@open-design/sidecar";
|
||
|
|
|
||
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||
|
|
const ENTRY_DIR_NAME = path.basename(__dirname);
|
||
|
|
|
||
|
|
export const WORKSPACE_ROOT = path.resolve(__dirname, ENTRY_DIR_NAME === "dist" ? "../../.." : "../../..");
|
||
|
|
|
||
|
|
export const ALL_APPS = [APP_KEYS.DAEMON, APP_KEYS.WEB, APP_KEYS.DESKTOP] as const;
|
||
|
|
export const DEFAULT_START_APPS = [APP_KEYS.DAEMON, APP_KEYS.WEB, APP_KEYS.DESKTOP] as const;
|
||
|
|
export const DEFAULT_RUN_APPS = [APP_KEYS.DAEMON, APP_KEYS.WEB] as const;
|
||
|
|
export const DEFAULT_STOP_APPS = [APP_KEYS.DESKTOP, APP_KEYS.WEB, APP_KEYS.DAEMON] as const;
|
||
|
|
|
||
|
|
export type ToolDevAppName = (typeof ALL_APPS)[number];
|
||
|
|
|
||
|
|
export type ToolDevOptions = {
|
||
|
|
daemonPort?: number | string | null;
|
||
|
|
json?: boolean;
|
||
|
|
namespace?: string;
|
||
|
|
prod?: boolean;
|
||
|
|
toolsDevRoot?: string;
|
||
|
|
webPort?: number | string | null;
|
||
|
|
};
|
||
|
|
|
||
|
|
export type ToolDevAppConfig = {
|
||
|
|
app: ToolDevAppName;
|
||
|
|
ipcPath: string;
|
||
|
|
latestLogPath: string;
|
||
|
|
logDir: string;
|
||
|
|
};
|
||
|
|
|
||
|
|
export type ToolDevConfig = {
|
||
|
|
apps: {
|
||
|
|
daemon: ToolDevAppConfig & {
|
||
|
|
sidecarEntryPath: string;
|
||
|
|
};
|
||
|
|
desktop: ToolDevAppConfig & {
|
||
|
|
electronBinaryPath: string;
|
||
|
|
mainEntryPath: string;
|
||
|
|
packageJsonPath: string;
|
||
|
|
};
|
||
|
|
web: ToolDevAppConfig & {
|
||
|
|
nextDistDir: string;
|
||
|
|
nextTsconfigPath: string;
|
||
|
|
sidecarEntryPath: string;
|
||
|
|
};
|
||
|
|
};
|
||
|
|
namespace: string;
|
||
|
|
namespaceRoot: string;
|
||
|
|
toolsDevRoot: string;
|
||
|
|
tsxCliPath: string;
|
||
|
|
workspaceRoot: string;
|
||
|
|
};
|
||
|
|
|
||
|
|
function resolveTsxCliPath(): string {
|
||
|
|
const require = createRequire(import.meta.url);
|
||
|
|
return require.resolve("tsx/cli");
|
||
|
|
}
|
||
|
|
|
||
|
|
function resolveElectronBinaryPath(workspaceRoot: string): string {
|
||
|
|
const packageJsonPath = path.join(workspaceRoot, "apps/desktop/package.json");
|
||
|
|
const require = createRequire(packageJsonPath);
|
||
|
|
const electron = require("electron") as unknown;
|
||
|
|
if (typeof electron === "string" && electron.length > 0) return electron;
|
||
|
|
return require.resolve("electron/cli.js");
|
||
|
|
}
|
||
|
|
|
||
|
|
function resolveAppConfig(options: {
|
||
|
|
app: ToolDevAppName;
|
||
|
|
namespace: string;
|
||
|
|
namespaceRoot: string;
|
||
|
|
toolsDevRoot: string;
|
||
|
|
}): ToolDevAppConfig {
|
||
|
|
return {
|
||
|
|
app: options.app,
|
||
|
|
ipcPath: resolveAppIpcPath({
|
||
|
|
app: options.app,
|
||
|
|
contract: OPEN_DESIGN_SIDECAR_CONTRACT,
|
||
|
|
namespace: options.namespace,
|
||
|
|
}),
|
||
|
|
latestLogPath: resolveLogFilePath({ runtimeRoot: options.namespaceRoot, app: options.app, contract: OPEN_DESIGN_SIDECAR_CONTRACT }),
|
||
|
|
logDir: path.dirname(resolveLogFilePath({ runtimeRoot: options.namespaceRoot, app: options.app, contract: OPEN_DESIGN_SIDECAR_CONTRACT })),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
export function isToolDevAppName(value: string): value is ToolDevAppName {
|
||
|
|
return ALL_APPS.includes(value as ToolDevAppName);
|
||
|
|
}
|
||
|
|
|
||
|
|
function unsupportedAppError(value: string): Error {
|
||
|
|
return new Error(`unsupported tools-dev app: ${value} (expected one of: ${ALL_APPS.join(", ")})`);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function resolveTargetApps(appName: string | undefined, defaults: readonly ToolDevAppName[]): ToolDevAppName[] {
|
||
|
|
if (appName == null) return [...defaults];
|
||
|
|
if (!isToolDevAppName(appName)) throw unsupportedAppError(appName);
|
||
|
|
return [appName];
|
||
|
|
}
|
||
|
|
|
||
|
|
export function resolveStartApps(appName: string | undefined): ToolDevAppName[] {
|
||
|
|
if (appName == null) return [...DEFAULT_START_APPS];
|
||
|
|
if (!isToolDevAppName(appName)) throw unsupportedAppError(appName);
|
||
|
|
if (appName === APP_KEYS.WEB) return [APP_KEYS.DAEMON, APP_KEYS.WEB];
|
||
|
|
if (appName === APP_KEYS.DESKTOP) return [APP_KEYS.DAEMON, APP_KEYS.WEB, APP_KEYS.DESKTOP];
|
||
|
|
return [APP_KEYS.DAEMON];
|
||
|
|
}
|
||
|
|
|
||
|
|
export function resolveRunApps(appName: string | undefined): ToolDevAppName[] {
|
||
|
|
if (appName == null) return [...DEFAULT_RUN_APPS];
|
||
|
|
return resolveStartApps(appName);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function resolveStopApps(appName: string | undefined): ToolDevAppName[] {
|
||
|
|
if (appName == null) return [...DEFAULT_STOP_APPS];
|
||
|
|
if (!isToolDevAppName(appName)) throw unsupportedAppError(appName);
|
||
|
|
if (appName === APP_KEYS.WEB) return [APP_KEYS.WEB, APP_KEYS.DAEMON];
|
||
|
|
if (appName === APP_KEYS.DESKTOP) return [APP_KEYS.DESKTOP];
|
||
|
|
return [APP_KEYS.DAEMON];
|
||
|
|
}
|
||
|
|
|
||
|
|
export function parsePortOption(value: number | string | null | undefined, optionName: string): number | null {
|
||
|
|
if (value == null || value === "") return null;
|
||
|
|
const parsed = Number(value);
|
||
|
|
if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
|
||
|
|
throw new Error(`${optionName} must be an integer between 1 and 65535`);
|
||
|
|
}
|
||
|
|
return parsed;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function resolveToolDevConfig(options: ToolDevOptions = {}): ToolDevConfig {
|
||
|
|
const namespace = resolveNamespace({ namespace: options.namespace, env: process.env, contract: OPEN_DESIGN_SIDECAR_CONTRACT });
|
||
|
|
const toolsDevRoot = resolveSidecarBase({
|
||
|
|
base: options.toolsDevRoot ?? process.env[SIDECAR_ENV.BASE] ?? resolveSourceRuntimeRoot({
|
||
|
|
contract: OPEN_DESIGN_SIDECAR_CONTRACT,
|
||
|
|
projectRoot: WORKSPACE_ROOT,
|
||
|
|
source: SIDECAR_SOURCES.TOOLS_DEV,
|
||
|
|
}),
|
||
|
|
contract: OPEN_DESIGN_SIDECAR_CONTRACT,
|
||
|
|
env: process.env,
|
||
|
|
projectRoot: WORKSPACE_ROOT,
|
||
|
|
source: SIDECAR_SOURCES.TOOLS_DEV,
|
||
|
|
});
|
||
|
|
const namespaceRoot = resolveNamespaceRoot({ base: toolsDevRoot, namespace, contract: OPEN_DESIGN_SIDECAR_CONTRACT });
|
||
|
|
const daemon = resolveAppConfig({ app: APP_KEYS.DAEMON, namespace, namespaceRoot, toolsDevRoot });
|
||
|
|
const desktop = resolveAppConfig({ app: APP_KEYS.DESKTOP, namespace, namespaceRoot, toolsDevRoot });
|
||
|
|
const web = resolveAppConfig({ app: APP_KEYS.WEB, namespace, namespaceRoot, toolsDevRoot });
|
||
|
|
const desktopPackageJsonPath = path.join(WORKSPACE_ROOT, "apps/desktop/package.json");
|
||
|
|
let cachedElectronBinaryPath: string | undefined;
|
||
|
|
|
||
|
|
return {
|
||
|
|
apps: {
|
||
|
|
daemon: {
|
||
|
|
...daemon,
|
||
|
|
sidecarEntryPath: path.join(WORKSPACE_ROOT, "apps/daemon/src/sidecar/index.ts"),
|
||
|
|
},
|
||
|
|
desktop: {
|
||
|
|
...desktop,
|
||
|
|
get electronBinaryPath() {
|
||
|
|
if (cachedElectronBinaryPath == null) cachedElectronBinaryPath = resolveElectronBinaryPath(WORKSPACE_ROOT);
|
||
|
|
return cachedElectronBinaryPath;
|
||
|
|
},
|
||
|
|
mainEntryPath: path.join(WORKSPACE_ROOT, "apps/desktop/dist/main/index.js"),
|
||
|
|
packageJsonPath: desktopPackageJsonPath,
|
||
|
|
},
|
||
|
|
web: {
|
||
|
|
...web,
|
||
|
|
nextDistDir: resolveAppRuntimePath({ app: APP_KEYS.WEB, namespaceRoot, fileName: "next", contract: OPEN_DESIGN_SIDECAR_CONTRACT }),
|
||
|
|
nextTsconfigPath: resolveAppRuntimePath({ app: APP_KEYS.WEB, namespaceRoot, fileName: "tsconfig.json", contract: OPEN_DESIGN_SIDECAR_CONTRACT }),
|
||
|
|
sidecarEntryPath: path.join(WORKSPACE_ROOT, "apps/web/sidecar/index.ts"),
|
||
|
|
},
|
||
|
|
},
|
||
|
|
namespace,
|
||
|
|
namespaceRoot,
|
||
|
|
toolsDevRoot,
|
||
|
|
tsxCliPath: resolveTsxCliPath(),
|
||
|
|
workspaceRoot: WORKSPACE_ROOT,
|
||
|
|
};
|
||
|
|
}
|