]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
refactor: remove circular dep
authorEduardo San Martin Morote <posva13@gmail.com>
Tue, 11 May 2021 20:53:20 +0000 (22:53 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Tue, 11 May 2021 20:58:17 +0000 (22:58 +0200)
__tests__/store.spec.ts
jest.config.js
src/createPinia.ts [new file with mode: 0644]
src/devtools/formatting.ts [new file with mode: 0644]
src/devtools/index.ts [new file with mode: 0644]
src/devtools/plugin.ts [moved from src/devtools.ts with 78% similarity]
src/index.ts
src/rootStore.ts
src/store.ts

index 6699b0eebea4801086af752b6c469fe2d81036d5..60f4495641df560265dae4cf89c6c9594c7db97f 100644 (file)
@@ -1,4 +1,10 @@
-import { createPinia, defineStore, setActivePinia, Pinia } from '../src'
+import {
+  createPinia,
+  defineStore,
+  setActivePinia,
+  Pinia,
+  MutationType,
+} from '../src'
 import { mount } from '@vue/test-utils'
 import { defineComponent, getCurrentInstance, nextTick, watch } from 'vue'
 
@@ -148,11 +154,11 @@ describe('Store', () => {
     store.$state.a = false
 
     expect(spy).toHaveBeenCalledWith(
-      {
+      expect.objectContaining({
         payload: {},
         storeName: 'main',
-        type: expect.stringContaining('in place'),
-      },
+        type: MutationType.direct,
+      }),
       store.$state
     )
   })
@@ -166,11 +172,11 @@ describe('Store', () => {
     store.$patch(patch)
 
     expect(spy).toHaveBeenCalledWith(
-      {
+      expect.objectContaining({
         payload: patch,
         storeName: 'main',
-        type: expect.stringContaining('patch'),
-      },
+        type: MutationType.patchObject,
+      }),
       store.$state
     )
   })
index 16629a554c8b66e37d574e4098ef20c2f99aa0b8..c5d0241a1526a4df9da5306bdd5f96e571b142fd 100644 (file)
@@ -8,7 +8,7 @@ module.exports = {
     '/node_modules/',
     'src/index.ts',
     '\\.d\\.ts$',
-    'src/devtools.ts',
+    'src/devtools',
     'src/deprecated.ts',
   ],
   testMatch: ['<rootDir>/__tests__/**/*.spec.ts'],
diff --git a/src/createPinia.ts b/src/createPinia.ts
new file mode 100644 (file)
index 0000000..910194b
--- /dev/null
@@ -0,0 +1,61 @@
+import {
+  Pinia,
+  PiniaStorePlugin,
+  setActivePinia,
+  piniaSymbol,
+} from './rootStore'
+import { ref, App } from 'vue'
+import { devtoolsPlugin } from './devtools'
+import { IS_CLIENT } from './env'
+
+/**
+ * Creates a Pinia instance to be used by the application
+ */
+export function createPinia(): Pinia {
+  // NOTE: here we could check the window object for a state and directly set it
+  // if there is anything like it with Vue 3 SSR
+  const state = ref({})
+
+  let localApp: App | undefined
+  let _p: Pinia['_p'] = []
+  // plugins added before calling app.use(pinia)
+  const toBeInstalled: PiniaStorePlugin[] = []
+
+  const pinia: Pinia = {
+    install(app: App) {
+      pinia._a = localApp = app
+      // pinia._a = app
+      app.provide(piniaSymbol, pinia)
+      app.config.globalProperties.$pinia = pinia
+      // TODO: write test
+      // only set the app on client for devtools
+      if (__BROWSER__ && IS_CLIENT) {
+        // this allows calling useStore() outside of a component setup after
+        // installing pinia's plugin
+        setActivePinia(pinia)
+      }
+      toBeInstalled.forEach((plugin) => _p.push(plugin))
+    },
+
+    use(plugin) {
+      if (!localApp) {
+        toBeInstalled.push(plugin)
+      } else {
+        _p.push(plugin)
+      }
+      return this
+    },
+
+    _p,
+    // it's actually undefined here
+    _a: localApp!,
+
+    state,
+  }
+
+  if (IS_CLIENT && __BROWSER__ && __DEV__) {
+    pinia.use(devtoolsPlugin)
+  }
+
+  return pinia
+}
diff --git a/src/devtools/formatting.ts b/src/devtools/formatting.ts
new file mode 100644 (file)
index 0000000..0b3aaaf
--- /dev/null
@@ -0,0 +1,88 @@
+import { CustomInspectorNode, CustomInspectorState } from '@vue/devtools-api'
+import { GenericStore, GettersTree, MutationType, StateTree } from '../types'
+import { DebuggerEvent } from 'vue'
+
+export function formatDisplay(display: string) {
+  return {
+    _custom: {
+      display,
+    },
+  }
+}
+
+export function formatStoreForInspectorTree(
+  store: GenericStore
+): CustomInspectorNode {
+  return {
+    id: store.$id,
+    label: store.$id,
+    tags: [],
+  }
+}
+
+export function formatStoreForInspectorState(
+  store: GenericStore
+): CustomInspectorState[string] {
+  const fields: CustomInspectorState[string] = [
+    { editable: false, key: 'id', value: formatDisplay(store.$id) },
+    { editable: true, key: 'state', value: store.$state },
+  ]
+
+  // avoid adding empty getters
+  if (store._getters?.length) {
+    fields.push({
+      editable: false,
+      key: 'getters',
+      value: store._getters.reduce((getters, key) => {
+        getters[key] = store[key]
+        return getters
+      }, {} as GettersTree<StateTree>),
+    })
+  }
+
+  return fields
+}
+
+export function formatEventData(
+  events: DebuggerEvent[] | DebuggerEvent | undefined
+) {
+  if (!events) return {}
+  if (Array.isArray(events)) {
+    // TODO: handle add and delete for arrays and objects
+    return events.reduce(
+      (data, event) => {
+        data.keys.push(event.key)
+        data.operations.push(event.type)
+        data.oldValue[event.key] = event.oldValue
+        data.newValue[event.key] = event.newValue
+        return data
+      },
+      {
+        oldValue: {} as Record<string, any>,
+        keys: [] as string[],
+        operations: [] as string[],
+        newValue: {} as Record<string, any>,
+      }
+    )
+  } else {
+    return {
+      operation: formatDisplay(events.type),
+      key: formatDisplay(events.key),
+      oldValue: events.oldValue,
+      newValue: events.newValue,
+    }
+  }
+}
+
+export function formatMutationType(type: MutationType): string {
+  switch (type) {
+    case MutationType.direct:
+      return 'mutation'
+    case MutationType.patchFunction:
+      return '$patch'
+    case MutationType.patchObject:
+      return '$patch'
+    default:
+      return 'unknown'
+  }
+}
diff --git a/src/devtools/index.ts b/src/devtools/index.ts
new file mode 100644 (file)
index 0000000..d81c448
--- /dev/null
@@ -0,0 +1 @@
+export { devtoolsPlugin } from './plugin'
similarity index 78%
rename from src/devtools.ts
rename to src/devtools/plugin.ts
index 8b62bb12872892b41e1f7244062c66d53200e832..d9e1c8570fedc046dcf708e1e2038c0a66ad0fa8 100644 (file)
@@ -1,49 +1,25 @@
-import {
-  CustomInspectorNode,
-  CustomInspectorState,
-  setupDevtoolsPlugin,
-  TimelineEvent,
-} from '@vue/devtools-api'
-import { App, DebuggerEvent } from 'vue'
-import { PiniaPluginContext, setActivePinia } from './rootStore'
+import { setupDevtoolsPlugin, TimelineEvent } from '@vue/devtools-api'
+import { App } from 'vue'
+import { PiniaPluginContext, setActivePinia } from '../rootStore'
 import {
   GenericStore,
   GettersTree,
   MutationType,
   StateTree,
   _Method,
-} from './types'
-
-function formatDisplay(display: string) {
-  return {
-    _custom: {
-      display,
-    },
-  }
-}
+} from '../types'
+import {
+  formatEventData,
+  formatMutationType,
+  formatStoreForInspectorState,
+  formatStoreForInspectorTree,
+} from './formatting'
 
 /**
  * Registered stores used for devtools.
  */
 const registeredStores = /*#__PURE__*/ new Set<GenericStore>()
 
