]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
refactor: apply code from readme
authorEduardo San Martin Morote <posva13@gmail.com>
Tue, 26 Nov 2019 23:03:40 +0000 (00:03 +0100)
committerEduardo San Martin Morote <posva13@gmail.com>
Tue, 26 Nov 2019 23:03:40 +0000 (00:03 +0100)
__tests__/getters.spec.ts
__tests__/pinia/stores/cart.ts
__tests__/pinia/stores/user.ts
__tests__/ssr/app/main.ts
__tests__/ssr/app/store.ts
__tests__/store.patch.spec.ts
__tests__/store.spec.ts
__tests__/tds/store.test-d.ts
rollup.config.js
src/index.ts
src/store.ts [new file with mode: 0644]

index cdab1b7d3286914cd19f77223aec05a794d7223f..61b585e52467ddcc445af6b9d4b27de919fc0caf 100644 (file)
@@ -1,27 +1,25 @@
 import { createStore } from '../src'
 
 describe('Store', () => {
-  function buildStore() {
-    return createStore(
-      'main',
-      () => ({
-        name: 'Eduardo',
-      }),
-      {
-        upperCaseName: ({ name }) => name.toUpperCase(),
-      }
-    )
-  }
+  const useStore = createStore(
+    'main',
+    () => ({
+      name: 'Eduardo',
+    }),
+    {
+      upperCaseName: ({ name }) => name.toUpperCase(),
+    }
+  ).bind(null, true)
 
   it('adds getters to the store', () => {
-    const store = buildStore()
+    const store = useStore()
     expect(store.upperCaseName.value).toBe('EDUARDO')
     store.state.name = 'Ed'
     expect(store.upperCaseName.value).toBe('ED')
   })
 
   it('updates the value', () => {
-    const store = buildStore()
+    const store = useStore()
     store.state.name = 'Ed'
     expect(store.upperCaseName.value).toBe('ED')
   })
index 4a09328e405ffe4250ba6df6d41b79d34d8cb981..1f93406b4ba1bab7ae8bbeba388b42ade829deb0 100644 (file)
@@ -1,7 +1,7 @@
-import { makeStore } from '../../../src'
-import { userStore } from './user'
+import { createStore } from '../../../src'
+import { useUserStore } from './user'
 
-export const cartStore = makeStore(
+export const useCartStore = createStore(
   'cart',
   () => ({
     rawItems: [] as string[],
@@ -23,19 +23,19 @@ export const cartStore = makeStore(
 )
 
 export function addItem(name: string) {
-  const store = cartStore.useStore()
+  const store = useCartStore()
   store.state.rawItems.push(name)
 }
 
 export function removeItem(name: string) {
-  const store = cartStore.useStore()
+  const store = useCartStore()
   const i = store.state.rawItems.indexOf(name)
   if (i > -1) store.state.rawItems.splice(i, 1)
 }
 
 export async function purchaseItems() {
-  const cart = cartStore.useStore()
-  const user = userStore.useStore()
+  const cart = useCartStore()
+  const user = useUserStore()
   if (!user.state.name) return
 
   console.log('Purchasing', cart.items.value)
index 1bd282b50e0041a76e122c7b1a992d5cae56756a..d30467b8c493dd8efca73bffc2bd6cb21f7d8bb6 100644 (file)
@@ -1,6 +1,6 @@
-import { makeStore } from '../../../src'
+import { createStore } from '../../../src'
 
-export const userStore = makeStore(
+export const useUserStore = createStore(
   'user',
   () => ({
     name: 'Eduardo',
@@ -10,7 +10,7 @@ export const userStore = makeStore(
 )
 
 export function logout() {
-  const store = userStore.useStore()
+  const store = useUserStore()
 
   store.patch({
     name: '',
@@ -26,7 +26,7 @@ function apiLogin(a: string, p: string) {
 }
 
 export async function login(user: string, password: string) {
-  const store = userStore.useStore()
+  const store = useUserStore()
   const userData = await apiLogin(user, password)
 
   store.patch({
index fabda8610c97b93dd05eaf108339123b3a8e724b..548cfb1786ca6e831006730c6c1688e9f9d735bb 100644 (file)
@@ -1,14 +1,14 @@
 import Vue from 'vue'
 // import VueCompositionApi from '@vue/composition-api'
 import App from './App'
-import { useStore, clear } from './store'
+import { useStore } from './store'
 
 // Done in setup.ts
 // Vue.use(VueCompositionApi)
 
 export function createApp() {
   // create router and store instances
-  const store = useStore()
+  const store = useStore(true)
   store.reset()
 
   store.state.counter++
index 55d09aaeb59b770aed95a47c406337df89900290..ca1cbf3b89e325a338beaf725f1e27b30f932690 100644 (file)
@@ -1,6 +1,6 @@
-import { makeStore } from '../../../src'
+import { createStore } from '../../../src'
 
-export const { useStore, clear } = makeStore('main', () => ({
+export const useStore = createStore('main', () => ({
   counter: 0,
   name: 'anon',
 }))
index bbdde42b511585229390e9216f02b74467275c8f..d8e74a6409a85b1a7bd612fb52b27cf8d874cfef 100644 (file)
@@ -1,20 +1,18 @@
 import { createStore } from '../src'
 
 describe('store.patch', () => {
-  function buildStore() {
-    return createStore('main', () => ({
-      // TODO: the boolean cas shouldn't be necessary
-      // https://www.typescriptlang.org/play/#code/MYewdgzgLgBCMF4YG8CwAoGWYEMBcMUATgK4CmGAvhhiAHQ6IwBmOANhBehqJLMETI4oZJgAoAlIgB8MMclwFi5GJQk10vaDGBMBQkZI3AGTVhzJA
-      a: true as boolean,
-      nested: {
-        foo: 'foo',
-        a: { b: 'string' },
-      },
-    }))
-  }
+  const useStore = createStore('main', () => ({
+    // TODO: the boolean cas shouldn't be necessary
+    // https://www.typescriptlang.org/play/#code/MYewdgzgLgBCMF4YG8CwAoGWYEMBcMUATgK4CmGAvhhiAHQ6IwBmOANhBehqJLMETI4oZJgAoAlIgB8MMclwFi5GJQk10vaDGBMBQkZI3AGTVhzJA
+    a: true as boolean,
+    nested: {
+      foo: 'foo',
+      a: { b: 'string' },
+    },
+  })).bind(null, true) // force always a fresh instance
 
   it('patches a property without touching the rest', () => {
-    const store = buildStore()
+    const store = useStore()
     store.patch({ a: false })
     expect(store.state).toEqual({
       a: false,
@@ -26,7 +24,7 @@ describe('store.patch', () => {
   })
 
   it('patches a nested property without touching the rest', () => {
-    const store = buildStore()
+    const store = useStore()
     store.patch({ nested: { foo: 'bar' } })
     expect(store.state).toEqual({
       a: true,
@@ -46,7 +44,7 @@ describe('store.patch', () => {
   })
 
   it('patches multiple properties at the same time', () => {
-    const store = buildStore()
+    const store = useStore()
     store.patch({ a: false, nested: { foo: 'hello' } })
     expect(store.state).toEqual({
       a: false,
index c042a6ca327732ec787b5e3bb82f0e18af377a0f..39ac7d29e79c3956fdbd9b3bb8a0a33684b4f266 100644 (file)
@@ -1,18 +1,18 @@
 import { createStore } from '../src'
 
 describe('Store', () => {
-  function buildStore() {
-    return createStore('main', () => ({
-      a: true as boolean,
-      nested: {
-        foo: 'foo',
-        a: { b: 'string' },
-      },
-    }))
-  }
+  const useStore = createStore('main', () => ({
+    // TODO: the boolean cas shouldn't be necessary
+    // https://www.typescriptlang.org/play/#code/MYewdgzgLgBCMF4YG8CwAoGWYEMBcMUATgK4CmGAvhhiAHQ6IwBmOANhBehqJLMETI4oZJgAoAlIgB8MMclwFi5GJQk10vaDGBMBQkZI3AGTVhzJA
+    a: true as boolean,
+    nested: {
+      foo: 'foo',
+      a: { b: 'string' },
+    },
+  })).bind(null, true) // force always a fresh instance
 
   it('sets the initial state', () => {
-    const store = buildStore()
+    const store = useStore()
     expect(store.state).toEqual({
       a: true,
       nested: {
@@ -23,7 +23,7 @@ describe('Store', () => {
   })
 
   it('can replace its state', () => {
-    const store = buildStore()
+    const store = useStore()
     store.state = {
       a: false,
       nested: {
@@ -43,8 +43,8 @@ describe('Store', () => {
   })
 
   it('do not share the state between same id store', () => {
-    const store = buildStore()
-    const store2 = buildStore()
+    const store = useStore()
+    const store2 = useStore()
     expect(store.state).not.toBe(store2.state)
     store.state.nested.a.b = 'hey'
     expect(store2.state.nested.a.b).toBe('string')
index a401b3594c208cd816cb658cf85797be38626189..b018fb45e347e0cb9b93b2edf64727eb185314a9 100644 (file)
@@ -1,10 +1,12 @@
 import { createStore } from '../../src'
 import { expectType, expectError } from 'tsd'
 
-const store = createStore('name', () => ({ a: 'on' as 'on' | 'off' }), {
+const useStore = createStore('name', () => ({ a: 'on' as 'on' | 'off' }), {
   upper: state => state.a.toUpperCase(),
 })
 
+const store = useStore()
+
 expectType<{ a: 'on' | 'off' }>(store.state)
 
 expectError(() => store.nonExistant)
index 4f8b7811c10f33f5bd2a9dc45e484a79d463a306..199db11ed62dede00459e46dc88d6dc126c6ddeb 100644 (file)
@@ -91,7 +91,10 @@ function createEntry({
       terser({
         module: format === 'es',
         output: {
-          preamble: banner,
+          // comments: false,
+          // already added by rollup
+          // only necessary if removing others
+          //   preamble: banner,
         },
       })
     )
index f92d7f00a6375253b94e1cd5ba6aedc206ad65ba..97b1efdcb59636f0d547c211740fe09588c03437 100644 (file)
@@ -1,200 +1 @@
-import { ref, watch, computed } from '@vue/composition-api'
-import { Ref } from '@vue/composition-api/dist/reactivity'
-import {
-  StateTree,
-  Store,
-  SubscriptionCallback,
-  DeepPartial,
-  isPlainObject,
-  StoreGetters,
-  StoreGetter,
-} from './types'
-import { devtoolPlugin } from './devtools'
-
-function innerPatch<T extends StateTree>(
-  target: T,
-  patchToApply: DeepPartial<T>
-): T {
-  // TODO: get all keys
-  for (const key in patchToApply) {
-    const subPatch = patchToApply[key]
-    const targetValue = target[key]
-    if (isPlainObject(targetValue) && isPlainObject(subPatch)) {
-      target[key] = innerPatch(targetValue, subPatch)
-    } else {
-      // @ts-ignore
-      target[key] = subPatch
-    }
-  }
-
-  return target
-}
-
-/**
- * NOTE: by allowing users to name stores correctly, they can nest them the way
- * 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,
-  G extends Record<string, StoreGetter<S>>
->(
-  id: Id,
-  buildState: () => S,
-  getters: G = {} as G
-  // methods: Record<string | symbol, StoreMethod>
-): CombinedStore<Id, S, G> {
-  const state: Ref<S> = ref(buildState())
-
-  let isListening = true
-  let subscriptions: SubscriptionCallback<S>[] = []
-
-  watch(
-    () => state.value,
-    state => {
-      if (isListening) {
-        subscriptions.forEach(callback => {
-          callback({ storeName: id, type: '🧩 in place', payload: {} }, state)
-        })
-      }
-    },
-    {
-      deep: true,
-      flush: 'sync',
-    }
-  )
-
-  function patch(partialState: DeepPartial<S>): void {
-    isListening = false
-    innerPatch(state.value, partialState)
-    isListening = true
-    subscriptions.forEach(callback => {
-      callback(
-        { storeName: id, type: '⤵️ patch', payload: partialState },
-        state.value
-      )
-    })
-  }
-
-  function subscribe(callback: SubscriptionCallback<S>): void {
-    subscriptions.push(callback)
-    // TODO: return function to remove subscription
-  }
-
-  function reset() {
-    subscriptions = []
-    state.value = buildState()
-  }
-
-  const storeWithState: Store<Id, S> = {
-    id,
-    // it is replaced below by a getter
-    state: state.value,
-
-    patch,
-    subscribe,
-    reset,
-  }
-
-  // @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,
-    set: (newState: S) => {
-      isListening = false
-      state.value = newState
-      isListening = true
-    },
-  })
-
-  // Devtools injection hue hue
-  devtoolPlugin(store)
-
-  return store
-}
-
-/**
- * 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 requests on the server
- */
-
-/**
- *
- * @param id id of the store we are creating
- * @param buildState function that returns a state
- */
-
-export function makeStore<
-  Id extends string,
-  S extends StateTree,
-  G extends Record<string, StoreGetter<S>>
->(id: Id, buildState: () => S, getters: G = {} as G) {
-  let store: CombinedStore<Id, S, G> | undefined
-
-  function useStore(): CombinedStore<Id, S, G> {
-    if (!store) store = createStore(id, buildState, getters)
-
-    return store
-  }
-
-  function clear(): void {
-    store = undefined
-  }
-
-  return {
-    useStore,
-    clear,
-  }
-}
-
-// export const store = createStore('main', initialState)
-
-// 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',
-//   nested: {
-//     a: {
-//       b: {
-//         c: 'one',
-//       },
-//     },
-//   },
-// })
+export { createStore } from './store'
diff --git a/src/store.ts b/src/store.ts
new file mode 100644 (file)
index 0000000..be2b558
--- /dev/null
@@ -0,0 +1,168 @@
+import { ref, watch, computed } from '@vue/composition-api'
+import { Ref } from '@vue/composition-api/dist/reactivity'
+import {
+  StateTree,
+  Store,
+  SubscriptionCallback,
+  DeepPartial,
+  isPlainObject,
+  StoreGetters,
+  StoreGetter,
+} from './types'
+import { devtoolPlugin } from './devtools'
+
+function innerPatch<T extends StateTree>(
+  target: T,
+  patchToApply: DeepPartial<T>
+): T {
+  // TODO: get all keys like symbols as well
+  for (const key in patchToApply) {
+    const subPatch = patchToApply[key]
+    const targetValue = target[key]
+    if (isPlainObject(targetValue) && isPlainObject(subPatch)) {
+      target[key] = innerPatch(targetValue, subPatch)
+    } else {
+      // @ts-ignore
+      target[key] = subPatch
+    }
+  }
+
+  return target
+}
+
+/**
+ * NOTE: by allowing users to name stores correctly, they can nest them the way
+ * 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 buildStore<
+  Id extends string,
+  S extends StateTree,
+  G extends Record<string, StoreGetter<S>>
+>(
+  id: Id,
+  buildState: () => S,
+  getters: G = {} as G
+  // methods: Record<string | symbol, StoreMethod>
+): CombinedStore<Id, S, G> {
+  const state: Ref<S> = ref(buildState())
+
+  let isListening = true
+  let subscriptions: SubscriptionCallback<S>[] = []
+
+  watch(
+    () => state.value,
+    state => {
+      if (isListening) {
+        subscriptions.forEach(callback => {
+          callback({ storeName: id, type: '🧩 in place', payload: {} }, state)
+        })
+      }
+    },
+    {
+      deep: true,
+      flush: 'sync',
+    }
+  )
+
+  function patch(partialState: DeepPartial<S>): void {
+    isListening = false
+    innerPatch(state.value, partialState)
+    isListening = true
+    subscriptions.forEach(callback => {
+      callback(
+        { storeName: id, type: '⤵️ patch', payload: partialState },
+        state.value
+      )
+    })
+  }
+
+  function subscribe(callback: SubscriptionCallback<S>): void {
+    subscriptions.push(callback)
+    // TODO: return function to remove subscription
+  }
+
+  function reset() {
+    subscriptions = []
+    state.value = buildState()
+  }
+
+  const storeWithState: Store<Id, S> = {
+    id,
+    // it is replaced below by a getter
+    state: state.value,
+
+    patch,
+    subscribe,
+    reset,
+  }
+
+  // @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,
+    set: (newState: S) => {
+      isListening = false
+      state.value = newState
+      isListening = true
+    },
+  })
+
+  // Devtools injection hue hue
+  devtoolPlugin(store)
+
+  return store
+}
+
+/**
+ * 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 requests on the server
+ */
+
+/**
+ * Creates a `useStore` function that retrieves the store instance
+ * @param id id of the store we are creating
+ * @param buildState function that returns a state
+ * @param getters optional object of getters
+ */
+
+export function createStore<
+  Id extends string,
+  S extends StateTree,
+  G extends Record<string, StoreGetter<S>>
+>(id: Id, buildState: () => S, getters: G = {} as G) {
+  let store: CombinedStore<Id, S, G> | undefined
+
+  return function useStore(forceNewStore = false): CombinedStore<Id, S, G> {
+    if (!store || forceNewStore) store = buildStore(id, buildState, getters)
+
+    return store
+  }
+}