From 2553c43fd357cd375a8856879f4c50eb26b43956 Mon Sep 17 00:00:00 2001 From: Tommaso Casaburi Date: Sat, 18 Apr 2026 13:54:43 +0700 Subject: [PATCH] fix(dev): auto-open portless URL after vite is ready Vite's built-in server.open fired before portless was reachable and used the wrong hardcoded URL, so yarn start opened a 404. Disable vite's open when PORTLESS_URL is set and explicitly open the branch-scoped URL from start-dev.js after HTTP 2xx/3xx readiness. --- scripts/start-dev.js | 49 ++++++++++++++++++++++++++++++++++++++++++++ vite.config.js | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/scripts/start-dev.js b/scripts/start-dev.js index 016f41b2..87da7579 100644 --- a/scripts/start-dev.js +++ b/scripts/start-dev.js @@ -1,6 +1,7 @@ import { existsSync } from 'node:fs'; import { join } from 'node:path'; import { spawn, spawnSync } from 'node:child_process'; +import { get } from 'node:http'; import { resolvePort } from './dev-server-utils.mjs'; const isWindows = process.platform === 'win32'; @@ -123,6 +124,17 @@ const child = spawn(command, args, { env: process.env, }); +if (publicUrl && process.env.BROWSER !== 'none') { + waitForHttpReady(publicUrl, 30_000) + .then(() => { + console.log(`Opening ${publicUrl} in browser...`); + openInBrowser(publicUrl); + }) + .catch((error) => { + console.warn(`Could not auto-open ${publicUrl}: ${error.message}`); + }); +} + child.on('exit', (code, signal) => { if (signal) { process.kill(process.pid, signal); @@ -131,3 +143,40 @@ child.on('exit', (code, signal) => { process.exit(code ?? 0); }); + +async function waitForHttpReady(url, timeoutMs) { + const startedAt = Date.now(); + + while (Date.now() - startedAt < timeoutMs) { + const ready = await new Promise((resolve) => { + const request = get(url, (response) => { + response.resume(); + const statusCode = response.statusCode ?? 500; + resolve(statusCode >= 200 && statusCode < 400); + }); + + request.on('error', () => resolve(false)); + request.setTimeout(2_000, () => { + request.destroy(); + resolve(false); + }); + }); + + if (ready) { + return; + } + + await new Promise((resolve) => setTimeout(resolve, 200)); + } + + throw new Error(`Timed out waiting for ${url}`); +} + +function openInBrowser(url) { + const opener = + process.platform === 'darwin' ? { cmd: 'open', args: [url] } + : process.platform === 'win32' ? { cmd: 'cmd', args: ['/c', 'start', '""', url] } + : { cmd: 'xdg-open', args: [url] }; + + spawn(opener.cmd, opener.args, { stdio: 'ignore', detached: true }).unref(); +} diff --git a/vite.config.js b/vite.config.js index d5bef9eb..04fdc789 100644 --- a/vite.config.js +++ b/vite.config.js @@ -190,7 +190,7 @@ export default defineConfig({ }, server: { port: 3000, - open: process.env.PORT ? 'http://seedit.localhost:1355/' : true, + open: process.env.PORTLESS_URL ? false : true, watch: { usePolling: true, },