From: Eduardo San Martin Morote Date: Mon, 8 May 2023 16:34:38 +0000 (+0200) Subject: feat: allow app injections in setup stores X-Git-Tag: @pinia/nuxt@0.4.11~17 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6a71019ef2a633dcb097a63be895a8b1714863b6;p=thirdparty%2Fvuejs%2Fpinia.git feat: allow app injections in setup stores Close #1784 --- diff --git a/packages/pinia/__tests__/storeSetup.spec.ts b/packages/pinia/__tests__/storeSetup.spec.ts index b7709c3e..cd5e9e1a 100644 --- a/packages/pinia/__tests__/storeSetup.spec.ts +++ b/packages/pinia/__tests__/storeSetup.spec.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, it, expect, vi } from 'vitest' import { createPinia, defineStore, setActivePinia } from '../src' -import { computed, nextTick, ref, watch } from 'vue' +import { computed, inject, nextTick, ref, watch, version } from 'vue' function expectType(_value: T): void {} @@ -132,4 +132,18 @@ describe('store with setup syntax', () => { expect(store.counter).toBe(2) expect(counter.value).toBe(2) }) + + // TODO: + it.todo('can use app level injections', async () => { + const pinia = createPinia() + setActivePinia(pinia) + const useStore = defineStore('id', () => { + const injected = ref(inject('hello', 'nope')) + + return { injected } + }) + + const store = useStore() + expect(store.injected).toBe('pinia') + }) }) diff --git a/packages/pinia/src/rootStore.ts b/packages/pinia/src/rootStore.ts index cc212401..0eab00df 100644 --- a/packages/pinia/src/rootStore.ts +++ b/packages/pinia/src/rootStore.ts @@ -1,11 +1,6 @@ -import { - App, - EffectScope, - getCurrentInstance, - inject, - InjectionKey, - Ref, -} from 'vue-demi' +import { App, EffectScope, inject, InjectionKey, Ref } from 'vue-demi' +// FIXME: move to vue-demi when available +import { hasInjectionContext } from 'vue' import { StateTree, PiniaCustomProperties, @@ -43,7 +38,7 @@ interface _SetActivePinia { * Get the currently active pinia if there is any. */ export const getActivePinia = () => - (getCurrentInstance() && inject(piniaSymbol)) || activePinia + (hasInjectionContext() && inject(piniaSymbol)) || activePinia /** * Every application must own its own pinia to be able to create stores diff --git a/packages/pinia/src/store.ts b/packages/pinia/src/store.ts index 7803dfdc..57cc7b32 100644 --- a/packages/pinia/src/store.ts +++ b/packages/pinia/src/store.ts @@ -23,6 +23,8 @@ import { nextTick, isVue2, } from 'vue-demi' +// FIXME: move to vue-demi when available +import { hasInjectionContext } from 'vue' import { StateTree, SubscriptionCallback, @@ -51,6 +53,8 @@ import { IS_CLIENT, USE_DEVTOOLS } from './env' import { patchObject } from './hmr' import { addSubscription, triggerSubscriptions, noop } from './subscriptions' +const fallbackRunWithContext = (fn: Function) => fn() + type _ArrayType = AT extends Array ? T : never function mergeReactiveObjects< @@ -472,10 +476,13 @@ function createSetupStore< // creating infinite loops. pinia._s.set($id, store) + const runWithContext = + (pinia._a && pinia._a.runWithContext) || fallbackRunWithContext + // TODO: idea create skipSerialize that marks properties as non serializable and they are skipped const setupStore = pinia._e.run(() => { scope = effectScope() - return scope.run(() => setup()) + return runWithContext(() => scope.run(() => setup())) })! // overwrite existing actions to support $onAction @@ -888,12 +895,12 @@ export function defineStore( } function useStore(pinia?: Pinia | null, hot?: StoreGeneric): StoreGeneric { - const currentInstance = getCurrentInstance() + const hasContext = hasInjectionContext() pinia = // in test mode, ignore the argument provided as we can always retrieve a // pinia instance with getActivePinia() (__TEST__ && activePinia && activePinia._testing ? null : pinia) || - (currentInstance && inject(piniaSymbol, null)) + (hasContext ? inject(piniaSymbol, null) : null) if (pinia) setActivePinia(pinia) if (__DEV__ && !activePinia) { @@ -937,18 +944,19 @@ export function defineStore( pinia._s.delete(hotId) } - // save stores in instances to access them devtools - if ( - __DEV__ && - IS_CLIENT && - currentInstance && - currentInstance.proxy && - // avoid adding stores that are just built for hot module replacement - !hot - ) { - const vm = currentInstance.proxy - const cache = '_pStores' in vm ? vm._pStores! : (vm._pStores = {}) - cache[id] = store + if (__DEV__ && IS_CLIENT) { + const currentInstance = getCurrentInstance() + // save stores in instances to access them devtools + if ( + currentInstance && + currentInstance.proxy && + // avoid adding stores that are just built for hot module replacement + !hot + ) { + const vm = currentInstance.proxy + const cache = '_pStores' in vm ? vm._pStores! : (vm._pStores = {}) + cache[id] = store + } } // StoreGeneric cannot be casted towards Store diff --git a/packages/playground/src/main.ts b/packages/playground/src/main.ts index cb71b2fd..48f82f44 100644 --- a/packages/playground/src/main.ts +++ b/packages/playground/src/main.ts @@ -1,4 +1,4 @@ -import { computed, createApp, markRaw, Ref } from 'vue' +import { computed, createApp, inject, markRaw, Ref } from 'vue' import App from './App.vue' import { createPinia } from 'pinia' import { router } from './router' @@ -6,6 +6,7 @@ import { RouteLocationNormalized, RouteLocationNormalizedLoaded, } from 'vue-router' +import { useCounter } from './stores/counterSetup' const pinia = createPinia() @@ -60,4 +61,13 @@ if (import.meta.hot) { // } } -createApp(App).use(router).use(pinia).mount('#app') +const app = createApp(App).use(pinia).use(router).provide('hello', 'injections') + +app.mount('#app') + +console.log( + 'hello', + app.runWithContext(() => inject('hello')) +) + +const store = useCounter() diff --git a/packages/playground/src/stores/counterSetup.ts b/packages/playground/src/stores/counterSetup.ts index 4c5330da..04cafdf5 100644 --- a/packages/playground/src/stores/counterSetup.ts +++ b/packages/playground/src/stores/counterSetup.ts @@ -1,5 +1,6 @@ -import { computed, toRefs, reactive } from 'vue' +import { computed, toRefs, reactive, inject } from 'vue' import { acceptHMRUpdate, defineStore } from 'pinia' +import { useRoute } from 'vue-router' const delay = (t: number) => new Promise((r) => setTimeout(r, t)) @@ -11,6 +12,11 @@ export const useCounter = defineStore('counter-setup', () => { numbers: [] as number[], }) + const route = useRoute() + console.log('route in setup', route) + + console.log('injection', inject('hello')) + const double = computed(() => state.n * 2) function increment(amount = 1) { diff --git a/packages/playground/tsconfig.json b/packages/playground/tsconfig.json index 860623e5..b3d868bc 100644 --- a/packages/playground/tsconfig.json +++ b/packages/playground/tsconfig.json @@ -10,7 +10,10 @@ "resolveJsonModule": true, "esModuleInterop": true, "lib": ["esnext", "dom"], - "types": ["vite/client"] - } + "types": ["vite/client"], + "paths": { + "pinia": ["../pinia/src/index.ts"] + } + }, // "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] } diff --git a/packages/playground/vite.config.ts b/packages/playground/vite.config.ts index 456e8764..f484acf5 100644 --- a/packages/playground/vite.config.ts +++ b/packages/playground/vite.config.ts @@ -7,7 +7,7 @@ import path from 'path' export default defineConfig({ plugins: [vue(), copyPiniaPlugin()], define: { - // __DEV__: 'true', + __DEV__: 'true', // __BROWSER__: 'true', __TEST__: 'false', }, @@ -15,7 +15,10 @@ export default defineConfig({ // alias: { // '@vue/composition-api': 'vue-demi', // }, - dedupe: ['vue-demi', 'vue'], + dedupe: ['vue-demi', 'vue', 'pinia'], + alias: { + pinia: path.resolve(__dirname, '../pinia/src/index.ts'), + }, }, optimizeDeps: { exclude: ['vue-demi', '@vueuse/shared', '@vueuse/core', 'pinia'],