mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 15:15:19 +00:00
feat(nuxt,schema): add appId
and improve chunk determinism (#27258)
This commit is contained in:
parent
dad89c2b16
commit
3c42e13b68
@ -1,7 +1,6 @@
|
|||||||
import type { MatcherExport, RouteMatcher } from 'radix3'
|
import type { MatcherExport, RouteMatcher } from 'radix3'
|
||||||
import { createMatcherFromExport, createRouter as createRadixRouter, toRouteMatcher } from 'radix3'
|
import { createMatcherFromExport, createRouter as createRadixRouter, toRouteMatcher } from 'radix3'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { useAppConfig } from '../config'
|
|
||||||
import { useRuntimeConfig } from '../nuxt'
|
import { useRuntimeConfig } from '../nuxt'
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import { appManifest as isAppManifestEnabled } from '#build/nuxt.config.mjs'
|
import { appManifest as isAppManifestEnabled } from '#build/nuxt.config.mjs'
|
||||||
@ -25,9 +24,7 @@ function fetchManifest () {
|
|||||||
if (!isAppManifestEnabled) {
|
if (!isAppManifestEnabled) {
|
||||||
throw new Error('[nuxt] app manifest should be enabled with `experimental.appManifest`')
|
throw new Error('[nuxt] app manifest should be enabled with `experimental.appManifest`')
|
||||||
}
|
}
|
||||||
// @ts-expect-error private property
|
manifest = $fetch<NuxtAppManifest>(buildAssetsURL(`builds/meta/${useRuntimeConfig().app.buildId}.json`))
|
||||||
const buildId = useAppConfig().nuxt?.buildId
|
|
||||||
manifest = $fetch<NuxtAppManifest>(buildAssetsURL(`builds/meta/${buildId}.json`))
|
|
||||||
manifest.then((m) => {
|
manifest.then((m) => {
|
||||||
matcher = createMatcherFromExport(m.matcher)
|
matcher = createMatcherFromExport(m.matcher)
|
||||||
})
|
})
|
||||||
|
@ -3,7 +3,6 @@ import { parse } from 'devalue'
|
|||||||
import { useHead } from '@unhead/vue'
|
import { useHead } from '@unhead/vue'
|
||||||
import { getCurrentInstance } from 'vue'
|
import { getCurrentInstance } from 'vue'
|
||||||
import { useNuxtApp, useRuntimeConfig } from '../nuxt'
|
import { useNuxtApp, useRuntimeConfig } from '../nuxt'
|
||||||
import { useAppConfig } from '../config'
|
|
||||||
|
|
||||||
import { useRoute } from './router'
|
import { useRoute } from './router'
|
||||||
import { getAppManifest, getRouteRules } from './manifest'
|
import { getAppManifest, getRouteRules } from './manifest'
|
||||||
@ -57,8 +56,9 @@ function _getPayloadURL (url: string, opts: LoadPayloadOptions = {}) {
|
|||||||
if (u.host !== 'localhost' || hasProtocol(u.pathname, { acceptRelative: true })) {
|
if (u.host !== 'localhost' || hasProtocol(u.pathname, { acceptRelative: true })) {
|
||||||
throw new Error('Payload URL must not include hostname: ' + url)
|
throw new Error('Payload URL must not include hostname: ' + url)
|
||||||
}
|
}
|
||||||
const hash = opts.hash || (opts.fresh ? Date.now() : (useAppConfig().nuxt as any)?.buildId)
|
const config = useRuntimeConfig()
|
||||||
return joinURL(useRuntimeConfig().app.baseURL, u.pathname, filename + (hash ? `?${hash}` : ''))
|
const hash = opts.hash || (opts.fresh ? Date.now() : config.app.buildId)
|
||||||
|
return joinURL(config.app.baseURL, u.pathname, filename + (hash ? `?${hash}` : ''))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _importPayload (payloadURL: string) {
|
async function _importPayload (payloadURL: string) {
|
||||||
|
@ -20,13 +20,13 @@ import type { LoadingIndicator } from '../app/composables/loading-indicator'
|
|||||||
import type { RouteAnnouncer } from '../app/composables/route-announcer'
|
import type { RouteAnnouncer } from '../app/composables/route-announcer'
|
||||||
import type { ViewTransition } from './plugins/view-transitions.client'
|
import type { ViewTransition } from './plugins/view-transitions.client'
|
||||||
|
|
||||||
|
// @ts-expect-error virtual file
|
||||||
|
import { appId } from '#build/nuxt.config.mjs'
|
||||||
|
|
||||||
import type { NuxtAppLiterals } from '#app'
|
import type { NuxtAppLiterals } from '#app'
|
||||||
|
|
||||||
// @ts-expect-error virtual import
|
function getNuxtAppCtx (appName = appId || 'nuxt-app') {
|
||||||
import { buildId } from '#build/nuxt.config.mjs'
|
return getContext<NuxtApp>(appName, {
|
||||||
|
|
||||||
function getNuxtAppCtx (appName?: string) {
|
|
||||||
return getContext<NuxtApp>(appName || buildId || 'nuxt-app', {
|
|
||||||
asyncContext: !!__NUXT_ASYNC_CONTEXT__ && import.meta.server,
|
asyncContext: !!__NUXT_ASYNC_CONTEXT__ && import.meta.server,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -244,7 +244,7 @@ export interface CreateOptions {
|
|||||||
export function createNuxtApp (options: CreateOptions) {
|
export function createNuxtApp (options: CreateOptions) {
|
||||||
let hydratingCount = 0
|
let hydratingCount = 0
|
||||||
const nuxtApp: NuxtApp = {
|
const nuxtApp: NuxtApp = {
|
||||||
name: buildId,
|
_name: appId || 'nuxt-app',
|
||||||
_scope: effectScope(),
|
_scope: effectScope(),
|
||||||
provide: undefined,
|
provide: undefined,
|
||||||
globalName: 'nuxt',
|
globalName: 'nuxt',
|
||||||
|
@ -218,8 +218,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
|
|
||||||
// Add app manifest handler and prerender configuration
|
// Add app manifest handler and prerender configuration
|
||||||
if (nuxt.options.experimental.appManifest) {
|
if (nuxt.options.experimental.appManifest) {
|
||||||
// @ts-expect-error untyped nuxt property
|
const buildId = nuxt.options.runtimeConfig.app.buildId ||=
|
||||||
const buildId = nuxt.options.appConfig.nuxt!.buildId ||=
|
|
||||||
(nuxt.options.dev ? 'dev' : nuxt.options.test ? 'test' : nuxt.options.buildId)
|
(nuxt.options.dev ? 'dev' : nuxt.options.test ? 'test' : nuxt.options.buildId)
|
||||||
const buildTimestamp = Date.now()
|
const buildTimestamp = Date.now()
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import type { HeadEntryOptions } from '@unhead/schema'
|
|||||||
import type { Link, Script, Style } from '@unhead/vue'
|
import type { Link, Script, Style } from '@unhead/vue'
|
||||||
import { createServerHead } from '@unhead/vue'
|
import { createServerHead } from '@unhead/vue'
|
||||||
|
|
||||||
import { defineRenderHandler, getRouteRules, useAppConfig, useRuntimeConfig, useStorage } from '#internal/nitro'
|
import { defineRenderHandler, getRouteRules, useRuntimeConfig, useStorage } from '#internal/nitro'
|
||||||
import { useNitroApp } from '#internal/nitro/app'
|
import { useNitroApp } from '#internal/nitro/app'
|
||||||
|
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
@ -327,7 +327,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
|||||||
|
|
||||||
// Whether we are prerendering route
|
// Whether we are prerendering route
|
||||||
const _PAYLOAD_EXTRACTION = import.meta.prerender && process.env.NUXT_PAYLOAD_EXTRACTION && !ssrContext.noSSR && !isRenderingIsland
|
const _PAYLOAD_EXTRACTION = import.meta.prerender && process.env.NUXT_PAYLOAD_EXTRACTION && !ssrContext.noSSR && !isRenderingIsland
|
||||||
const payloadURL = _PAYLOAD_EXTRACTION ? joinURL(ssrContext.runtimeConfig.app.baseURL, url, process.env.NUXT_JSON_PAYLOADS ? '_payload.json' : '_payload.js') + '?' + (useAppConfig().nuxt as any)?.buildId : undefined
|
const payloadURL = _PAYLOAD_EXTRACTION ? joinURL(ssrContext.runtimeConfig.app.baseURL, url, process.env.NUXT_JSON_PAYLOADS ? '_payload.json' : '_payload.js') + '?' + ssrContext.runtimeConfig.app.buildId : undefined
|
||||||
if (import.meta.prerender) {
|
if (import.meta.prerender) {
|
||||||
ssrContext.payload.prerenderedAt = Date.now()
|
ssrContext.payload.prerenderedAt = Date.now()
|
||||||
}
|
}
|
||||||
|
@ -397,7 +397,7 @@ export const nuxtConfigTemplate: NuxtTemplate = {
|
|||||||
`export const fetchDefaults = ${JSON.stringify(fetchDefaults)}`,
|
`export const fetchDefaults = ${JSON.stringify(fetchDefaults)}`,
|
||||||
`export const vueAppRootContainer = ${ctx.nuxt.options.app.rootId ? `'#${ctx.nuxt.options.app.rootId}'` : `'body > ${ctx.nuxt.options.app.rootTag}'`}`,
|
`export const vueAppRootContainer = ${ctx.nuxt.options.app.rootId ? `'#${ctx.nuxt.options.app.rootId}'` : `'body > ${ctx.nuxt.options.app.rootTag}'`}`,
|
||||||
`export const viewTransition = ${ctx.nuxt.options.experimental.viewTransition}`,
|
`export const viewTransition = ${ctx.nuxt.options.experimental.viewTransition}`,
|
||||||
`export const buildId = ${JSON.stringify(ctx.nuxt.options.buildId)}`,
|
`export const appId = ${JSON.stringify(ctx.nuxt.options.appId)}`,
|
||||||
].join('\n\n')
|
].join('\n\n')
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -154,6 +154,13 @@ export default defineUntypedSchema({
|
|||||||
$resolve: async (val: string | undefined, get): Promise<string> => resolve(await get('rootDir') as string, val || '.nuxt'),
|
$resolve: async (val: string | undefined, get): Promise<string> => resolve(await get('rootDir') as string, val || '.nuxt'),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For multi-app projects, the unique name of the Nuxt application.
|
||||||
|
*/
|
||||||
|
appId: {
|
||||||
|
$resolve: (val: string) => val ?? 'nuxt-app',
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A unique identifier matching the build. This may contain the hash of the current state of the project.
|
* A unique identifier matching the build. This may contain the hash of the current state of the project.
|
||||||
*/
|
*/
|
||||||
@ -528,11 +535,12 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
$resolve: async (val: RuntimeConfig, get): Promise<Record<string, unknown>> => {
|
$resolve: async (val: RuntimeConfig, get): Promise<Record<string, unknown>> => {
|
||||||
const app = await get('app') as Record<string, string>
|
const [app, buildId] = await Promise.all([get('app') as Promise<Record<string, string>>, get('buildId') as Promise<string>])
|
||||||
provideFallbackValues(val)
|
provideFallbackValues(val)
|
||||||
return defu(val, {
|
return defu(val, {
|
||||||
public: {},
|
public: {},
|
||||||
app: {
|
app: {
|
||||||
|
buildId,
|
||||||
baseURL: app.baseURL,
|
baseURL: app.baseURL,
|
||||||
buildAssetsDir: app.buildAssetsDir,
|
buildAssetsDir: app.buildAssetsDir,
|
||||||
cdnURL: app.cdnURL,
|
cdnURL: app.cdnURL,
|
||||||
|
@ -2028,15 +2028,9 @@ describe('app config', () => {
|
|||||||
fromLayer: true,
|
fromLayer: true,
|
||||||
userConfig: 123,
|
userConfig: 123,
|
||||||
}
|
}
|
||||||
if (isTestingAppManifest) {
|
expect.soft(html).toContain(JSON.stringify(expectedAppConfig))
|
||||||
expectedAppConfig.nuxt.buildId = 'test'
|
|
||||||
}
|
|
||||||
expect.soft(html.replace(/"nuxt":\{"buildId":"[^"]+"\}/, '"nuxt":{"buildId":"test"}')).toContain(JSON.stringify(expectedAppConfig))
|
|
||||||
|
|
||||||
const serverAppConfig = await $fetch('/api/app-config')
|
const serverAppConfig = await $fetch('/api/app-config')
|
||||||
if (isTestingAppManifest) {
|
|
||||||
serverAppConfig.appConfig.nuxt.buildId = 'test'
|
|
||||||
}
|
|
||||||
expect(serverAppConfig).toMatchObject({ appConfig: expectedAppConfig })
|
expect(serverAppConfig).toMatchObject({ appConfig: expectedAppConfig })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -2649,23 +2643,23 @@ describe('defineNuxtComponent watch duplicate', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('namespace access to useNuxtApp', () => {
|
describe('namespace access to useNuxtApp', () => {
|
||||||
it('should return the nuxt instance when used with correct buildId', async () => {
|
it('should return the nuxt instance when used with correct appId', async () => {
|
||||||
const { page, pageErrors } = await renderPage('/namespace-nuxt-app')
|
const { page, pageErrors } = await renderPage('/namespace-nuxt-app')
|
||||||
|
|
||||||
expect(pageErrors).toEqual([])
|
expect(pageErrors).toEqual([])
|
||||||
|
|
||||||
await page.waitForFunction(() => window.useNuxtApp?.() && !window.useNuxtApp?.().isHydrating)
|
await page.waitForFunction(() => window.useNuxtApp?.() && !window.useNuxtApp?.().isHydrating)
|
||||||
|
|
||||||
// Defaulting to buildId
|
// Defaulting to appId
|
||||||
await page.evaluate(() => window.useNuxtApp?.())
|
await page.evaluate(() => window.useNuxtApp?.())
|
||||||
// Using correct configured buildId
|
// Using correct configured appId
|
||||||
// @ts-expect-error not public API yet
|
// @ts-expect-error not public API yet
|
||||||
await page.evaluate(() => window.useNuxtApp?.('nuxt-app-basic'))
|
await page.evaluate(() => window.useNuxtApp?.('nuxt-app-basic'))
|
||||||
|
|
||||||
await page.close()
|
await page.close()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should throw an error when used with wrong buildId', async () => {
|
it('should throw an error when used with wrong appId', async () => {
|
||||||
const { page, pageErrors } = await renderPage('/namespace-nuxt-app')
|
const { page, pageErrors } = await renderPage('/namespace-nuxt-app')
|
||||||
|
|
||||||
expect(pageErrors).toEqual([])
|
expect(pageErrors).toEqual([])
|
||||||
@ -2674,7 +2668,7 @@ describe('namespace access to useNuxtApp', () => {
|
|||||||
|
|
||||||
let error: unknown
|
let error: unknown
|
||||||
try {
|
try {
|
||||||
// Using wrong/unknown buildId
|
// Using wrong/unknown appId
|
||||||
// @ts-expect-error not public API yet
|
// @ts-expect-error not public API yet
|
||||||
await page.evaluate(() => window.useNuxtApp?.('nuxt-app-unknown'))
|
await page.evaluate(() => window.useNuxtApp?.('nuxt-app-unknown'))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
2
test/fixtures/basic-types/types.ts
vendored
2
test/fixtures/basic-types/types.ts
vendored
@ -534,7 +534,7 @@ describe('composables', () => {
|
|||||||
describe('app config', () => {
|
describe('app config', () => {
|
||||||
it('merges app config as expected', () => {
|
it('merges app config as expected', () => {
|
||||||
interface ExpectedMergedAppConfig {
|
interface ExpectedMergedAppConfig {
|
||||||
nuxt: { buildId: string }
|
nuxt: {}
|
||||||
fromLayer: boolean
|
fromLayer: boolean
|
||||||
fromNuxtConfig: boolean
|
fromNuxtConfig: boolean
|
||||||
nested: {
|
nested: {
|
||||||
|
2
test/fixtures/basic/nuxt.config.ts
vendored
2
test/fixtures/basic/nuxt.config.ts
vendored
@ -32,7 +32,7 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
buildDir: process.env.NITRO_BUILD_DIR,
|
buildDir: process.env.NITRO_BUILD_DIR,
|
||||||
builder: process.env.TEST_BUILDER as 'webpack' | 'vite' ?? 'vite',
|
builder: process.env.TEST_BUILDER as 'webpack' | 'vite' ?? 'vite',
|
||||||
buildId: 'nuxt-app-basic',
|
appId: 'nuxt-app-basic',
|
||||||
build: {
|
build: {
|
||||||
transpile: [
|
transpile: [
|
||||||
(ctx) => {
|
(ctx) => {
|
||||||
|
2
test/fixtures/minimal-types/types.ts
vendored
2
test/fixtures/minimal-types/types.ts
vendored
@ -51,7 +51,7 @@ describe('config typings', () => {
|
|||||||
it('appConfig', () => {
|
it('appConfig', () => {
|
||||||
expectTypeOf(useAppConfig().foo).toEqualTypeOf<unknown>()
|
expectTypeOf(useAppConfig().foo).toEqualTypeOf<unknown>()
|
||||||
expectTypeOf(useAppConfig()).toEqualTypeOf<{
|
expectTypeOf(useAppConfig()).toEqualTypeOf<{
|
||||||
nuxt: { buildId: string }
|
nuxt: {}
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}>()
|
}>()
|
||||||
})
|
})
|
||||||
|
@ -30,9 +30,7 @@ describe('app config', () => {
|
|||||||
const appConfig = useAppConfig()
|
const appConfig = useAppConfig()
|
||||||
expect(appConfig).toMatchInlineSnapshot(`
|
expect(appConfig).toMatchInlineSnapshot(`
|
||||||
{
|
{
|
||||||
"nuxt": {
|
"nuxt": {},
|
||||||
"buildId": "override",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
updateAppConfig({
|
updateAppConfig({
|
||||||
@ -44,7 +42,6 @@ describe('app config', () => {
|
|||||||
{
|
{
|
||||||
"new": "value",
|
"new": "value",
|
||||||
"nuxt": {
|
"nuxt": {
|
||||||
"buildId": "override",
|
|
||||||
"nested": 42,
|
"nested": 42,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -13,12 +13,11 @@ export default defineVitestConfig({
|
|||||||
environmentOptions: {
|
environmentOptions: {
|
||||||
nuxt: {
|
nuxt: {
|
||||||
overrides: {
|
overrides: {
|
||||||
buildId: 'nuxt-app',
|
|
||||||
experimental: {
|
experimental: {
|
||||||
appManifest: process.env.TEST_MANIFEST !== 'manifest-off',
|
appManifest: process.env.TEST_MANIFEST !== 'manifest-off',
|
||||||
},
|
},
|
||||||
appConfig: {
|
runtimeConfig: {
|
||||||
nuxt: {
|
app: {
|
||||||
buildId: 'override',
|
buildId: 'override',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user