-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'
store.$state.a = false
expect(spy).toHaveBeenCalledWith(
- {
+ expect.objectContaining({
payload: {},
storeName: 'main',
- type: expect.stringContaining('in place'),
- },
+ type: MutationType.direct,
+ }),
store.$state
)
})
store.$patch(patch)
expect(spy).toHaveBeenCalledWith(
- {
+ expect.objectContaining({
payload: patch,
storeName: 'main',
- type: expect.stringContaining('patch'),
- },
+ type: MutationType.patchObject,
+ }),
store.$state
)
})
'/node_modules/',
'src/index.ts',
'\\.d\\.ts$',
- 'src/devtools.ts',
+ 'src/devtools',
'src/deprecated.ts',
],
testMatch: ['<rootDir>/__tests__/**/*.spec.ts'],
--- /dev/null
+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
+}
--- /dev/null
+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'
+ }
+}
--- /dev/null
+export { devtoolsPlugin } from './plugin'
-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
)
}
-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
}
/**
- * 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)
+ }
+}
-export { setActivePinia, createPinia } from './rootStore'
+export { setActivePinia } from './rootStore'
+export { createPinia } from './createPinia'
export type { Pinia, PiniaStorePlugin, PiniaPluginContext } from './rootStore'
export { defineStore } from './store'
PiniaCustomProperties,
DefineStoreOptions,
} from './types'
+export { MutationType } from './types'
export {
mapActions,
-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,
>
>()
-/**
- * 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
*/
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
}
})
}
- 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