import { setupDevtoolsPlugin, TimelineEvent } from '@vue/devtools-api'
-import { App } from 'vue'
+import { App, ComponentPublicInstance } from 'vue'
import { PiniaPluginContext, setActivePinia } from '../rootStore'
import {
Store,
/**
* Registered stores used for devtools.
*/
-const registeredStores = /*#__PURE__*/ new Set<Store>()
+const registeredStores = /*#__PURE__*/ new Map<string, Store>()
let isAlreadyInstalled: boolean | undefined
// timeline can be paused when directly changing the state
const INSPECTOR_ID = 'pinia'
export function addDevtools(app: App, store: Store) {
- registeredStores.add(store)
- componentStateTypes.push('🍍 ' + store.$id)
+ if (!registeredStores.has(store.$id)) {
+ registeredStores.set(store.$id, store)
+ componentStateTypes.push('🍍 ' + store.$id)
+ }
+
setupDevtoolsPlugin(
{
id: 'dev.esm.pinia',
app,
},
(api) => {
- // watch(router.currentRoute, () => {
- // // @ts-ignore
- // api.notifyComponentUpdate()
- // })
-
- // TODO: only load stores used by the component?
- api.on.inspectComponent((payload, ctx) => {
- if (payload.instanceData) {
- payload.instanceData.state.push({
- type: '🍍 ' + store.$id,
- key: 'state',
- editable: false,
- value: store.$state,
- })
-
- if (store._getters?.length) {
- payload.instanceData.state.push({
- type: '🍍 ' + store.$id,
- key: 'getters',
- editable: false,
- value: store._getters.reduce((getters, key) => {
- getters[key] = store[key]
- return getters
- }, {} as GettersTree<StateTree>),
- })
- }
- }
- })
-
if (!isAlreadyInstalled) {
api.addTimelineLayer({
id: MUTATIONS_LAYER_ID,
treeFilterPlaceholder: 'Search stores',
})
+ api.on.inspectComponent((payload, ctx) => {
+ if (
+ (
+ payload.componentInstance?.proxy as
+ | ComponentPublicInstance
+ | undefined
+ )?._pStores
+ ) {
+ const piniaStores = (
+ payload.componentInstance.proxy as ComponentPublicInstance
+ )._pStores!
+
+ Object.values(piniaStores).forEach((store) => {
+ payload.instanceData.state.push({
+ type: '🍍 ' + store.$id,
+ key: 'state',
+ editable: false,
+ value: store.$state,
+ })
+
+ if (store._getters?.length) {
+ payload.instanceData.state.push({
+ type: '🍍 ' + store.$id,
+ key: 'getters',
+ editable: false,
+ value: store._getters.reduce((getters, key) => {
+ getters[key] = store[key]
+ return getters
+ }, {} as GettersTree<StateTree>),
+ })
+ }
+ })
+ }
+ })
+
api.on.getInspectorTree((payload) => {
if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {
- const stores = Array.from(registeredStores)
+ const stores = Array.from(registeredStores.values())
payload.rootNodes = (
payload.filter
api.on.getInspectorState((payload) => {
if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {
- const store = Array.from(registeredStores).find(
+ const store = Array.from(registeredStores.values()).find(
(store) => store.$id === payload.nodeId
)
api.on.editInspectorState((payload) => {
if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {
- const store = Array.from(registeredStores).find(
+ const store = Array.from(registeredStores.values()).find(
(store) => store.$id === payload.nodeId
)
const { id, state, getters, actions } = options
function useStore(pinia?: Pinia | null): Store<Id, S, G, A> {
- const hasInstance = getCurrentInstance()
+ const currentInstance = getCurrentInstance()
// only run provide when pinia hasn't been manually passed
- const shouldProvide = hasInstance && !pinia
+ const shouldProvide = currentInstance && !pinia
// avoid injecting if `useStore` when not possible
- pinia = pinia || (hasInstance && inject(piniaSymbol))
+ pinia = pinia || (currentInstance && inject(piniaSymbol))
if (pinia) setActivePinia(pinia)
// TODO: worth warning on server if no piniaKey as it can leak data
pinia = getActivePinia()
InjectionKey<Store<Id, S, G, A>>
]
| undefined
+
+ let store: Store<Id, S, G, A>
+
if (!storeAndDescriptor) {
storeAndDescriptor = initStore(id, state, pinia.state.value[id])
// annoying to type
stores.set(id, storeAndDescriptor as any)
- const store = buildStoreToUse<
+ store = buildStoreToUse<
Id,
S,
G,
if (shouldProvide) {
provide(storeAndDescriptor[2], store)
}
+ } else {
+ store =
+ // null avoids the warning for not found injection key
+ (currentInstance && inject(storeAndDescriptor[2], null)) ||
+ buildStoreToUse<
+ Id,
+ S,
+ G,
+ // @ts-expect-error: A without extends
+ A
+ >(
+ storeAndDescriptor[0],
+ storeAndDescriptor[1],
+ id,
+ getters as GettersTree<S> | undefined,
+ actions as A | undefined,
+ options
+ )
+ }
- return store
+ // save stores in instances to access them devtools
+ if (
+ __DEV__ &&
+ __BROWSER__ &&
+ IS_CLIENT &&
+ currentInstance &&
+ currentInstance.proxy
+ ) {
+ 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
+ cache[store.$id] = store
}
- return (
- // null avoids the warning for not found injection key
- (hasInstance && inject(storeAndDescriptor[2], null)) ||
- buildStoreToUse<
- Id,
- S,
- G,
- // @ts-expect-error: A without extends
- A
- >(
- storeAndDescriptor[0],
- storeAndDescriptor[1],
- id,
- getters as GettersTree<S> | undefined,
- actions as A | undefined,
- options
- )
- )
+ return store
}
// needed by map helpers