From 5645a879c1b051a5ec24cc12b52d9fcbb260e3b6 Mon Sep 17 00:00:00 2001 From: Julien Huang Date: Sun, 22 Feb 2026 23:35:54 +0100 Subject: [PATCH] refactor: move from NuxtApp augment to NuxtPayload --- client/app/composables/host.ts | 4 ++-- client/app/plugins/hydration.ts | 2 +- src/runtime/hydration/composables.ts | 2 +- src/runtime/hydration/plugin.client.ts | 2 +- src/runtime/lazy-load/composables.ts | 17 ++++++++++------- src/runtime/lazy-load/plugin.client.ts | 4 ++-- .../third-party-scripts/plugin.client.ts | 6 +++++- src/runtime/types.d.ts | 19 +++++++++++-------- src/runtime/web-vitals/plugin.client.ts | 14 +++++++------- test/runtime/lazy-load/composables.test.ts | 8 ++++---- 10 files changed, 44 insertions(+), 34 deletions(-) diff --git a/client/app/composables/host.ts b/client/app/composables/host.ts index 12b72d3..7e05066 100644 --- a/client/app/composables/host.ts +++ b/client/app/composables/host.ts @@ -10,7 +10,7 @@ export function useHostThirdPartyScripts() { } return { - scripts: client.host.nuxt.__hints_tpc, + scripts: client.host.nuxt.payload.__hints.thirdPartyScripts, isUsingNuxtScripts: Boolean(client.host.nuxt.$scripts), } } @@ -47,7 +47,7 @@ export function useHostWebVitals() { export function useHostHydration() { const host = useHostNuxt() - return { hydration: host.__hints.hydration } + return { hydration: host.payload.__hints.hydration } } export function useHostNuxt() { diff --git a/client/app/plugins/hydration.ts b/client/app/plugins/hydration.ts index f290e6a..b516737 100644 --- a/client/app/plugins/hydration.ts +++ b/client/app/plugins/hydration.ts @@ -7,7 +7,7 @@ export default defineNuxtPlugin(() => { const nuxtApp = useNuxtApp() const hydrationMismatches = ref<(HydrationMismatchPayload | LocalHydrationMismatch)[]>([]) - hydrationMismatches.value = [...host.__hints.hydration] + hydrationMismatches.value = [...host.payload.__hints.hydration] $fetch(new URL(HYDRATION_ROUTE, window.location.origin).href).then((data: { mismatches: HydrationMismatchPayload[] }) => { hydrationMismatches.value = [...hydrationMismatches.value, ...data.mismatches.filter(m => !hydrationMismatches.value.some(existing => existing.id === m.id))] diff --git a/src/runtime/hydration/composables.ts b/src/runtime/hydration/composables.ts index 573f84f..2a7e84c 100644 --- a/src/runtime/hydration/composables.ts +++ b/src/runtime/hydration/composables.ts @@ -43,7 +43,7 @@ export function useHydrationCheck() { method: 'POST', body, }).then((payload) => { - nuxtApp.__hints.hydration.push({ + nuxtApp.payload.__hints.hydration.push({ ...payload, instance, vnode: vnodePrehydration, diff --git a/src/runtime/hydration/plugin.client.ts b/src/runtime/hydration/plugin.client.ts index 2ff1ac0..24486f5 100644 --- a/src/runtime/hydration/plugin.client.ts +++ b/src/runtime/hydration/plugin.client.ts @@ -5,7 +5,7 @@ export default defineNuxtPlugin({ name: '@nuxt/hints:hydration', setup() { const nuxtApp = useNuxtApp() - nuxtApp.__hints = defu(nuxtApp.__hints, { + nuxtApp.payload.__hints = defu(nuxtApp.payload.__hints, { hydration: [], }) }, diff --git a/src/runtime/lazy-load/composables.ts b/src/runtime/lazy-load/composables.ts index 6154764..c0c84c1 100644 --- a/src/runtime/lazy-load/composables.ts +++ b/src/runtime/lazy-load/composables.ts @@ -1,18 +1,21 @@ import type { DefineComponent } from 'vue' import { useNuxtApp } from '#imports' +import { defu } from 'defu' import type { DirectImportInfo } from './schema' export function useLazyComponentTracking(components: DirectImportInfo[] = []) { const nuxtApp = useNuxtApp() - if (!nuxtApp.payload._lazyHydrationState) { - nuxtApp.payload._lazyHydrationState = { - directImports: new Map(), - hasReported: false, - pageLoaded: false, - } + if (!nuxtApp.payload.__hints?.lazyHydrationState) { + nuxtApp.payload.__hints = defu(nuxtApp.payload.__hints, { + lazyHydrationState: { + directImports: new Map(), + hasReported: false, + pageLoaded: false, + }, + }) } - const state = nuxtApp.payload._lazyHydrationState + const state = nuxtApp.payload.__hints.lazyHydrationState for (const comp of components) { state.directImports.set(comp.componentName, comp) diff --git a/src/runtime/lazy-load/plugin.client.ts b/src/runtime/lazy-load/plugin.client.ts index f0735b9..cc81db5 100644 --- a/src/runtime/lazy-load/plugin.client.ts +++ b/src/runtime/lazy-load/plugin.client.ts @@ -11,7 +11,7 @@ export default defineNuxtPlugin({ setup() { const nuxtApp = useNuxtApp() - nuxtApp.__hints = defu(nuxtApp.__hints, { + nuxtApp.payload.__hints = defu(nuxtApp.payload.__hints, { lazyComponents: [], }) @@ -52,7 +52,7 @@ function checkAndReport(state: ComponentLazyLoadState) { function reportSuggestions(suggestions: DirectImportInfo[]) { const route = useRoute() const nuxtApp = useNuxtApp() - nuxtApp.__hints.lazyComponents = suggestions + nuxtApp.payload.__hints.lazyComponents = suggestions logger.info( `${suggestions.length} component has not been rendered in SSR nor rendered at hydration time. Consider lazy loading it:\n`, diff --git a/src/runtime/third-party-scripts/plugin.client.ts b/src/runtime/third-party-scripts/plugin.client.ts index f62b364..597418c 100644 --- a/src/runtime/third-party-scripts/plugin.client.ts +++ b/src/runtime/third-party-scripts/plugin.client.ts @@ -1,4 +1,5 @@ import { defineNuxtPlugin, ref, useNuxtApp } from '#imports' +import { defu } from 'defu' import { logger } from '../logger' const EXTENSIONS_SCHEMES_RE = /^(chrome-extension|moz-extension|safari-extension|ms-browser-extension):/ @@ -32,7 +33,10 @@ export default defineNuxtPlugin({ setup() { const nuxtApp = useNuxtApp() - const scripts = nuxtApp.__hints_tpc = ref<{ element: HTMLScriptElement, loaded: boolean }[]>([]) + nuxtApp.payload.__hints = defu(nuxtApp.payload.__hints, { + thirdPartyScripts: ref<{ element: HTMLScriptElement, loaded: boolean }[]>([]), + }) + const scripts = nuxtApp.payload.__hints.thirdPartyScripts const isUsingNuxtScripts = !!nuxtApp.$scripts diff --git a/src/runtime/types.d.ts b/src/runtime/types.d.ts index 6892451..84cec0a 100644 --- a/src/runtime/types.d.ts +++ b/src/runtime/types.d.ts @@ -27,15 +27,20 @@ declare module '#app' { 'hints:scripts:added': (script: HTMLScriptElement) => void 'hints:scripts:loaded': (script: HTMLScriptElement) => void - 'hints:webvitals:sync': (webvitals: NuxtApp['__hints']['webvitals']) => void + 'hints:webvitals:sync': (webvitals: NuxtPayload['__hints']['webvitals']) => void 'hints:webvitals:lcp': (metric: LCPMetricWithAttribution) => void 'hints:webvitals:inp': (metric: INPMetricWithAttribution) => void 'hints:webvitals:cls': (metric: CLSMetricWithAttribution) => void } interface NuxtApp { - __hints_tpc: Ref<{ element: HTMLScriptElement, loaded: boolean }[]> + __tracerOverlay: typeof import('vite-plugin-vue-tracer/client/overlay') + __tracerRecord: typeof import('vite-plugin-vue-tracer/client/record') + } + + interface NuxtPayload { __hints: { + lazyHydrationState?: LazyHydrationState hydration: LocalHydrationMismatch[] lazyComponents: DirectImportInfo[] webvitals: { @@ -43,13 +48,11 @@ declare module '#app' { inp: Ref cls: Ref } + thirdPartyScripts: Ref<{ + element: HTMLScriptElement + loaded: boolean + }[]> } - __tracerOverlay: typeof import('vite-plugin-vue-tracer/client/overlay') - __tracerRecord: typeof import('vite-plugin-vue-tracer/client/record') - } - - interface NuxtPayload { - _lazyHydrationState?: LazyHydrationState } } diff --git a/src/runtime/web-vitals/plugin.client.ts b/src/runtime/web-vitals/plugin.client.ts index e29c3d5..8c91783 100644 --- a/src/runtime/web-vitals/plugin.client.ts +++ b/src/runtime/web-vitals/plugin.client.ts @@ -28,7 +28,7 @@ export default defineNuxtPlugin({ name: 'nuxt-hints:performance', setup() { const nuxtApp = useNuxtApp() - nuxtApp.__hints = defu(nuxtApp.__hints, { + nuxtApp.payload.__hints = defu(nuxtApp.payload.__hints, { webvitals: { lcp: ref([]), inp: ref([]), @@ -37,9 +37,9 @@ export default defineNuxtPlugin({ }) nuxtApp.hook('hints:webvitals:sync', (webvitals) => { - webvitals.lcp.value = [...nuxtApp.__hints.webvitals.lcp.value] - webvitals.inp.value = [...nuxtApp.__hints.webvitals.inp.value] - webvitals.cls.value = [...nuxtApp.__hints.webvitals.cls.value] + webvitals.lcp.value = [...nuxtApp.payload.__hints.webvitals.lcp.value] + webvitals.inp.value = [...nuxtApp.payload.__hints.webvitals.inp.value] + webvitals.cls.value = [...nuxtApp.payload.__hints.webvitals.cls.value] }) nuxtApp.hook('app:mounted', () => { @@ -51,7 +51,7 @@ export default defineNuxtPlugin({ '[web-vitals] INP Metric: ', metric, ) - nuxtApp.__hints.webvitals.inp.value.push(metric) + nuxtApp.payload.__hints.webvitals.inp.value.push(metric) nuxtApp.callHook('hints:webvitals:inp', metric) }, { reportAllChanges: true, @@ -65,7 +65,7 @@ export default defineNuxtPlugin({ `[web-vitals] LCP Metric: `, metric, ) - nuxtApp.__hints.webvitals.lcp.value.push(metric) + nuxtApp.payload.__hints.webvitals.lcp.value.push(metric) nuxtApp.callHook('hints:webvitals:lcp', metric) for (const performanceEntry of metric.entries) { @@ -130,7 +130,7 @@ export default defineNuxtPlugin({ metric, ) // Push the metric as-is; components will access entries[0] directly for element - nuxtApp.__hints.webvitals.cls.value.push(metric) + nuxtApp.payload.__hints.webvitals.cls.value.push(metric) for (const entry of metric.entries) { const performanceEntry = entry diff --git a/test/runtime/lazy-load/composables.test.ts b/test/runtime/lazy-load/composables.test.ts index cb722b3..dcaf340 100644 --- a/test/runtime/lazy-load/composables.test.ts +++ b/test/runtime/lazy-load/composables.test.ts @@ -19,7 +19,7 @@ describe('lazy-load composables', () => { beforeEach(() => { const nuxtApp = useNuxtApp() // Reset lazy hydration state between tests - delete nuxtApp.payload._lazyHydrationState + delete nuxtApp.payload.__hints?.lazyHydrationState }) describe('useLazyComponentTracking', () => { @@ -90,7 +90,7 @@ describe('lazy-load composables', () => { wrapped.setup!({}, { attrs: {}, slots: {}, emit: () => {}, expose: () => {} }) const nuxtApp = useNuxtApp() - const state = nuxtApp.payload._lazyHydrationState + const state = nuxtApp.payload.__hints?.lazyHydrationState expect(state).toBeDefined() expect(state.directImports.get('Child')).toEqual(imports[0]) }) @@ -121,7 +121,7 @@ describe('lazy-load composables', () => { wrapped.setup!({}, { attrs: {}, slots: {}, emit: () => {}, expose: () => {} }) const nuxtApp = useNuxtApp() - const state = nuxtApp.payload._lazyHydrationState + const state = nuxtApp.payload.__hints?.lazyHydrationState expect(state).toBeDefined() expect(state.directImports.get('MyComp')).toEqual({ componentName: 'MyComp', @@ -142,7 +142,7 @@ describe('lazy-load composables', () => { wrapped.setup!({}, { attrs: {}, slots: {}, emit: () => {}, expose: () => {} }) const nuxtApp = useNuxtApp() - const state = nuxtApp.payload._lazyHydrationState + const state = nuxtApp.payload.__hints?.lazyHydrationState // Should keep original import source since entry already existed expect(state.directImports.get('MyComp')!.importSource).toBe('./Original.vue') // But rendered should be true