From e7387c41468e2f0cd82e10bb36b03d8ab5afe4d9 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Thu, 30 Jan 2025 17:31:53 +0100 Subject: [PATCH 01/13] docs: update prop comment Close #2899 --- packages/docs/core-concepts/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs/core-concepts/index.md b/packages/docs/core-concepts/index.md index 2cc3474b77..3f40d479d8 100644 --- a/packages/docs/core-concepts/index.md +++ b/packages/docs/core-concepts/index.md @@ -127,7 +127,7 @@ You can define as many stores as you want and **you should define each store in Once the store is instantiated, you can access any property defined in `state`, `getters`, and `actions` directly on the store. We will look at these in detail in the next pages but autocompletion will help you. -Note that `store` is an object wrapped with `reactive`, meaning there is no need to write `.value` after getters but, like `props` in `setup`, **we cannot destructure it**: +Note that `store` is an object wrapped with `reactive`, meaning there is no need to write `.value` after getters but, like any `reactive()` object in Vue, [**we lose reactivity when destructuring it**](https://vuejs.org/guide/essentials/reactivity-fundamentals.html#limitations-of-reactive): ```vue ``` -If your action doesn't resolve a value, you can add any non nullish value: +Depending on your requirements, you can choose to run the action only once on the client, or on every navigation (which is closer to data fetching behavior of `useFetch()`/`useAsyncData()`) ```vue{3} ``` From 2e0feece4ba7e395dd8e3915c6089f41c7c65f56 Mon Sep 17 00:00:00 2001 From: Paul Guilbert Date: Wed, 11 Dec 2024 15:33:52 +0100 Subject: [PATCH 10/13] fix: throw when a store is used outside of a Nuxt-aware context. Prefer the Nuxt Pinia instance over the global active Pinia instance. Since the Nuxt Pinia instance is discarded after each request, it ensures that we can't accidentally use one from another request. Additionally, `usePinia` will throw an error when used outside of a Nuxt-aware context. The error is as follows in dev : > [nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function. --- .../playground/pages/usage-after-await.vue | 17 ++++++++++++++ packages/nuxt/src/runtime/composables.ts | 23 +++++++++++++++++++ packages/nuxt/test/nuxt.spec.ts | 4 ++++ 3 files changed, 44 insertions(+) create mode 100644 packages/nuxt/playground/pages/usage-after-await.vue diff --git a/packages/nuxt/playground/pages/usage-after-await.vue b/packages/nuxt/playground/pages/usage-after-await.vue new file mode 100644 index 0000000000..adc86268d0 --- /dev/null +++ b/packages/nuxt/playground/pages/usage-after-await.vue @@ -0,0 +1,17 @@ + + + diff --git a/packages/nuxt/src/runtime/composables.ts b/packages/nuxt/src/runtime/composables.ts index 07fcf54767..098511a766 100644 --- a/packages/nuxt/src/runtime/composables.ts +++ b/packages/nuxt/src/runtime/composables.ts @@ -1,4 +1,27 @@ import { useNuxtApp } from '#app' +import { + defineStore as _defineStore, + type Pinia, + type StoreGeneric, +} from 'pinia' export * from 'pinia' export const usePinia = () => useNuxtApp().$pinia + +export const defineStore = (...args) => { + if (!import.meta.server) { + return _defineStore(...args) + } + + const store = _defineStore(...args) + + function useStore(pinia?: Pinia | null, hot?: StoreGeneric): StoreGeneric { + if (pinia) { + return store(pinia, hot) + } + + return store(usePinia(), hot) + } + + return useStore +} diff --git a/packages/nuxt/test/nuxt.spec.ts b/packages/nuxt/test/nuxt.spec.ts index 8bcbfccc05..a1bd848258 100644 --- a/packages/nuxt/test/nuxt.spec.ts +++ b/packages/nuxt/test/nuxt.spec.ts @@ -33,4 +33,8 @@ describe('works with nuxt', async () => { expect(html).not.toContain('I should not be serialized or hydrated') expect(html).toContain('skipHydrate-wrapped state is correct') }) + + it('throws an error server-side when the nuxt context is not available', async () => { + await expect($fetch('/usage-after-await')).rejects.toThrow() + }) }) From 5b6c66d5c98a8e2c380f0cf3ba81c25458ff4f89 Mon Sep 17 00:00:00 2001 From: Paul Guilbert Date: Fri, 13 Dec 2024 17:25:50 +0100 Subject: [PATCH 11/13] feat: improve typing --- packages/nuxt/src/runtime/composables.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/nuxt/src/runtime/composables.ts b/packages/nuxt/src/runtime/composables.ts index 098511a766..93fdc60a55 100644 --- a/packages/nuxt/src/runtime/composables.ts +++ b/packages/nuxt/src/runtime/composables.ts @@ -6,22 +6,22 @@ import { } from 'pinia' export * from 'pinia' -export const usePinia = () => useNuxtApp().$pinia +export const usePinia = () => useNuxtApp().$pinia as Pinia | undefined -export const defineStore = (...args) => { +export const defineStore: typeof _defineStore = ( + ...args: [idOrOptions: any, setup?: any, setupOptions?: any] +) => { if (!import.meta.server) { return _defineStore(...args) } - const store = _defineStore(...args) - + const originalUseStore = _defineStore(...args) function useStore(pinia?: Pinia | null, hot?: StoreGeneric): StoreGeneric { - if (pinia) { - return store(pinia, hot) - } - - return store(usePinia(), hot) + return originalUseStore(pinia || usePinia(), hot) } + useStore.$id = originalUseStore.$id + useStore._pinia = originalUseStore._pinia + return useStore } From 1f3d17aa8cc1330f77409650a576a5deed13fcd7 Mon Sep 17 00:00:00 2001 From: Paul Guilbert Date: Fri, 13 Dec 2024 18:02:00 +0100 Subject: [PATCH 12/13] test: improve tests --- packages/nuxt/playground/pages/usage-after-await.vue | 7 +++++++ packages/nuxt/test/nuxt.spec.ts | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/nuxt/playground/pages/usage-after-await.vue b/packages/nuxt/playground/pages/usage-after-await.vue index adc86268d0..64af35d2ed 100644 --- a/packages/nuxt/playground/pages/usage-after-await.vue +++ b/packages/nuxt/playground/pages/usage-after-await.vue @@ -6,6 +6,13 @@ const useFancyCounter = async () => { return useCounter() } +const event = useRequestEvent() +useNuxtApp().hook('vue:error', (error) => { + if (event) { + setResponseStatus(event, 500, String(error)) + } +}) + const counter = await useFancyCounter() diff --git a/packages/nuxt/test/nuxt.spec.ts b/packages/nuxt/test/nuxt.spec.ts index a1bd848258..1e082e4b8c 100644 --- a/packages/nuxt/test/nuxt.spec.ts +++ b/packages/nuxt/test/nuxt.spec.ts @@ -35,6 +35,8 @@ describe('works with nuxt', async () => { }) it('throws an error server-side when the nuxt context is not available', async () => { - await expect($fetch('/usage-after-await')).rejects.toThrow() + await expect($fetch('/usage-after-await')).rejects.toThrowError( + '[nuxt] instance unavailable' + ) }) }) From 5cf66cf7477416c46fc1debd542221e472b67e03 Mon Sep 17 00:00:00 2001 From: Paul Guilbert Date: Fri, 2 May 2025 15:18:52 +0200 Subject: [PATCH 13/13] feat: use pinia instance from nuxt only in dev mode --- packages/nuxt/global.d.ts | 2 ++ packages/nuxt/src/runtime/composables.ts | 32 +++++++++++++----------- packages/nuxt/tsconfig.json | 1 + 3 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 packages/nuxt/global.d.ts diff --git a/packages/nuxt/global.d.ts b/packages/nuxt/global.d.ts new file mode 100644 index 0000000000..cc0c1cb92a --- /dev/null +++ b/packages/nuxt/global.d.ts @@ -0,0 +1,2 @@ +// Global compile-time constants +declare var __TEST__: boolean diff --git a/packages/nuxt/src/runtime/composables.ts b/packages/nuxt/src/runtime/composables.ts index 93fdc60a55..a4b395bc83 100644 --- a/packages/nuxt/src/runtime/composables.ts +++ b/packages/nuxt/src/runtime/composables.ts @@ -8,20 +8,24 @@ export * from 'pinia' export const usePinia = () => useNuxtApp().$pinia as Pinia | undefined -export const defineStore: typeof _defineStore = ( - ...args: [idOrOptions: any, setup?: any, setupOptions?: any] -) => { - if (!import.meta.server) { - return _defineStore(...args) - } +export const defineStore: typeof _defineStore = + process.env.NODE_ENV === 'production' && !__TEST__ + ? _defineStore + : (...args: [idOrOptions: any, setup?: any, setupOptions?: any]) => { + if (!import.meta.server) { + return _defineStore(...args) + } - const originalUseStore = _defineStore(...args) - function useStore(pinia?: Pinia | null, hot?: StoreGeneric): StoreGeneric { - return originalUseStore(pinia || usePinia(), hot) - } + const originalUseStore = _defineStore(...args) + function useStore( + pinia?: Pinia | null, + hot?: StoreGeneric + ): StoreGeneric { + return originalUseStore(pinia || usePinia(), hot) + } - useStore.$id = originalUseStore.$id - useStore._pinia = originalUseStore._pinia + useStore.$id = originalUseStore.$id + useStore._pinia = originalUseStore._pinia - return useStore -} + return useStore + } diff --git a/packages/nuxt/tsconfig.json b/packages/nuxt/tsconfig.json index 3666c452fc..9fc3ea221f 100644 --- a/packages/nuxt/tsconfig.json +++ b/packages/nuxt/tsconfig.json @@ -2,6 +2,7 @@ "extends": "./playground/.nuxt/tsconfig.json", "include": [ "./shims.d.ts", + "./global.d.ts", // missing in the playground "./src" ]