@wordpress/blocks
Version:
Block API for WordPress.
170 lines (163 loc) • 7.23 kB
JavaScript
/**
* External dependencies
*/
import { isPlainObject } from 'is-plain-object';
import { isValidElementType } from 'react-is';
/**
* WordPress dependencies
*/
import deprecated from '@wordpress/deprecated';
import { applyFilters } from '@wordpress/hooks';
import warning from '@wordpress/warning';
/**
* Internal dependencies
*/
import { isValidIcon, normalizeIconObject, omit } from '../api/utils';
import { BLOCK_ICON_DEFAULT, DEPRECATED_ENTRY_KEYS } from '../api/constants';
/** @typedef {import('../api/registration').WPBlockType} WPBlockType */
/**
* Mapping of legacy category slugs to their latest normal values, used to
* accommodate updates of the default set of block categories.
*
* @type {Record<string,string>}
*/
const LEGACY_CATEGORY_MAPPING = {
common: 'text',
formatting: 'text',
layout: 'design'
};
/**
* Merge block variations bootstrapped from the server and client.
*
* When a variation is registered in both places, its properties are merged.
*
* @param {Array} bootstrappedVariations - A block type variations from the server.
* @param {Array} clientVariations - A block type variations from the client.
* @return {Array} The merged array of block variations.
*/
function mergeBlockVariations(bootstrappedVariations = [], clientVariations = []) {
const result = [...bootstrappedVariations];
clientVariations.forEach(clientVariation => {
const index = result.findIndex(bootstrappedVariation => bootstrappedVariation.name === clientVariation.name);
if (index !== -1) {
result[index] = {
...result[index],
...clientVariation
};
} else {
result.push(clientVariation);
}
});
return result;
}
/**
* Takes the unprocessed block type settings, merges them with block type metadata
* and applies all the existing filters for the registered block type.
* Next, it validates all the settings and performs additional processing to the block type definition.
*
* @param {string} name Block name.
* @param {WPBlockType} blockSettings Unprocessed block type settings.
*
* @return {WPBlockType | undefined} The block, if it has been processed and can be registered; otherwise `undefined`.
*/
export const processBlockType = (name, blockSettings) => ({
select
}) => {
const bootstrappedBlockType = select.getBootstrappedBlockType(name);
const blockType = {
apiVersion: 1,
name,
icon: BLOCK_ICON_DEFAULT,
keywords: [],
attributes: {},
providesContext: {},
usesContext: [],
selectors: {},
supports: {},
styles: [],
blockHooks: {},
save: () => null,
...bootstrappedBlockType,
...blockSettings,
// blockType.variations can be defined as a filePath.
variations: mergeBlockVariations(Array.isArray(bootstrappedBlockType?.variations) ? bootstrappedBlockType.variations : [], Array.isArray(blockSettings?.variations) ? blockSettings.variations : [])
};
const settings = applyFilters('blocks.registerBlockType', blockType, name, null);
if (settings.apiVersion <= 2) {
globalThis.SCRIPT_DEBUG === true ? warning(`The block "${name}" is registered with API version 2 or lower. This means that the post editor may work as a non-iframe editor.\n` + `Since all editors are planned to work as iframes in the future, set the \`apiVersion\` field to 3 and test the block inside the iframe editor.\n` + `See: https://developer.wordpress.org/block-editor/reference-guides/block-api/block-api-versions/#version-3-wordpress-6-3`) : void 0;
}
if (settings.description && typeof settings.description !== 'string') {
deprecated('Declaring non-string block descriptions', {
since: '6.2'
});
}
if (settings.deprecated) {
settings.deprecated = settings.deprecated.map(deprecation => Object.fromEntries(Object.entries(
// Only keep valid deprecation keys.
applyFilters('blocks.registerBlockType',
// Merge deprecation keys with pre-filter settings
// so that filters that depend on specific keys being
// present don't fail.
{
// Omit deprecation keys here so that deprecations
// can opt out of specific keys like "supports".
...omit(blockType, DEPRECATED_ENTRY_KEYS),
...deprecation
}, blockType.name, deprecation)).filter(([key]) => DEPRECATED_ENTRY_KEYS.includes(key))));
}
if (!isPlainObject(settings)) {
globalThis.SCRIPT_DEBUG === true ? warning('Block settings must be a valid object.') : void 0;
return;
}
if (typeof settings.save !== 'function') {
globalThis.SCRIPT_DEBUG === true ? warning('The "save" property must be a valid function.') : void 0;
return;
}
if ('edit' in settings && !isValidElementType(settings.edit)) {
globalThis.SCRIPT_DEBUG === true ? warning('The "edit" property must be a valid component.') : void 0;
return;
}
// Canonicalize legacy categories to equivalent fallback.
if (LEGACY_CATEGORY_MAPPING.hasOwnProperty(settings.category)) {
settings.category = LEGACY_CATEGORY_MAPPING[settings.category];
}
if ('category' in settings && !select.getCategories().some(({
slug
}) => slug === settings.category)) {
globalThis.SCRIPT_DEBUG === true ? warning('The block "' + name + '" is registered with an invalid category "' + settings.category + '".') : void 0;
delete settings.category;
}
if (!('title' in settings) || settings.title === '') {
globalThis.SCRIPT_DEBUG === true ? warning('The block "' + name + '" must have a title.') : void 0;
return;
}
if (typeof settings.title !== 'string') {
globalThis.SCRIPT_DEBUG === true ? warning('Block titles must be strings.') : void 0;
return;
}
settings.icon = normalizeIconObject(settings.icon);
if (!isValidIcon(settings.icon.src)) {
globalThis.SCRIPT_DEBUG === true ? warning('The icon passed is invalid. ' + 'The icon should be a string, an element, a function, or an object following the specifications documented in https://developer.wordpress.org/block-editor/developers/block-api/block-registration/#icon-optional') : void 0;
return;
}
if (typeof settings?.parent === 'string' || settings?.parent instanceof String) {
settings.parent = [settings.parent];
globalThis.SCRIPT_DEBUG === true ? warning('Parent must be undefined or an array of strings (block types), but it is a string.') : void 0;
// Intentionally continue:
//
// While string values were never supported, they appeared to work with some unintended side-effects
// that have been fixed by [#66250](https://github.com/WordPress/gutenberg/pull/66250).
//
// To be backwards-compatible, this code that automatically migrates strings to arrays.
}
if (!Array.isArray(settings?.parent) && settings?.parent !== undefined) {
globalThis.SCRIPT_DEBUG === true ? warning('Parent must be undefined or an array of block types, but it is ', settings.parent) : void 0;
return;
}
if (1 === settings?.parent?.length && name === settings.parent[0]) {
globalThis.SCRIPT_DEBUG === true ? warning('Block "' + name + '" cannot be a parent of itself. Please remove the block name from the parent list.') : void 0;
return;
}
return settings;
};
//# sourceMappingURL=process-block-type.js.map