]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
feat: add getters
authorEduardo San Martin Morote <posva13@gmail.com>
Mon, 25 Nov 2019 13:17:07 +0000 (14:17 +0100)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 25 Nov 2019 13:17:07 +0000 (14:17 +0100)
__tests__/getters.spec.ts [new file with mode: 0644]
rollup.config.js
src/index.ts
src/types.ts

diff --git a/__tests__/getters.spec.ts b/__tests__/getters.spec.ts
new file mode 100644 (file)
index 0000000..cdab1b7
--- /dev/null
@@ -0,0 +1,28 @@
+import { createStore } from '../src'
+
+describe('Store', () => {
+  function buildStore() {
+    return createStore(
+      'main',
+      () => ({
+        name: 'Eduardo',
+      }),
+      {
+        upperCaseName: ({ name }) => name.toUpperCase(),
+      }
+    )
+  }
+
+  it('adds getters to the store', () => {
+    const store = buildStore()
+    expect(store.upperCaseName.value).toBe('EDUARDO')
+    store.state.name = 'Ed'
+    expect(store.upperCaseName.value).toBe('ED')
+  })
+
+  it('updates the value', () => {
+    const store = buildStore()
+    store.state.name = 'Ed'
+    expect(store.upperCaseName.value).toBe('ED')
+  })
+})
index db0b321a2dcb0634f7b0bac2cbed31231484117a..4f8b7811c10f33f5bd2a9dc45e484a79d463a306 100644 (file)
@@ -49,6 +49,10 @@ function createEntry({
       banner,
       file: `dist/${pkg.name}.UNKNOWN.js`,
       format,
+
+      globals: {
+        '@vue/composition-api': 'VueCompositionApi',
+      },
     },
   }
 
@@ -65,9 +69,6 @@ function createEntry({
     config.plugins.push(resolve(), commonjs())
   } else {
     config.external = external
-    config.globals = {
-      '@vue/composition-api': 'VueCompositionApi',
-    }
   }
 
   config.plugins.push(
index 11e9101f0a90dd2290718acb6a4c2330f7b4665f..0377b9e42794388d2b904055fb689c173fe9e88f 100644 (file)
@@ -1,4 +1,4 @@
-import { ref, watch } from '@vue/composition-api'
+import { ref, watch, computed } from '@vue/composition-api'
 import { Ref } from '@vue/composition-api/dist/reactivity'
 import {
   StateTree,
@@ -6,6 +6,8 @@ import {
   SubscriptionCallback,
   DeepPartial,
   isPlainObject,
+  StoreGetters,
+  StoreGetter,
 } from './types'
 import { devtoolPlugin } from './devtools'
 
@@ -33,17 +35,28 @@ function innerPatch<T extends StateTree>(
  * they want, no? like user/cart
  */
 
+type CombinedStore<
+  Id extends string,
+  S extends StateTree,
+  G extends Record<string, StoreGetter<S>>
+> = Store<Id, S> & StoreGetters<S, G>
+
 /**
  * Creates a store instance
  * @param id unique identifier of the store, like a name. eg: main, cart, user
  * @param initialState initial state applied to the store, Must be correctly typed to infer typings
  */
 
-export function createStore<Id extends string, S extends StateTree>(
+export function createStore<
+  Id extends string,
+  S extends StateTree,
+  G extends Record<string, StoreGetter<S>>
+>(
   id: Id,
-  buildState: () => S
+  buildState: () => S,
+  getters: G
   // methods: Record<string | symbol, StoreMethod>
-): Store<Id, S> {
+): CombinedStore<Id, S, G> {
   const state: Ref<S> = ref(buildState())
   function replaceState(newState: S) {
     state.value = newState
@@ -84,7 +97,7 @@ export function createStore<Id extends string, S extends StateTree>(
     // TODO: return function to remove subscription
   }
 
-  const store: Store<Id, S> = {
+  const storeWithState: Store<Id, S> = {
     id,
     // it is replaced below by a getter
     state: state.value,
@@ -98,6 +111,21 @@ export function createStore<Id extends string, S extends StateTree>(
     },
   }
 
+  // @ts-ignore we have to build it
+  const computedGetters: StoreGetters<S, G> = {}
+  for (const getterName in getters) {
+    const method = getters[getterName]
+    // @ts-ignore
+    computedGetters[getterName] = computed<ReturnType<typeof method>>(() =>
+      getters[getterName](state.value)
+    )
+  }
+
+  const store = {
+    ...storeWithState,
+    ...computedGetters,
+  }
+
   // make state access invisible
   Object.defineProperty(store, 'state', {
     get: () => state.value,
@@ -121,14 +149,15 @@ export function createStore<Id extends string, S extends StateTree>(
  * @param buildState function that returns a state
  */
 
-export function makeStore<Id extends string, S extends StateTree>(
-  id: Id,
-  buildState: () => S
-) {
-  let store: Store<Id, S> | undefined
+export function makeStore<
+  Id extends string,
+  S extends StateTree,
+  G extends Record<string, StoreGetter<S>>
+>(id: Id, buildState: () => S, getters: G) {
+  let store: CombinedStore<Id, S, G> | undefined
 
-  function useStore(): Store<Id, S> {
-    if (!store) store = createStore(id, buildState)
+  function useStore(): CombinedStore<Id, S, G> {
+    if (!store) store = createStore(id, buildState, getters)
 
     return store
   }
@@ -144,9 +173,17 @@ export function makeStore<Id extends string, S extends StateTree>(
 }
 
 // export const store = createStore('main', initialState)
-// export const cartStore = createStore('cart', {
+
+// type StateI = ReturnType<typeof buildState>
+// const buildState = () => ({
 //   items: ['thing 1'],
 // })
+// export const cartStore = createStore('cart', buildState, {
+//   amount: state => state.items.length,
+// })
+
+// cartStore.nonueo
+// cartStore.amount.value * 2
 
 // store.patch({
 //   toggle: 'off',
index 76cce8eb61fc4b2fbd5f707b055f6b977804f915..431de4329aa782d031800a23da59a30bd57026b7 100644 (file)
@@ -1,3 +1,5 @@
+import { Ref } from '@vue/composition-api'
+
 interface JSONSerializable {
   toJSON(): string
 }
@@ -40,7 +42,11 @@ export function isPlainObject(
 // eslint-disable-next-line @typescript-eslint/no-empty-interface
 interface StateTreeArray extends Array<StateTreeValue> {}
 
-// type TODO = any
+export interface StoreGetter<S extends StateTree, T = any> {
+  (state: S): T
+}
+
+type TODO = any
 // type StoreMethod = TODO
 export type DeepPartial<T> = { [K in keyof T]?: DeepPartial<T[K]> }
 // type DeepReadonly<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> }
@@ -50,6 +56,13 @@ export type SubscriptionCallback<S> = (
   state: S
 ) => void
 
+export type StoreGetters<
+  S extends StateTree,
+  G extends Record<string, StoreGetter<S>>
+> = {
+  [k in keyof G]: G[k] extends StoreGetter<S, infer V> ? Ref<V> : never
+}
+
 export interface Store<Id extends string, S extends StateTree> {
   /**
    * Unique identifier of the store
@@ -60,6 +73,7 @@ export interface Store<Id extends string, S extends StateTree> {
    * State of the Store
    */
   state: S
+
   /**
    * Applies a state patch to current state. Allows passing nested values
    * @param partialState patch to apply to the state