UNPKG

@wordpress/data

Version:
212 lines (198 loc) 6.69 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.createPersistenceInterface = createPersistenceInterface; exports.withLazySameState = exports.default = void 0; var _isPlainObject = require("is-plain-object"); var _deepmerge = _interopRequireDefault(require("deepmerge")); var _default2 = _interopRequireDefault(require("./storage/default")); var _ = require("../../"); /** * External dependencies */ /** * Internal dependencies */ /** @typedef {import('../../registry').WPDataRegistry} WPDataRegistry */ /** @typedef {import('../../registry').WPDataPlugin} WPDataPlugin */ /** * @typedef {Object} WPDataPersistencePluginOptions Persistence plugin options. * * @property {Storage} storage Persistent storage implementation. This must * at least implement `getItem` and `setItem` of * the Web Storage API. * @property {string} storageKey Key on which to set in persistent storage. */ /** * Default plugin storage. * * @type {Storage} */ const DEFAULT_STORAGE = _default2.default; /** * Default plugin storage key. * * @type {string} */ const DEFAULT_STORAGE_KEY = 'WP_DATA'; /** * Higher-order reducer which invokes the original reducer only if state is * inequal from that of the action's `nextState` property, otherwise returning * the original state reference. * * @param {Function} reducer Original reducer. * * @return {Function} Enhanced reducer. */ const withLazySameState = reducer => (state, action) => { if (action.nextState === state) { return state; } return reducer(state, action); }; /** * Creates a persistence interface, exposing getter and setter methods (`get` * and `set` respectively). * * @param {WPDataPersistencePluginOptions} options Plugin options. * * @return {Object} Persistence interface. */ exports.withLazySameState = withLazySameState; function createPersistenceInterface(options) { const { storage = DEFAULT_STORAGE, storageKey = DEFAULT_STORAGE_KEY } = options; let data; /** * Returns the persisted data as an object, defaulting to an empty object. * * @return {Object} Persisted data. */ function getData() { if (data === undefined) { // If unset, getItem is expected to return null. Fall back to // empty object. const persisted = storage.getItem(storageKey); if (persisted === null) { data = {}; } else { try { data = JSON.parse(persisted); } catch (error) { // Similarly, should any error be thrown during parse of // the string (malformed JSON), fall back to empty object. data = {}; } } } return data; } /** * Merges an updated reducer state into the persisted data. * * @param {string} key Key to update. * @param {*} value Updated value. */ function setData(key, value) { data = { ...data, [key]: value }; storage.setItem(storageKey, JSON.stringify(data)); } return { get: getData, set: setData }; } /** * Data plugin to persist store state into a single storage key. * * @param {WPDataRegistry} registry Data registry. * @param {?WPDataPersistencePluginOptions} pluginOptions Plugin options. * * @return {WPDataPlugin} Data plugin. */ function persistencePlugin(registry, pluginOptions) { const persistence = createPersistenceInterface(pluginOptions); /** * Creates an enhanced store dispatch function, triggering the state of the * given store name to be persisted when changed. * * @param {Function} getState Function which returns current state. * @param {string} storeName Store name. * @param {?Array<string>} keys Optional subset of keys to save. * * @return {Function} Enhanced dispatch function. */ function createPersistOnChange(getState, storeName, keys) { let getPersistedState; if (Array.isArray(keys)) { // Given keys, the persisted state should by produced as an object // of the subset of keys. This implementation uses combineReducers // to leverage its behavior of returning the same object when none // of the property values changes. This allows a strict reference // equality to bypass a persistence set on an unchanging state. const reducers = keys.reduce((accumulator, key) => Object.assign(accumulator, { [key]: (state, action) => action.nextState[key] }), {}); getPersistedState = withLazySameState((0, _.combineReducers)(reducers)); } else { getPersistedState = (state, action) => action.nextState; } let lastState = getPersistedState(undefined, { nextState: getState() }); return () => { const state = getPersistedState(lastState, { nextState: getState() }); if (state !== lastState) { persistence.set(storeName, state); lastState = state; } }; } return { registerStore(storeName, options) { if (!options.persist) { return registry.registerStore(storeName, options); } // Load from persistence to use as initial state. const persistedState = persistence.get()[storeName]; if (persistedState !== undefined) { let initialState = options.reducer(options.initialState, { type: '@@WP/PERSISTENCE_RESTORE' }); if ((0, _isPlainObject.isPlainObject)(initialState) && (0, _isPlainObject.isPlainObject)(persistedState)) { // If state is an object, ensure that: // - Other keys are left intact when persisting only a // subset of keys. // - New keys in what would otherwise be used as initial // state are deeply merged as base for persisted value. initialState = (0, _deepmerge.default)(initialState, persistedState, { isMergeableObject: _isPlainObject.isPlainObject }); } else { // If there is a mismatch in object-likeness of default // initial or persisted state, defer to persisted value. initialState = persistedState; } options = { ...options, initialState }; } const store = registry.registerStore(storeName, options); store.subscribe(createPersistOnChange(store.getState, storeName, options.persist)); return store; } }; } persistencePlugin.__unstableMigrate = () => {}; var _default = exports.default = persistencePlugin; //# sourceMappingURL=index.js.map