Skip to content

Commit e645fc1

Browse files
kabalageposva
andauthored
feat(nuxt): do not serialize skipHydrate properties
Co-authored-by: Eduardo San Martin Morote <posva13@gmail.com>
1 parent 3d238f7 commit e645fc1

File tree

12 files changed

+99
-29
lines changed

12 files changed

+99
-29
lines changed

packages/nuxt/__tests__/nuxt.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,10 @@ describe('works with nuxt', async () => {
3333
const html = await $fetch('/')
3434
expect(html).toContain('Count: 101')
3535
})
36+
37+
it('drops state that is marked with skipHydrate', async () => {
38+
const html = await $fetch('/skip-hydrate')
39+
expect(html).not.toContain('I should not be serialized or hydrated')
40+
expect(html).toContain('skipHydrate-wrapped state is correct')
41+
})
3642
})

packages/nuxt/playground/app.vue

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,3 @@
1-
<script lang="ts" setup>
2-
// import {useCounter }from '~/stores/counter'
3-
4-
const counter = useCounter()
5-
6-
useTestStore()
7-
useSomeStoreStore()
8-
9-
// await useAsyncData('counter', () => counter.asyncIncrement().then(() => true))
10-
11-
if (import.meta.server) {
12-
counter.increment()
13-
}
14-
</script>
15-
161
<template>
17-
<div>
18-
<p>Count: {{ counter.$state.count }}</p>
19-
<button @click="counter.increment()">+</button>
20-
</div>
2+
<NuxtPage />
213
</template>

packages/nuxt/playground/nuxt.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import { defineNuxtConfig } from 'nuxt/config'
33
import piniaModule from '../src/module'
44

55
export default defineNuxtConfig({
6+
devtools: { enabled: true },
67
alias: {
78
pinia: fileURLToPath(new URL('../../pinia/src/index.ts', import.meta.url)),
89
},
10+
911
modules: [piniaModule],
1012

1113
pinia: {
@@ -19,4 +21,6 @@ export default defineNuxtConfig({
1921
__TEST__: false,
2022
},
2123
},
24+
25+
compatibilityDate: '2024-09-26',
2226
})
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script lang="ts" setup>
2+
// import {useCounter }from '~/stores/counter'
3+
4+
const counter = useCounter()
5+
6+
useTestStore()
7+
useSomeStoreStore()
8+
9+
// await useAsyncData('counter', () => counter.asyncIncrement().then(() => true))
10+
11+
if (import.meta.server) {
12+
counter.increment()
13+
}
14+
</script>
15+
16+
<template>
17+
<div>
18+
<p>Count: {{ counter.$state.count }}</p>
19+
<button @click="counter.increment()">+</button>
20+
</div>
21+
</template>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script lang="ts" setup>
2+
const store = useWithSkipHydrateStore()
3+
const skipHydrateState = computed(() => {
4+
return store.skipped?.text === 'I should not be serialized or hydrated'
5+
? 'skipHydrate-wrapped state is correct'
6+
: 'skipHydrate-wrapped state is incorrect'
7+
})
8+
</script>
9+
10+
<template>
11+
<h2>skipHydrate() test</h2>
12+
<p>{{ skipHydrateState }}</p>
13+
</template>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { skipHydrate } from 'pinia'
2+
3+
export const useWithSkipHydrateStore = defineStore('with-skip-hydrate', () => {
4+
const skipped = skipHydrate(
5+
ref({
6+
text: 'I should not be serialized or hydrated',
7+
})
8+
)
9+
return { skipped }
10+
})
11+
12+
if (import.meta.hot) {
13+
import.meta.hot.accept(
14+
acceptHMRUpdate(useWithSkipHydrateStore, import.meta.hot)
15+
)
16+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "./.nuxt/tsconfig.json"
3+
}

packages/nuxt/src/module.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
addImportsDir,
1212
} from '@nuxt/kit'
1313
import type { NuxtModule } from '@nuxt/schema'
14+
import { fileURLToPath } from 'node:url'
1415

