]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
feat(hmr): working version
authorEduardo San Martin Morote <posva13@gmail.com>
Thu, 15 Jul 2021 09:40:03 +0000 (11:40 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 19 Jul 2021 09:51:56 +0000 (11:51 +0200)
playground/src/App.vue
playground/src/main.ts
playground/src/stores/counter.ts
src/devtools/plugin.ts
src/store.ts

index 3edb305218c32f4c9549b47264cc9e9e20ee21c8..07db57d7c514b4bcf3e0fdda8a59f4ddbad079ef 100644 (file)
@@ -1,5 +1,6 @@
 <template>
   <button @click="n++">Increment {{ n }}</button>
+  <button @click="counter.changeMe()"><code>counter.changeMe()</code></button>
   <pre>{{ counter.$state }}</pre>
   <!-- <button @click="counter.newOne()">Click me</button> -->
   <TestStore />
index f31805e7426470a4d76f9c3b1cc5668bea16f2e1..bfb1990d65e434eeadb226e35f1931461db0a2ee 100644 (file)
@@ -1,9 +1,6 @@
 import { createApp } from 'vue'
 import App from './App.vue'
-import { createPinia, StoreDefinition } from '../../src'
-import { ha } from './test'
-
-console.log({ ha })
+import { createPinia } from '../../src'
 
 const pinia = createPinia()
 
index e90085274205e7588248c2492ab67e9f1f544f90..4941ba03cdc1c3a02bd7bc00ec637286eb317214 100644 (file)
@@ -6,7 +6,7 @@ export const useCounter = defineStore({
   id: 'counter',
 
   state: () => ({
-    n: 5,
+    n: 8,
     incrementedTimes: 0,
     decrementedTimes: 0,
     numbers: [] as number[],
@@ -25,8 +25,8 @@ export const useCounter = defineStore({
       this.n += amount
     },
 
-    newOne() {
-      console.log('neeeew')
+    changeMe() {
+      console.log('change me to test HMR')
     },
 
     async fail() {
@@ -55,7 +55,7 @@ export const useCounter = defineStore({
           //   state.decrementedTimes++
           // })
         } else {
-          this.n--
+          this.n -= 1
         }
         await delay(interval)
       }
@@ -104,7 +104,6 @@ if (import.meta.hot) {
           // TODO: replace the useCounter var???
           return
         }
-        console.log('patching')
         useStore(pinia, existingStore)
         // remove the existing store from the cache to force a new one
         // pinia._s.delete(id)
index 17e21c0efe5a32acef8c367f203e28ad126f7aa7..526a0b2734feeb87fb46d7812180af79608b4ceb 100644 (file)
@@ -359,33 +359,24 @@ let runningActionId = 0
 let activeAction: number | undefined
 
 /**
- * pinia.use(devtoolsPlugin)
+ * Patches a store to enable action grouping in devtools by wrapping the store with a Proxy that is passed as the context of all actions, allowing us to set `runningAction` on each access and effectively associating any state mutation to the action.
+ *
+ * @param store - store to patch
+ * @param actionNames - list of actionst to patch
  */
-export function devtoolsPlugin<
-  Id extends string = string,
-  S extends StateTree = StateTree,
-  G extends GettersTree<S> = GettersTree<S>,
-  A /* extends ActionsTree */ = ActionsTree
->({ app, store, options, pinia }: PiniaPluginContext<Id, S, G, A>) {
-  // HMR module
-  if (store.$id.startsWith('__hot:')) {
-    return
-  }
+function patchActionForGrouping(store: Store, actionNames: string[]) {
   // original actions of the store as they are given by pinia. We are going to override them
-  const actions = Object.keys(options.actions).reduce(
-    (storeActions, actionName) => {
-      // @ts-expect-error
-      // use toRaw to avoid tracking #541
-      storeActions[actionName] = toRaw(store)[actionName]
-      return storeActions
-    },
-    {} as ActionsTree
-  )
+  const actions = actionNames.reduce((storeActions, actionName) => {
+    // use toRaw to avoid tracking #541
+    // @ts-expect-error
+    storeActions[actionName] = toRaw(store)[actionName]
+    return storeActions
+  }, {} as ActionsTree)
 
   for (const actionName in actions) {
     // @ts-expect-error
     store[actionName] = function () {
-      setActivePinia(pinia)
+      setActivePinia(store._p)
       // the running action id is incremented in a before action hook
       const _actionId = runningActionId
       const trackedStore = new Proxy(store, {
@@ -404,8 +395,38 @@ export function devtoolsPlugin<
       )
     }
   }
+}
 
-  // TODO: replace existing one for HMR?
+/**
+ * pinia.use(devtoolsPlugin)
+ */
+export function devtoolsPlugin<
+  Id extends string = string,
+  S extends StateTree = StateTree,
+  G extends GettersTree<S> = GettersTree<S>,
+  A /* extends ActionsTree */ = ActionsTree
+>({ app, store, options }: PiniaPluginContext<Id, S, G, A>) {
+  // HMR module
+  if (store.$id.startsWith('__hot:')) {
+    return
+  }
+
+  patchActionForGrouping(
+    // @ts-expect-error: can cast the store...
+    store,
+    Object.keys(options.actions)
+  )
+
+  const originalHotUpdate = store.hotUpdate
+
+  toRaw(store).hotUpdate = function (newStore) {
+    originalHotUpdate.apply(this, arguments as any)
+    patchActionForGrouping(
+      // @ts-expect-error: can cast the store...
+      store,
+      Object.keys(toRaw(newStore)._hmrPayload.actions)
+    )
+  }
 
   addDevtools(
     app,
index 568f1b97304699d11a343ba273bcc767e869eaf6..9e1fa9fa56b54cc57324b4f2f5c08cbe0f359d35 100644 (file)
@@ -437,42 +437,9 @@ function createSetupStore<
     set: (state) => (pinia.state.value[$id] = state),
   })
 
-  // apply all plugins
-  pinia._p.forEach((extender) => {
-    if (__DEV__ && IS_CLIENT) {
-      const extensions = extender({
-        // @ts-expect-error: conflict between A and ActionsTree
-        store,
-        app: pinia._a,
-        pinia,
-        // @ts-expect-error
-        options: optionsForPlugin,
-      })
-      Object.keys(extensions || {}).forEach((key) =>
-        store._customProperties.add(key)
-      )
-      assign(store, extensions)
-    } else {
-      assign(
-        store,
-        extender({
-          // @ts-expect-error: conflict between A and ActionsTree
-          store,
-          app: pinia._a,
-          pinia,
-          // @ts-expect-error
-          options: optionsForPlugin,
-        })
-      )
-    }
-  })
-
-  if (initialState) {
-    ;(options.hydrate || innerPatch)(store, initialState)
-  }
-
+  // add the hotUpdate before plugins to allow them to override it
   if (__DEV__) {
-    store.hotUpdate = (newStore) => {
+    store.hotUpdate = markRaw((newStore) => {
       newStore._hmrPayload.state.forEach((stateKey) => {
         if (!(stateKey in store.$state)) {
           console.log('setting new key', stateKey)
@@ -518,7 +485,41 @@ function createSetupStore<
       }
 
       // TODO: remove old actions and getters
+    })
+  }
+
+  // apply all plugins
+  pinia._p.forEach((extender) => {
+    if (__DEV__ && IS_CLIENT) {
+      const extensions = extender({
+        // @ts-expect-error: conflict between A and ActionsTree
+        store,
+        app: pinia._a,
+        pinia,
+        // @ts-expect-error
+        options: optionsForPlugin,
+      })
+      Object.keys(extensions || {}).forEach((key) =>
+        store._customProperties.add(key)
+      )
+      assign(store, extensions)
+    } else {
+      assign(
+        store,
+        extender({
+          // @ts-expect-error: conflict between A and ActionsTree
+          store,
+          app: pinia._a,
+          pinia,
+          // @ts-expect-error
+          options: optionsForPlugin,
+        })
+      )
     }
+  })
+
+  if (initialState) {
+    ;(options.hydrate || innerPatch)(store, initialState)
   }
 
   isListening = true
@@ -651,9 +652,6 @@ export function defineSetupStore<Id extends string, SS>(
       // cleanup the things
       delete pinia.state.value[hotId]
       pinia._s.delete(hotId)
-
-      // TODO: add the patched store to devtools again to override its previous version
-      // addDevtools(pinia._a, hot)
     }
 
     // save stores in instances to access them devtools
@@ -731,13 +729,16 @@ export function defineStore<
       // cleanup the things
       delete pinia.state.value[hotId]
       pinia._s.delete(hotId)
-
-      // TODO: add the patched store to devtools again to override its previous version
-      // addDevtools(pinia._a, hot)
     }
 
     // save stores in instances to access them devtools
-    if (__DEV__ && IS_CLIENT && currentInstance && currentInstance.proxy) {
+    if (
+      __DEV__ &&
+      IS_CLIENT &&
+      currentInstance &&
+      currentInstance.proxy &&
+      !hot
+    ) {
       const vm = currentInstance.proxy
       const cache = '_pStores' in vm ? vm._pStores! : (vm._pStores = {})
       // @ts-expect-error: still can't cast Store with generics to Store