@@ -30,6 +30,9 @@ export default class VueRenderer {
30
30
spaTemplate : undefined ,
31
31
errorTemplate : this . parseTemplate ( 'Nuxt.js Internal Server Error' )
32
32
} )
33
+
34
+ // Keep time of last shown messages
35
+ this . _lastWaitingForResource = new Date ( )
33
36
}
34
37
35
38
get assetsMapping ( ) {
@@ -110,7 +113,6 @@ export default class VueRenderer {
110
113
111
114
async ready ( ) {
112
115
// -- Development mode --
113
-
114
116
if ( this . context . options . dev ) {
115
117
this . context . nuxt . hook ( 'build:resources' , mfs => this . loadResources ( mfs , true ) )
116
118
return
@@ -121,7 +123,7 @@ export default class VueRenderer {
121
123
// Try once to load SSR resources from fs
122
124
await this . loadResources ( fs )
123
125
124
- // Without using`nuxt start` (Programatic, Tests and Generate)
126
+ // Without using `nuxt start` (Programatic, Tests and Generate)
125
127
if ( ! this . context . options . _start ) {
126
128
this . context . nuxt . hook ( 'build:resources' , ( ) => this . loadResources ( fs ) )
127
129
}
@@ -296,64 +298,55 @@ export default class VueRenderer {
296
298
return fn ( opts )
297
299
}
298
300
299
- async renderRoute ( url , context = { } , retries = 5 ) {
300
- /* istanbul ignore if */
301
- if ( ! this . isReady ) {
302
- if ( this . context . options . dev && retries > 0 ) {
303
- consola . info ( 'Waiting for server resources...' )
304
- await waitFor ( 1000 )
305
- return this . renderRoute ( url , context , retries - 1 )
306
- } else {
307
- throw new Error ( 'Server resources are not available!' )
308
- }
301
+ async renderSPA ( context ) {
302
+ const content = await this . renderer . spa . render ( context )
303
+
304
+ const APP =
305
+ `<div id="${ this . context . globals . id } ">${ this . context . resources . loadingHTML } </div>` +
306
+ content . BODY_SCRIPTS
307
+
308
+ // Prepare template params
309
+ const templateParams = {
310
+ ...content ,
311
+ APP ,
312
+ ENV : this . context . options . env
309
313
}
310
314
311
- // Log rendered url
312
- consola . debug ( `Rendering url ${ url } ` )
315
+ // Call spa:templateParams hook
316
+ this . context . nuxt . callHook ( 'vue-renderer:spa:templateParams' , templateParams )
313
317
314
- // Add url and isSever to the context
315
- context . url = url
318
+ // Render with SPA template
319
+ const html = this . renderTemplate ( false , templateParams )
316
320
317
- // Basic response if SSR is disabled or SPA data provided
318
- const { req, res } = context
319
- const spa = context . spa || ( res && res . spa )
320
- const ENV = this . context . options . env
321
-
322
- if ( ! this . SSR || spa ) {
323
- const {
324
- HTML_ATTRS ,
325
- HEAD_ATTRS ,
326
- BODY_ATTRS ,
327
- HEAD ,
328
- BODY_SCRIPTS ,
329
- getPreloadFiles
330
- } = await this . renderer . spa . render ( context )
331
- const APP =
332
- `<div id="${ this . context . globals . id } ">${ this . context . resources . loadingHTML } </div>` + BODY_SCRIPTS
333
-
334
- const html = this . renderTemplate ( false , {
335
- HTML_ATTRS ,
336
- HEAD_ATTRS ,
337
- BODY_ATTRS ,
338
- HEAD ,
339
- APP ,
340
- ENV
321
+ return {
322
+ html,
323
+ getPreloadFiles : this . getPreloadFiles . bind ( this , {
324
+ getPreloadFiles : content . getPreloadFiles
341
325
} )
342
-
343
- return { html, getPreloadFiles : this . getPreloadFiles . bind ( this , { getPreloadFiles } ) }
344
326
}
327
+ }
345
328
346
- let APP
329
+ async renderSSR ( context ) {
347
330
// Call renderToString from the bundleRenderer and generate the HTML (will update the context as well)
348
- if ( req && req . modernMode ) {
349
- APP = await this . renderer . modern . renderToString ( context )
350
- } else {
351
- APP = await this . renderer . ssr . renderToString ( context )
352
- }
331
+ const renderer = context . modern ? this . renderer . modern : this . renderer . ssr
332
+
333
+ // Call ssr:context hook to extend context from modules
334
+ await this . context . nuxt . callHook ( 'vue-renderer:ssr:prepareContext' , context )
353
335
336
+ // Call Vue renderer renderToString
337
+ let APP = await renderer . renderToString ( context )
338
+
339
+ // Call ssr:context hook
340
+ await this . context . nuxt . callHook ( 'vue-renderer:ssr:context' , context )
341
+ // TODO: Remove in next major release
342
+ await this . context . nuxt . callHook ( 'render:routeContext' , context . nuxt )
343
+
344
+ // Fallback to empty response
354
345
if ( ! context . nuxt . serverRendered ) {
355
346
APP = `<div id="${ this . context . globals . id } "></div>`
356
347
}
348
+
349
+ // Inject head meta
357
350
const m = context . meta . inject ( )
358
351
let HEAD =
359
352
m . title . text ( ) +
@@ -362,18 +355,25 @@ export default class VueRenderer {
362
355
m . style . text ( ) +
363
356
m . script . text ( ) +
364
357
m . noscript . text ( )
358
+
359
+ // Add <base href=""> meta if router base specified
365
360
if ( this . context . options . _routerBaseSpecified ) {
366
361
HEAD += `<base href="${ this . context . options . router . base } ">`
367
362
}
368
363
364
+ // Inject resource hints
369
365
if ( this . context . options . render . resourceHints ) {
370
366
HEAD += this . renderResourceHints ( context )
371
367
}
372
368
373
- await this . context . nuxt . callHook ( 'render:routeContext' , context . nuxt )
369
+ // Inject styles
370
+ HEAD += context . renderStyles ( )
374
371
372
+ // Serialize state
375
373
const serializedSession = `window.${ this . context . globals . context } =${ devalue ( context . nuxt ) } ;`
374
+ APP += `<script>${ serializedSession } </script>`
376
375
376
+ // Calculate CSP hashes
377
377
const cspScriptSrcHashes = [ ]
378
378
if ( this . context . options . render . csp ) {
379
379
const { hashAlgorithm } = this . context . options . render . csp
@@ -382,21 +382,29 @@ export default class VueRenderer {
382
382
cspScriptSrcHashes . push ( `'${ hashAlgorithm } -${ hash . digest ( 'base64' ) } '` )
383
383
}
384
384
385
- APP += `<script>${ serializedSession } </script>`
385
+ // Call ssr:csp hook
386
+ await this . context . nuxt . callHook ( 'vue-renderer:ssr:csp' , cspScriptSrcHashes )
387
+
388
+ // Prepend scripts
386
389
APP += this . renderScripts ( context )
387
390
APP += m . script . text ( { body : true } )
388
391
APP += m . noscript . text ( { body : true } )
389
392
390
- HEAD += context . renderStyles ( )
391
-
392
- const html = this . renderTemplate ( true , {
393
+ // Template params
394
+ const templateParams = {
393
395
HTML_ATTRS : 'data-n-head-ssr ' + m . htmlAttrs . text ( ) ,
394
396
HEAD_ATTRS : m . headAttrs . text ( ) ,
395
397
BODY_ATTRS : m . bodyAttrs . text ( ) ,
396
398
HEAD ,
397
399
APP ,
398
- ENV
399
- } )
400
+ ENV : this . context . options . env
401
+ }
402
+
403
+ // Call ssr:templateParams hook
404
+ await this . context . nuxt . callHook ( 'vue-renderer:ssr:templateParams' , templateParams )
405
+
406
+ // Render with SSR template
407
+ const html = this . renderTemplate ( true , templateParams )
400
408
401
409
return {
402
410
html,
@@ -407,6 +415,49 @@ export default class VueRenderer {
407
415
}
408
416
}
409
417
418
+ async renderRoute ( url , context = { } , retries = 5 ) {
419
+ /* istanbul ignore if */
420
+ if ( ! this . isReady ) {
421
+ if ( ! this . context . options . dev || retries <= 0 ) {
422
+ throw new Error ( 'Server resources are not available!' )
423
+ }
424
+
425
+ const now = new Date ( )
426
+ if ( now - this . _lastWaitingForResource > 3000 ) {
427
+ consola . info ( 'Waiting for server resources...' )
428
+ this . _lastWaitingForResource = now
429
+ }
430
+ await waitFor ( 1000 )
431
+
432
+ return this . renderRoute ( url , context , retries - 1 )
433
+ }
434
+
435
+ // Log rendered url
436
+ consola . debug ( `Rendering url ${ url } ` )
437
+
438
+ // Add url to the context
439
+ context . url = url
440
+
441
+ // context.spa
442
+ if ( context . spa === undefined ) {
443
+ // TODO: Remove reading from context.res in Nuxt3
444
+ context . spa = ! this . SSR || context . spa || ( context . req && context . req . spa ) || ( context . res && context . res . spa )
445
+ }
446
+
447
+ // context.modern
448
+ if ( context . modern === undefined ) {
449
+ context . modern = context . req ? ( context . req . modernMode || context . req . modern ) : false
450
+ }
451
+
452
+ // Call context hook
453
+ await this . context . nuxt . callHook ( 'vue-renderer:context' , context )
454
+
455
+ // Render SPA or SSR
456
+ return context . spa
457
+ ? this . renderSPA ( context )
458
+ : this . renderSSR ( context )
459
+ }
460
+
410
461
get resourceMap ( ) {
411
462
return {
412
463
clientManifest : {
0 commit comments