Comparing version 2.0.0-alpha.7 to 2.0.0-alpha.8
@@ -0,1 +1,12 @@ | ||
# [2.0.0-alpha.8](https://github.com/posva/pinia/compare/v2.0.0-alpha.7...v2.0.0-alpha.8) (2021-03-29) | ||
### Bug Fixes | ||
- use assign instead of spread ([b2bb5ba](https://github.com/posva/pinia/commit/b2bb5ba4faf52c41479a7d77490b85016b853229)) | ||
- **cjs:** ensure dev checks on cjs build ([a255735](https://github.com/posva/pinia/commit/a255735211b796120d5f76470ea18759f1eb5d97)) | ||
### Features | ||
- **devtools:** logo and titles ([0963fd0](https://github.com/posva/pinia/commit/0963fd0b647b0e5414782f78167c770cbab24a83)) | ||
# [2.0.0-alpha.7](https://github.com/posva/pinia/compare/v2.0.0-alpha.6...v2.0.0-alpha.7) (2021-01-21) | ||
@@ -2,0 +13,0 @@ |
/*! | ||
* pinia v2.0.0-alpha.7 | ||
* pinia v2.0.0-alpha.8 | ||
* (c) 2021 Eduardo San Martin Morote | ||
@@ -30,3 +30,3 @@ * @license MIT | ||
const getActivePinia = () => { | ||
if ( !activePinia) { | ||
if ((process.env.NODE_ENV !== 'production') && !activePinia) { | ||
vue.warn(`[🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?\n\n` + | ||
@@ -40,9 +40,10 @@ `const pinia = createPinia()\n` + | ||
/** | ||
* The api needs more work we must be able to use the store easily in any | ||
* function by calling `useStore` to get the store Instance and we also need to | ||
* be able to reset the store instance between applications on the server | ||
* Map of stores based on a Pinia instance. Allows setting and retrieving stores | ||
* for the current running application (with its pinia). | ||
*/ | ||
const storesMap = new WeakMap(); | ||
const piniaSymbol = ( Symbol('pinia') | ||
); | ||
const piniaSymbol = ((process.env.NODE_ENV !== 'production') | ||
? Symbol('pinia') | ||
: /* istanbul ignore next */ | ||
Symbol()); | ||
/** | ||
@@ -68,2 +69,6 @@ * Creates a Pinia instance to be used by the application | ||
use(plugin) { | ||
/* istanbul ignore next */ | ||
if ((process.env.NODE_ENV !== 'production')) { | ||
console.warn(`[🍍]: The plugin API has plans to change to bring better extensibility. "pinia.use()" signature will change in the next release. It is recommended to avoid using this API.`); | ||
} | ||
if (!localApp) { | ||
@@ -106,2 +111,3 @@ toBeInstalled.push(plugin); | ||
} | ||
const { assign } = Object; | ||
/** | ||
@@ -134,4 +140,4 @@ * Create an object of computed properties referring to | ||
function initStore($id, buildState = () => ({}), initialState) { | ||
const _p = getActivePinia(); | ||
_p.state.value[$id] = initialState || buildState(); | ||
const pinia = getActivePinia(); | ||
pinia.state.value[$id] = initialState || buildState(); | ||
// const state: Ref<S> = toRef(_p.state.value, $id) | ||
@@ -142,7 +148,7 @@ let isListening = true; | ||
isListening = false; | ||
innerPatch(_p.state.value[$id], partialState); | ||
innerPatch(pinia.state.value[$id], partialState); | ||
isListening = true; | ||
// because we paused the watcher, we need to manually call the subscriptions | ||
subscriptions.forEach((callback) => { | ||
callback({ storeName: $id, type: '⤵️ patch', payload: partialState }, _p.state.value[$id]); | ||
callback({ storeName: $id, type: '⤵️ patch', payload: partialState }, pinia.state.value[$id]); | ||
}); | ||
@@ -154,3 +160,3 @@ } | ||
// e.g. inside the setup of a component | ||
const stopWatcher = vue.watch(() => _p.state.value[$id], (state) => { | ||
const stopWatcher = vue.watch(() => pinia.state.value[$id], (state) => { | ||
if (isListening) { | ||
@@ -175,7 +181,7 @@ subscriptions.forEach((callback) => { | ||
subscriptions = []; | ||
_p.state.value[$id] = buildState(); | ||
pinia.state.value[$id] = buildState(); | ||
} | ||
const storeWithState = { | ||
$id, | ||
_p, | ||
_p: pinia, | ||
// $state is added underneath | ||
@@ -189,6 +195,6 @@ $patch, | ||
{ | ||
get: () => _p.state.value[$id], | ||
get: () => pinia.state.value[$id], | ||
set: (newState) => { | ||
isListening = false; | ||
_p.state.value[$id] = newState; | ||
pinia.state.value[$id] = newState; | ||
isListening = true; | ||
@@ -212,7 +218,7 @@ }, | ||
function buildStoreToUse(partialStore, descriptor, $id, getters = {}, actions = {}) { | ||
const _p = getActivePinia(); | ||
const pinia = getActivePinia(); | ||
const computedGetters = {}; | ||
for (const getterName in getters) { | ||
computedGetters[getterName] = vue.computed(() => { | ||
setActivePinia(_p); | ||
setActivePinia(pinia); | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
@@ -225,3 +231,3 @@ return getters[getterName].call(store, store); | ||
wrappedActions[actionName] = function () { | ||
setActivePinia(_p); | ||
setActivePinia(pinia); | ||
// eslint-disable-next-line | ||
@@ -231,14 +237,6 @@ return actions[actionName].apply(store, arguments); | ||
} | ||
const extensions = _p._p.reduce((extended, extender) => ({ | ||
...extended, | ||
...extender(), | ||
}), {}); | ||
const store = vue.reactive({ | ||
...extensions, | ||
...partialStore, | ||
// using this means no new properties can be added as state | ||
...computedFromState(_p.state, $id), | ||
...computedGetters, | ||
...wrappedActions, | ||
}); | ||
const extensions = pinia._p.reduce((extended, extender) => assign({}, extended, extender()), {}); | ||
const store = vue.reactive(assign({}, extensions, partialStore, | ||
// using this means no new properties can be added as state | ||
computedFromState(pinia.state, $id), computedGetters, wrappedActions)); | ||
// use this instead of a computed with setter to be able to create it anywhere | ||
@@ -275,3 +273,4 @@ // without linking the computed lifespan to wherever the store is first | ||
false && | ||
true /*|| __FEATURE_PROD_DEVTOOLS__*/) { | ||
(process.env.NODE_ENV !== 'production') /*|| __FEATURE_PROD_DEVTOOLS__*/) { | ||
/* istanbul ignore else */ | ||
if (!isDevWarned && !false) { | ||
@@ -278,0 +277,0 @@ isDevWarned = true; |
/*! | ||
* pinia v2.0.0-alpha.7 | ||
* pinia v2.0.0-alpha.8 | ||
* (c) 2021 Eduardo San Martin Morote | ||
@@ -33,8 +33,8 @@ * @license MIT | ||
/** | ||
* The api needs more work we must be able to use the store easily in any | ||
* function by calling `useStore` to get the store Instance and we also need to | ||
* be able to reset the store instance between applications on the server | ||
* Map of stores based on a Pinia instance. Allows setting and retrieving stores | ||
* for the current running application (with its pinia). | ||
*/ | ||
const storesMap = new WeakMap(); | ||
const piniaSymbol = ( Symbol()); | ||
const piniaSymbol = (/* istanbul ignore next */ | ||
Symbol()); | ||
/** | ||
@@ -97,2 +97,3 @@ * Creates a Pinia instance to be used by the application | ||
} | ||
const { assign } = Object; | ||
/** | ||
@@ -125,4 +126,4 @@ * Create an object of computed properties referring to | ||
function initStore($id, buildState = () => ({}), initialState) { | ||
const _p = getActivePinia(); | ||
_p.state.value[$id] = initialState || buildState(); | ||
const pinia = getActivePinia(); | ||
pinia.state.value[$id] = initialState || buildState(); | ||
// const state: Ref<S> = toRef(_p.state.value, $id) | ||
@@ -133,7 +134,7 @@ let isListening = true; | ||
isListening = false; | ||
innerPatch(_p.state.value[$id], partialState); | ||
innerPatch(pinia.state.value[$id], partialState); | ||
isListening = true; | ||
// because we paused the watcher, we need to manually call the subscriptions | ||
subscriptions.forEach((callback) => { | ||
callback({ storeName: $id, type: '⤵️ patch', payload: partialState }, _p.state.value[$id]); | ||
callback({ storeName: $id, type: '⤵️ patch', payload: partialState }, pinia.state.value[$id]); | ||
}); | ||
@@ -145,3 +146,3 @@ } | ||
// e.g. inside the setup of a component | ||
const stopWatcher = vue.watch(() => _p.state.value[$id], (state) => { | ||
const stopWatcher = vue.watch(() => pinia.state.value[$id], (state) => { | ||
if (isListening) { | ||
@@ -166,7 +167,7 @@ subscriptions.forEach((callback) => { | ||
subscriptions = []; | ||
_p.state.value[$id] = buildState(); | ||
pinia.state.value[$id] = buildState(); | ||
} | ||
const storeWithState = { | ||
$id, | ||
_p, | ||
_p: pinia, | ||
// $state is added underneath | ||
@@ -180,6 +181,6 @@ $patch, | ||
{ | ||
get: () => _p.state.value[$id], | ||
get: () => pinia.state.value[$id], | ||
set: (newState) => { | ||
isListening = false; | ||
_p.state.value[$id] = newState; | ||
pinia.state.value[$id] = newState; | ||
isListening = true; | ||
@@ -203,7 +204,7 @@ }, | ||
function buildStoreToUse(partialStore, descriptor, $id, getters = {}, actions = {}) { | ||
const _p = getActivePinia(); | ||
const pinia = getActivePinia(); | ||
const computedGetters = {}; | ||
for (const getterName in getters) { | ||
computedGetters[getterName] = vue.computed(() => { | ||
setActivePinia(_p); | ||
setActivePinia(pinia); | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
@@ -216,3 +217,3 @@ return getters[getterName].call(store, store); | ||
wrappedActions[actionName] = function () { | ||
setActivePinia(_p); | ||
setActivePinia(pinia); | ||
// eslint-disable-next-line | ||
@@ -222,14 +223,6 @@ return actions[actionName].apply(store, arguments); | ||
} | ||
const extensions = _p._p.reduce((extended, extender) => ({ | ||
...extended, | ||
...extender(), | ||
}), {}); | ||
const store = vue.reactive({ | ||
...extensions, | ||
...partialStore, | ||
// using this means no new properties can be added as state | ||
...computedFromState(_p.state, $id), | ||
...computedGetters, | ||
...wrappedActions, | ||
}); | ||
const extensions = pinia._p.reduce((extended, extender) => assign({}, extended, extender()), {}); | ||
const store = vue.reactive(assign({}, extensions, partialStore, | ||
// using this means no new properties can be added as state | ||
computedFromState(pinia.state, $id), computedGetters, wrappedActions)); | ||
// use this instead of a computed with setter to be able to create it anywhere | ||
@@ -267,2 +260,3 @@ // without linking the computed lifespan to wherever the store is first | ||
false /*|| __FEATURE_PROD_DEVTOOLS__*/) { | ||
/* istanbul ignore else */ | ||
if (!isDevWarned && !false) { | ||
@@ -269,0 +263,0 @@ isDevWarned = true; |
/*! | ||
* pinia v2.0.0-alpha.7 | ||
* pinia v2.0.0-alpha.8 | ||
* (c) 2021 Eduardo San Martin Morote | ||
@@ -26,3 +26,3 @@ * @license MIT | ||
const getActivePinia = () => { | ||
if ( !activePinia) { | ||
if (!activePinia) { | ||
warn(`[🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?\n\n` + | ||
@@ -36,5 +36,4 @@ `const pinia = createPinia()\n` + | ||
/** | ||
* The api needs more work we must be able to use the store easily in any | ||
* function by calling `useStore` to get the store Instance and we also need to | ||
* be able to reset the store instance between applications on the server | ||
* Map of stores based on a Pinia instance. Allows setting and retrieving stores | ||
* for the current running application (with its pinia). | ||
*/ | ||
@@ -48,3 +47,3 @@ const storesMap = new WeakMap(); | ||
const getClientApp = () => clientApp; | ||
const piniaSymbol = ( Symbol('pinia') | ||
const piniaSymbol = (Symbol('pinia') | ||
); | ||
@@ -70,3 +69,3 @@ /** | ||
// only set the app on client for devtools | ||
if ( IS_CLIENT) { | ||
if (IS_CLIENT) { | ||
setClientApp(app); | ||
@@ -80,2 +79,6 @@ // this allows calling useStore() outside of a component setup after | ||
use(plugin) { | ||
/* istanbul ignore next */ | ||
{ | ||
console.warn(`[🍍]: The plugin API has plans to change to bring better extensibility. "pinia.use()" signature will change in the next release. It is recommended to avoid using this API.`); | ||
} | ||
if (!localApp) { | ||
@@ -93,10 +96,2 @@ toBeInstalled.push(plugin); | ||
} | ||
/** | ||
* Registered stores | ||
*/ | ||
const stores = /*#__PURE__*/ new Set(); | ||
function registerStore(store) { | ||
stores.add(store); | ||
} | ||
const getRegisteredStores = () => stores; | ||
@@ -148,2 +143,6 @@ function isPlainObject( | ||
} | ||
/** | ||
* Registered stores used for devtools. | ||
*/ | ||
const registeredStores = /*#__PURE__*/ new Set(); | ||
function toastMessage(message, type) { | ||
@@ -165,19 +164,15 @@ const piniaMessage = '🍍 ' + message; | ||
let isAlreadyInstalled; | ||
const componentStateTypes = []; | ||
function addDevtools(app, store) { | ||
registerStore(store); | ||
registeredStores.add(store); | ||
componentStateTypes.push('🍍 ' + store.$id); | ||
setupDevtoolsPlugin({ | ||
id: 'pinia', | ||
id: 'dev.esm.pinia', | ||
label: 'Pinia 🍍', | ||
logo: 'https://pinia.esm.dev/logo.svg', | ||
packageName: 'pinia', | ||
homepage: 'https://pinia.esm.dev', | ||
componentStateTypes, | ||
app, | ||
}, (api) => { | ||
api.on.inspectComponent((payload, ctx) => { | ||
if (payload.instanceData) { | ||
payload.instanceData.state.push({ | ||
type: '🍍 ' + store.$id, | ||
key: 'state', | ||
editable: false, | ||
value: store.$state, | ||
}); | ||
} | ||
}); | ||
// watch(router.currentRoute, () => { | ||
@@ -190,2 +185,12 @@ // // @ts-ignore | ||
if (!isAlreadyInstalled) { | ||
api.on.inspectComponent((payload, ctx) => { | ||
if (payload.instanceData) { | ||
payload.instanceData.state.push({ | ||
type: '🍍 ' + store.$id, | ||
key: 'state', | ||
editable: false, | ||
value: store.$state, | ||
}); | ||
} | ||
}); | ||
api.addTimelineLayer({ | ||
@@ -205,3 +210,2 @@ id: mutationsLayerId, | ||
else { | ||
// @ts-ignore | ||
api.notifyComponentUpdate(); | ||
@@ -215,3 +219,3 @@ api.sendInspectorTree(piniaInspectorId); | ||
store: formatDisplay(mutation.storeName), | ||
type: formatDisplay(mutation.type), | ||
// type: formatDisplay(mutation.type), | ||
}; | ||
@@ -221,3 +225,2 @@ if (mutation.payload) { | ||
} | ||
// @ts-ignore | ||
api.notifyComponentUpdate(); | ||
@@ -229,5 +232,4 @@ api.sendInspectorState(piniaInspectorId); | ||
time: Date.now(), | ||
title: mutation.type, | ||
data, | ||
// TODO: remove when fixed | ||
meta: {}, | ||
}, | ||
@@ -238,3 +240,3 @@ }); | ||
if (payload.app === app && payload.inspectorId === piniaInspectorId) { | ||
const stores = Array.from(getRegisteredStores()); | ||
const stores = Array.from(registeredStores); | ||
payload.rootNodes = (payload.filter | ||
@@ -247,3 +249,3 @@ ? stores.filter((store) => store.$id.toLowerCase().includes(payload.filter.toLowerCase())) | ||
if (payload.app === app && payload.inspectorId === piniaInspectorId) { | ||
const stores = Array.from(getRegisteredStores()); | ||
const stores = Array.from(registeredStores); | ||
const store = stores.find((store) => store.$id === payload.nodeId); | ||
@@ -296,2 +298,3 @@ if (store) { | ||
} | ||
const { assign } = Object; | ||
/** | ||
@@ -324,4 +327,4 @@ * Create an object of computed properties referring to | ||
function initStore($id, buildState = () => ({}), initialState) { | ||
const _p = getActivePinia(); | ||
_p.state.value[$id] = initialState || buildState(); | ||
const pinia = getActivePinia(); | ||
pinia.state.value[$id] = initialState || buildState(); | ||
// const state: Ref<S> = toRef(_p.state.value, $id) | ||
@@ -332,7 +335,7 @@ let isListening = true; | ||
isListening = false; | ||
innerPatch(_p.state.value[$id], partialState); | ||
innerPatch(pinia.state.value[$id], partialState); | ||
isListening = true; | ||
// because we paused the watcher, we need to manually call the subscriptions | ||
subscriptions.forEach((callback) => { | ||
callback({ storeName: $id, type: '⤵️ patch', payload: partialState }, _p.state.value[$id]); | ||
callback({ storeName: $id, type: '⤵️ patch', payload: partialState }, pinia.state.value[$id]); | ||
}); | ||
@@ -344,3 +347,3 @@ } | ||
// e.g. inside the setup of a component | ||
const stopWatcher = watch(() => _p.state.value[$id], (state) => { | ||
const stopWatcher = watch(() => pinia.state.value[$id], (state) => { | ||
if (isListening) { | ||
@@ -365,7 +368,7 @@ subscriptions.forEach((callback) => { | ||
subscriptions = []; | ||
_p.state.value[$id] = buildState(); | ||
pinia.state.value[$id] = buildState(); | ||
} | ||
const storeWithState = { | ||
$id, | ||
_p, | ||
_p: pinia, | ||
// $state is added underneath | ||
@@ -379,6 +382,6 @@ $patch, | ||
{ | ||
get: () => _p.state.value[$id], | ||
get: () => pinia.state.value[$id], | ||
set: (newState) => { | ||
isListening = false; | ||
_p.state.value[$id] = newState; | ||
pinia.state.value[$id] = newState; | ||
isListening = true; | ||
@@ -402,7 +405,7 @@ }, | ||
function buildStoreToUse(partialStore, descriptor, $id, getters = {}, actions = {}) { | ||
const _p = getActivePinia(); | ||
const pinia = getActivePinia(); | ||
const computedGetters = {}; | ||
for (const getterName in getters) { | ||
computedGetters[getterName] = computed(() => { | ||
setActivePinia(_p); | ||
setActivePinia(pinia); | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
@@ -415,3 +418,3 @@ return getters[getterName].call(store, store); | ||
wrappedActions[actionName] = function () { | ||
setActivePinia(_p); | ||
setActivePinia(pinia); | ||
// eslint-disable-next-line | ||
@@ -421,14 +424,6 @@ return actions[actionName].apply(store, arguments); | ||
} | ||
const extensions = _p._p.reduce((extended, extender) => ({ | ||
...extended, | ||
...extender(), | ||
}), {}); | ||
const store = reactive({ | ||
...extensions, | ||
...partialStore, | ||
// using this means no new properties can be added as state | ||
...computedFromState(_p.state, $id), | ||
...computedGetters, | ||
...wrappedActions, | ||
}); | ||
const extensions = pinia._p.reduce((extended, extender) => assign({}, extended, extender()), {}); | ||
const store = reactive(assign({}, extensions, partialStore, | ||
// using this means no new properties can be added as state | ||
computedFromState(pinia.state, $id), computedGetters, wrappedActions)); | ||
// use this instead of a computed with setter to be able to create it anywhere | ||
@@ -467,2 +462,3 @@ // without linking the computed lifespan to wherever the store is first | ||
const app = getClientApp(); | ||
/* istanbul ignore else */ | ||
if (app) { | ||
@@ -469,0 +465,0 @@ addDevtools(app, store); |
/*! | ||
* pinia v2.0.0-alpha.7 | ||
* pinia v2.0.0-alpha.8 | ||
* (c) 2021 Eduardo San Martin Morote | ||
@@ -35,5 +35,4 @@ * @license MIT | ||
/** | ||
* The api needs more work we must be able to use the store easily in any | ||
* function by calling `useStore` to get the store Instance and we also need to | ||
* be able to reset the store instance between applications on the server | ||
* Map of stores based on a Pinia instance. Allows setting and retrieving stores | ||
* for the current running application (with its pinia). | ||
*/ | ||
@@ -49,3 +48,4 @@ const storesMap = new WeakMap(); | ||
? Symbol('pinia') | ||
: Symbol()); | ||
: /* istanbul ignore next */ | ||
Symbol()); | ||
/** | ||
@@ -70,3 +70,3 @@ * Creates a Pinia instance to be used by the application | ||
// only set the app on client for devtools | ||
if ( IS_CLIENT) { | ||
if (IS_CLIENT) { | ||
setClientApp(app); | ||
@@ -80,2 +80,6 @@ // this allows calling useStore() outside of a component setup after | ||
use(plugin) { | ||
/* istanbul ignore next */ | ||
if ((process.env.NODE_ENV !== 'production')) { | ||
console.warn(`[🍍]: The plugin API has plans to change to bring better extensibility. "pinia.use()" signature will change in the next release. It is recommended to avoid using this API.`); | ||
} | ||
if (!localApp) { | ||
@@ -93,10 +97,2 @@ toBeInstalled.push(plugin); | ||
} | ||
/** | ||
* Registered stores | ||
*/ | ||
const stores = /*#__PURE__*/ new Set(); | ||
function registerStore(store) { | ||
stores.add(store); | ||
} | ||
const getRegisteredStores = () => stores; | ||
@@ -148,2 +144,6 @@ function isPlainObject( | ||
} | ||
/** | ||
* Registered stores used for devtools. | ||
*/ | ||
const registeredStores = /*#__PURE__*/ new Set(); | ||
function toastMessage(message, type) { | ||
@@ -165,19 +165,15 @@ const piniaMessage = '🍍 ' + message; | ||
let isAlreadyInstalled; | ||
const componentStateTypes = []; | ||
function addDevtools(app, store) { | ||
registerStore(store); | ||
registeredStores.add(store); | ||
componentStateTypes.push('🍍 ' + store.$id); | ||
setupDevtoolsPlugin({ | ||
id: 'pinia', | ||
id: 'dev.esm.pinia', | ||
label: 'Pinia 🍍', | ||
logo: 'https://pinia.esm.dev/logo.svg', | ||
packageName: 'pinia', | ||
homepage: 'https://pinia.esm.dev', | ||
componentStateTypes, | ||
app, | ||
}, (api) => { | ||
api.on.inspectComponent((payload, ctx) => { | ||
if (payload.instanceData) { | ||
payload.instanceData.state.push({ | ||
type: '🍍 ' + store.$id, | ||
key: 'state', | ||
editable: false, | ||
value: store.$state, | ||
}); | ||
} | ||
}); | ||
// watch(router.currentRoute, () => { | ||
@@ -190,2 +186,12 @@ // // @ts-ignore | ||
if (!isAlreadyInstalled) { | ||
api.on.inspectComponent((payload, ctx) => { | ||
if (payload.instanceData) { | ||
payload.instanceData.state.push({ | ||
type: '🍍 ' + store.$id, | ||
key: 'state', | ||
editable: false, | ||
value: store.$state, | ||
}); | ||
} | ||
}); | ||
api.addTimelineLayer({ | ||
@@ -205,3 +211,2 @@ id: mutationsLayerId, | ||
else { | ||
// @ts-ignore | ||
api.notifyComponentUpdate(); | ||
@@ -215,3 +220,3 @@ api.sendInspectorTree(piniaInspectorId); | ||
store: formatDisplay(mutation.storeName), | ||
type: formatDisplay(mutation.type), | ||
// type: formatDisplay(mutation.type), | ||
}; | ||
@@ -221,3 +226,2 @@ if (mutation.payload) { | ||
} | ||
// @ts-ignore | ||
api.notifyComponentUpdate(); | ||
@@ -229,5 +233,4 @@ api.sendInspectorState(piniaInspectorId); | ||
time: Date.now(), | ||
title: mutation.type, | ||
data, | ||
// TODO: remove when fixed | ||
meta: {}, | ||
}, | ||
@@ -238,3 +241,3 @@ }); | ||
if (payload.app === app && payload.inspectorId === piniaInspectorId) { | ||
const stores = Array.from(getRegisteredStores()); | ||
const stores = Array.from(registeredStores); | ||
payload.rootNodes = (payload.filter | ||
@@ -247,3 +250,3 @@ ? stores.filter((store) => store.$id.toLowerCase().includes(payload.filter.toLowerCase())) | ||
if (payload.app === app && payload.inspectorId === piniaInspectorId) { | ||
const stores = Array.from(getRegisteredStores()); | ||
const stores = Array.from(registeredStores); | ||
const store = stores.find((store) => store.$id === payload.nodeId); | ||
@@ -296,2 +299,3 @@ if (store) { | ||
} | ||
const { assign } = Object; | ||
/** | ||
@@ -324,4 +328,4 @@ * Create an object of computed properties referring to | ||
function initStore($id, buildState = () => ({}), initialState) { | ||
const _p = getActivePinia(); | ||
_p.state.value[$id] = initialState || buildState(); | ||
const pinia = getActivePinia(); | ||
pinia.state.value[$id] = initialState || buildState(); | ||
// const state: Ref<S> = toRef(_p.state.value, $id) | ||
@@ -332,7 +336,7 @@ let isListening = true; | ||
isListening = false; | ||
innerPatch(_p.state.value[$id], partialState); | ||
innerPatch(pinia.state.value[$id], partialState); | ||
isListening = true; | ||
// because we paused the watcher, we need to manually call the subscriptions | ||
subscriptions.forEach((callback) => { | ||
callback({ storeName: $id, type: '⤵️ patch', payload: partialState }, _p.state.value[$id]); | ||
callback({ storeName: $id, type: '⤵️ patch', payload: partialState }, pinia.state.value[$id]); | ||
}); | ||
@@ -344,3 +348,3 @@ } | ||
// e.g. inside the setup of a component | ||
const stopWatcher = watch(() => _p.state.value[$id], (state) => { | ||
const stopWatcher = watch(() => pinia.state.value[$id], (state) => { | ||
if (isListening) { | ||
@@ -365,7 +369,7 @@ subscriptions.forEach((callback) => { | ||
subscriptions = []; | ||
_p.state.value[$id] = buildState(); | ||
pinia.state.value[$id] = buildState(); | ||
} | ||
const storeWithState = { | ||
$id, | ||
_p, | ||
_p: pinia, | ||
// $state is added underneath | ||
@@ -379,6 +383,6 @@ $patch, | ||
{ | ||
get: () => _p.state.value[$id], | ||
get: () => pinia.state.value[$id], | ||
set: (newState) => { | ||
isListening = false; | ||
_p.state.value[$id] = newState; | ||
pinia.state.value[$id] = newState; | ||
isListening = true; | ||
@@ -402,7 +406,7 @@ }, | ||
function buildStoreToUse(partialStore, descriptor, $id, getters = {}, actions = {}) { | ||
const _p = getActivePinia(); | ||
const pinia = getActivePinia(); | ||
const computedGetters = {}; | ||
for (const getterName in getters) { | ||
computedGetters[getterName] = computed(() => { | ||
setActivePinia(_p); | ||
setActivePinia(pinia); | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
@@ -415,3 +419,3 @@ return getters[getterName].call(store, store); | ||
wrappedActions[actionName] = function () { | ||
setActivePinia(_p); | ||
setActivePinia(pinia); | ||
// eslint-disable-next-line | ||
@@ -421,14 +425,6 @@ return actions[actionName].apply(store, arguments); | ||
} | ||
const extensions = _p._p.reduce((extended, extender) => ({ | ||
...extended, | ||
...extender(), | ||
}), {}); | ||
const store = reactive({ | ||
...extensions, | ||
...partialStore, | ||
// using this means no new properties can be added as state | ||
...computedFromState(_p.state, $id), | ||
...computedGetters, | ||
...wrappedActions, | ||
}); | ||
const extensions = pinia._p.reduce((extended, extender) => assign({}, extended, extender()), {}); | ||
const store = reactive(assign({}, extensions, partialStore, | ||
// using this means no new properties can be added as state | ||
computedFromState(pinia.state, $id), computedGetters, wrappedActions)); | ||
// use this instead of a computed with setter to be able to create it anywhere | ||
@@ -467,2 +463,3 @@ // without linking the computed lifespan to wherever the store is first | ||
const app = getClientApp(); | ||
/* istanbul ignore else */ | ||
if (app) { | ||
@@ -469,0 +466,0 @@ addDevtools(app, store); |
/*! | ||
* pinia v2.0.0-alpha.7 | ||
* pinia v2.0.0-alpha.8 | ||
* (c) 2021 Eduardo San Martin Morote | ||
@@ -27,3 +27,3 @@ * @license MIT | ||
const getActivePinia = () => { | ||
if ( !activePinia) { | ||
if (!activePinia) { | ||
vue.warn(`[🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?\n\n` + | ||
@@ -37,5 +37,4 @@ `const pinia = createPinia()\n` + | ||
/** | ||
* The api needs more work we must be able to use the store easily in any | ||
* function by calling `useStore` to get the store Instance and we also need to | ||
* be able to reset the store instance between applications on the server | ||
* Map of stores based on a Pinia instance. Allows setting and retrieving stores | ||
* for the current running application (with its pinia). | ||
*/ | ||
@@ -49,3 +48,3 @@ const storesMap = new WeakMap(); | ||
const getClientApp = () => clientApp; | ||
const piniaSymbol = ( Symbol('pinia') | ||
const piniaSymbol = (Symbol('pinia') | ||
); | ||
@@ -71,3 +70,3 @@ /** | ||
// only set the app on client for devtools | ||
if ( IS_CLIENT) { | ||
if (IS_CLIENT) { | ||
setClientApp(app); | ||
@@ -81,2 +80,6 @@ // this allows calling useStore() outside of a component setup after | ||
use(plugin) { | ||
/* istanbul ignore next */ | ||
{ | ||
console.warn(`[🍍]: The plugin API has plans to change to bring better extensibility. "pinia.use()" signature will change in the next release. It is recommended to avoid using this API.`); | ||
} | ||
if (!localApp) { | ||
@@ -94,10 +97,2 @@ toBeInstalled.push(plugin); | ||
} | ||
/** | ||
* Registered stores | ||
*/ | ||
const stores = /*#__PURE__*/ new Set(); | ||
function registerStore(store) { | ||
stores.add(store); | ||
} | ||
const getRegisteredStores = () => stores; | ||
@@ -149,2 +144,6 @@ function isPlainObject( | ||
} | ||
/** | ||
* Registered stores used for devtools. | ||
*/ | ||
const registeredStores = /*#__PURE__*/ new Set(); | ||
function toastMessage(message, type) { | ||
@@ -166,19 +165,15 @@ const piniaMessage = '🍍 ' + message; | ||
let isAlreadyInstalled; | ||
const componentStateTypes = []; | ||
function addDevtools(app, store) { | ||
registerStore(store); | ||
registeredStores.add(store); | ||
componentStateTypes.push('🍍 ' + store.$id); | ||
setupDevtoolsPlugin({ | ||
id: 'pinia', | ||
id: 'dev.esm.pinia', | ||
label: 'Pinia 🍍', | ||
logo: 'https://pinia.esm.dev/logo.svg', | ||
packageName: 'pinia', | ||
homepage: 'https://pinia.esm.dev', | ||
componentStateTypes, | ||
app, | ||
}, (api) => { | ||
api.on.inspectComponent((payload, ctx) => { | ||
if (payload.instanceData) { | ||
payload.instanceData.state.push({ | ||
type: '🍍 ' + store.$id, | ||
key: 'state', | ||
editable: false, | ||
value: store.$state, | ||
}); | ||
} | ||
}); | ||
// watch(router.currentRoute, () => { | ||
@@ -191,2 +186,12 @@ // // @ts-ignore | ||
if (!isAlreadyInstalled) { | ||
api.on.inspectComponent((payload, ctx) => { | ||
if (payload.instanceData) { | ||
payload.instanceData.state.push({ | ||
type: '🍍 ' + store.$id, | ||
key: 'state', | ||
editable: false, | ||
value: store.$state, | ||
}); | ||
} | ||
}); | ||
api.addTimelineLayer({ | ||
@@ -206,3 +211,2 @@ id: mutationsLayerId, | ||
else { | ||
// @ts-ignore | ||
api.notifyComponentUpdate(); | ||
@@ -216,3 +220,3 @@ api.sendInspectorTree(piniaInspectorId); | ||
store: formatDisplay(mutation.storeName), | ||
type: formatDisplay(mutation.type), | ||
// type: formatDisplay(mutation.type), | ||
}; | ||
@@ -222,3 +226,2 @@ if (mutation.payload) { | ||
} | ||
// @ts-ignore | ||
api.notifyComponentUpdate(); | ||
@@ -230,5 +233,4 @@ api.sendInspectorState(piniaInspectorId); | ||
time: Date.now(), | ||
title: mutation.type, | ||
data, | ||
// TODO: remove when fixed | ||
meta: {}, | ||
}, | ||
@@ -239,3 +241,3 @@ }); | ||
if (payload.app === app && payload.inspectorId === piniaInspectorId) { | ||
const stores = Array.from(getRegisteredStores()); | ||
const stores = Array.from(registeredStores); | ||
payload.rootNodes = (payload.filter | ||
@@ -248,3 +250,3 @@ ? stores.filter((store) => store.$id.toLowerCase().includes(payload.filter.toLowerCase())) | ||
if (payload.app === app && payload.inspectorId === piniaInspectorId) { | ||
const stores = Array.from(getRegisteredStores()); | ||
const stores = Array.from(registeredStores); | ||
const store = stores.find((store) => store.$id === payload.nodeId); | ||
@@ -297,2 +299,3 @@ if (store) { | ||
} | ||
const { assign } = Object; | ||
/** | ||
@@ -325,4 +328,4 @@ * Create an object of computed properties referring to | ||
function initStore($id, buildState = () => ({}), initialState) { | ||
const _p = getActivePinia(); | ||
_p.state.value[$id] = initialState || buildState(); | ||
const pinia = getActivePinia(); | ||
pinia.state.value[$id] = initialState || buildState(); | ||
// const state: Ref<S> = toRef(_p.state.value, $id) | ||
@@ -333,7 +336,7 @@ let isListening = true; | ||
isListening = false; | ||
innerPatch(_p.state.value[$id], partialState); | ||
innerPatch(pinia.state.value[$id], partialState); | ||
isListening = true; | ||
// because we paused the watcher, we need to manually call the subscriptions | ||
subscriptions.forEach((callback) => { | ||
callback({ storeName: $id, type: '⤵️ patch', payload: partialState }, _p.state.value[$id]); | ||
callback({ storeName: $id, type: '⤵️ patch', payload: partialState }, pinia.state.value[$id]); | ||
}); | ||
@@ -345,3 +348,3 @@ } | ||
// e.g. inside the setup of a component | ||
const stopWatcher = vue.watch(() => _p.state.value[$id], (state) => { | ||
const stopWatcher = vue.watch(() => pinia.state.value[$id], (state) => { | ||
if (isListening) { | ||
@@ -366,7 +369,7 @@ subscriptions.forEach((callback) => { | ||
subscriptions = []; | ||
_p.state.value[$id] = buildState(); | ||
pinia.state.value[$id] = buildState(); | ||
} | ||
const storeWithState = { | ||
$id, | ||
_p, | ||
_p: pinia, | ||
// $state is added underneath | ||
@@ -380,6 +383,6 @@ $patch, | ||
{ | ||
get: () => _p.state.value[$id], | ||
get: () => pinia.state.value[$id], | ||
set: (newState) => { | ||
isListening = false; | ||
_p.state.value[$id] = newState; | ||
pinia.state.value[$id] = newState; | ||
isListening = true; | ||
@@ -403,7 +406,7 @@ }, | ||
function buildStoreToUse(partialStore, descriptor, $id, getters = {}, actions = {}) { | ||
const _p = getActivePinia(); | ||
const pinia = getActivePinia(); | ||
const computedGetters = {}; | ||
for (const getterName in getters) { | ||
computedGetters[getterName] = vue.computed(() => { | ||
setActivePinia(_p); | ||
setActivePinia(pinia); | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
@@ -416,3 +419,3 @@ return getters[getterName].call(store, store); | ||
wrappedActions[actionName] = function () { | ||
setActivePinia(_p); | ||
setActivePinia(pinia); | ||
// eslint-disable-next-line | ||
@@ -422,14 +425,6 @@ return actions[actionName].apply(store, arguments); | ||
} | ||
const extensions = _p._p.reduce((extended, extender) => ({ | ||
...extended, | ||
...extender(), | ||
}), {}); | ||
const store = vue.reactive({ | ||
...extensions, | ||
...partialStore, | ||
// using this means no new properties can be added as state | ||
...computedFromState(_p.state, $id), | ||
...computedGetters, | ||
...wrappedActions, | ||
}); | ||
const extensions = pinia._p.reduce((extended, extender) => assign({}, extended, extender()), {}); | ||
const store = vue.reactive(assign({}, extensions, partialStore, | ||
// using this means no new properties can be added as state | ||
computedFromState(pinia.state, $id), computedGetters, wrappedActions)); | ||
// use this instead of a computed with setter to be able to create it anywhere | ||
@@ -468,2 +463,3 @@ // without linking the computed lifespan to wherever the store is first | ||
const app = getClientApp(); | ||
/* istanbul ignore else */ | ||
if (app) { | ||
@@ -470,0 +466,0 @@ addDevtools(app, store); |
/*! | ||
* pinia v2.0.0-alpha.7 | ||
* pinia v2.0.0-alpha.8 | ||
* (c) 2021 Eduardo San Martin Morote | ||
* @license MIT | ||
*/ | ||
var Pinia=function(e,t){"use strict";const n="undefined"!=typeof window;let o;const c=e=>o=e,a=()=>o,r=new WeakMap;let s;const u=Symbol();function i(e){return e&&"object"==typeof e&&"[object Object]"===Object.prototype.toString.call(e)&&"function"!=typeof e.toJSON}function l(e,t){for(const n in t){const o=t[n],c=e[n];e[n]=i(c)&&i(o)?l(c,o):o}return e}function f(e,n){const o={},c=e.value[n];for(const a in c)o[a]=t.computed({get:()=>e.value[n][a],set:t=>e.value[n][a]=t});return o}function p(e,n=(()=>({})),o){const c=a();c.state.value[e]=o||n();let r=!0,s=[];return[{$id:e,_p:c,$patch:function(t){r=!1,l(c.state.value[e],t),r=!0,s.forEach(n=>{n({storeName:e,type:"⤵️ patch",payload:t},c.state.value[e])})},$subscribe:function(n){s.push(n);const o=t.watch(()=>c.state.value[e],t=>{r&&s.forEach(n=>{n({storeName:e,type:"🧩 in place",payload:{}},t)})},{deep:!0,flush:"sync"});return()=>{const e=s.indexOf(n);e>-1&&(s.splice(e,1),o())}},$reset:function(){s=[],c.state.value[e]=n()}},{get:()=>c.state.value[e],set:t=>{r=!1,c.state.value[e]=t,r=!0}}]}function d(e,n,o,r={},s={}){const u=a(),i={};for(const e in r)i[e]=t.computed(()=>(c(u),r[e].call(d,d)));const l={};for(const e in s)l[e]=function(){return c(u),s[e].apply(d,arguments)};const p=u._p.reduce((e,t)=>({...e,...t()}),{}),d=t.reactive({...p,...e,...f(u.state,o),...i,...l});return Object.defineProperty(d,"$state",n),d}function v(e){const{id:n,state:o,getters:s,actions:i}=e;return function(e){(e=e||t.getCurrentInstance()&&t.inject(u))&&c(e),e=a();let l=r.get(e);l||r.set(e,l=new Map);let f=l.get(n);if(!f){f=p(n,o,e.state.value[n]),l.set(n,f);const t=d(f[0],f[1],n,s,i);return t}return d(f[0],f[1],n,s,i)}}return e.createPinia=function(){const e=t.ref({});let o,a=[];const r=[],i={install(e){o=e,e.provide(u,i),e.config.globalProperties.$pinia=i,n&&((e=>{s=e})(e),c(i)),r.forEach(e=>a.push(e.bind(null,o)))},use(e){o?a.push(e.bind(null,o)):r.push(e)},_p:a,state:e};return i},e.createStore=e=>(console.warn('[🍍]: "createStore" has been deprecated and will be removed on the sable release, use "defineStore" instead.'),v(e)),e.defineStore=v,e.setActivePinia=c,Object.defineProperty(e,"__esModule",{value:!0}),e}({},Vue); | ||
var Pinia=function(e,t){"use strict";const n="undefined"!=typeof window;let o;const c=e=>o=e,s=()=>o,a=new WeakMap;let r;const u=Symbol();function i(e){return e&&"object"==typeof e&&"[object Object]"===Object.prototype.toString.call(e)&&"function"!=typeof e.toJSON}function l(e,t){for(const n in t){const o=t[n],c=e[n];e[n]=i(c)&&i(o)?l(c,o):o}return e}const{assign:f}=Object;function p(e,n=(()=>({})),o){const c=s();c.state.value[e]=o||n();let a=!0,r=[];return[{$id:e,_p:c,$patch:function(t){a=!1,l(c.state.value[e],t),a=!0,r.forEach(n=>{n({storeName:e,type:"⤵️ patch",payload:t},c.state.value[e])})},$subscribe:function(n){r.push(n);const o=t.watch(()=>c.state.value[e],t=>{a&&r.forEach(n=>{n({storeName:e,type:"🧩 in place",payload:{}},t)})},{deep:!0,flush:"sync"});return()=>{const e=r.indexOf(n);e>-1&&(r.splice(e,1),o())}},$reset:function(){r=[],c.state.value[e]=n()}},{get:()=>c.state.value[e],set:t=>{a=!1,c.state.value[e]=t,a=!0}}]}function d(e,n,o,a={},r={}){const u=s(),i={};for(const e in a)i[e]=t.computed(()=>(c(u),a[e].call(d,d)));const l={};for(const e in r)l[e]=function(){return c(u),r[e].apply(d,arguments)};const p=u._p.reduce((e,t)=>f({},e,t()),{}),d=t.reactive(f({},p,e,function(e,n){const o={},c=e.value[n];for(const s in c)o[s]=t.computed({get:()=>e.value[n][s],set:t=>e.value[n][s]=t});return o}(u.state,o),i,l));return Object.defineProperty(d,"$state",n),d}function v(e){const{id:n,state:o,getters:r,actions:i}=e;return function(e){(e=e||t.getCurrentInstance()&&t.inject(u))&&c(e),e=s();let l=a.get(e);l||a.set(e,l=new Map);let f=l.get(n);if(!f){f=p(n,o,e.state.value[n]),l.set(n,f);const t=d(f[0],f[1],n,r,i);return t}return d(f[0],f[1],n,r,i)}}return e.createPinia=function(){const e=t.ref({});let o,s=[];const a=[],i={install(e){o=e,e.provide(u,i),e.config.globalProperties.$pinia=i,n&&((e=>{r=e})(e),c(i)),a.forEach(e=>s.push(e.bind(null,o)))},use(e){o?s.push(e.bind(null,o)):a.push(e)},_p:s,state:e};return i},e.createStore=e=>(console.warn('[🍍]: "createStore" has been deprecated and will be removed on the sable release, use "defineStore" instead.'),v(e)),e.defineStore=v,e.setActivePinia=c,Object.defineProperty(e,"__esModule",{value:!0}),e}({},Vue); |
{ | ||
"name": "pinia", | ||
"version": "2.0.0-alpha.7", | ||
"description": "Some awesome description", | ||
"version": "2.0.0-alpha.8", | ||
"description": "Intuitive, type safe and flexible Store for Vue", | ||
"main": "dist/pinia.cjs.js", | ||
@@ -10,2 +10,10 @@ "module": "dist/pinia.esm-bundler.js", | ||
"types": "dist/pinia.d.ts", | ||
"exports": { | ||
".": { | ||
"require": "./dist/pinia.cjs.js", | ||
"browser": "./dist/pinia.esm-browser.js", | ||
"import": "./dist/pinia.esm-bundler.js" | ||
}, | ||
"./package.json": "./package.json" | ||
}, | ||
"sideEffects": false, | ||
@@ -17,3 +25,5 @@ "author": { | ||
"scripts": { | ||
"build": "rollup -c rollup.config.js", | ||
"docs": "vitepress dev docs", | ||
"docs:build": "vitepress build docs", | ||
"build": "rimraf dist && rollup -c rollup.config.js", | ||
"build:dts": "api-extractor run --local --verbose", | ||
@@ -42,3 +52,5 @@ "size": "rollup -c size-checks/rollup.config.js && node scripts/check-size.js", | ||
"store", | ||
"pina", | ||
"pinia", | ||
"piña", | ||
"pigna", | ||
"composition", | ||
@@ -55,27 +67,29 @@ "api", | ||
"devDependencies": { | ||
"@microsoft/api-extractor": "7.12.1", | ||
"@nuxt/types": "^2.14.6", | ||
"@rollup/plugin-alias": "^3.1.1", | ||
"@rollup/plugin-commonjs": "^17.0.0", | ||
"@rollup/plugin-node-resolve": "^11.0.0", | ||
"@rollup/plugin-replace": "^2.3.3", | ||
"@types/jest": "^26.0.14", | ||
"@types/node": "^14.11.2", | ||
"@vue/devtools-api": "^6.0.0-beta.2", | ||
"@vue/server-renderer": "^3.0.0", | ||
"@vue/test-utils": "^2.0.0-beta.5", | ||
"@microsoft/api-extractor": "7.13.2", | ||
"@nuxt/types": "^2.15.3", | ||
"@rollup/plugin-alias": "^3.1.2", | ||
"@rollup/plugin-commonjs": "^18.0.0", | ||
"@rollup/plugin-node-resolve": "^11.2.1", | ||
"@rollup/plugin-replace": "^2.4.2", | ||
"@types/jest": "^26.0.22", | ||
"@types/node": "^14.14.37", | ||
"@vue/devtools-api": "^6.0.0-beta.7", | ||
"@vue/server-renderer": "^3.0.9", | ||
"@vue/test-utils": "^2.0.0-rc.4", | ||
"brotli": "^1.3.2", | ||
"codecov": "^3.6.1", | ||
"conventional-changelog-cli": "^2.1.0", | ||
"jest": "^26.4.2", | ||
"lint-staged": "^10.2.11", | ||
"codecov": "^3.8.1", | ||
"conventional-changelog-cli": "^2.1.1", | ||
"jest": "^26.6.3", | ||
"jest-mock-warn": "^1.1.0", | ||
"lint-staged": "^10.5.4", | ||
"pascalcase": "^1.0.0", | ||
"prettier": "^2.1.2", | ||
"rimraf": "^3.0.1", | ||
"rollup": "^2.28.1", | ||
"prettier": "^2.2.1", | ||
"rimraf": "^3.0.2", | ||
"rollup": "^2.44.0", | ||
"rollup-plugin-terser": "^7.0.2", | ||
"rollup-plugin-typescript2": "^0.29.0", | ||
"ts-jest": "^26.4.0", | ||
"typescript": "^4.0.3", | ||
"vue": "^3.0.0", | ||
"rollup-plugin-typescript2": "^0.30.0", | ||
"ts-jest": "^26.5.4", | ||
"typescript": "^4.2.3", | ||
"vitepress": "^0.12.2", | ||
"vue": "^3.0.7", | ||
"yorkie": "^2.0.0" | ||
@@ -82,0 +96,0 @@ }, |
328
README.md
@@ -1,27 +0,31 @@ | ||
# Pinia [![Build Status](https://badgen.net/circleci/github/posva/pinia/v2)](https://circleci.com/gh/posva/pinia) [![npm package](https://badgen.net/npm/v/pinia/next)](https://www.npmjs.com/package/pinia) [![coverage](https://badgen.net/codecov/c/github/posva/pinia/next)](https://codecov.io/github/posva/pinia) [![thanks](https://badgen.net/badge/thanks/♥/pink)](https://github.com/posva/thanks) | ||
<p align="center"> | ||
<a href="https://pinia.esm.dev" target="_blank" rel="noopener noreferrer"> | ||
<img width="180" src="https://pinia.esm.dev/logo.svg" alt="Pinia logo"> | ||
</a> | ||
</p> | ||
<br/> | ||
<p align="center"> | ||
<a href="https://npmjs.com/package/pinia"><img src="https://badgen.net/npm/v/pinia/next" alt="npm package"></a> | ||
<a href="https://github.com/posva/pinia/actions/workflows/test.yml"><img src="https://github.com/posva/pinia/workflows/test/badge.svg?branch=v2" alt="build status"></a> | ||
<a href="https://codecov.io/github/posva/pinia"><img src="https://badgen.net/codecov/c/github/posva/pinia/v2" alt="code coverage"></a> | ||
</p> | ||
<br/> | ||
> Pronounced like the fruit in Spanish, _Piña_ | ||
> | ||
> _Piña_ is also an invalid package name... that's why it has to be _pinia_ which sounds very similar | ||
# Pinia | ||
🍍 Automatically Typed, Modular and lightweight (but **Experimental**) Store for Vue 3.x based on the composition api with devtools support | ||
> Intuitive, type safe and flexible Store for Vue | ||
## 👉 [Demo on CodeSandbox](https://j4qzw.csb.app/) | ||
- 💡 Intuitive | ||
- 🔑 Type Safe | ||
- ⚙️ Devtools support | ||
- 🔌 Extensible | ||
- 🏗 Modular by design | ||
- 📦 Extremely light | ||
⚠️⚠️⚠️ This project is experimental, it's an exploration of what a _Store_ could be like using [the composition api](https://vue-composition-api-rfc.netlify.com). It works both for Vue 2.x and Vue 3.x and you are currently on the branch that supports Vue 3.x. [Go here for the Vue 2.x compatible version](https://github.com/posva/pinia/tree/v1). | ||
Pinia works both for Vue 2.x and Vue 3.x and you are currently on the branch that supports Vue 3.x. **If you are looking for the version compatible with Vue 2.x, check the [`v1` branch](https://github.com/posva/pinia/tree/v1)**. | ||
What I want is to inspire others to think about ways to improve Vuex and come up with something that works very well with the composition api. Ideally it could also be used without it. | ||
Pinia is is the most similar English pronunciation of the word _pineapple_ in Spanish: _piña_. A pineapple is in reality a group of individual flowers that join together to create a multiple fruit. Similar to stores, each one is born individually, but they are all connected at the end. It's also a delicious tropical fruit indigenous to South America. | ||
These are the core principles that I try to achieve with this experiment: | ||
## 👉 [Demo on CodeSandbox](https://j4qzw.csb.app/) | ||
- Autocompletion: even if you write your code in JavaScript! | ||
- Flat modular structure 🍍 No nesting, only stores, compose them as needed | ||
- Light layer on top of Vue 💨 keep it very lightweight | ||
- Only `state`, `getters` and `actions` | ||
- No more verbose mutations, 👐 `patch` is _**the** mutation_ | ||
- Actions are like _methods_ ⚗️ Group your business logic there | ||
- Import what you need, let webpack code split 📦 No need for dynamically registered modules | ||
- SSR support ⚙️ | ||
- DevTools support 💻 Which is crucial to make this enjoyable | ||
## Help me keep working on this project 💚 | ||
@@ -35,2 +39,10 @@ | ||
<h4 align="center">Gold Sponsors</h4> | ||
<p align="center"> | ||
<a href="https://passionatepeople.io/" target="_blank" rel="noopener noreferrer"> | ||
<img src="https://img2.storyblok.com/0x200/filters::format(webp)/f/86387/x/4cf6a70a8c/logo-white-text.svg" height="72px" alt="Passionate People"> | ||
</a> | ||
</p> | ||
<h4 align="center">Silver Sponsors</h4> | ||
@@ -40,8 +52,12 @@ | ||
<a href="https://www.vuemastery.com" target="_blank" rel="noopener noreferrer"> | ||
<img src="https://www.vuemastery.com/images/vuemastery.svg" height="48px" alt="Vue Mastery"> | ||
<img src="https://www.vuemastery.com/images/vuemastery.svg" height="42px" alt="Vue Mastery"> | ||
</a> | ||
<a href="https://vuetifyjs.com" target="_blank" rel="noopener noreferrer"> | ||
<img src="https://cdn.vuetifyjs.com/docs/images/logos/vuetify-logo-light-text.svg" alt="Vuetify" height="48px"> | ||
<img src="https://cdn.vuetifyjs.com/docs/images/logos/vuetify-logo-light-text.svg" alt="Vuetify" height="42px"> | ||
</a> | ||
<a href="https://www.codestream.com/?utm_source=github&utm_campaign=vuerouter&utm_medium=banner" target="_blank" rel="noopener noreferrer"> | ||
<img src="https://alt-images.codestream.com/codestream_logo_vuerouter.png" alt="CodeStream" height="42px"> | ||
</a> | ||
</p> | ||
@@ -84,3 +100,3 @@ | ||
```sh | ||
```bash | ||
yarn add pinia@next | ||
@@ -104,3 +120,2 @@ # or with npm | ||
This will also add devtools support. Some features like time traveling and editing are still not supported because vue-devtools doesn't expose the necessary APIs yet. | ||
**NOTE**: this API is still experimental and is currently only used for devtools and SSR but that might change in the future. | ||
@@ -130,3 +145,3 @@ ### Creating a Store | ||
doubleCountPlusOne() { | ||
return this.doubleCount * 2 | ||
return this.doubleCount * 2 + 1 | ||
}, | ||
@@ -165,269 +180,8 @@ }, | ||
Note: the SSR implementation on Pinia might change, but if you intend having SSR on your application, you should avoid using `useStore` functions at the root level of a file to make sure the correct store is retrieved for your currently running application instance. Here is an example: | ||
## Documentation | ||
**Avoid doing this**: | ||
To learn more about Pinia, check [its documentation](https://pinia.esm.dev). | ||
```ts | ||
import { createRouter } from 'vue-router' | ||
const router = createRouter({ | ||
// ... | ||
}) | ||
// ❌ Depending on where you do this it will fail | ||
const main = useMainStore() | ||
router.beforeEach((to, from, next) => { | ||
if (main.isLoggedIn) next() | ||
else next('/login') | ||
}) | ||
``` | ||
Instead, call `useMainStore()` at the top of `setup`, like `inject` and `provide` in Vue: | ||
```ts | ||
export default defineComponent({ | ||
setup() { | ||
// ✅ This will work | ||
const main = useMainStore() | ||
return {} | ||
}, | ||
}) | ||
// In a different file... | ||
const pinia = createPinia() | ||
app.use(pinia) | ||
router.beforeEach((to) => { | ||
// ✅ This will work (requires pinia param when outside of setup on both | ||
// Client and Server. See the SSR section below for more information) | ||
const main = useMainStore(pinia) | ||
if (to.meta.requiresAuth && !main.isLoggedIn) return '/login' | ||
}) | ||
``` | ||
⚠️: Note that if you are developing an SSR application, [you will need to do a bit more](#ssr). | ||
You can access any property defined in `state` and `getters` directly on the store, similar to `data` and `computed` properties in a Vue component. | ||
```ts | ||
export default defineComponent({ | ||
setup() { | ||
const main = useMainStore() | ||
const text = main.name // "eduardo" | ||
const doubleCount = main.doubleCount // 2 | ||
return { | ||
text, // will always be "eduardo" | ||
textDynamic: computed(() => main.name), // reactive value | ||
} | ||
}, | ||
}) | ||
``` | ||
The `main` store in an object wrapped with `reactive`, meaning there is no need to write `.value` after getters but, like `props` in `setup`, we cannot destructure it: | ||
```ts | ||
export default defineComponent({ | ||
setup() { | ||
// ❌ This won't work because it breaks reactivity | ||
// it's the same as destructuring from `props` | ||
const { name, doubleCount } = useMainStore() | ||
return { name, doubleCount } | ||
}, | ||
}) | ||
``` | ||
Actions are invoked like methods: | ||
```ts | ||
export default defineComponent({ | ||
setup() { | ||
const main = useMainStore() | ||
// call the action as a method of the store | ||
main.reset() | ||
return {} | ||
}, | ||
}) | ||
``` | ||
### Mutating the `state` | ||
To mutate the state you can either directly change something: | ||
```ts | ||
main.counter++ | ||
``` | ||
or call the method `$patch` that allows you apply multiple changes at the same time with a partial `state` object: | ||
```ts | ||
main.$patch({ | ||
counter: -1, | ||
name: 'Abalam', | ||
}) | ||
``` | ||
The main difference here is that `$patch` allows you to group multiple changes into one single entry in the devtools. | ||
### Replacing the `state` | ||
Simply set it to a new object; | ||
```ts | ||
main.state = { counter: 666, name: 'Paimon' } | ||
``` | ||
### SSR | ||
Creating stores with Pinia should work out of the box for SSR as long as you call your `useStore()` functions at the top of `setup` functions, `getters` and `actions`: | ||
```ts | ||
export default defineComponent({ | ||
setup() { | ||
// this works because pinia knows what application is running | ||
const main = useMainStore() | ||
return { main } | ||
}, | ||
}) | ||
``` | ||
If you need to use the store somewhere else, you need to pass the `pinia` instance [that was passed to the app](#install-the-plugin) to the `useStore()` function call: | ||
```ts | ||
const pinia = createPinia() | ||
const app = createApp(App) | ||
app.use(router) | ||
app.use(pinia) | ||
router.beforeEach((to) => { | ||
// ✅ This will work make sure the correct store is used for the current running app | ||
const main = useMainStore(pinia) | ||
if (to.meta.requiresAuth && !main.isLoggedIn) return '/login' | ||
}) | ||
``` | ||
To hydrate the initial state, you need to make sure the rootState is included somewhere in the HTML for Pinia to pick it up later on: | ||
```js | ||
import { createPinia } from 'pinia' | ||
// retrieve the rootState server side | ||
const pinia = createPinia() | ||
const app = createApp(App) | ||
app.use(router) | ||
app.use(pinia) | ||
// after rendering the page, the root state is build and can be read | ||
// serialize, escape (VERY important if the content of the state can be changed | ||
// by the user, which is almost always the case), and place it somewhere on | ||
// the page, for example, as a global variable. | ||
JSON.stringify(pinia.state.value) | ||
``` | ||
On client side, you must hydrate pinia's state before calling any `useStore()` function. For example, if we serialize the state into a `<script>` tag to make it accessible globally on client side through `window.__pinia`, we can write this: | ||
```js | ||
const pinia = createPinia() | ||
const app = createApp(App) | ||
app.use(pinia) | ||
// must be set by the user | ||
if (isClient) { | ||
pinia.state.value = JSON.parse(window.__pinia) | ||
} | ||
``` | ||
### Composing Stores | ||
Composing stores may look hard at first glance but there is only one rule to follow really: | ||
If **multiple stores use each other** or you need to use **multiple stores** at the same time, you must create a **separate file** where you import all of them. | ||
If one store uses an other store, there is no need to create a new file, you can directly import it. Think of it as nesting. | ||
You can call `useOtherStore()` at the top of any getter an action: | ||
```ts | ||
import { useUserStore } from './user' | ||
export const cartStore = defineStore({ | ||
id: 'cart', | ||
getters: { | ||
// ... other getters | ||
summary() { | ||
const user = useUserStore() | ||
return `Hi ${user.name}, you have ${this.list.length} items in your cart. It costs ${this.price}.` | ||
}, | ||
}, | ||
actions: { | ||
purchase() { | ||
const user = useUserStore() | ||
return apiPurchase(user.id, this.list) | ||
}, | ||
}, | ||
}) | ||
``` | ||
#### Shared Getters | ||
If you need to compute a value based on the `state` and/or `getters` of multiple stores, you may be able to import all the stores but one into the remaining store, but depending on how your stores are used across your application, **this would hurt your code splitting** because importing the store that imports all others stores, would result in **one single big chunk** with all of your stores. | ||
To prevent this, **we follow the rule above** and we create a new file with a new store: | ||
```ts | ||
import { defineStore } from 'pinia' | ||
import { useUserStore } from './user' | ||
import { useCartStore } from './cart' | ||
export const useSharedStore = defineStore({ | ||
id: 'shared', | ||
getters: { | ||
summary() { | ||
const user = useUserStore() | ||
const cart = useCartStore() | ||
return `Hi ${user.name}, you have ${cart.list.length} items in your cart. It costs ${cart.price}.` | ||
}, | ||
}, | ||
}) | ||
``` | ||
#### Shared Actions | ||
When an actions needs to use multiple stores, we do the same, we create a new file with a new store: | ||
```ts | ||
import { defineStore } from 'pinia' | ||
import { useUserStore } from './user' | ||
import { useCartStore } from './cart' | ||
export const useSharedStore = defineStore({ | ||
id: 'shared', | ||
state: () => ({}), | ||
actions: { | ||
async orderCart() { | ||
const user = useUserStore() | ||
const cart = useCartStore() | ||
try { | ||
await apiOrderCart(user.token, cart.items) | ||
cart.emptyCart() | ||
} catch (err) { | ||
displayError(err) | ||
} | ||
}, | ||
}, | ||
}) | ||
``` | ||
## Related | ||
## License | ||
[MIT](http://opensource.org/licenses/MIT) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
91634
28
11
2074
182
9