From e645fc12ea115a2d2cc395ad83e4cc3df350c4ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Kaufmann?= Date: Thu, 17 Oct 2024 05:03:05 +0200 Subject: [PATCH] feat(nuxt): do not serialize skipHydrate properties Co-authored-by: Eduardo San Martin Morote --- packages/nuxt/__tests__/nuxt.spec.ts | 6 ++++++ packages/nuxt/playground/app.vue | 20 +----------------- packages/nuxt/playground/nuxt.config.ts | 4 ++++ packages/nuxt/playground/pages/index.vue | 21 +++++++++++++++++++ .../nuxt/playground/pages/skip-hydrate.vue | 13 ++++++++++++ .../playground/stores/with-skip-hydrate.ts | 16 ++++++++++++++ packages/nuxt/playground/tsconfig.json | 3 +++ packages/nuxt/src/module.ts | 18 +++++++++------- packages/nuxt/src/runtime/payload-plugin.ts | 20 ++++++++++++++++++ packages/nuxt/src/runtime/plugin.vue3.ts | 3 ++- packages/pinia/src/index.ts | 2 +- packages/pinia/src/store.ts | 2 +- 12 files changed, 99 insertions(+), 29 deletions(-) create mode 100644 packages/nuxt/playground/pages/index.vue create mode 100644 packages/nuxt/playground/pages/skip-hydrate.vue create mode 100644 packages/nuxt/playground/stores/with-skip-hydrate.ts create mode 100644 packages/nuxt/playground/tsconfig.json create mode 100644 packages/nuxt/src/runtime/payload-plugin.ts diff --git a/packages/nuxt/__tests__/nuxt.spec.ts b/packages/nuxt/__tests__/nuxt.spec.ts index 51f7cf5a83..563e94b432 100644 --- a/packages/nuxt/__tests__/nuxt.spec.ts +++ b/packages/nuxt/__tests__/nuxt.spec.ts @@ -33,4 +33,10 @@ describe('works with nuxt', async () => { const html = await $fetch('/') expect(html).toContain('Count: 101') }) + + it('drops state that is marked with skipHydrate', async () => { + const html = await $fetch('/skip-hydrate') + expect(html).not.toContain('I should not be serialized or hydrated') + expect(html).toContain('skipHydrate-wrapped state is correct') + }) }) diff --git a/packages/nuxt/playground/app.vue b/packages/nuxt/playground/app.vue index 450e65a2d3..8f62b8bf92 100644 --- a/packages/nuxt/playground/app.vue +++ b/packages/nuxt/playground/app.vue @@ -1,21 +1,3 @@ - - diff --git a/packages/nuxt/playground/nuxt.config.ts b/packages/nuxt/playground/nuxt.config.ts index 973b5d95a3..1431c79015 100644 --- a/packages/nuxt/playground/nuxt.config.ts +++ b/packages/nuxt/playground/nuxt.config.ts @@ -3,9 +3,11 @@ import { defineNuxtConfig } from 'nuxt/config' import piniaModule from '../src/module' export default defineNuxtConfig({ + devtools: { enabled: true }, alias: { pinia: fileURLToPath(new URL('../../pinia/src/index.ts', import.meta.url)), }, + modules: [piniaModule], pinia: { @@ -19,4 +21,6 @@ export default defineNuxtConfig({ __TEST__: false, }, }, + + compatibilityDate: '2024-09-26', }) diff --git a/packages/nuxt/playground/pages/index.vue b/packages/nuxt/playground/pages/index.vue new file mode 100644 index 0000000000..450e65a2d3 --- /dev/null +++ b/packages/nuxt/playground/pages/index.vue @@ -0,0 +1,21 @@ + + + diff --git a/packages/nuxt/playground/pages/skip-hydrate.vue b/packages/nuxt/playground/pages/skip-hydrate.vue new file mode 100644 index 0000000000..c3ad2aaf5d --- /dev/null +++ b/packages/nuxt/playground/pages/skip-hydrate.vue @@ -0,0 +1,13 @@ + + + diff --git a/packages/nuxt/playground/stores/with-skip-hydrate.ts b/packages/nuxt/playground/stores/with-skip-hydrate.ts new file mode 100644 index 0000000000..3cbbc31bc2 --- /dev/null +++ b/packages/nuxt/playground/stores/with-skip-hydrate.ts @@ -0,0 +1,16 @@ +import { skipHydrate } from 'pinia' + +export const useWithSkipHydrateStore = defineStore('with-skip-hydrate', () => { + const skipped = skipHydrate( + ref({ + text: 'I should not be serialized or hydrated', + }) + ) + return { skipped } +}) + +if (import.meta.hot) { + import.meta.hot.accept( + acceptHMRUpdate(useWithSkipHydrateStore, import.meta.hot) + ) +} diff --git a/packages/nuxt/playground/tsconfig.json b/packages/nuxt/playground/tsconfig.json new file mode 100644 index 0000000000..4b34df1571 --- /dev/null +++ b/packages/nuxt/playground/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./.nuxt/tsconfig.json" +} diff --git a/packages/nuxt/src/module.ts b/packages/nuxt/src/module.ts index b528bad4f6..08148ad940 100644 --- a/packages/nuxt/src/module.ts +++ b/packages/nuxt/src/module.ts @@ -11,6 +11,7 @@ import { addImportsDir, } from '@nuxt/kit' import type { NuxtModule } from '@nuxt/schema' +import { fileURLToPath } from 'node:url' export interface ModuleOptions { /** @@ -44,7 +45,9 @@ const module: NuxtModule = defineNuxtModule({ disableVuex: true, }, setup(options, nuxt) { - const resolver = createResolver(import.meta.url) + // configure transpilation + const { resolve } = createResolver(import.meta.url) + const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url)) // Disable default Vuex store (Nuxt v2.10+ only) if ( @@ -58,7 +61,7 @@ const module: NuxtModule = defineNuxtModule({ } // Transpile runtime - nuxt.options.build.transpile.push(resolver.resolve('./runtime')) + nuxt.options.build.transpile.push(resolve(runtimeDir)) // Make sure we use the mjs build for pinia nuxt.options.alias.pinia = @@ -76,14 +79,15 @@ const module: NuxtModule = defineNuxtModule({ // https://github.com/nuxt/framework/issues/9130 nuxt.hook('modules:done', () => { if (isNuxt2()) { - addPlugin(resolver.resolve('./runtime/plugin.vue2')) + addPlugin(resolve(runtimeDir, 'plugin.vue2')) } else { - addPlugin(resolver.resolve('./runtime/plugin.vue3')) + addPlugin(resolve(runtimeDir, 'plugin.vue3')) + addPlugin(resolve(runtimeDir, 'payload-plugin')) } }) // Add auto imports - const composables = resolver.resolve('./runtime/composables') + const composables = resolve(runtimeDir, 'composables') addImports([ { from: composables, name: 'defineStore' }, { from: composables, name: 'acceptHMRUpdate' }, @@ -93,12 +97,12 @@ const module: NuxtModule = defineNuxtModule({ if (!options.storesDirs) { // resolve it against the src dir which is the root by default - options.storesDirs = [resolver.resolve(nuxt.options.srcDir, 'stores')] + options.storesDirs = [resolve(nuxt.options.srcDir, 'stores')] } if (options.storesDirs) { for (const storeDir of options.storesDirs) { - addImportsDir(resolver.resolve(nuxt.options.rootDir, storeDir)) + addImportsDir(resolve(nuxt.options.rootDir, storeDir)) } } }, diff --git a/packages/nuxt/src/runtime/payload-plugin.ts b/packages/nuxt/src/runtime/payload-plugin.ts new file mode 100644 index 0000000000..8563543257 --- /dev/null +++ b/packages/nuxt/src/runtime/payload-plugin.ts @@ -0,0 +1,20 @@ +import { + definePayloadPlugin, + definePayloadReducer, + definePayloadReviver, +} from '#imports' +import { shouldHydrate } from 'pinia' + +/** + * Removes properties marked with `skipHydrate()` to avoid sending unused data to the client. + */ +const payloadPlugin = definePayloadPlugin(() => { + definePayloadReducer( + 'skipHydrate', + // We need to return something truthy to be treated as a match + (data: unknown) => !shouldHydrate(data) && 1 + ) + definePayloadReviver('skipHydrate', (_data: 1) => undefined) +}) + +export default payloadPlugin diff --git a/packages/nuxt/src/runtime/plugin.vue3.ts b/packages/nuxt/src/runtime/plugin.vue3.ts index f15db36457..1e101d066a 100644 --- a/packages/nuxt/src/runtime/plugin.vue3.ts +++ b/packages/nuxt/src/runtime/plugin.vue3.ts @@ -1,6 +1,7 @@ import { createPinia, setActivePinia } from 'pinia' import type { Pinia } from 'pinia' import { defineNuxtPlugin, type Plugin } from '#app' +import { toRaw } from 'vue' const plugin: Plugin<{ pinia: Pinia }> = defineNuxtPlugin({ name: 'pinia', @@ -10,7 +11,7 @@ const plugin: Plugin<{ pinia: Pinia }> = defineNuxtPlugin({ setActivePinia(pinia) if (import.meta.server) { - nuxtApp.payload.pinia = pinia.state.value + nuxtApp.payload.pinia = toRaw(pinia.state.value) } else if (nuxtApp.payload && nuxtApp.payload.pinia) { pinia.state.value = nuxtApp.payload.pinia as any } diff --git a/packages/pinia/src/index.ts b/packages/pinia/src/index.ts index d06aedc4fa..be7b83475b 100644 --- a/packages/pinia/src/index.ts +++ b/packages/pinia/src/index.ts @@ -11,7 +11,7 @@ export type { PiniaPluginContext, } from './rootStore' -export { defineStore, skipHydrate } from './store' +export { defineStore, skipHydrate, shouldHydrate } from './store' export type { StoreActions, StoreGetters, diff --git a/packages/pinia/src/store.ts b/packages/pinia/src/store.ts index 3bd239bdfb..16ae7aa4a1 100644 --- a/packages/pinia/src/store.ts +++ b/packages/pinia/src/store.ts @@ -139,7 +139,7 @@ export function skipHydrate(obj: T): T { * @param obj - target variable * @returns true if `obj` should be hydrated */ -function shouldHydrate(obj: any) { +export function shouldHydrate(obj: any) { return isVue2 ? /* istanbul ignore next */ !skipHydrateMap.has(obj) : !isPlainObject(obj) || !obj.hasOwnProperty(skipHydrateSymbol)