-function toastMessage(
-  message: string,
-  type?: 'normal' | 'error' | 'warning' | undefined
-) {
-  const piniaMessage = '🍍 ' + message
-
-  if (typeof __VUE_DEVTOOLS_TOAST__ === 'function') {
-    __VUE_DEVTOOLS_TOAST__(piniaMessage, type)
-  } else if (type === 'error') {
-    console.error(piniaMessage)
-  } else if (type === 'warning') {
-    console.warn(piniaMessage)
-  } else {
-    console.log(piniaMessage)
-  }
-}
-
 let isAlreadyInstalled: boolean | undefined
 // timeline can be paused when directly changing the state
 let isTimelineActive = true
@@ -270,79 +246,6 @@ export function addDevtools(app: App, store: GenericStore) {
   )
 }
 
-function formatStoreForInspectorTree(store: GenericStore): CustomInspectorNode {
-  return {
-    id: store.$id,
-    label: store.$id,
-    tags: [],
-  }
-}
-
-function formatStoreForInspectorState(
-  store: GenericStore
-): CustomInspectorState[string] {
-  const fields: CustomInspectorState[string] = [
-    { editable: false, key: 'id', value: formatDisplay(store.$id) },
-    { editable: true, key: 'state', value: store.$state },
-  ]
-
-  // avoid adding empty getters
-  if (store._getters?.length) {
-    fields.push({
-      editable: false,
-      key: 'getters',
-      value: store._getters.reduce((getters, key) => {
-        getters[key] = store[key]
-        return getters
-      }, {} as GettersTree<StateTree>),
-    })
-  }
-
-  return fields
-}
-
-function formatEventData(events: DebuggerEvent[] | DebuggerEvent | undefined) {
-  if (!events) return {}
-  if (Array.isArray(events)) {
-    // TODO: handle add and delete for arrays and objects
-    return events.reduce(
-      (data, event) => {
-        data.keys.push(event.key)
-        data.operations.push(event.type)
-        data.oldValue[event.key] = event.oldValue
-        data.newValue[event.key] = event.newValue
-        return data
-      },
-      {
-        oldValue: {} as Record<string, any>,
-        keys: [] as string[],
-        operations: [] as string[],
-        newValue: {} as Record<string, any>,
-      }
-    )
-  } else {
-    return {
-      operation: formatDisplay(events.type),
-      key: formatDisplay(events.key),
-      oldValue: events.oldValue,
-      newValue: events.newValue,
-    }
-  }
-}
-
-function formatMutationType(type: MutationType): string {
-  switch (type) {
-    case MutationType.direct:
-      return 'mutation'
-    case MutationType.patchFunction:
-      return '$patch'
-    case MutationType.patchObject:
-      return '$patch'
-    default:
-      return 'unknown'
-  }
-}
-
 let runningActionId = 0
 let activeAction: number | undefined
 
