]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
feat: allow app injections in setup stores
authorEduardo San Martin Morote <posva13@gmail.com>
Mon, 8 May 2023 16:34:38 +0000 (18:34 +0200)
committerEduardo San Martin Morote <posva@users.noreply.github.com>
Wed, 17 May 2023 08:26:48 +0000 (10:26 +0200)
Close #1784

packages/pinia/__tests__/storeSetup.spec.ts
packages/pinia/src/rootStore.ts
packages/pinia/src/store.ts
packages/playground/src/main.ts
packages/playground/src/stores/counterSetup.ts
packages/playground/tsconfig.json
packages/playground/vite.config.ts

index b7709c3eca08ff4504c59fc1b1527ec0635e60ad..cd5e9e1a80612343e29c50cfea3b66476b1e1881 100644 (file)
@@ -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<T>(_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')
+  })
 })
index cc212401a826dc81e4cd05a2d082f6963204db8e..0eab00dfd227ecf449bcea28b6e79e023086546f 100644 (file)
@@ -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
index 7803dfdcd24871082a1974d3cd5e4e91ffdb9cc1..57cc7b32741bca8406149afd05db4c7132001963 100644 (file)
@@ -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> = AT extends Array<infer T> ? 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
index cb71b2fd75af0034591d82c8451f7fc989f42a07..48f82f449f236ec052c31ae82b056df717ff1206 100644 (file)
@@ -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()
index 4c5330da2336a4aa16b874968a7b3af97839dff6..04cafdf53961b23e55be65ba4d1351bfe61dcd78 100644 (file)
@@ -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) {
index 860623e50c1dc431976d670664ba3f2b19557862..b3d868bcd85f83d750e6e39c33668eee76308cc1 100644 (file)
     "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"]
 }
index 456e8764f598839621adaf563c2f2c89a2ab69da..f484acf563a17904a5bc392f76ad8bcee3785f8e 100644 (file)
@@ -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'],