1516
export interface ModuleOptions {
1617
/**
@@ -44,7 +45,9 @@ const module: NuxtModule<ModuleOptions> = defineNuxtModule<ModuleOptions>({
4445
disableVuex: true,
4546
},
4647
setup(options, nuxt) {
47-
const resolver = createResolver(import.meta.url)
48+
// configure transpilation
49+
const { resolve } = createResolver(import.meta.url)
50+
const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url))
4851

4952
// Disable default Vuex store (Nuxt v2.10+ only)
5053
if (
@@ -58,7 +61,7 @@ const module: NuxtModule<ModuleOptions> = defineNuxtModule<ModuleOptions>({
5861
}
5962

6063
// Transpile runtime
61-
nuxt.options.build.transpile.push(resolver.resolve('./runtime'))
64+
nuxt.options.build.transpile.push(resolve(runtimeDir))
6265

6366
// Make sure we use the mjs build for pinia
6467
nuxt.options.alias.pinia =
@@ -76,14 +79,15 @@ const module: NuxtModule<ModuleOptions> = defineNuxtModule<ModuleOptions>({
7679
// https://github.com/nuxt/framework/issues/9130
7780
nuxt.hook('modules:done', () => {
7881
if (isNuxt2()) {
79-
addPlugin(resolver.resolve('./runtime/plugin.vue2'))
82+
addPlugin(resolve(runtimeDir, 'plugin.vue2'))
8083
} else {
81-
addPlugin(resolver.resolve('./runtime/plugin.vue3'))
84+
addPlugin(resolve(runtimeDir, 'plugin.vue3'))
85+
addPlugin(resolve(runtimeDir, 'payload-plugin'))
8286
}
8387
})
8488

8589
// Add auto imports
86-
const composables = resolver.resolve('./runtime/composables')
90+
const composables = resolve(runtimeDir, 'composables')
8791
addImports([
8892
{ from: composables, name: 'defineStore' },
8993
{ from: composables, name: 'acceptHMRUpdate' },
@@ -93,12 +97,12 @@ const module: NuxtModule<ModuleOptions> = defineNuxtModule<ModuleOptions>({
9397

9498
if (!options.storesDirs) {
9599
// resolve it against the src dir which is the root by default
96-
options.storesDirs = [resolver.resolve(nuxt.options.srcDir, 'stores')]
100+
options.storesDirs = [resolve(nuxt.options.srcDir, 'stores')]
97101
}
98102

99103
if (options.storesDirs) {
100104
for (const storeDir of options.storesDirs) {
101-
addImportsDir(resolver.resolve(nuxt.options.rootDir, storeDir))
105+
addImportsDir(resolve(nuxt.options.rootDir, storeDir))
102106
}
103107
}
104108
},
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import {
2+
definePayloadPlugin,
3+
definePayloadReducer,
4+
definePayloadReviver,
5+
} from '#imports'
6+
import { shouldHydrate } from 'pinia'
7+
8+
/**
9+
* Removes properties marked with `skipHydrate()` to avoid sending unused data to the client.
10+
*/
11+
const payloadPlugin = definePayloadPlugin(() => {
12+
definePayloadReducer(
13+
'skipHydrate',
14+
// We need to return something truthy to be treated as a match
15+
(data: unknown) => !shouldHydrate(data) && 1
16+
)
17+
definePayloadReviver('skipHydrate', (_data: 1) => undefined)
18+
})
19+
20+
export default payloadPlugin

packages/nuxt/src/runtime/plugin.vue3.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createPinia, setActivePinia } from 'pinia'
22
import type { Pinia } from 'pinia'
33
import { defineNuxtPlugin, type Plugin } from '#app'
4+
import { toRaw } from 'vue'
45

56
const plugin: Plugin<{ pinia: Pinia }> = defineNuxtPlugin({
67
name: 'pinia',
@@ -10,7 +11,7 @@ const plugin: Plugin<{ pinia: Pinia }> = defineNuxtPlugin({
1011
setActivePinia(pinia)
1112

1213
if (import.meta.server) {
13-
nuxtApp.payload.pinia = pinia.state.value
14+
nuxtApp.payload.pinia = toRaw(pinia.state.value)
1415
} else if (nuxtApp.payload && nuxtApp.payload.pinia) {
1516
pinia.state.value = nuxtApp.payload.pinia as any
1617
}

0 commit comments

Comments
 (0)