]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
refactor(hmr): expose acceptHMRUpdate
authorEduardo San Martin Morote <posva13@gmail.com>
Thu, 15 Jul 2021 13:09:51 +0000 (15:09 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 19 Jul 2021 09:52:24 +0000 (11:52 +0200)
playground/src/stores/counter.ts
src/hmr.ts [new file with mode: 0644]
src/index.ts
src/store.ts

index 8c7d9bc3c1e1e3364872ee749efde3c4373c8c6c..327ad32ace1076ef13566ea76d943248f163fede 100644 (file)
@@ -1,4 +1,4 @@
-import { defineStore, Pinia, Store, StoreDefinition } from '../../../src'
+import { acceptHMRUpdate, defineStore, setupViteHMR } from '../../../src'
 
 const delay = (t: number) => new Promise((r) => setTimeout(r, t))
 
@@ -64,47 +64,5 @@ export const useCounter = defineStore({
 })
 
 if (import.meta.hot) {
-  const isUseStore = (fn: any): fn is StoreDefinition => {
-    return typeof fn === 'function' && typeof fn.$id === 'string'
-  }
-
-  const oldUseStore = useCounter
-  import.meta.hot.accept((newStore) => {
-    if (!import.meta.hot) throw new Error('import.meta.hot disappeared')
-
-    const pinia: Pinia | undefined =
-      import.meta.hot.data.pinia || oldUseStore._pinia
-
-    if (!pinia) {
-      // this store is still not used
-      return
-    }
-
-    // preserve the pinia instance across loads
-    import.meta.hot.data.pinia = pinia
-
-    // console.log('got data', newStore)
-    for (const exportName in newStore) {
-      const useStore = newStore[exportName]
-      // console.log('checking for', exportName)
-      if (isUseStore(useStore) && pinia._s.has(useStore.$id)) {
-        // console.log('Accepting update for', useStore.$id)
-        const id = useStore.$id
-
-        if (id !== oldUseStore.$id) {
-          console.warn(
-            `The id of the store changed from "${oldUseStore.$id}" to "${id}". Reloading.`
-          )
-          return import.meta.hot!.invalidate()
-        }
-
-        const existingStore: Store = pinia._s.get(id)!
-        if (!existingStore) {
-          console.log(`skipping hmr because store doesn't exist yet`)
-          return
-        }
-        useStore(pinia, existingStore)
-      }
-    }
-  })
+  import.meta.hot.accept(acceptHMRUpdate(useCounter, import.meta.hot))
 }
diff --git a/src/hmr.ts b/src/hmr.ts
new file mode 100644 (file)
index 0000000..ae2cd6a
--- /dev/null
@@ -0,0 +1,110 @@
+import { Pinia } from './rootStore'
+import { Store, StoreDefinition, _Method } from './types'
+
+/**
+ * Setups the hot module replacement for a `useStore` in a Vite environment.
+ *
+ * @param useStore - function created by defineStore
+ */
+export function setupViteHMR(
+  useStore: StoreDefinition<string, any, any, any>,
+  acceptUpdate: _Method,
+  invalidateUpdate: _Method,
+  data: any
+) {
+  const initialUseStore = useStore
+  // @ts-ignore: this would require importing vite types
+  // import.meta.hot.accept((newModule) => {
+
+  acceptUpdate((newModule) => {
+    // @ts-ignore
+    // if (!import.meta.hot) {
+    //   throw new Error('import.meta.hot disappeared')
+    // }
+
+    const pinia: Pinia | undefined =
+      (import.meta as any).hot.data.pinia || initialUseStore._pinia
+
+    if (!pinia) {
+      // this store is still not used
+      return
+    }
+
+    // preserve the pinia instance across loads
+    // @ts-ignore
+    // import.meta.hot.data.pinia = pinia
+    data.pinia = pinia
+
+    // console.log('got data', newStore)
+    for (const exportName in newModule) {
+      const useStore = newModule[exportName]
+      // console.log('checking for', exportName)
+      if (isUseStore(useStore) && pinia._s.has(useStore.$id)) {
+        // console.log('Accepting update for', useStore.$id)
+        const id = useStore.$id
+
+        if (id !== initialUseStore.$id) {
+          console.warn(
+            `The id of the store changed from "${initialUseStore.$id}" to "${id}". Reloading.`
+          )
+          // @ts-ignore
+          // return import.meta.hot.invalidate()
+          return invalidateUpdate()
+        }
+
+        const existingStore: Store = pinia._s.get(id)!
+        if (!existingStore) {
+          console.log(`skipping hmr because store doesn't exist yet`)
+          return
+        }
+        useStore(pinia, existingStore)
+      }
+    }
+  })
+}
+
+export const isUseStore = (fn: any): fn is StoreDefinition => {
+  return typeof fn === 'function' && typeof fn.$id === 'string'
+}
+
+export function acceptHMRUpdate(
+  initialUseStore: StoreDefinition<string, any, any, any>,
+  hot: any
+) {
+  return (newModule: any) => {
+    const pinia: Pinia | undefined = hot.data.pinia || initialUseStore._pinia
+
+    if (!pinia) {
+      // this store is still not used
+      return
+    }
+
+    // preserve the pinia instance across loads
+    hot.data.pinia = pinia
+
+    // console.log('got data', newStore)
+    for (const exportName in newModule) {
+      const useStore = newModule[exportName]
+      // console.log('checking for', exportName)
+      if (isUseStore(useStore) && pinia._s.has(useStore.$id)) {
+        // console.log('Accepting update for', useStore.$id)
+        const id = useStore.$id
+
+        if (id !== initialUseStore.$id) {
+          console.warn(
+            `The id of the store changed from "${initialUseStore.$id}" to "${id}". Reloading.`
+          )
+          // return import.meta.hot.invalidate()
+          return hot.invalidate()
+        }
+
+        const existingStore: Store = pinia._s.get(id)!
+        if (!existingStore) {
+          console.log(`skipping hmr because store doesn't exist yet`)
+          return
+        }
+        useStore(pinia, existingStore)
+      }
+    }
+  }
+}
index d4ece6c25f8fbdcd2a5cbf371a850f97f37a9709..a94b847e208f098f16ed2d4c4f8bcfeee98e23ec 100644 (file)
@@ -54,3 +54,5 @@ export type {
 
 export { createTestingPinia } from './testing'
 export type { TestingOptions } from './testing'
+
+export * from './hmr'
index 9e1fa9fa56b54cc57324b4f2f5c08cbe0f359d35..49b85012f1df347f330ac3fb9f3225513c8a0336 100644 (file)
@@ -45,7 +45,6 @@ import {
   activePinia,
 } from './rootStore'
 import { IS_CLIENT } from './env'
-import { createPinia } from './createPinia'
 
 function innerPatch<T extends StateTree>(
   target: T,
@@ -155,7 +154,8 @@ function createSetupStore<
     $subscribeOptions.onTrigger = (event) => {
       if (isListening) {
         debuggerEvents = event
-      } else {
+        // avoid triggering this while the store is being built and the state is being set in pinia
+      } else if (isListening == false) {
         // let patch send all the events together later
         /* istanbul ignore else */
         if (Array.isArray(debuggerEvents)) {
@@ -170,7 +170,7 @@ function createSetupStore<
   }
 
   // internal state
-  let isListening = false // set to true at the end
+  let isListening: boolean // set to true at the end
   let subscriptions: SubscriptionCallback<S>[] = markRaw([])
   let actionSubscriptions: StoreOnActionListener<Id, S, G, A>[] = markRaw([])
   let debuggerEvents: DebuggerEvent[] | DebuggerEvent
@@ -188,12 +188,10 @@ function createSetupStore<
   }
 
   if (__DEV__ && !pinia._e.active) {
-    // TODO: warn in dev
     throw new Error('Pinia destroyed')
   }
 
   // TODO: idea create skipSerialize that marks properties as non serializable and they are skipped
-  // TODO: store the scope somewhere
   const setupStore = pinia._e.run(() => {
     scope = effectScope()
     return scope.run(() => {