From e6f6e7f8a46a0f9bdf2d2cfbbd616ab540878eaf Mon Sep 17 00:00:00 2001 From: boban Date: Sun, 5 Jul 2026 11:06:53 +0200 Subject: [PATCH] fix(dev): read VITE_HMR_HOST from the .env FILE (loadEnv), restoring dev HMR assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regression from the leak-scrub: vite.config read $process.env.VITE_HMR_HOST, but the dev container bind-mounts .env without exporting it into the process environment — so it was ALWAYS empty and the hmr host fell back to 'localhost'. On a remote dev box the browser then loaded the Vite client + app.css + app.js from localhost:5173 (ERR_CONNECTION_REFUSED) → the whole panel rendered unstyled. Switch to Vite's loadEnv (reads the .env file, same source the other VITE_* vars use) and fall back to undefined (Vite infers the host from the request) instead of a hard 'localhost'. The real address stays in the untracked dev .env (documented in .env.example, no IP committed). Verified: dev assets load 200 + the login renders styled, and `npm run build` (prod) still succeeds. --- .env.example | 5 +++ vite.config.js | 85 ++++++++++++++++++++++++++++---------------------- 2 files changed, 52 insertions(+), 38 deletions(-) diff --git a/.env.example b/.env.example index b3972b8..33beffb 100644 --- a/.env.example +++ b/.env.example @@ -65,6 +65,11 @@ FILESYSTEM_DISK=local # ── Docker Compose knobs (host ports / uid / image) ────────────────── APP_PORT=80 VITE_PORT=5173 +# Dev HMR only: the address the BROWSER uses to reach this box's Vite dev server (the dev VM's LAN IP +# or hostname, e.g. 192.0.2.10). Needed when developing against a REMOTE box — otherwise the CSS/JS +# load from the wrong origin. Leave unset for purely local dev; Vite then infers it from the request +# host. Never used in prod (assets are built). Keep the real address in the untracked .env only. +# VITE_HMR_HOST= REVERB_HOST_PORT=8080 DB_PORT=3306 # Containers run as this host user (must own the bind-mount). nexxo=1002 on this VM. diff --git a/vite.config.js b/vite.config.js index 2fa3389..394a8a2 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,43 +1,52 @@ -import { defineConfig } from 'vite'; +import { defineConfig, loadEnv } from 'vite'; import laravel from 'laravel-vite-plugin'; import tailwindcss from '@tailwindcss/vite'; -export default defineConfig({ - plugins: [ - laravel({ - input: ['resources/css/app.css', 'resources/js/app.js'], - refresh: true, - }), - tailwindcss(), - ], - server: { - // Bind all interfaces inside the container; the browser reaches the VM. - host: '0.0.0.0', - port: 5173, - strictPort: true, - cors: true, - // Remote HMR: hot file + websocket point at the dev VM, not 0.0.0.0/localhost. The host is - // env-driven (VITE_HMR_HOST) so no private/internal address is committed or baked into the - // shipped image; set it in the dev .env only. Dev-only — prod builds assets, no HMR server. - hmr: { - host: process.env.VITE_HMR_HOST || 'localhost', +export default defineConfig(({ mode }) => { + // Read the .env FILE via Vite's loadEnv — NOT process.env: the dev container bind-mounts .env but + // does not export it into the process environment, so process.env.VITE_HMR_HOST is always empty + // there. VITE_HMR_HOST is the address the BROWSER uses to reach the dev VM (HMR websocket + the + // hot-asset origin). It lives ONLY in the gitignored dev .env, so no private/internal address is + // committed or baked into the shipped image. Unset → undefined → Vite infers it from the request + // host (never a broken hard 'localhost' that 404s the CSS/JS on a remote dev box). + const env = loadEnv(mode, process.cwd(), 'VITE_'); + + return { + plugins: [ + laravel({ + input: ['resources/css/app.css', 'resources/js/app.js'], + refresh: true, + }), + tailwindcss(), + ], + server: { + // Bind all interfaces inside the container; the browser reaches the VM. + host: '0.0.0.0', + port: 5173, + strictPort: true, + cors: true, + // Remote HMR: hot file + websocket point at the dev VM (VITE_HMR_HOST), not 0.0.0.0. + // Dev-only — prod builds assets, there is no HMR server. + hmr: { + host: env.VITE_HMR_HOST || undefined, + }, + // The repo is rooted in $HOME — keep the watcher off heavy/irrelevant trees. + watch: { + ignored: [ + '**/vendor/**', + '**/node_modules/**', + '**/storage/**', + '**/.git/**', + '**/.cache/**', + '**/.npm/**', + '**/.vscode-server/**', + '**/.dotnet/**', + '**/.composer/**', + '**/.config/**', + '**/.claude/**', + '**/.local/**', + ], + }, }, - // The repo is rooted in $HOME — keep the watcher off heavy/irrelevant trees. - watch: { - ignored: [ - '**/vendor/**', - '**/node_modules/**', - '**/storage/**', - '**/.git/**', - '**/.cache/**', - '**/.npm/**', - '**/.vscode-server/**', - '**/.dotnet/**', - '**/.composer/**', - '**/.config/**', - '**/.claude/**', - '**/.local/**', - ], - }, - }, + }; });