Merge branch 'main' into patch-21

This commit is contained in:
Michael Brevard 2024-08-13 23:13:16 +03:00 committed by GitHub
commit 3426fca610
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 99 additions and 10 deletions

View File

@ -79,7 +79,7 @@ import type { UseFetchOptions } from 'nuxt/app'
export function useAPI<T>( export function useAPI<T>(
url: string | (() => string), url: string | (() => string),
options: Omit<UseFetchOptions<T>, 'default'> & { default: () => T | Ref<T> }, options?: UseFetchOptions<T>,
) { ) {
return useFetch(url, { return useFetch(url, {
...options, ...options,

View File

@ -239,18 +239,17 @@ export function useAsyncData<
options.deep = options.deep ?? asyncDataDefaults.deep options.deep = options.deep ?? asyncDataDefaults.deep
options.dedupe = options.dedupe ?? 'cancel' options.dedupe = options.dedupe ?? 'cancel'
// TODO: make more precise when v4 lands
const hasCachedData = () => options.getCachedData!(key, nuxtApp) !== undefined
// Create or use a shared asyncData entity // Create or use a shared asyncData entity
const initialCachedData = options.getCachedData!(key, nuxtApp)
const hasCachedData = typeof initialCachedData !== 'undefined'
if (!nuxtApp._asyncData[key] || !options.immediate) { if (!nuxtApp._asyncData[key] || !options.immediate) {
nuxtApp.payload._errors[key] ??= undefined nuxtApp.payload._errors[key] ??= undefined
const _ref = options.deep ? ref : shallowRef const _ref = options.deep ? ref : shallowRef
const cachedData = options.getCachedData!(key, nuxtApp)
nuxtApp._asyncData[key] = { nuxtApp._asyncData[key] = {
data: _ref(typeof cachedData !== 'undefined' ? cachedData : options.default!()), data: _ref(hasCachedData ? initialCachedData : options.default!()),
pending: ref(!hasCachedData()), pending: ref(!hasCachedData),
error: toRef(nuxtApp.payload._errors, key), error: toRef(nuxtApp.payload._errors, key),
status: ref('idle'), status: ref('idle'),
_default: options.default!, _default: options.default!,
@ -272,8 +271,11 @@ export function useAsyncData<
(nuxtApp._asyncDataPromises[key] as any).cancelled = true (nuxtApp._asyncDataPromises[key] as any).cancelled = true
} }
// Avoid fetching same key that is already fetched // Avoid fetching same key that is already fetched
if ((opts._initial || (nuxtApp.isHydrating && opts._initial !== false)) && hasCachedData()) { if ((opts._initial || (nuxtApp.isHydrating && opts._initial !== false))) {
return Promise.resolve(options.getCachedData!(key, nuxtApp)) const cachedData = opts._initial ? initialCachedData : options.getCachedData!(key, nuxtApp)
if (typeof cachedData !== 'undefined') {
return Promise.resolve(cachedData)
}
} }
asyncData.pending.value = true asyncData.pending.value = true
asyncData.status.value = 'pending' asyncData.status.value = 'pending'
@ -362,7 +364,7 @@ export function useAsyncData<
onUnmounted(() => cbs.splice(0, cbs.length)) onUnmounted(() => cbs.splice(0, cbs.length))
} }
if (fetchOnServer && nuxtApp.isHydrating && (asyncData.error.value || hasCachedData())) { if (fetchOnServer && nuxtApp.isHydrating && (asyncData.error.value || typeof initialCachedData !== 'undefined')) {
// 1. Hydration (server: true): no fetch // 1. Hydration (server: true): no fetch
asyncData.pending.value = false asyncData.pending.value = false
asyncData.status.value = asyncData.error.value ? 'error' : 'success' asyncData.status.value = asyncData.error.value ? 'error' : 'success'

View File

@ -0,0 +1,63 @@
import { mkdir, readFile, writeFile } from 'node:fs/promises'
import { dirname, resolve } from 'pathe'
import type { Plugin as RollupPlugin } from 'rollup'
import type { Plugin as VitePlugin } from 'vite'
export const createSourcemapPreserver = () => {
let outputDir: string
const ids = new Set<string>()
const vitePlugin = {
name: 'nuxt:sourcemap-export',
configResolved (config) {
outputDir = config.build.outDir
},
async writeBundle (_options, bundle) {
for (const chunk of Object.values(bundle)) {
if (chunk.type !== 'chunk' || !chunk.map) { continue }
const id = resolve(outputDir, chunk.fileName)
ids.add(id)
const dest = id + '.map.json'
await mkdir(dirname(dest), { recursive: true })
await writeFile(dest, JSON.stringify({
file: chunk.map.file,
mappings: chunk.map.mappings,
names: chunk.map.names,
sources: chunk.map.sources,
sourcesContent: chunk.map.sourcesContent,
version: chunk.map.version,
}))
}
},
} satisfies VitePlugin
const nitroPlugin = {
name: 'nuxt:sourcemap-import',
async load (id) {
id = resolve(id)
if (!ids.has(id)) { return }
const [code, map] = await Promise.all([
readFile(id, 'utf-8').catch(() => undefined),
readFile(id + '.map.json', 'utf-8').catch(() => undefined),
])
if (!code) {
this.warn('Failed loading file')
return null
}
return {
code,
map,
}
},
} satisfies RollupPlugin
return {
vitePlugin,
nitroPlugin,
}
}

View File

@ -5,11 +5,13 @@ import viteJsxPlugin from '@vitejs/plugin-vue-jsx'
import { logger, resolvePath, tryImportModule } from '@nuxt/kit' import { logger, resolvePath, tryImportModule } from '@nuxt/kit'
import { joinURL, withTrailingSlash, withoutLeadingSlash } from 'ufo' import { joinURL, withTrailingSlash, withoutLeadingSlash } from 'ufo'
import type { ViteConfig } from '@nuxt/schema' import type { ViteConfig } from '@nuxt/schema'
import defu from 'defu'
import type { ViteBuildContext } from './vite' import type { ViteBuildContext } from './vite'
import { createViteLogger } from './utils/logger' import { createViteLogger } from './utils/logger'
import { initViteNodeServer } from './vite-node' import { initViteNodeServer } from './vite-node'
import { writeManifest } from './manifest' import { writeManifest } from './manifest'
import { transpile } from './utils/transpile' import { transpile } from './utils/transpile'
import { createSourcemapPreserver } from './plugins/nitro-sourcemap'
export async function buildServer (ctx: ViteBuildContext) { export async function buildServer (ctx: ViteBuildContext) {
const helper = ctx.nuxt.options.nitro.imports !== false ? '' : 'globalThis.' const helper = ctx.nuxt.options.nitro.imports !== false ? '' : 'globalThis.'
@ -121,6 +123,17 @@ export async function buildServer (ctx: ViteBuildContext) {
} }
} }
// tell rollup's nitro build about the original sources of the generated vite server build
if (ctx.nuxt.options.sourcemap.server && !ctx.nuxt.options.dev) {
const { vitePlugin, nitroPlugin } = createSourcemapPreserver()
serverConfig.plugins!.push(vitePlugin)
ctx.nuxt.hook('nitro:build:before', (nitro) => {
nitro.options.rollupConfig = defu(nitro.options.rollupConfig, {
plugins: [nitroPlugin],
})
})
}
serverConfig.customLogger = createViteLogger(serverConfig) serverConfig.customLogger = createViteLogger(serverConfig)
await ctx.nuxt.callHook('vite:extendConfig', serverConfig, { isClient: false, isServer: true }) await ctx.nuxt.callHook('vite:extendConfig', serverConfig, { isClient: false, isServer: true })

View File

@ -224,6 +224,17 @@ describe('useAsyncData', () => {
`) `)
}) })
it('should only call getCachedData once', async () => {
const getCachedData = vi.fn(() => ({ val: false }))
const { data } = await useAsyncData(() => Promise.resolve({ val: true }), { getCachedData })
expect(data.value).toMatchInlineSnapshot(`
{
"val": false,
}
`)
expect(getCachedData).toHaveBeenCalledTimes(1)
})
it('should use default while pending', async () => { it('should use default while pending', async () => {
const promise = useAsyncData(() => Promise.resolve('test'), { default: () => 'default' }) const promise = useAsyncData(() => Promise.resolve('test'), { default: () => 'default' })
const { data, pending } = promise const { data, pending } = promise