From 81aa42c3a226c83e0a4da5a021db60f591e0311c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 18:18:34 +0000 Subject: [PATCH] feat(plugin-vite): support custom server host in renderer dev server URL Previously, the Vite plugin hardcoded the development server URL exposed to the main process to http://localhost:, which prevented custom host configurations (network debugging across devices, Docker container development, remote development setups). The URL now respects a string `server.host` from the resolved Vite config. Wildcard hosts (`true`, `0.0.0.0`, `::`) fall back to `localhost`, since the main process runs on the same machine and wildcard addresses are not reliably loadable in Chromium, and IPv6 literals are bracketed to form valid URLs. Ported from #4030 onto the `next` branch, with wildcard/IPv6 normalization and test coverage added. Co-authored-by: chenjiahao Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_012hunyjnAGo97Vz7v2XgVJt --- .../vite/spec/config/vite.base.config.spec.ts | 41 +++++++++++++++++++ .../vite/src/config/vite.base.config.ts | 16 +++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/packages/plugin/vite/spec/config/vite.base.config.spec.ts b/packages/plugin/vite/spec/config/vite.base.config.spec.ts index eecfc4e255..2a05abe4dd 100644 --- a/packages/plugin/vite/spec/config/vite.base.config.spec.ts +++ b/packages/plugin/vite/spec/config/vite.base.config.spec.ts @@ -113,6 +113,47 @@ describe('vite.base.config', () => { expect(define1).toEqual(define2); }); + it('getBuildDefine:serve with custom server host', async () => { + const hosts = ['127.0.0.1', '0.0.0.0']; + const servers = await Promise.all( + forgeConfig.renderer.map(({ name }, index) => + createServer({ + publicDir: false, + server: { host: hosts[index] }, + plugins: [pluginExposeRenderer(name)], + }), + ), + ); + let port = 5183; + + for (const server of servers) { + await server.listen(port); + port++; + } + + const define1 = getBuildDefine({ + command: 'serve', + mode: 'development', + root: configRoot, + forgeConfig, + forgeConfigSelf: forgeConfig.build[0], + }); + const define2 = { + // Custom string hosts are exposed as-is. + MAIN_WINDOW_VITE_DEV_SERVER_URL: '"http://127.0.0.1:5183"', + MAIN_WINDOW_VITE_NAME: '"main_window"', + // Wildcard hosts fall back to localhost. + SECOND_WINDOW_VITE_DEV_SERVER_URL: '"http://localhost:5184"', + SECOND_WINDOW_VITE_NAME: '"second_window"', + }; + + for (const server of servers) { + await server.close(); + } + + expect(define1).toEqual(define2); + }); + describe('pluginHotRestart', () => { let dispose: (() => void) | undefined; diff --git a/packages/plugin/vite/src/config/vite.base.config.ts b/packages/plugin/vite/src/config/vite.base.config.ts index 9551db1484..1d35ec220a 100644 --- a/packages/plugin/vite/src/config/vite.base.config.ts +++ b/packages/plugin/vite/src/config/vite.base.config.ts @@ -75,6 +75,20 @@ export function getBuildDefine(env: ConfigEnv<'build'>) { return define; } +/** + * Resolve the host to use in the dev server URL exposed to the main process. + * Wildcard hosts (`true`, `0.0.0.0`, `::`) are mapped to `localhost` since the + * main process runs on the same machine and wildcard addresses are not + * reliably loadable in Chromium. + */ +function resolveDevHost(host: string | boolean | undefined): string { + if (typeof host !== 'string' || host === '0.0.0.0' || host === '::') { + return 'localhost'; + } + // IPv6 literals must be bracketed in URLs. + return host.includes(':') ? `[${host}]` : host; +} + export function pluginExposeRenderer(name: string): Plugin { const { VITE_DEV_SERVER_URL } = getDefineKeys([name])[name]; @@ -88,7 +102,7 @@ export function pluginExposeRenderer(name: string): Plugin { const addressInfo = server.httpServer?.address() as AddressInfo; // Expose env constant for main process use. viteDevServerUrls[VITE_DEV_SERVER_URL] = - `http://localhost:${addressInfo?.port}`; + `http://${resolveDevHost(server.config.server.host)}:${addressInfo?.port}`; }); }, };