mirror of
https://github.com/nuxt/nuxt.git
synced 2025-03-03 23:35:44 +00:00
Merge branch 'main' into patch-21
This commit is contained in:
commit
3426fca610
@ -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,
|
||||||
|
@ -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'
|
||||||
|
63
packages/vite/src/plugins/nitro-sourcemap.ts
Normal file
63
packages/vite/src/plugins/nitro-sourcemap.ts
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
@ -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 })
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user