From b5c928da20efc532de84d8b8498d56f306a40e03 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 24 Mar 2021 17:00:08 +0100 Subject: [PATCH] feat: add plugin api wip --- __tests__/storePlugins.spec.ts | 4 ++-- docs/core-concepts/plugins.md | 17 +++++++++++++++++ src/rootStore.ts | 20 +++++++++++++++----- src/store.ts | 28 ++++++++++++---------------- 4 files changed, 46 insertions(+), 23 deletions(-) create mode 100644 docs/core-concepts/plugins.md diff --git a/__tests__/storePlugins.spec.ts b/__tests__/storePlugins.spec.ts index 0f7a1b45..13e5d248 100644 --- a/__tests__/storePlugins.spec.ts +++ b/__tests__/storePlugins.spec.ts @@ -33,7 +33,7 @@ describe('store plugins', () => { mount({ template: 'none' }, { global: { plugins: [pinia] } }) // must call use after installing the plugin - pinia.use((app) => { + pinia.use(({ app }) => { return { n: 20, uid: app._uid } }) @@ -51,7 +51,7 @@ describe('store plugins', () => { const pinia = createPinia() pinia.use(() => ({ n: 1 })) - pinia.use((app) => ({ uid: app._uid })) + pinia.use(({ app }) => ({ uid: app._uid })) mount({ template: 'none' }, { global: { plugins: [pinia] } }) diff --git a/docs/core-concepts/plugins.md b/docs/core-concepts/plugins.md new file mode 100644 index 00000000..fb1a413b --- /dev/null +++ b/docs/core-concepts/plugins.md @@ -0,0 +1,17 @@ +# Plugins + +Pinia stores can be fully extended thanks to a low level API. Here is a list of things you can do: + +- Add new properties to stores +- Add new methods to stores +- Wrap existing methods +- Change or even cancel actions +- Implement side effects like local storage +- Apply **only** to specific stores + +Plugins are added to pinia with `pinia.use()`: + +```js +// add a property named `secret` to every store that is created after this plugin is installed +pinia.use(() => ({ secret: '' })) +``` diff --git a/src/rootStore.ts b/src/rootStore.ts index 9cfad32d..d7e5f629 100644 --- a/src/rootStore.ts +++ b/src/rootStore.ts @@ -4,6 +4,7 @@ import { StateTree, StoreWithState, StateDescriptor, + GenericStore, PiniaCustomProperties, GenericStore, } from './types' @@ -60,7 +61,7 @@ export const getClientApp = () => clientApp * Plugin to extend every store */ export interface PiniaStorePlugin { - (app: App): Partial + (context: { app: App; store: GenericStore }): Partial } /** @@ -86,7 +87,14 @@ export interface Pinia { * * @internal */ - _p: Array<() => Partial> + _p: Array + + /** + * App linked to this Pinia instance + * + * @internal + */ + _a: App } declare module '@vue/runtime-core' { @@ -125,7 +133,7 @@ export function createPinia(): Pinia { const pinia: Pinia = { install(app: App) { - localApp = app + pinia._a = localApp = app // pinia._a = app app.provide(piniaSymbol, pinia) app.config.globalProperties.$pinia = pinia @@ -137,7 +145,7 @@ export function createPinia(): Pinia { // installing pinia's plugin setActivePinia(pinia) } - toBeInstalled.forEach((plugin) => _p.push(plugin.bind(null, localApp!))) + toBeInstalled.forEach((plugin) => _p.push(plugin)) }, use(plugin) { @@ -150,11 +158,13 @@ export function createPinia(): Pinia { if (!localApp) { toBeInstalled.push(plugin) } else { - _p.push(plugin.bind(null, localApp)) + _p.push(plugin) } }, _p, + // it's actually undefined here + _a: localApp!, state, } diff --git a/src/store.ts b/src/store.ts index 6daaab6d..329f4672 100644 --- a/src/store.ts +++ b/src/store.ts @@ -230,28 +230,24 @@ function buildStoreToUse< } as StoreWithActions[typeof actionName] } - const extensions = pinia._p.reduce( - (extended, extender) => assign({}, extended, extender()), - {} as PiniaCustomProperties - ) - - const store: Store = reactive( - assign( - {}, - extensions, - partialStore, - // using this means no new properties can be added as state - computedFromState(pinia.state, $id), - computedGetters, - wrappedActions - ) - ) as Store + const store: Store = reactive({ + ...partialStore, + // using this means no new properties can be added as state + ...computedFromState(pinia.state, $id), + ...computedGetters, + ...wrappedActions, + }) as Store // use this instead of a computed with setter to be able to create it anywhere // without linking the computed lifespan to wherever the store is first // created. Object.defineProperty(store, '$state', descriptor) + // apply all plugins + pinia._p.forEach((extender) => { + Object.assign(store, extender({ store, app: pinia._a })) + }) + return store } -- 2.47.3