@wordpress/data
Version:
Data module for WordPress.
366 lines (300 loc) • 11.2 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createRegistry = createRegistry;
var _lodash = require("lodash");
var _deprecated = _interopRequireDefault(require("@wordpress/deprecated"));
var _reduxStore = _interopRequireDefault(require("./redux-store"));
var _store = _interopRequireDefault(require("./store"));
var _emitter = require("./utils/emitter");
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
/** @typedef {import('./types').StoreDescriptor} StoreDescriptor */
/**
* @typedef {Object} WPDataRegistry An isolated orchestrator of store registrations.
*
* @property {Function} registerGenericStore Given a namespace key and settings
* object, registers a new generic
* store.
* @property {Function} registerStore Given a namespace key and settings
* object, registers a new namespace
* store.
* @property {Function} subscribe Given a function callback, invokes
* the callback on any change to state
* within any registered store.
* @property {Function} select Given a namespace key, returns an
* object of the store's registered
* selectors.
* @property {Function} dispatch Given a namespace key, returns an
* object of the store's registered
* action dispatchers.
*/
/**
* @typedef {Object} WPDataPlugin An object of registry function overrides.
*
* @property {Function} registerStore registers store.
*/
/**
* Creates a new store registry, given an optional object of initial store
* configurations.
*
* @param {Object} storeConfigs Initial store configurations.
* @param {Object?} parent Parent registry.
*
* @return {WPDataRegistry} Data registry.
*/
function createRegistry() {
let storeConfigs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
let parent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
const stores = {};
const emitter = (0, _emitter.createEmitter)();
const listeningStores = new Set();
/**
* Global listener called for each store's update.
*/
function globalListener() {
emitter.emit();
}
/**
* Subscribe to changes to any data.
*
* @param {Function} listener Listener function.
*
* @return {Function} Unsubscribe function.
*/
const subscribe = listener => {
return emitter.subscribe(listener);
};
/**
* Calls a selector given the current state and extra arguments.
*
* @param {string|StoreDescriptor} storeNameOrDescriptor Unique namespace identifier for the store
* or the store descriptor.
*
* @return {*} The selector's returned value.
*/
function select(storeNameOrDescriptor) {
const storeName = (0, _lodash.isObject)(storeNameOrDescriptor) ? storeNameOrDescriptor.name : storeNameOrDescriptor;
listeningStores.add(storeName);
const store = stores[storeName];
if (store) {
return store.getSelectors();
}
return parent === null || parent === void 0 ? void 0 : parent.select(storeName);
}
function __unstableMarkListeningStores(callback, ref) {
listeningStores.clear();
try {
return callback.call(this);
} finally {
ref.current = Array.from(listeningStores);
}
}
/**
* Given the name of a registered store, returns an object containing the store's
* selectors pre-bound to state so that you only need to supply additional arguments,
* and modified so that they return promises that resolve to their eventual values,
* after any resolvers have ran.
*
* @param {string|StoreDescriptor} storeNameOrDescriptor Unique namespace identifier for the store
* or the store descriptor.
*
* @return {Object} Each key of the object matches the name of a selector.
*/
function resolveSelect(storeNameOrDescriptor) {
const storeName = (0, _lodash.isObject)(storeNameOrDescriptor) ? storeNameOrDescriptor.name : storeNameOrDescriptor;
listeningStores.add(storeName);
const store = stores[storeName];
if (store) {
return store.getResolveSelectors();
}
return parent && parent.resolveSelect(storeName);
}
/**
* Given the name of a registered store, returns an object containing the store's
* selectors pre-bound to state so that you only need to supply additional arguments,
* and modified so that they throw promises in case the selector is not resolved yet.
*
* @param {string|StoreDescriptor} storeNameOrDescriptor Unique namespace identifier for the store
* or the store descriptor.
*
* @return {Object} Object containing the store's suspense-wrapped selectors.
*/
function suspendSelect(storeNameOrDescriptor) {
const storeName = (0, _lodash.isObject)(storeNameOrDescriptor) ? storeNameOrDescriptor.name : storeNameOrDescriptor;
listeningStores.add(storeName);
const store = stores[storeName];
if (store) {
return store.getSuspendSelectors();
}
return parent && parent.suspendSelect(storeName);
}
/**
* Returns the available actions for a part of the state.
*
* @param {string|StoreDescriptor} storeNameOrDescriptor Unique namespace identifier for the store
* or the store descriptor.
*
* @return {*} The action's returned value.
*/
function dispatch(storeNameOrDescriptor) {
const storeName = (0, _lodash.isObject)(storeNameOrDescriptor) ? storeNameOrDescriptor.name : storeNameOrDescriptor;
const store = stores[storeName];
if (store) {
return store.getActions();
}
return parent && parent.dispatch(storeName);
} //
// Deprecated
// TODO: Remove this after `use()` is removed.
function withPlugins(attributes) {
return (0, _lodash.mapValues)(attributes, (attribute, key) => {
if (typeof attribute !== 'function') {
return attribute;
}
return function () {
return registry[key].apply(null, arguments);
};
});
}
/**
* Registers a store instance.
*
* @param {string} name Store registry name.
* @param {Object} store Store instance object (getSelectors, getActions, subscribe).
*/
function registerStoreInstance(name, store) {
if (typeof store.getSelectors !== 'function') {
throw new TypeError('store.getSelectors must be a function');
}
if (typeof store.getActions !== 'function') {
throw new TypeError('store.getActions must be a function');
}
if (typeof store.subscribe !== 'function') {
throw new TypeError('store.subscribe must be a function');
} // The emitter is used to keep track of active listeners when the registry
// get paused, that way, when resumed we should be able to call all these
// pending listeners.
store.emitter = (0, _emitter.createEmitter)();
const currentSubscribe = store.subscribe;
store.subscribe = listener => {
const unsubscribeFromEmitter = store.emitter.subscribe(listener);
const unsubscribeFromStore = currentSubscribe(() => {
if (store.emitter.isPaused) {
store.emitter.emit();
return;
}
listener();
});
return () => {
unsubscribeFromStore === null || unsubscribeFromStore === void 0 ? void 0 : unsubscribeFromStore();
unsubscribeFromEmitter === null || unsubscribeFromEmitter === void 0 ? void 0 : unsubscribeFromEmitter();
};
};
stores[name] = store;
store.subscribe(globalListener);
}
/**
* Registers a new store given a store descriptor.
*
* @param {StoreDescriptor} store Store descriptor.
*/
function register(store) {
registerStoreInstance(store.name, store.instantiate(registry));
}
function registerGenericStore(name, store) {
(0, _deprecated.default)('wp.data.registerGenericStore', {
since: '5.9',
alternative: 'wp.data.register( storeDescriptor )'
});
registerStoreInstance(name, store);
}
/**
* Registers a standard `@wordpress/data` store.
*
* @param {string} storeName Unique namespace identifier.
* @param {Object} options Store description (reducer, actions, selectors, resolvers).
*
* @return {Object} Registered store object.
*/
function registerStore(storeName, options) {
if (!options.reducer) {
throw new TypeError('Must specify store reducer');
}
const store = (0, _reduxStore.default)(storeName, options).instantiate(registry);
registerStoreInstance(storeName, store);
return store.store;
}
/**
* Subscribe handler to a store.
*
* @param {string[]} storeName The store name.
* @param {Function} handler The function subscribed to the store.
* @return {Function} A function to unsubscribe the handler.
*/
function __unstableSubscribeStore(storeName, handler) {
if (storeName in stores) {
return stores[storeName].subscribe(handler);
} // Trying to access a store that hasn't been registered,
// this is a pattern rarely used but seen in some places.
// We fallback to regular `subscribe` here for backward-compatibility for now.
// See https://github.com/WordPress/gutenberg/pull/27466 for more info.
if (!parent) {
return subscribe(handler);
}
return parent.__unstableSubscribeStore(storeName, handler);
}
function batch(callback) {
emitter.pause();
(0, _lodash.forEach)(stores, store => store.emitter.pause());
callback();
emitter.resume();
(0, _lodash.forEach)(stores, store => store.emitter.resume());
}
let registry = {
batch,
stores,
namespaces: stores,
// TODO: Deprecate/remove this.
subscribe,
select,
resolveSelect,
suspendSelect,
dispatch,
use,
register,
registerGenericStore,
registerStore,
__unstableMarkListeningStores,
__unstableSubscribeStore
}; //
// TODO:
// This function will be deprecated as soon as it is no longer internally referenced.
function use(plugin, options) {
if (!plugin) {
return;
}
registry = { ...registry,
...plugin(registry, options)
};
return registry;
}
registry.register(_store.default);
for (const [name, config] of Object.entries(storeConfigs)) {
registry.register((0, _reduxStore.default)(name, config));
}
if (parent) {
parent.subscribe(globalListener);
}
return withPlugins(registry);
}
//# sourceMappingURL=registry.js.map