From ced4fb63db4924bb1c689a4d60b8acc7903de122 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Sun, 2 Aug 2026 06:41:49 +0000 Subject: [PATCH 1/2] fix(dev): show loading screen in initial startup --- packages/nuxt-cli/src/dev/utils.ts | 24 ++++--------- packages/nuxt-cli/test/e2e/dev.spec.ts | 48 ++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/packages/nuxt-cli/src/dev/utils.ts b/packages/nuxt-cli/src/dev/utils.ts index 370a21f2e..755b2c851 100644 --- a/packages/nuxt-cli/src/dev/utils.ts +++ b/packages/nuxt-cli/src/dev/utils.ts @@ -217,14 +217,6 @@ export class NuxtDevServer extends EventEmitter { return this.load(true, reason) }) - let _initResolve: () => void - const _initPromise = new Promise((resolve) => { - _initResolve = resolve - }) - this.once('ready', () => { - _initResolve() - }) - this.#cwd = options.cwd this.handler = async (req, res) => { @@ -232,15 +224,7 @@ export class NuxtDevServer extends EventEmitter { void renderError(req, res, this.#loadingError) return } - await _initPromise - if (this.#handler) { - this.#inflightResponses.add(res) - res.once('close', () => { - this.#inflightResponses.delete(res) - }) - this.#handler(req, res) - } - else { + if (!this.#handler) { await this.#renderLoadingScreen(req, res).catch((error) => { debug('Could not render the loading screen:', error) if (res.headersSent) { @@ -250,7 +234,13 @@ export class NuxtDevServer extends EventEmitter { res.statusCode = 503 res.end('Dev server is loading...') }) + return } + this.#inflightResponses.add(res) + res.once('close', () => { + this.#inflightResponses.delete(res) + }) + this.#handler(req, res) } } diff --git a/packages/nuxt-cli/test/e2e/dev.spec.ts b/packages/nuxt-cli/test/e2e/dev.spec.ts index 52f0223d9..f9b1e344c 100644 --- a/packages/nuxt-cli/test/e2e/dev.spec.ts +++ b/packages/nuxt-cli/test/e2e/dev.spec.ts @@ -1,9 +1,11 @@ import { readFile, rm } from 'node:fs/promises' import { join } from 'node:path' +import { setTimeout as sleep } from 'node:timers/promises' import { fileURLToPath } from 'node:url' import { getPort } from 'get-port-please' import { afterEach, describe, expect, it, vi } from 'vitest' import { runCommand } from '../../src' +import { NuxtDevServer } from '../../src/dev/utils' import { createDevFixture } from '../utils' const NEWLINE_RE = /\r?\n/ @@ -20,6 +22,52 @@ describe('dev server', () => { vi.unstubAllEnvs() }) + it('should serve the loading screen during initial startup', { timeout: 50_000 }, async () => { + const host = '127.0.0.1' + const port = await getPort({ host, port: 3030 }) + const buildDir = join(fixtureDir, `.nuxt-loading-screen-${port}`) + const devServer = new NuxtDevServer({ + cwd: fixtureDir, + dotenv: {}, + overrides: { + buildDir, + hooks: { + ready: () => sleep(2000), + }, + }, + loadingTemplate: ({ loading }) => `

${loading}

`, + listenOverrides: { hostname: host, port }, + }) + const initPromise = devServer.init() + + try { + let response: Response | undefined + while (!response) { + response = await fetch(`http://${host}:${port}`, { + headers: { accept: 'text/html' }, + }).catch(() => undefined) + if (!response) { + await sleep(20) + } + } + + expect(response.status).toBe(503) + expect(response.headers.get('refresh')).toBe('3') + await expect(response.text()).resolves.toContain('Starting Nuxt...') + await initPromise + } + finally { + await initPromise.catch(() => {}) + devServer.closeWatchers() + await Promise.all([ + devServer.listener?.close(), + devServer.close(), + ]) + devServer.releaseLock() + await rm(buildDir, { recursive: true, force: true }) + } + }) + it('should expose dev server address to nuxt options', { timeout: 50_000 }, async () => { await rm(join(fixtureDir, '.nuxt'), { recursive: true, force: true }) const host = '127.0.0.1' From ffe0a9e5429c8bc0d2aff07961cfc7c22a628200 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Sun, 2 Aug 2026 07:12:38 +0000 Subject: [PATCH 2/2] test(dev): bound loading screen startup polling --- packages/nuxt-cli/test/e2e/dev.spec.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/nuxt-cli/test/e2e/dev.spec.ts b/packages/nuxt-cli/test/e2e/dev.spec.ts index f9b1e344c..d7524f35d 100644 --- a/packages/nuxt-cli/test/e2e/dev.spec.ts +++ b/packages/nuxt-cli/test/e2e/dev.spec.ts @@ -39,10 +39,21 @@ describe('dev server', () => { listenOverrides: { hostname: host, port }, }) const initPromise = devServer.init() + let initializationFailure: { error: unknown } | undefined + void initPromise.catch((error) => { + initializationFailure = { error } + }) try { + const deadline = Date.now() + 10_000 let response: Response | undefined while (!response) { + if (initializationFailure) { + throw initializationFailure.error + } + if (Date.now() >= deadline) { + throw new Error('Timed out waiting for the dev server listener') + } response = await fetch(`http://${host}:${port}`, { headers: { accept: 'text/html' }, }).catch(() => undefined)