Skip to content

Commit

Permalink
refactor: remove circular dep
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed May 11, 2021
1 parent 236314c commit de93d61
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 213 deletions.
20 changes: 13 additions & 7 deletions __tests__/store.spec.ts
Original file line number Diff line number Diff line change
@@ -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'

Expand Down Expand Up @@ -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
)
})
Expand All @@ -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
)
})
Expand Down
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand Down
61 changes: 61 additions & 0 deletions src/createPinia.ts
Original file line number Diff line number Diff line change
@@ -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
}
88 changes: 88 additions & 0 deletions src/devtools/formatting.ts
Original file line number Diff line number Diff line change
@@ -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'
}
}
1 change: 1 addition & 0 deletions src/devtools/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { devtoolsPlugin } from './plugin'
138 changes: 30 additions & 108 deletions src/devtools.ts → src/devtools/plugin.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)
}
}
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -18,6 +19,7 @@ export type {
PiniaCustomProperties,
DefineStoreOptions,
} from './types'
export { MutationType } from './types'

export {
mapActions,
Expand Down
Loading

0 comments on commit de93d61

Please sign in to comment.