mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-16 02:44:51 +00:00
134 lines
3.8 KiB
TypeScript
134 lines
3.8 KiB
TypeScript
import { hasProtocol, joinURL } from 'ufo'
|
|
import { parse } from 'devalue'
|
|
import { useHead } from '@unhead/vue'
|
|
import { getCurrentInstance } from 'vue'
|
|
import { useNuxtApp, useRuntimeConfig } from '../nuxt'
|
|
|
|
// @ts-expect-error virtual import
|
|
import { renderJsonPayloads } from '#build/nuxt.config.mjs'
|
|
|
|
interface LoadPayloadOptions {
|
|
fresh?: boolean
|
|
hash?: string
|
|
}
|
|
|
|
export function loadPayload (url: string, opts: LoadPayloadOptions = {}): Record<string, any> | Promise<Record<string, any>> | null {
|
|
if (process.server) { return null }
|
|
const payloadURL = _getPayloadURL(url, opts)
|
|
const nuxtApp = useNuxtApp()
|
|
const cache = nuxtApp._payloadCache = nuxtApp._payloadCache || {}
|
|
if (cache[payloadURL]) {
|
|
return cache[payloadURL]
|
|
}
|
|
cache[payloadURL] = _importPayload(payloadURL).then((payload) => {
|
|
if (!payload) {
|
|
delete cache[payloadURL]
|
|
return null
|
|
}
|
|
return payload
|
|
})
|
|
return cache[payloadURL]
|
|
}
|
|
|
|
export function preloadPayload (url: string, opts: LoadPayloadOptions = {}) {
|
|
const payloadURL = _getPayloadURL(url, opts)
|
|
useHead({
|
|
link: [
|
|
{ rel: 'modulepreload', href: payloadURL }
|
|
]
|
|
})
|
|
}
|
|
|
|
// --- Internal ---
|
|
|
|
const extension = renderJsonPayloads ? 'json' : 'js'
|
|
function _getPayloadURL (url: string, opts: LoadPayloadOptions = {}) {
|
|
const u = new URL(url, 'http://localhost')
|
|
if (u.search) {
|
|
throw new Error('Payload URL cannot contain search params: ' + url)
|
|
}
|
|
if (u.host !== 'localhost' || hasProtocol(u.pathname, { acceptRelative: true })) {
|
|
throw new Error('Payload URL must not include hostname: ' + url)
|
|
}
|
|
const hash = opts.hash || (opts.fresh ? Date.now() : '')
|
|
return joinURL(useRuntimeConfig().app.baseURL, u.pathname, hash ? `_payload.${hash}.${extension}` : `_payload.${extension}`)
|
|
}
|
|
|
|
async function _importPayload (payloadURL: string) {
|
|
if (process.server) { return null }
|
|
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) {
|
|
console.warn('[nuxt] Cannot load payload ', payloadURL, err)
|
|
}
|
|
return null
|
|
}
|
|
|
|
export function isPrerendered () {
|
|
// Note: Alternative for server is checking x-nitro-prerender header
|
|
const nuxtApp = useNuxtApp()
|
|
return !!nuxtApp.payload.prerenderedAt
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|