<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 />
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()
id: 'counter',
state: () => ({
- n: 5,
+ n: 8,
incrementedTimes: 0,
decrementedTimes: 0,
numbers: [] as number[],
this.n += amount
},
- newOne() {
- console.log('neeeew')
+ changeMe() {
+ console.log('change me to test HMR')
},
async fail() {
// state.decrementedTimes++
// })
} else {
- this.n--
+ this.n -= 1
}
await delay(interval)
}
// 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)
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, {
)
}
}
+}
- // 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,
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)
}
// 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
// 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
// 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