Skip to content

Commit 4d1edb4

Browse files
committed
fix(dev): serve the loading page Nuxt would render, and stop it reloading itself
1 parent 88de5da commit 4d1edb4

4 files changed

Lines changed: 62 additions & 4 deletions

File tree

‎packages/nuxt-cli/src/dev/loading-template.ts‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { withNodePath } from '../utils/paths'
55

66
exporttypeLoadingTemplate=(data: {loading?: string})=>string
77

8-
letcached: Promise<LoadingTemplate|undefined>|undefined
8+
constcache=newMap<string,Promise<LoadingTemplate|undefined>>()
99

1010
/**
1111
* The loading page Nuxt itself would render, read from the project's own
@@ -15,12 +15,18 @@ let cached: Promise<LoadingTemplate | undefined> | undefined
1515
* `devServer.loadingTemplate` with something unusable.
1616
*/
1717
exportfunctionresolveDefaultLoadingTemplate(cwd: string): Promise<LoadingTemplate|undefined>{
18-
returncached??=importDefaultLoadingTemplate(cwd)
18+
lettemplate=cache.get(cwd)
19+
if(!template){
20+
template=importDefaultLoadingTemplate(cwd)
21+
cache.set(cwd,template)
22+
}
23+
returntemplate
1924
}
2025

2126
asyncfunctionimportDefaultLoadingTemplate(cwd: string): Promise<LoadingTemplate|undefined>{
2227
try{
23-
constschemaPath=resolveModulePath('@nuxt/schema',{from: withNodePath(cwd),try: true})
28+
constnuxtPath=resolveModulePath('nuxt',{from: withNodePath(cwd),try: true})
29+
constschemaPath=resolveModulePath('@nuxt/schema',{from: withNodePath(nuxtPath||cwd),try: true})
2430
if(!schemaPath){
2531
return
2632
}

‎packages/nuxt-cli/src/dev/utils.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,8 @@ export class NuxtDevServer extends EventEmitter<DevServerEventMap> {
342342
res.setHeader('Cache-Control','no-store')
343343
res.setHeader('Refresh','3')
344344

345-
if(!req.headers.accept?.includes('text/html')){
345+
constaccept=req.headers.accept
346+
if(accept&&!accept.includes('text/html')&&!accept.includes('*/*')){
346347
res.setHeader('Content-Type','application/json')
347348
res.end(JSON.stringify({
348349
error: true,

‎packages/nuxt-cli/test/e2e/dev-loading.spec.ts‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,42 @@ describe('dev server loading screen', () => {
9191
vi.unstubAllEnvs()
9292
}
9393
})
94+
95+
it('should answer the page\'s own readiness poll with the page, not json',{timeout: 90_000},async()=>{
96+
vi.stubEnv('NUXT_IGNORE_LOCK','1')
97+
98+
consthost='127.0.0.1'
99+
constport=awaitgetPort({ host,port: 3087})
100+
constbase=`http://${host}:${port}`
101+
102+
// `fetch(location.href)` from the loading page sends `accept: *\/*`. Answered
103+
// with json, the page cannot find its own marker, decides the app is up and
104+
// reloads, which loops for as long as the build takes.
105+
constpolled=newPromise<Response>((resolve)=>{
106+
constpoll=async()=>{
107+
constresponse=awaitfetch(base,{headers: {accept: '*/*'}}).catch(()=>undefined)
108+
if(!response||response.status!==503){
109+
setTimeout(poll,5)
110+
return
111+
}
112+
resolve(response)
113+
}
114+
voidpoll()
115+
})
116+
117+
const{ close }=awaitinitialize({cwd: fixtureDir,args: {}},{
118+
listenOverrides: {hostname: host, port },
119+
showBanner: false,
120+
})
121+
122+
try{
123+
constresponse=awaitpolled
124+
expect(response.headers.get('content-type')).toContain('text/html')
125+
expect(awaitresponse.text()).toContain('__NUXT_LOADING__')
126+
}
127+
finally{
128+
awaitclose()
129+
vi.unstubAllEnvs()
130+
}
131+
})
94132
})
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import{fileURLToPath}from'node:url'
2+
import{describe,expect,it}from'vitest'
3+
import{resolveDefaultLoadingTemplate}from'../../src/dev/loading-template'
4+
5+
describe('resolveDefaultLoadingTemplate',()=>{
6+
it('should reach `@nuxt/schema` through the project\'s own nuxt',async()=>{
7+
constcwd=fileURLToPath(newURL('../../../../playground',import.meta.url))
8+
consttemplate=awaitresolveDefaultLoadingTemplate(cwd)
9+
10+
expect(template).toBeTypeOf('function')
11+
expect(template!({loading: 'Starting Nuxt...'})).toContain('nuxt-loader-bar')
12+
})
13+
})

0 commit comments

Comments
 (0)