@@ -395,5 +298,24 @@ export function devtoolsPlugin<
 }
 
 /**
- * Another idea: wrap the getter/setter of all state properties to call setActiveAction. This way, we can directly use it in $subscribe to attach it to its action
+ * Shows a toast or console.log
+ *
+ * @param message - message to log
+ * @param type - different color of the tooltip
  */
+function toastMessage(
+  message: string,
+  type?: 'normal' | 'error' | 'warning' | undefined
+) {
+  const piniaMessage = '🍍 ' + message
+
+  if (typeof __VUE_DEVTOOLS_TOAST__ === 'function') {
+    __VUE_DEVTOOLS_TOAST__(piniaMessage, type)
+  } else if (type === 'error') {
+    console.error(piniaMessage)
+  } else if (type === 'warning') {
+    console.warn(piniaMessage)
+  } else {
+    console.log(piniaMessage)
+  }
+}
index c65f147b664128a53055c7b911e12180f934cbe8..cd8d286bb510cb0994c5effc62fde7f3776443d5 100644 (file)
@@ -1,4 +1,5 @@
-export { setActivePinia, createPinia } from './rootStore'
+export { setActivePinia } from './rootStore'
+export { createPinia } from './createPinia'
 export type { Pinia, PiniaStorePlugin, PiniaPluginContext } from './rootStore'
 
 export { defineStore } from './store'
@@ -18,6 +19,7 @@ export type {
   PiniaCustomProperties,
   DefineStoreOptions,
 } from './types'
