: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
function moveTo(array, from, to, count = 1) {
const withoutMovedElements = [...array];
withoutMovedElements.splice(from, count);
return insertAt(withoutMovedElements, array.slice(from, from + count), to);
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/store/reducer.js
* Given an array of blocks, returns an object where each key is a nesting
* context, the value of which is an array of block client IDs existing within
* @param {Array} blocks Blocks to map.
* @param {?string} rootClientId Assumed root client ID.
* @return {Object} Block order map object.
function mapBlockOrder(blocks, rootClientId = '') {
const result = new Map();
result.set(rootClientId, current);
blocks.forEach(block => {
mapBlockOrder(innerBlocks, clientId).forEach((order, subClientId) => {
result.set(subClientId, order);
* Given an array of blocks, returns an object where each key contains
* the clientId of the block and the value is the parent of the block.
* @param {Array} blocks Blocks to map.
* @param {?string} rootClientId Assumed root client ID.
* @return {Object} Block order map object.
function mapBlockParents(blocks, rootClientId = '') {
const stack = [[rootClientId, blocks]];
const [parent, currentBlocks] = stack.shift();
result.push([block.clientId, parent]);
if (innerBlocks?.length) {
stack.push([block.clientId, innerBlocks]);
* Helper method to iterate through all blocks, recursing into inner blocks,
* applying a transformation function to each one.
* Returns a flattened object with the transformed blocks.
* @param {Array} blocks Blocks to flatten.
* @param {Function} transform Transforming function to be applied to each block.
* @return {Array} Flattened object.
function flattenBlocks(blocks, transform = identity) {
const stack = [...blocks];
stack.push(...innerBlocks);
result.push([block.clientId, transform(block)]);
function getFlattenedClientIds(blocks) {
const stack = [...blocks];
stack.push(...innerBlocks);
result[block.clientId] = true;
* Given an array of blocks, returns an object containing all blocks, without
* attributes, recursing into inner blocks. Keys correspond to the block client
* ID, the value of which is the attributes object.
* @param {Array} blocks Blocks to flatten.
* @return {Array} Flattened block attributes object.
function getFlattenedBlocksWithoutAttributes(blocks) {
return flattenBlocks(blocks, block => {
* Given an array of blocks, returns an object containing all block attributes,
* recursing into inner blocks. Keys correspond to the block client ID, the
* value of which is the attributes object.
* @param {Array} blocks Blocks to flatten.
* @return {Array} Flattened block attributes object.
function getFlattenedBlockAttributes(blocks) {
return flattenBlocks(blocks, block => block.attributes);
* Returns true if the two object arguments have the same keys, or false
* @param {Object} a First object.
* @param {Object} b Second object.
* @return {boolean} Whether the two objects have the same keys.
function hasSameKeys(a, b) {
return es6_default()(Object.keys(a), Object.keys(b));
* Returns true if, given the currently dispatching action and the previously
* dispatched action, the two actions are updating the same block attribute, or
* @param {Object} action Currently dispatching action.
* @param {Object} lastAction Previously dispatched action.
* @return {boolean} Whether actions are updating the same block attribute.
function isUpdatingSameBlockAttribute(action, lastAction) {
return action.type === 'UPDATE_BLOCK_ATTRIBUTES' && lastAction !== undefined && lastAction.type === 'UPDATE_BLOCK_ATTRIBUTES' && es6_default()(action.clientIds, lastAction.clientIds) && hasSameKeys(action.attributes, lastAction.attributes);
function updateBlockTreeForBlocks(state, blocks) {
const treeToUpdate = state.tree;
const stack = [...blocks];
const flattenedBlocks = [...blocks];
const block = stack.shift();
stack.push(...block.innerBlocks);
flattenedBlocks.push(...block.innerBlocks);
// Create objects before mutating them, that way it's always defined.
for (const block of flattenedBlocks) {
treeToUpdate.set(block.clientId, {});
for (const block of flattenedBlocks) {
treeToUpdate.set(block.clientId, Object.assign(treeToUpdate.get(block.clientId), {
...state.byClientId.get(block.clientId),
attributes: state.attributes.get(block.clientId),
innerBlocks: block.innerBlocks.map(subBlock => treeToUpdate.get(subBlock.clientId))
function updateParentInnerBlocksInTree(state, updatedClientIds, updateChildrenOfUpdatedClientIds = false) {
const treeToUpdate = state.tree;
const uncontrolledParents = new Set([]);
const controlledParents = new Set();
for (const clientId of updatedClientIds) {
let current = updateChildrenOfUpdatedClientIds ? clientId : state.parents.get(clientId);
if (state.controlledInnerBlocks[current]) {
// Should stop on controlled blocks.
// If we reach a controlled parent, break out of the loop.
controlledParents.add(current);
// Else continue traversing up through parents.
uncontrolledParents.add(current);
current = state.parents.get(current);
} while (current !== undefined);
// To make sure the order of assignments doesn't matter,
// we first create empty objects and mutates the inner blocks later.
for (const clientId of uncontrolledParents) {
treeToUpdate.set(clientId, {
...treeToUpdate.get(clientId)
for (const clientId of uncontrolledParents) {
treeToUpdate.get(clientId).innerBlocks = (state.order.get(clientId) || []).map(subClientId => treeToUpdate.get(subClientId));
// Controlled parent blocks, need a dedicated key for their inner blocks
// to be used when doing getBlocks( controlledBlockClientId ).
for (const clientId of controlledParents) {
treeToUpdate.set('controlled||' + clientId, {
innerBlocks: (state.order.get(clientId) || []).map(subClientId => treeToUpdate.get(subClientId))
* Higher-order reducer intended to compute full block objects key for each block in the post.
* This is a denormalization to optimize the performance of the getBlock selectors and avoid
* recomputing the block objects and avoid heavy memoization.
* @param {Function} reducer Original reducer function.
* @return {Function} Enhanced reducer function.
const withBlockTree = reducer => (state = {}, action) => {
const newState = reducer(state, action);
if (newState === state) {
newState.tree = state.tree ? state.tree : new Map();
newState.tree = new Map(newState.tree);
updateBlockTreeForBlocks(newState, action.blocks);
updateParentInnerBlocksInTree(newState, action.rootClientId ? [action.rootClientId] : [''], true);
newState.tree = new Map(newState.tree);
newState.tree.set(action.clientId, {
...newState.tree.get(action.clientId),
...newState.byClientId.get(action.clientId),
attributes: newState.attributes.get(action.clientId)
updateParentInnerBlocksInTree(newState, [action.clientId], false);
case 'SYNC_DERIVED_BLOCK_ATTRIBUTES':
case 'UPDATE_BLOCK_ATTRIBUTES':
newState.tree = new Map(newState.tree);
action.clientIds.forEach(clientId => {
newState.tree.set(clientId, {
...newState.tree.get(clientId),
attributes: newState.attributes.get(clientId)
updateParentInnerBlocksInTree(newState, action.clientIds, false);
case 'REPLACE_BLOCKS_AUGMENTED_WITH_CHILDREN':
const inserterClientIds = getFlattenedClientIds(action.blocks);
newState.tree = new Map(newState.tree);
action.replacedClientIds.forEach(clientId => {
newState.tree.delete(clientId);
// Controlled inner blocks are only removed
// if the block doesn't move to another position
// otherwise their content will be lost.
if (!inserterClientIds[clientId]) {
newState.tree.delete('controlled||' + clientId);
updateBlockTreeForBlocks(newState, action.blocks);
updateParentInnerBlocksInTree(newState, action.blocks.map(b => b.clientId), false);
// If there are no replaced blocks, it means we're removing blocks so we need to update their parent.
const parentsOfRemovedBlocks = [];
for (const clientId of action.clientIds) {
const parentId = state.parents.get(clientId);
if (parentId !== undefined && (parentId === '' || newState.byClientId.get(parentId))) {
parentsOfRemovedBlocks.push(parentId);
updateParentInnerBlocksInTree(newState, parentsOfRemovedBlocks, true);
case 'REMOVE_BLOCKS_AUGMENTED_WITH_CHILDREN':
const parentsOfRemovedBlocks = [];
for (const clientId of action.clientIds) {
const parentId = state.parents.get(clientId);
if (parentId !== undefined && (parentId === '' || newState.byClientId.get(parentId))) {
parentsOfRemovedBlocks.push(parentId);
newState.tree = new Map(newState.tree);
action.removedClientIds.forEach(clientId => {
newState.tree.delete(clientId);
newState.tree.delete('controlled||' + clientId);
updateParentInnerBlocksInTree(newState, parentsOfRemovedBlocks, true);
case 'MOVE_BLOCKS_TO_POSITION':
const updatedBlockUids = [];
if (action.fromRootClientId) {
updatedBlockUids.push(action.fromRootClientId);
updatedBlockUids.push('');
if (action.toRootClientId) {
updatedBlockUids.push(action.toRootClientId);
newState.tree = new Map(newState.tree);
updateParentInnerBlocksInTree(newState, updatedBlockUids, true);
const updatedBlockUids = [action.rootClientId ? action.rootClientId : ''];
newState.tree = new Map(newState.tree);
updateParentInnerBlocksInTree(newState, updatedBlockUids, true);
case 'SAVE_REUSABLE_BLOCK_SUCCESS':
const updatedBlockUids = [];
newState.attributes.forEach((attributes, clientId) => {
if (newState.byClientId.get(clientId).name === 'core/block' && attributes.ref === action.updatedId) {
updatedBlockUids.push(clientId);
newState.tree = new Map(newState.tree);
updatedBlockUids.forEach(clientId => {
newState.tree.set(clientId, {
...newState.byClientId.get(clientId),
attributes: newState.attributes.get(clientId),
innerBlocks: newState.tree.get(clientId).innerBlocks
updateParentInnerBlocksInTree(newState, updatedBlockUids, false);
* Higher-order reducer intended to augment the blocks reducer, assigning an
* `isPersistentChange` property value corresponding to whether a change in
* state can be considered as persistent. All changes are considered persistent
* except when updating the same block attribute as in the previous action.
* @param {Function} reducer Original reducer function.
* @return {Function} Enhanced reducer function.
function withPersistentBlockChange(reducer) {
let markNextChangeAsNotPersistent = false;
return (state, action) => {
let nextState = reducer(state, action);
let nextIsPersistentChange;
if (action.type === 'SET_EXPLICIT_PERSISTENT') {
var _state$isPersistentCh;
explicitPersistent = action.isPersistentChange;
nextIsPersistentChange = (_state$isPersistentCh = state.isPersistentChange) !== null && _state$isPersistentCh !== void 0 ? _state$isPersistentCh : true;
if (explicitPersistent !== undefined) {
nextIsPersistentChange = explicitPersistent;
return nextIsPersistentChange === nextState.isPersistentChange ? nextState : {
isPersistentChange: nextIsPersistentChange
const isExplicitPersistentChange = action.type === 'MARK_LAST_CHANGE_AS_PERSISTENT' || markNextChangeAsNotPersistent;
// Defer to previous state value (or default) unless changing or
// explicitly marking as persistent.
if (state === nextState && !isExplicitPersistentChange) {
var _state$isPersistentCh2;
markNextChangeAsNotPersistent = action.type === 'MARK_NEXT_CHANGE_AS_NOT_PERSISTENT';
nextIsPersistentChange = (_state$isPersistentCh2 = state?.isPersistentChange) !== null && _state$isPersistentCh2 !== void 0 ? _state$isPersistentCh2 : true;
if (state.isPersistentChange === nextIsPersistentChange) {
isPersistentChange: nextIsPersistentChange
isPersistentChange: isExplicitPersistentChange ? !markNextChangeAsNotPersistent : !isUpdatingSameBlockAttribute(action, lastAction)
// In comparing against the previous action, consider only those which
// would have qualified as one which would have been ignored or not
// have resulted in a changed state.
markNextChangeAsNotPersistent = action.type === 'MARK_NEXT_CHANGE_AS_NOT_PERSISTENT';
* Higher-order reducer intended to augment the blocks reducer, assigning an
* `isIgnoredChange` property value corresponding to whether a change in state
* can be considered as ignored. A change is considered ignored when the result
* of an action not incurred by direct user interaction.
* @param {Function} reducer Original reducer function.
* @return {Function} Enhanced reducer function.
function withIgnoredBlockChange(reducer) {
* Set of action types for which a blocks state change should be ignored.
const IGNORED_ACTION_TYPES = new Set(['RECEIVE_BLOCKS']);
return (state, action) => {
const nextState = reducer(state, action);
if (nextState !== state) {
nextState.isIgnoredChange = IGNORED_ACTION_TYPES.has(action.type);
* Higher-order reducer targeting the combined blocks reducer, augmenting
* block client IDs in remove action to include cascade of inner blocks.
* @param {Function} reducer Original reducer function.
* @return {Function} Enhanced reducer function.
const withInnerBlocksRemoveCascade = reducer => (state, action) => {
// Gets all children which need to be removed.
const getAllChildren = clientIds => {
for (let i = 0; i < result.length; i++) {
if (!state.order.get(result[i]) || action.keepControlledInnerBlocks && action.keepControlledInnerBlocks[result[i]]) {
if (result === clientIds) {
result.push(...state.order.get(result[i]));
type: 'REMOVE_BLOCKS_AUGMENTED_WITH_CHILDREN',
removedClientIds: getAllChildren(action.clientIds)
type: 'REPLACE_BLOCKS_AUGMENTED_WITH_CHILDREN',
replacedClientIds: getAllChildren(action.clientIds)
return reducer(state, action);
* Higher-order reducer which targets the combined blocks reducer and handles
* the `RESET_BLOCKS` action. When dispatched, this action will replace all