Skip to content

Commit

Permalink
feat(nuxt): do not serialize skipHydrate properties
Browse files Browse the repository at this point in the history
Co-authored-by: Eduardo San Martin Morote <[email protected]>
  • Loading branch information
kabalage and posva authored Oct 17, 2024
1 parent 3d238f7 commit e645fc1
Show file tree
Hide file tree
Showing 12 changed files with 99 additions and 29 deletions.
6 changes: 6 additions & 0 deletions packages/nuxt/__tests__/nuxt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
})
})
20 changes: 1 addition & 19 deletions packages/nuxt/playground/app.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,3 @@
<script lang="ts" setup>
// import {useCounter }from '~/stores/counter'
const counter = useCounter()
useTestStore()
useSomeStoreStore()
// await useAsyncData('counter', () => counter.asyncIncrement().then(() => true))
if (import.meta.server) {
counter.increment()
}
</script>

<template>
<div>
<p>Count: {{ counter.$state.count }}</p>
<button @click="counter.increment()">+</button>
</div>
<NuxtPage />
</template>
4 changes: 4 additions & 0 deletions packages/nuxt/playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -19,4 +21,6 @@ export default defineNuxtConfig({
__TEST__: false,
},
},

compatibilityDate: '2024-09-26',
})
21 changes: 21 additions & 0 deletions packages/nuxt/playground/pages/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script lang="ts" setup>
// import {useCounter }from '~/stores/counter'
const counter = useCounter()
useTestStore()
useSomeStoreStore()
// await useAsyncData('counter', () => counter.asyncIncrement().then(() => true))
if (import.meta.server) {
counter.increment()
}
</script>

<template>
<div>
<p>Count: {{ counter.$state.count }}</p>
<button @click="counter.increment()">+</button>
</div>
</template>
13 changes: 13 additions & 0 deletions packages/nuxt/playground/pages/skip-hydrate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script lang="ts" setup>
const store = useWithSkipHydrateStore()
const skipHydrateState = computed(() => {
return store.skipped?.text === 'I should not be serialized or hydrated'
? 'skipHydrate-wrapped state is correct'
: 'skipHydrate-wrapped state is incorrect'
})
</script>

<template>
<h2>skipHydrate() test</h2>
<p>{{ skipHydrateState }}</p>
</template>
16 changes: 16 additions & 0 deletions packages/nuxt/playground/stores/with-skip-hydrate.ts
Original file line number Diff line number Diff line change
@@ -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)
)
}
3 changes: 3 additions & 0 deletions packages/nuxt/playground/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "./.nuxt/tsconfig.json"
}
18 changes: 11 additions & 7 deletions packages/nuxt/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
addImportsDir,
} from '@nuxt/kit'
import type { NuxtModule } from '@nuxt/schema'
import { fileURLToPath } from 'node:url'

export interface ModuleOptions {
/**
Expand Down Expand Up @@ -44,7 +45,9 @@ const module: NuxtModule<ModuleOptions> = defineNuxtModule<ModuleOptions>({
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 (
Expand All @@ -58,7 +61,7 @@ const module: NuxtModule<ModuleOptions> = defineNuxtModule<ModuleOptions>({
}

// 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 =
Expand All @@ -76,14 +79,15 @@ const module: NuxtModule<ModuleOptions> = defineNuxtModule<ModuleOptions>({
// 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' },
Expand All @@ -93,12 +97,12 @@ const module: NuxtModule<ModuleOptions> = defineNuxtModule<ModuleOptions>({

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))
}
}
},
Expand Down
20 changes: 20 additions & 0 deletions packages/nuxt/src/runtime/payload-plugin.ts
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion packages/nuxt/src/runtime/plugin.vue3.ts
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion packages/pinia/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type {
PiniaPluginContext,
} from './rootStore'

export { defineStore, skipHydrate } from './store'
export { defineStore, skipHydrate, shouldHydrate } from './store'
export type {
StoreActions,
StoreGetters,
Expand Down
2 changes: 1 addition & 1 deletion packages/pinia/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export function skipHydrate<T = any>(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)
Expand Down

0 comments on commit e645fc1

Please sign in to comment.