]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
feat(devtools): add root state
authorEduardo San Martin Morote <posva13@gmail.com>
Tue, 1 Jun 2021 16:45:47 +0000 (18:45 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Tue, 1 Jun 2021 16:45:47 +0000 (18:45 +0200)
docs/.vitepress/theme/index.js
docs/index.md
src/devtools/formatting.ts
src/devtools/plugin.ts

index 4a1d56ee121e0e45d5cfd80a38adc71dc0f6b650..bf5b9b08bc3dea40ebf85832dd99a661c31805ed 100644 (file)
@@ -2,7 +2,7 @@ import Theme from 'vitepress/theme'
 import { Layout } from './Layout'
 import './custom.css'
 import './code-theme.css'
-// import { createPinia } from '../../../src'
+import { createPinia } from '../../../src'
 
 /** @type {import('vitepress').Theme} */
 const config = {
@@ -11,7 +11,7 @@ const config = {
   Layout,
 
   enhanceApp({ app }) {
-    // app.use(createPinia())
+    app.use(createPinia())
   },
 }
 
index dd53b6dc90ef33bb4bfff25941d718b3bd50c358..0bb20b45f72fdd0bf67d9b5475ec40bb92723278 100644 (file)
@@ -24,12 +24,12 @@ footer: MIT Licensed | Copyright © 2019-present Eduardo San Martin Morote
 ---
 
 <ThemeToggle/>
-<!-- <TestStore/> -->
+<TestStore/>
 
 <HomeSponsors />
 
 <script setup>
 import HomeSponsors from './.vitepress/components/HomeSponsors.vue'
 import ThemeToggle from './.vitepress/components/ThemeToggle.vue'
-// import TestStore from './.vitepress/components/TestStore.vue'
+import TestStore from './.vitepress/components/TestStore.vue'
 </script>
index 31465b1cb2a795813feeb692342e66552dc67866..b37bb403a651415038c537f43bd9397ec81d8909 100644 (file)
@@ -1,6 +1,7 @@
 import { CustomInspectorNode, CustomInspectorState } from '@vue/devtools-api'
-import { Store, GettersTree, MutationType, StateTree } from '../types'
+import { Store, MutationType } from '../types'
 import { DebuggerEvent } from 'vue'
+import { Pinia } from '../rootStore'
 
 export function formatDisplay(display: string) {
   return {
@@ -10,36 +11,69 @@ export function formatDisplay(display: string) {
   }
 }
 
-export function formatStoreForInspectorTree(store: Store): CustomInspectorNode {
-  return {
-    id: store.$id,
-    label: store.$id,
-    tags: [],
-  }
+export const PINIA_ROOT_LABEL = '🍍 Pinia (root)'
+export const PINIA_ROOT_ID = '_root'
+
+export function formatStoreForInspectorTree(
+  store: Store | Pinia
+): CustomInspectorNode {
+  return '$id' in store
+    ? {
+        id: store.$id,
+        label: store.$id,
+      }
+    : {
+        id: PINIA_ROOT_ID,
+        label: PINIA_ROOT_LABEL,
+      }
 }
 
 export function formatStoreForInspectorState(
-  store: Store
-): CustomInspectorState[string] {
-  const fields: CustomInspectorState[string] = [
-    { editable: false, key: 'id', value: formatDisplay(store.$id) },
-    { editable: true, key: 'state', value: store.$state },
-  ]
+  store: Store | Pinia
+): CustomInspectorState {
+  if (!('$id' in store)) {
+    const state: CustomInspectorState = {
+      state: Object.keys(store.state.value).map((storeId) => ({
+        editable: true,
+        key: storeId,
+        value: store.state.value[storeId],
+      })),
+    }
+    // TODO: use this version when possible
+    // Object.keys(store.state.value).forEach((storeId) => {
+    //   const currentState = store.state.value[storeId]
+    //   state[storeId] = Object.keys(currentState).map((key) => ({
+    //     // is not possible to made editable because no way to get the storeId in
+    //     // edit inspector state callback
+    //     editable: false,
+    //     key,
+    //     value: currentState[key],
+    //   }))
+    // })
+
+    return state
+  }
+
+  const state: CustomInspectorState = {
+    state: Object.keys(store.$state).map((key) => ({
+      editable: true,
+      key,
+      // @ts-expect-error
+      value: store.$state[key],
+    })),
+  }
 
   // avoid adding empty getters
   if (store._getters && store._getters.length) {
-    fields.push({
+    state.getters = store._getters.map((getterName) => ({
       editable: false,
-      key: 'getters',
-      value: store._getters.reduce((getters, key) => {
-        // @ts-expect-error
-        getters[key] = store[key]
-        return getters
-      }, {} as GettersTree<StateTree>),
-    })
+      key: getterName,
+      // @ts-expect-error
+      value: store[getterName],
+    }))
   }
 
-  return fields
+  return state
 }
 
 export function formatEventData(
index 330ebafa23607bbece596cf4fdfdea263652fe6c..9e90aae7caf9f1e89e8251aaf47ef3eb848aa64b 100644 (file)
@@ -1,6 +1,6 @@
 import { setupDevtoolsPlugin, TimelineEvent } from '@vue/devtools-api'
 import { App, ComponentPublicInstance } from 'vue'
-import { PiniaPluginContext, setActivePinia } from '../rootStore'
+import { Pinia, PiniaPluginContext, setActivePinia } from '../rootStore'
 import {
   Store,
   GettersTree,
@@ -20,6 +20,8 @@ import {
   formatMutationType,
   formatStoreForInspectorState,
   formatStoreForInspectorTree,
+  PINIA_ROOT_ID,
+  PINIA_ROOT_LABEL,
 } from './formatting'
 import { toastMessage } from './utils'
 
@@ -142,14 +144,19 @@ function addDevtools(app: App, store: Store) {
 
         api.on.getInspectorTree((payload) => {
           if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {
-            const stores = Array.from(registeredStores.values())
+            let stores: Array<Store | Pinia> = [store._p]
+            stores = stores.concat(Array.from(registeredStores.values()))
 
             payload.rootNodes = (
               payload.filter
                 ? stores.filter((store) =>
-                    store.$id
-                      .toLowerCase()
-                      .includes(payload.filter.toLowerCase())
+                    '$id' in store
+                      ? store.$id
+                          .toLowerCase()
+                          .includes(payload.filter.toLowerCase())
+                      : PINIA_ROOT_LABEL.toLowerCase().includes(
+                          payload.filter.toLowerCase()
+                        )
                   )
                 : stores
             ).map(formatStoreForInspectorTree)
@@ -158,28 +165,32 @@ function addDevtools(app: App, store: Store) {
 
         api.on.getInspectorState((payload) => {
           if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {
-            const store = registeredStores.get(payload.nodeId)
+            const inspectedStore =
+              payload.nodeId === PINIA_ROOT_ID
+                ? store._p
+                : registeredStores.get(payload.nodeId)
 
-            if (!store) {
+            if (!inspectedStore) {
               return toastMessage(
                 `store "${payload.nodeId}" not found`,
                 'error'
               )
             }
 
-            if (store) {
-              payload.state = {
-                options: formatStoreForInspectorState(store),
-              }
+            if (inspectedStore) {
+              payload.state = formatStoreForInspectorState(inspectedStore)
             }
           }
         })
 
-        api.on.editInspectorState((payload) => {
+        api.on.editInspectorState((payload, ctx) => {
           if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {
-            const store = registeredStores.get(payload.nodeId)
+            const inspectedStore =
+              payload.nodeId === PINIA_ROOT_ID
+                ? store._p
+                : registeredStores.get(payload.nodeId)
 
-            if (!store) {
+            if (!inspectedStore) {
               return toastMessage(
                 `store "${payload.nodeId}" not found`,
                 'error'
@@ -187,17 +198,15 @@ function addDevtools(app: App, store: Store) {
             }
 
             const { path } = payload
-            if (path[0] !== 'state') {
-              return toastMessage(
-                `Invalid path for store "${payload.nodeId}":\n${path}\nOnly state can be modified.`
-              )
-            }
 
-            // rewrite the first entry to be able to directly set the state as
-            // well as any other path
-            path[0] = '$state'
+            if ('$id' in inspectedStore) {
+              // access only the state
+              path.unshift('$state')
+            } else {
+              path.unshift('state', 'value')
+            }
             isTimelineActive = false
-            payload.set(store, path, payload.state.value)
+            payload.set(inspectedStore, path, payload.state.value)
             isTimelineActive = true
           }
         })