-import {
- createPinia,
- defineStore,
- setActivePinia,
- setStateProvider,
- Pinia,
-} from '../src'
+import { createPinia, defineStore, setActivePinia, Pinia } from '../src'
import { mount } from '@vue/test-utils'
import { getCurrentInstance, nextTick, watch } from 'vue'
})
it('can hydrate the state', () => {
- setActivePinia(createPinia())
+ const pinia = createPinia()
+ setActivePinia(pinia)
const useStore = defineStore({
id: 'main',
state: () => ({
}),
})
- setStateProvider(() => ({
- main: {
- a: false,
- nested: {
- foo: 'bar',
- a: { b: 'string' },
- },
+ pinia.state.value.main = {
+ a: false,
+ nested: {
+ foo: 'bar',
+ a: { b: 'string' },
},
- }))
+ }
const store = useStore()
--- /dev/null
+import { createPinia, defineStore } from '../src'
+import { mount } from '@vue/test-utils'
+import { App } from 'vue'
+
+declare module '../src' {
+ export interface PiniaCustomProperties {
+ n: number
+ uid: App['_uid']
+ hasApp: boolean
+ }
+}
+
+describe('store plugins', () => {
+ const useStore = defineStore({ id: 'test' })
+ it('adds properties to stores', () => {
+ const pinia = createPinia()
+
+ mount({ template: 'none' }, { global: { plugins: [pinia] } })
+
+ // must call use after installing the plugin
+ pinia.use((app) => {
+ return { n: 20, uid: app._uid }
+ })
+
+ const store = useStore(pinia)
+
+ expect(store.n).toBe(20)
+ expect(store.uid).toBeDefined()
+ })
+
+ it('can install plugins before installing pinia', () => {
+ const pinia = createPinia()
+
+ pinia.use(() => ({ n: 1 }))
+ pinia.use((app) => ({ uid: app._uid }))
+
+ mount({ template: 'none' }, { global: { plugins: [pinia] } })
+
+ pinia.use((app) => ({ hasApp: !!app }))
+
+ const store = useStore(pinia)
+
+ expect(store.n).toBe(1)
+ expect(store.uid).toBeDefined()
+ expect(store.hasApp).toBe(true)
+ })
+})
getRootState,
createPinia,
Pinia,
+ PiniaStorePlugin,
+ PiniaCustomProperties,
} from './rootStore'
export { defineStore } from './store'
export {
export const setClientApp = (app: App) => (clientApp = app)
export const getClientApp = () => clientApp
+/**
+ * Plugin to extend every store
+ */
+export interface PiniaStorePlugin {
+ (app: App): Partial<PiniaCustomProperties>
+}
+
/**
* Every application must own its own pinia to be able to create stores
*/
/**
* root state
*/
- state: Ref<any>
+ state: Ref<Record<string, StateTree>>
+
+ /**
+ * Adds a store plugin to extend every store
+ *
+ * @param plugin - store plugin to add
+ */
+ use(plugin: PiniaStorePlugin): void
+
+ /**
+ * Installed store plugins
+ *
+ * @internal
+ */
+ _p: Array<() => Partial<PiniaCustomProperties>>
}
declare module '@vue/runtime-core' {
* 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) {
+ localApp = app
+ // pinia._a = app
app.provide(piniaSymbol, pinia)
app.config.globalProperties.$pinia = pinia
// TODO: write test
- // only set the app on client
+ // only set the app on client for devtools
if (__BROWSER__ && IS_CLIENT) {
setClientApp(app)
}
+ toBeInstalled.forEach((plugin) => _p.push(plugin.bind(null, localApp!)))
+ },
+
+ use(plugin) {
+ if (!localApp) {
+ toBeInstalled.push(plugin)
+ } else {
+ _p.push(plugin.bind(null, localApp))
+ }
},
+ _p,
+
state,
}
return pinia
}
+/**
+ * Properties that are added to every store by `pinia.use()`
+ */
+export interface PiniaCustomProperties {}
+
/**
* Registered stores
*/
getClientApp,
piniaSymbol,
Pinia,
+ PiniaCustomProperties,
} from './rootStore'
import { addDevtools } from './devtools'
import { IS_CLIENT } from './env'
} as StoreWithActions<A>[typeof actionName]
}
+ const extensions = _p._p.reduce(
+ (extended, extender) => ({
+ ...extended,
+ ...extender(),
+ }),
+ {} as PiniaCustomProperties
+ )
+
const store: Store<Id, S, G, A> = reactive({
+ ...extensions,
...partialStore,
// using this means no new properties can be added as state
...computedFromState(_p.state, $id),