+export { MutationType } from './types'
 
 export {
   mapActions,
index feeffce37123d0f797b61ff6f4c4578cec3b17e3..c54ef5bc060bd7454811652c66183dd984d1f07c 100644 (file)
@@ -1,6 +1,4 @@
-import { App, InjectionKey, Plugin, Ref, ref, warn } from 'vue'
-import { devtoolsPlugin } from './devtools'
-import { IS_CLIENT } from './env'
+import { App, InjectionKey, Plugin, Ref, warn } from 'vue'
 import {
   StateTree,
   StoreWithState,
@@ -61,49 +59,6 @@ export const storesMap = new WeakMap<
   >
 >()
 
-/**
- * Context argument passed to Pinia plugins.
- */
-export interface PiniaPluginContext<
-  Id extends string = string,
-  S extends StateTree = StateTree,
-  G extends GettersTree<S> = GettersTree<S>,
-  A = Record<string, _Method>
-> {
-  /**
-   * pinia instance.
-   */
-  pinia: Pinia
-
-  /**
-   * Current app created with `Vue.createApp()`.
-   */
-  app: App
-
-  /**
-   * Current store being extended.
-   */
-  store: Store<Id, S, G, A>
-
-  /**
-   * Current store being extended.
-   */
-  options: DefineStoreOptions<Id, S, G, A>
-}
-
-/**
- * Plugin to extend every store
- */
-export interface PiniaStorePlugin {
-  /**
-   * Plugin to extend every store. Returns an object to extend the store or
-   * nothing.
-   *
-   * @param context - Context
-   */
-  (context: PiniaPluginContext): Partial<PiniaCustomProperties> | void
-}
-
 /**
  * Every application must own its own pinia to be able to create stores
  */
@@ -160,53 +115,44 @@ export const piniaSymbol = (__DEV__
     Symbol()) as InjectionKey<Pinia>
 
 /**
- * Creates a Pinia instance to be used by the application
+ * Context argument passed to Pinia plugins.
  */
-export function createPinia(): Pinia {
-  // NOTE: here we could check the window object for a state and directly set it
-  // if there is anything like it with Vue 3 SSR
-  const state = ref({})
-
-  let localApp: App | undefined
-  let _p: Pinia['_p'] = []
-  // plugins added before calling app.use(pinia)
-  const toBeInstalled: PiniaStorePlugin[] = []
-
-  const pinia: Pinia = {
-    install(app: App) {
-      pinia._a = localApp = app
-      // pinia._a = app
-      app.provide(piniaSymbol, pinia)
-      app.config.globalProperties.$pinia = pinia
-      // TODO: write test
-      // only set the app on client for devtools
-      if (__BROWSER__ && IS_CLIENT) {
-        // this allows calling useStore() outside of a component setup after
-        // installing pinia's plugin
-        setActivePinia(pinia)
-      }
-      toBeInstalled.forEach((plugin) => _p.push(plugin))
-    },
-
-    use(plugin) {
-      if (!localApp) {
-        toBeInstalled.push(plugin)
-      } else {
-        _p.push(plugin)
-      }
-      return this
-    },
-
-    _p,
-    // it's actually undefined here
-    _a: localApp!,
-
-    state,
-  }
+export interface PiniaPluginContext<
+  Id extends string = string,
+  S extends StateTree = StateTree,
+  G extends GettersTree<S> = GettersTree<S>,
+  A = Record<string, _Method>
+> {
+  /**
+   * pinia instance.
+   */
+  pinia: Pinia
 
-  if (IS_CLIENT && __BROWSER__ && __DEV__) {
-    pinia.use(devtoolsPlugin)
-  }
+  /**
+   * Current app created with `Vue.createApp()`.
+   */
+  app: App
 
-  return pinia
+  /**
+   * Current store being extended.
+   */
+  store: Store<Id, S, G, A>
+
+  /**
+   * Current store being extended.
+   */
+  options: DefineStoreOptions<Id, S, G, A>
+}
+
+/**
+ * Plugin to extend every store
+ */
+export interface PiniaStorePlugin {
+  /**
+   * Plugin to extend every store. Returns an object to extend the store or
+   * nothing.
+   *
+   * @param context - Context
+   */
+  (context: PiniaPluginContext): Partial<PiniaCustomProperties> | void
 }
index 5aab347a6017732ff1b5f6e95269e520e15240bf..9eef8c41fa5084c5ecf7740c8309f531e504fca9 100644 (file)
@@ -140,10 +140,7 @@ function initStore<Id extends string, S extends StateTree>(
     })
   }
 
-  function $subscribe(
-    callback: SubscriptionCallback<S>,
-    onTrigger?: (event: DebuggerEvent) => void
-  ) {
+  function $subscribe(callback: SubscriptionCallback<S>) {
     subscriptions.push(callback)
 
     // watch here to link the subscription to the current active instance