UNPKG

@wordpress/data

Version:
8 lines (7 loc) 8.87 kB
{ "version": 3, "sources": ["../../../src/plugins/persistence/index.js"], "sourcesContent": ["/**\n * External dependencies\n */\nimport { isPlainObject } from 'is-plain-object';\nimport deepmerge from 'deepmerge';\n\n/**\n * Internal dependencies\n */\nimport defaultStorage from './storage/default';\nimport { combineReducers } from '../../';\n\n/** @typedef {import('../../registry').WPDataRegistry} WPDataRegistry */\n\n/** @typedef {import('../../registry').WPDataPlugin} WPDataPlugin */\n\n/**\n * @typedef {Object} WPDataPersistencePluginOptions Persistence plugin options.\n *\n * @property {Storage} storage Persistent storage implementation. This must\n * at least implement `getItem` and `setItem` of\n * the Web Storage API.\n * @property {string} storageKey Key on which to set in persistent storage.\n */\n\n/**\n * Default plugin storage.\n *\n * @type {Storage}\n */\nconst DEFAULT_STORAGE = defaultStorage;\n\n/**\n * Default plugin storage key.\n *\n * @type {string}\n */\nconst DEFAULT_STORAGE_KEY = 'WP_DATA';\n\n/**\n * Higher-order reducer which invokes the original reducer only if state is\n * inequal from that of the action's `nextState` property, otherwise returning\n * the original state reference.\n *\n * @param {Function} reducer Original reducer.\n *\n * @return {Function} Enhanced reducer.\n */\nexport const withLazySameState = ( reducer ) => ( state, action ) => {\n\tif ( action.nextState === state ) {\n\t\treturn state;\n\t}\n\n\treturn reducer( state, action );\n};\n\n/**\n * Creates a persistence interface, exposing getter and setter methods (`get`\n * and `set` respectively).\n *\n * @param {WPDataPersistencePluginOptions} options Plugin options.\n *\n * @return {Object} Persistence interface.\n */\nexport function createPersistenceInterface( options ) {\n\tconst { storage = DEFAULT_STORAGE, storageKey = DEFAULT_STORAGE_KEY } =\n\t\toptions;\n\n\tlet data;\n\n\t/**\n\t * Returns the persisted data as an object, defaulting to an empty object.\n\t *\n\t * @return {Object} Persisted data.\n\t */\n\tfunction getData() {\n\t\tif ( data === undefined ) {\n\t\t\t// If unset, getItem is expected to return null. Fall back to\n\t\t\t// empty object.\n\t\t\tconst persisted = storage.getItem( storageKey );\n\t\t\tif ( persisted === null ) {\n\t\t\t\tdata = {};\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tdata = JSON.parse( persisted );\n\t\t\t\t} catch ( error ) {\n\t\t\t\t\t// Similarly, should any error be thrown during parse of\n\t\t\t\t\t// the string (malformed JSON), fall back to empty object.\n\t\t\t\t\tdata = {};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn data;\n\t}\n\n\t/**\n\t * Merges an updated reducer state into the persisted data.\n\t *\n\t * @param {string} key Key to update.\n\t * @param {*} value Updated value.\n\t */\n\tfunction setData( key, value ) {\n\t\tdata = { ...data, [ key ]: value };\n\t\tstorage.setItem( storageKey, JSON.stringify( data ) );\n\t}\n\n\treturn {\n\t\tget: getData,\n\t\tset: setData,\n\t};\n}\n\n/**\n * Data plugin to persist store state into a single storage key.\n *\n * @param {WPDataRegistry} registry Data registry.\n * @param {?WPDataPersistencePluginOptions} pluginOptions Plugin options.\n *\n * @return {WPDataPlugin} Data plugin.\n */\nfunction persistencePlugin( registry, pluginOptions ) {\n\tconst persistence = createPersistenceInterface( pluginOptions );\n\n\t/**\n\t * Creates an enhanced store dispatch function, triggering the state of the\n\t * given store name to be persisted when changed.\n\t *\n\t * @param {Function} getState Function which returns current state.\n\t * @param {string} storeName Store name.\n\t * @param {?Array<string>} keys Optional subset of keys to save.\n\t *\n\t * @return {Function} Enhanced dispatch function.\n\t */\n\tfunction createPersistOnChange( getState, storeName, keys ) {\n\t\tlet getPersistedState;\n\t\tif ( Array.isArray( keys ) ) {\n\t\t\t// Given keys, the persisted state should by produced as an object\n\t\t\t// of the subset of keys. This implementation uses combineReducers\n\t\t\t// to leverage its behavior of returning the same object when none\n\t\t\t// of the property values changes. This allows a strict reference\n\t\t\t// equality to bypass a persistence set on an unchanging state.\n\t\t\tconst reducers = keys.reduce(\n\t\t\t\t( accumulator, key ) =>\n\t\t\t\t\tObject.assign( accumulator, {\n\t\t\t\t\t\t[ key ]: ( state, action ) => action.nextState[ key ],\n\t\t\t\t\t} ),\n\t\t\t\t{}\n\t\t\t);\n\n\t\t\tgetPersistedState = withLazySameState(\n\t\t\t\tcombineReducers( reducers )\n\t\t\t);\n\t\t} else {\n\t\t\tgetPersistedState = ( state, action ) => action.nextState;\n\t\t}\n\n\t\tlet lastState = getPersistedState( undefined, {\n\t\t\tnextState: getState(),\n\t\t} );\n\n\t\treturn () => {\n\t\t\tconst state = getPersistedState( lastState, {\n\t\t\t\tnextState: getState(),\n\t\t\t} );\n\t\t\tif ( state !== lastState ) {\n\t\t\t\tpersistence.set( storeName, state );\n\t\t\t\tlastState = state;\n\t\t\t}\n\t\t};\n\t}\n\n\treturn {\n\t\tregisterStore( storeName, options ) {\n\t\t\tif ( ! options.persist ) {\n\t\t\t\treturn registry.registerStore( storeName, options );\n\t\t\t}\n\n\t\t\t// Load from persistence to use as initial state.\n\t\t\tconst persistedState = persistence.get()[ storeName ];\n\t\t\tif ( persistedState !== undefined ) {\n\t\t\t\tlet initialState = options.reducer( options.initialState, {\n\t\t\t\t\ttype: '@@WP/PERSISTENCE_RESTORE',\n\t\t\t\t} );\n\n\t\t\t\tif (\n\t\t\t\t\tisPlainObject( initialState ) &&\n\t\t\t\t\tisPlainObject( persistedState )\n\t\t\t\t) {\n\t\t\t\t\t// If state is an object, ensure that:\n\t\t\t\t\t// - Other keys are left intact when persisting only a\n\t\t\t\t\t// subset of keys.\n\t\t\t\t\t// - New keys in what would otherwise be used as initial\n\t\t\t\t\t// state are deeply merged as base for persisted value.\n\t\t\t\t\tinitialState = deepmerge( initialState, persistedState, {\n\t\t\t\t\t\tisMergeableObject: isPlainObject,\n\t\t\t\t\t} );\n\t\t\t\t} else {\n\t\t\t\t\t// If there is a mismatch in object-likeness of default\n\t\t\t\t\t// initial or persisted state, defer to persisted value.\n\t\t\t\t\tinitialState = persistedState;\n\t\t\t\t}\n\n\t\t\t\toptions = {\n\t\t\t\t\t...options,\n\t\t\t\t\tinitialState,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst store = registry.registerStore( storeName, options );\n\n\t\t\tstore.subscribe(\n\t\t\t\tcreatePersistOnChange(\n\t\t\t\t\tstore.getState,\n\t\t\t\t\tstoreName,\n\t\t\t\t\toptions.persist\n\t\t\t\t)\n\t\t\t);\n\n\t\t\treturn store;\n\t\t},\n\t};\n}\n\npersistencePlugin.__unstableMigrate = () => {};\n\nexport default persistencePlugin;\n"], "mappings": "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", "names": ["defaultStorage", "deepmerge"] }