2023-04-07 16:02:47 +00:00
import { hasProtocol , joinURL } from 'ufo'
2023-04-07 10:34:35 +00:00
import { parse } from 'devalue'
2023-03-10 08:01:21 +00:00
import { useHead } from '@unhead/vue'
2023-04-07 10:34:35 +00:00
import { getCurrentInstance } from 'vue'
2023-02-09 06:28:58 +00:00
import { useNuxtApp , useRuntimeConfig } from '../nuxt'
2022-09-10 13:57:16 +00:00
2023-04-07 10:34:35 +00:00
// @ts-expect-error virtual import
import { renderJsonPayloads } from '#build/nuxt.config.mjs'
2022-09-10 13:57:16 +00:00
interface LoadPayloadOptions {
fresh? : boolean
hash? : string
}
2023-03-14 10:09:50 +00:00
export function loadPayload ( url : string , opts : LoadPayloadOptions = { } ) : Record < string , any > | Promise < Record < string , any > > | null {
2022-09-10 13:57:16 +00:00
if ( process . server ) { return null }
const payloadURL = _getPayloadURL ( url , opts )
const nuxtApp = useNuxtApp ( )
const cache = nuxtApp . _payloadCache = nuxtApp . _payloadCache || { }
2023-03-09 18:36:54 +00:00
if ( cache [ payloadURL ] ) {
return cache [ payloadURL ]
2022-09-10 13:57:16 +00:00
}
2023-03-09 18:36:54 +00:00
cache [ payloadURL ] = _importPayload ( payloadURL ) . then ( ( payload ) = > {
2022-09-10 13:57:16 +00:00
if ( ! payload ) {
2023-03-09 18:36:54 +00:00
delete cache [ payloadURL ]
2022-09-10 13:57:16 +00:00
return null
}
return payload
} )
2023-03-09 18:36:54 +00:00
return cache [ payloadURL ]
2022-09-10 13:57:16 +00:00
}
export function preloadPayload ( url : string , opts : LoadPayloadOptions = { } ) {
const payloadURL = _getPayloadURL ( url , opts )
useHead ( {
link : [
{ rel : 'modulepreload' , href : payloadURL }
]
} )
}
// --- Internal ---
2023-04-07 10:34:35 +00:00
const extension = renderJsonPayloads ? 'json' : 'js'
2022-09-10 13:57:16 +00:00
function _getPayloadURL ( url : string , opts : LoadPayloadOptions = { } ) {
2022-11-03 22:28:13 +00:00
const u = new URL ( url , 'http://localhost' )
if ( u . search ) {
2022-09-10 13:57:16 +00:00
throw new Error ( 'Payload URL cannot contain search params: ' + url )
}
2023-03-09 18:37:18 +00:00
if ( u . host !== 'localhost' || hasProtocol ( u . pathname , { acceptRelative : true } ) ) {
2023-02-16 16:59:06 +00:00
throw new Error ( 'Payload URL must not include hostname: ' + url )
}
2022-09-10 13:57:16 +00:00
const hash = opts . hash || ( opts . fresh ? Date . now ( ) : '' )
2023-04-07 10:34:35 +00:00
return joinURL ( useRuntimeConfig ( ) . app . baseURL , u . pathname , hash ? ` _payload. ${ hash } . ${ extension } ` : ` _payload. ${ extension } ` )
2022-09-10 13:57:16 +00:00
}
async function _importPayload ( payloadURL : string ) {
if ( process . server ) { return null }
2023-04-07 10:34:35 +00:00
try {
return renderJsonPayloads
? parsePayload ( await fetch ( payloadURL ) . then ( res = > res . text ( ) ) )
: await import ( /* webpackIgnore: true */ /* @vite-ignore */ payloadURL ) . then ( r = > r . default || r )
} catch ( err ) {
2022-09-10 13:57:16 +00:00
console . warn ( '[nuxt] Cannot load payload ' , payloadURL , err )
2023-04-07 10:34:35 +00:00
}
return null
2022-09-10 13:57:16 +00:00
}
export function isPrerendered ( ) {
// Note: Alternative for server is checking x-nitro-prerender header
const nuxtApp = useNuxtApp ( )
return ! ! nuxtApp . payload . prerenderedAt
}
2023-04-07 10:34:35 +00:00
let payloadCache : any = null
export async function getNuxtClientPayload ( ) {
if ( process . server ) {
return
}
if ( payloadCache ) {
return payloadCache
}
const el = document . getElementById ( '__NUXT_DATA__' )
if ( ! el ) {
return { }
}
const inlineData = parsePayload ( el . textContent || '' )
const externalData = el . dataset . src ? await _importPayload ( el . dataset . src ) : undefined
payloadCache = {
. . . inlineData ,
. . . externalData ,
. . . window . __NUXT__
}
return payloadCache
}
export function parsePayload ( payload : string ) {
return parse ( payload , useNuxtApp ( ) . _payloadRevivers )
}
/ * *
* This is an experimental function for configuring passing rich data from server - > client .
* /
export function definePayloadReducer (
name : string ,
reduce : ( data : any ) = > any
) {
if ( process . server ) {
useNuxtApp ( ) . ssrContext ! . _payloadReducers [ name ] = reduce
}
}
/ * *
* This is an experimental function for configuring passing rich data from server - > client .
*
* This function _must_ be called in a Nuxt plugin that is ` unshift ` ed to the beginning of the Nuxt plugins array .
* /
export function definePayloadReviver (
name : string ,
revive : ( data : string ) = > any | undefined
) {
if ( process . dev && getCurrentInstance ( ) ) {
console . warn ( '[nuxt] [definePayloadReviver] This function must be called in a Nuxt plugin that is `unshift`ed to the beginning of the Nuxt plugins array.' )
}
if ( process . client ) {
useNuxtApp ( ) . _payloadRevivers [ name ] = revive
}
}