From b21204c7a0c2ef128f0220d93676df056c7dcc2b Mon Sep 17 00:00:00 2001 From: sudacode Date: Tue, 10 Feb 2026 00:55:43 -0800 Subject: [PATCH] refactor: extract config generation startup flow --- package.json | 2 +- .../config-generation-runtime-service.test.ts | 67 +++++++++++++++++++ .../config-generation-runtime-service.ts | 26 +++++++ src/main.ts | 27 +++++--- 4 files changed, 110 insertions(+), 12 deletions(-) create mode 100644 src/core/services/config-generation-runtime-service.test.ts create mode 100644 src/core/services/config-generation-runtime-service.ts diff --git a/package.json b/package.json index 4854bd5..1c5a6a6 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "docs:build": "vitepress build docs", "docs:preview": "vitepress preview docs --host 0.0.0.0 --port 4173 --strictPort", "test:config": "pnpm run build && node --test dist/config/config.test.js", - "test:core": "pnpm run build && node --test dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command-service.test.js dist/core/services/numeric-shortcut-session-service.test.js dist/core/services/secondary-subtitle-service.test.js dist/core/services/mpv-render-metrics-service.test.js dist/core/services/mpv-runtime-service.test.js dist/core/services/runtime-options-runtime-service.test.js dist/core/services/overlay-modal-restore-service.test.js dist/core/services/runtime-config-service.test.js dist/core/services/overlay-bridge-runtime-service.test.js dist/core/services/overlay-visibility-facade-service.test.js dist/core/services/overlay-broadcast-runtime-service.test.js dist/core/services/app-ready-runtime-service.test.js dist/core/services/app-shutdown-runtime-service.test.js dist/core/services/mpv-client-deps-runtime-service.test.js dist/core/services/app-lifecycle-deps-runtime-service.test.js dist/core/services/runtime-options-manager-runtime-service.test.js dist/core/services/config-warning-runtime-service.test.js dist/core/services/app-logging-runtime-service.test.js dist/core/services/startup-resource-runtime-service.test.js", + "test:core": "pnpm run build && node --test dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command-service.test.js dist/core/services/numeric-shortcut-session-service.test.js dist/core/services/secondary-subtitle-service.test.js dist/core/services/mpv-render-metrics-service.test.js dist/core/services/mpv-runtime-service.test.js dist/core/services/runtime-options-runtime-service.test.js dist/core/services/overlay-modal-restore-service.test.js dist/core/services/runtime-config-service.test.js dist/core/services/overlay-bridge-runtime-service.test.js dist/core/services/overlay-visibility-facade-service.test.js dist/core/services/overlay-broadcast-runtime-service.test.js dist/core/services/app-ready-runtime-service.test.js dist/core/services/app-shutdown-runtime-service.test.js dist/core/services/mpv-client-deps-runtime-service.test.js dist/core/services/app-lifecycle-deps-runtime-service.test.js dist/core/services/runtime-options-manager-runtime-service.test.js dist/core/services/config-warning-runtime-service.test.js dist/core/services/app-logging-runtime-service.test.js dist/core/services/startup-resource-runtime-service.test.js dist/core/services/config-generation-runtime-service.test.js", "test:subtitle": "pnpm run build && node --test dist/subtitle/stages.test.js dist/subtitle/pipeline.test.js", "generate:config-example": "pnpm run build && node dist/generate-config-example.js", "start": "pnpm run build && electron . --start", diff --git a/src/core/services/config-generation-runtime-service.test.ts b/src/core/services/config-generation-runtime-service.test.ts new file mode 100644 index 0000000..2551292 --- /dev/null +++ b/src/core/services/config-generation-runtime-service.test.ts @@ -0,0 +1,67 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { runGenerateConfigFlowRuntimeService } from "./config-generation-runtime-service"; +import { CliArgs } from "../../cli/args"; + +function makeArgs(overrides: Partial = {}): CliArgs { + return { + start: false, + stop: false, + toggle: false, + toggleVisibleOverlay: false, + toggleInvisibleOverlay: false, + settings: false, + show: false, + hide: false, + showVisibleOverlay: false, + hideVisibleOverlay: false, + showInvisibleOverlay: false, + hideInvisibleOverlay: false, + copySubtitle: false, + copySubtitleMultiple: false, + mineSentence: false, + mineSentenceMultiple: false, + updateLastCardFromClipboard: false, + toggleSecondarySub: false, + triggerFieldGrouping: false, + triggerSubsync: false, + markAudioCard: false, + openRuntimeOptions: false, + texthooker: false, + help: false, + autoStartOverlay: false, + generateConfig: false, + backupOverwrite: false, + verbose: false, + ...overrides, + }; +} + +test("runGenerateConfigFlowRuntimeService starts flow when generateConfig is set and app should not start", async () => { + const calls: string[] = []; + const handled = runGenerateConfigFlowRuntimeService( + makeArgs({ generateConfig: true }), + { + shouldStartApp: () => false, + generateConfig: async () => 7, + onSuccess: (code) => calls.push(`success:${code}`), + onError: () => calls.push("error"), + }, + ); + assert.equal(handled, true); + await new Promise((resolve) => setImmediate(resolve)); + assert.deepEqual(calls, ["success:7"]); +}); + +test("runGenerateConfigFlowRuntimeService returns false when flow should not run", () => { + const handled = runGenerateConfigFlowRuntimeService( + makeArgs({ generateConfig: true, start: true }), + { + shouldStartApp: () => true, + generateConfig: async () => 0, + onSuccess: () => {}, + onError: () => {}, + }, + ); + assert.equal(handled, false); +}); diff --git a/src/core/services/config-generation-runtime-service.ts b/src/core/services/config-generation-runtime-service.ts new file mode 100644 index 0000000..14b34c6 --- /dev/null +++ b/src/core/services/config-generation-runtime-service.ts @@ -0,0 +1,26 @@ +import { CliArgs } from "../../cli/args"; + +export interface ConfigGenerationRuntimeDeps { + shouldStartApp: (args: CliArgs) => boolean; + generateConfig: (args: CliArgs) => Promise; + onSuccess: (exitCode: number) => void; + onError: (error: Error) => void; +} + +export function runGenerateConfigFlowRuntimeService( + args: CliArgs, + deps: ConfigGenerationRuntimeDeps, +): boolean { + if (!args.generateConfig || deps.shouldStartApp(args)) { + return false; + } + + deps.generateConfig(args) + .then((exitCode) => { + deps.onSuccess(exitCode); + }) + .catch((error: Error) => { + deps.onError(error); + }); + return true; +} diff --git a/src/main.ts b/src/main.ts index 587bcac..fa036f6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -211,6 +211,7 @@ import { createMecabTokenizerAndCheckRuntimeService, createSubtitleTimingTrackerRuntimeService, } from "./core/services/startup-resource-runtime-service"; +import { runGenerateConfigFlowRuntimeService } from "./core/services/config-generation-runtime-service"; import { runSubsyncManualFromIpcRuntimeService, triggerSubsyncFromConfigRuntimeService, @@ -455,22 +456,26 @@ const backendOverride = initialArgs.backend ?? null; const autoStartOverlay = initialArgs.autoStartOverlay; const texthookerOnlyMode = initialArgs.texthooker; -if (initialArgs.generateConfig && !shouldStartApp(initialArgs)) { - generateDefaultConfigFile(initialArgs, { - configDir: CONFIG_DIR, - defaultConfig: DEFAULT_CONFIG, - generateTemplate: (config) => generateConfigTemplate(config as never), - }) - .then((exitCode) => { +if ( + !runGenerateConfigFlowRuntimeService(initialArgs, { + shouldStartApp: (args) => shouldStartApp(args), + generateConfig: async (args) => + generateDefaultConfigFile(args, { + configDir: CONFIG_DIR, + defaultConfig: DEFAULT_CONFIG, + generateTemplate: (config) => generateConfigTemplate(config as never), + }), + onSuccess: (exitCode) => { process.exitCode = exitCode; app.quit(); - }) - .catch((error: Error) => { + }, + onError: (error) => { console.error(`Failed to generate config: ${error.message}`); process.exitCode = 1; app.quit(); - }); -} else { + }, + }) +) { startAppLifecycleService(initialArgs, createAppLifecycleDepsRuntimeService({ app, platform: process.platform,