index.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import { Store, MutationPayload } from "vuex";
  2. import merge from "deepmerge";
  3. import * as shvl from "shvl";
  4. interface Storage {
  5. getItem: (key: string) => any;
  6. setItem: (key: string, value: any) => void;
  7. removeItem: (key: string) => void;
  8. }
  9. interface Options<State> {
  10. key?: string;
  11. paths?: string[];
  12. reducer?: (state: State, paths: string[]) => object;
  13. subscriber?: (
  14. store: Store<State>
  15. ) => (handler: (mutation: any, state: State) => void) => void;
  16. storage?: Storage;
  17. getState?: (key: string, storage: Storage) => any;
  18. setState?: (key: string, state: any, storage: Storage) => void;
  19. filter?: (mutation: MutationPayload) => boolean;
  20. arrayMerger?: (state: any[], saved: any[]) => any;
  21. rehydrated?: (store: Store<State>) => void;
  22. fetchBeforeUse?: boolean;
  23. overwrite?: boolean;
  24. assertStorage?: (storage: Storage) => void | Error;
  25. }
  26. export default function <State>(
  27. options?: Options<State>
  28. ): (store: Store<State>) => void {
  29. options = options || {};
  30. const storage = options.storage || (window && window.localStorage);
  31. const key = options.key || "vuex";
  32. function getState(key, storage) {
  33. const value = storage.getItem(key);
  34. try {
  35. return (typeof value === "string")
  36. ? JSON.parse(value) : (typeof value === "object")
  37. ? value : undefined;
  38. } catch (err) {}
  39. return undefined;
  40. }
  41. function filter() {
  42. return true;
  43. }
  44. function setState(key, state, storage) {
  45. return storage.setItem(key, JSON.stringify(state));
  46. }
  47. function reducer(state, paths) {
  48. return Array.isArray(paths)
  49. ? paths.reduce(function (substate, path) {
  50. return shvl.set(substate, path, shvl.get(state, path));
  51. }, {})
  52. : state;
  53. }
  54. function subscriber(store) {
  55. return function (handler) {
  56. return store.subscribe(handler);
  57. };
  58. }
  59. const assertStorage =
  60. options.assertStorage ||
  61. (() => {
  62. storage.setItem("@@", 1);
  63. storage.removeItem("@@");
  64. });
  65. assertStorage(storage);
  66. const fetchSavedState = () => (options.getState || getState)(key, storage);
  67. let savedState;
  68. if (options.fetchBeforeUse) {
  69. savedState = fetchSavedState();
  70. }
  71. return function (store: Store<State>) {
  72. if (!options.fetchBeforeUse) {
  73. savedState = fetchSavedState();
  74. }
  75. if (typeof savedState === "object" && savedState !== null) {
  76. store.replaceState(
  77. options.overwrite
  78. ? savedState
  79. : merge(store.state, savedState, {
  80. arrayMerge:
  81. options.arrayMerger ||
  82. function (store, saved) {
  83. return saved;
  84. },
  85. clone: false,
  86. })
  87. );
  88. (options.rehydrated || function () {})(store);
  89. }
  90. (options.subscriber || subscriber)(store)(function (mutation, state) {
  91. if ((options.filter || filter)(mutation)) {
  92. (options.setState || setState)(
  93. key,
  94. (options.reducer || reducer)(state, options.paths),
  95. storage
  96. );
  97. }
  98. });
  99. };
  100. }