From: Eduardo San Martin Morote Date: Thu, 15 Jul 2021 13:09:51 +0000 (+0200) Subject: refactor(hmr): expose acceptHMRUpdate X-Git-Tag: v2.0.0-rc.0~71 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b6924885fbab975c81eb2bd075dbe79f08b20244;p=thirdparty%2Fvuejs%2Fpinia.git refactor(hmr): expose acceptHMRUpdate --- diff --git a/playground/src/stores/counter.ts b/playground/src/stores/counter.ts index 8c7d9bc3..327ad32a 100644 --- a/playground/src/stores/counter.ts +++ b/playground/src/stores/counter.ts @@ -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 index 00000000..ae2cd6a3 --- /dev/null +++ b/src/hmr.ts @@ -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, + 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, + 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) + } + } + } +} diff --git a/src/index.ts b/src/index.ts index d4ece6c2..a94b847e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,3 +54,5 @@ export type { export { createTestingPinia } from './testing' export type { TestingOptions } from './testing' + +export * from './hmr' diff --git a/src/store.ts b/src/store.ts index 9e1fa9fa..49b85012 100644 --- a/src/store.ts +++ b/src/store.ts @@ -45,7 +45,6 @@ import { activePinia, } from './rootStore' import { IS_CLIENT } from './env' -import { createPinia } from './createPinia' function innerPatch( 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[] = markRaw([]) let actionSubscriptions: StoreOnActionListener[] = 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(() => {