Blocks: Allow registering multiple items for all supported asset types

Follow-up #54337, [52069]. Part of https://github.com/WordPress/gutenberg/issues/41236. More details in https://github.com/WordPress/gutenberg/issues/33542.

Allow passing more than one script per block for `editorScript`, `script`, and `viewScript` fields in the `block.json` metadata file. This aligns with the previously added changes for `style` and `editorStyle` fields.

This change impacts the `WP_Block_Type` class and the REST API endpoint for block types. To ensure backward compatibiliy old names were soft deprecated in favor of new fields that work with array values and have `_handles` suffix.

Props zieladam, dlh, timothyblynjacobs, aristath, bernhard-reiter.
Fixes #56408.


Built from https://develop.svn.wordpress.org/trunk@54155


git-svn-id: http://core.svn.wordpress.org/trunk@53714 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
gziolo 2022-09-14 10:52:08 +00:00
parent 1f47c2bcbf
commit 714e57b2fc
8 changed files with 410 additions and 220 deletions

View File

@ -324,17 +324,16 @@ function _wp_get_iframed_editor_assets() {
$block_registry = WP_Block_Type_Registry::get_instance();
foreach ( $block_registry->get_all_registered() as $block_type ) {
if ( ! empty( $block_type->style ) ) {
$style_handles[] = $block_type->style;
}
$style_handles = array_merge(
$style_handles,
$block_type->style_handles,
$block_type->editor_style_handles
);
if ( ! empty( $block_type->editor_style ) ) {
$style_handles[] = $block_type->editor_style;
}
if ( ! empty( $block_type->script ) ) {
$script_handles[] = $block_type->script;
}
$script_handles = array_merge(
$script_handles,
$block_type->script_handles
);
}
$style_handles = array_unique( $style_handles );

View File

@ -35,12 +35,15 @@ function remove_block_asset_path_prefix( $asset_handle_or_path ) {
* and the field name provided.
*
* @since 5.5.0
* @since 6.1.0 Added `$index` parameter.
*
* @param string $block_name Name of the block.
* @param string $field_name Name of the metadata field.
* @param int $index Optional. Index of the asset when multiple items passed.
* Default 0.
* @return string Generated asset name for the block's field.
*/
function generate_block_asset_handle( $block_name, $field_name ) {
function generate_block_asset_handle( $block_name, $field_name, $index = 0 ) {
if ( 0 === strpos( $block_name, 'core/' ) ) {
$asset_handle = str_replace( 'core/', 'wp-block-', $block_name );
if ( 0 === strpos( $field_name, 'editor' ) ) {
@ -49,6 +52,9 @@ function generate_block_asset_handle( $block_name, $field_name ) {
if ( 0 === strpos( $field_name, 'view' ) ) {
$asset_handle .= '-view';
}
if ( $index > 0 ) {
$asset_handle .= '-' . ( $index + 1 );
}
return $asset_handle;
}
@ -59,8 +65,12 @@ function generate_block_asset_handle( $block_name, $field_name ) {
'editorStyle' => 'editor-style',
'style' => 'style',
);
return str_replace( '/', '-', $block_name ) .
$asset_handle = str_replace( '/', '-', $block_name ) .
'-' . $field_mappings[ $field_name ];
if ( $index > 0 ) {
$asset_handle .= '-' . ( $index + 1 );
}
return $asset_handle;
}
/**
@ -70,23 +80,34 @@ function generate_block_asset_handle( $block_name, $field_name ) {
* generated handle name. It returns unprocessed script handle otherwise.
*
* @since 5.5.0
* @since 6.1.0 Added `$index` parameter.
*
* @param array $metadata Block metadata.
* @param string $field_name Field name to pick from metadata.
* @param int $index Optional. Index of the script to register when multiple items passed.
* Default 0.
* @return string|false Script handle provided directly or created through
* script's registration, or false on failure.
*/
function register_block_script_handle( $metadata, $field_name ) {
function register_block_script_handle( $metadata, $field_name, $index = 0 ) {
if ( empty( $metadata[ $field_name ] ) ) {
return false;
}
$script_handle = $metadata[ $field_name ];
$script_path = remove_block_asset_path_prefix( $metadata[ $field_name ] );
if ( is_array( $script_handle ) ) {
if ( empty( $script_handle[ $index ] ) ) {
return false;
}
$script_handle = $script_handle[ $index ];
}
$script_path = remove_block_asset_path_prefix( $script_handle );
if ( $script_handle === $script_path ) {
return $script_handle;
}
$script_handle = generate_block_asset_handle( $metadata['name'], $field_name );
$script_handle = generate_block_asset_handle( $metadata['name'], $field_name, $index );
$script_asset_path = wp_normalize_path(
realpath(
dirname( $metadata['file'] ) . '/' .
@ -145,33 +166,49 @@ function register_block_script_handle( $metadata, $field_name ) {
* generated handle name. It returns unprocessed style handle otherwise.
*
* @since 5.5.0
* @since 6.1.0 Added `$index` parameter.
*
* @param array $metadata Block metadata.
* @param string $field_name Field name to pick from metadata.
* @param int $index Optional. Index of the style to register when multiple items passed.
* Default 0.
* @return string|false Style handle provided directly or created through
* style's registration, or false on failure.
*/
function register_block_style_handle( $metadata, $field_name ) {
function register_block_style_handle( $metadata, $field_name, $index = 0 ) {
if ( empty( $metadata[ $field_name ] ) ) {
return false;
}
$wpinc_path_norm = wp_normalize_path( realpath( ABSPATH . WPINC ) );
$theme_path_norm = wp_normalize_path( get_theme_file_path() );
$is_core_block = isset( $metadata['file'] ) && 0 === strpos( $metadata['file'], $wpinc_path_norm );
// Skip registering individual styles for each core block when a bundled version provided.
if ( $is_core_block && ! wp_should_load_separate_core_block_assets() ) {
return false;
}
// Check whether styles should have a ".min" suffix or not.
$suffix = SCRIPT_DEBUG ? '' : '.min';
$style_handle = $metadata[ $field_name ];
$style_path = remove_block_asset_path_prefix( $metadata[ $field_name ] );
if ( is_array( $style_handle ) ) {
if ( empty( $style_handle[ $index ] ) ) {
return false;
}
$style_handle = $style_handle[ $index ];
}
if ( $style_handle === $style_path && ! $is_core_block ) {
$style_path = remove_block_asset_path_prefix( $style_handle );
$is_style_handle = $style_handle === $style_path;
// Allow only passing style handles for core blocks.
if ( $is_core_block && ! $is_style_handle ) {
return false;
}
// Return the style handle unless it's the first item for every core block that requires special treatment.
if ( $is_style_handle && ! ( $is_core_block && 0 === $index ) ) {
return $style_handle;
}
// Check whether styles should have a ".min" suffix or not.
$suffix = SCRIPT_DEBUG ? '' : '.min';
$style_uri = plugins_url( $style_path, $metadata['file'] );
if ( $is_core_block ) {
$style_path = "style$suffix.css";
@ -185,9 +222,9 @@ function register_block_style_handle( $metadata, $field_name ) {
$style_uri = get_theme_file_uri( str_replace( $theme_path_norm, '', $style_path_norm ) );
}
$style_handle = generate_block_asset_handle( $metadata['name'], $field_name );
$style_handle = generate_block_asset_handle( $metadata['name'], $field_name, $index );
$block_dir = dirname( $metadata['file'] );
$style_file = realpath( "$block_dir/$style_path" );
$style_file = wp_normalize_path( realpath( "$block_dir/$style_path" ) );
$has_style_file = false !== $style_file;
$version = ! $is_core_block && isset( $metadata['version'] ) ? $metadata['version'] : false;
$style_uri = $has_style_file ? $style_uri : false;
@ -311,39 +348,69 @@ function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
}
}
if ( ! empty( $metadata['editorScript'] ) ) {
$settings['editor_script'] = register_block_script_handle(
$metadata,
'editorScript'
$script_fields = array(
'editorScript' => 'editor_script_handles',
'script' => 'script_handles',
'viewScript' => 'view_script_handles',
);
foreach ( $script_fields as $metadata_field_name => $settings_field_name ) {
if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
$scripts = $metadata[ $metadata_field_name ];
$processed_scripts = array();
if ( is_array( $scripts ) ) {
for ( $index = 0; $index < count( $scripts ); $index++ ) {
$result = register_block_script_handle(
$metadata,
$metadata_field_name,
$index
);
if ( $result ) {
$processed_scripts[] = $result;
}
}
} else {
$result = register_block_script_handle(
$metadata,
$metadata_field_name
);
if ( $result ) {
$processed_scripts[] = $result;
}
}
$settings[ $settings_field_name ] = $processed_scripts;
}
}
if ( ! empty( $metadata['script'] ) ) {
$settings['script'] = register_block_script_handle(
$metadata,
'script'
$style_fields = array(
'editorStyle' => 'editor_style_handles',
'style' => 'style_handles',
);
foreach ( $style_fields as $metadata_field_name => $settings_field_name ) {
if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
$styles = $metadata[ $metadata_field_name ];
$processed_styles = array();
if ( is_array( $styles ) ) {
for ( $index = 0; $index < count( $styles ); $index++ ) {
$result = register_block_style_handle(
$metadata,
$metadata_field_name,
$index
);
if ( $result ) {
$processed_styles[] = $result;
}
if ( ! empty( $metadata['viewScript'] ) ) {
$settings['view_script'] = register_block_script_handle(
$metadata,
'viewScript'
);
}
if ( ! empty( $metadata['editorStyle'] ) ) {
$settings['editor_style'] = register_block_style_handle(
} else {
$result = register_block_style_handle(
$metadata,
'editorStyle'
$metadata_field_name
);
if ( $result ) {
$processed_styles[] = $result;
}
}
$settings[ $settings_field_name ] = $processed_styles;
}
if ( ! empty( $metadata['style'] ) ) {
$settings['style'] = register_block_style_handle(
$metadata,
'style'
);
}
if ( ! empty( $metadata['render'] ) ) {
@ -1261,49 +1328,6 @@ function get_query_pagination_arrow( $block, $is_next ) {
return null;
}
/**
* Allows multiple block styles.
*
* @since 5.9.0
*
* @param array $metadata Metadata for registering a block type.
* @return array Metadata for registering a block type.
*/
function _wp_multiple_block_styles( $metadata ) {
foreach ( array( 'style', 'editorStyle' ) as $key ) {
if ( ! empty( $metadata[ $key ] ) && is_array( $metadata[ $key ] ) ) {
$default_style = array_shift( $metadata[ $key ] );
foreach ( $metadata[ $key ] as $handle ) {
$args = array( 'handle' => $handle );
if ( 0 === strpos( $handle, 'file:' ) && isset( $metadata['file'] ) ) {
$style_path = remove_block_asset_path_prefix( $handle );
$theme_path_norm = wp_normalize_path( get_theme_file_path() );
$style_path_norm = wp_normalize_path( realpath( dirname( $metadata['file'] ) . '/' . $style_path ) );
$is_theme_block = isset( $metadata['file'] ) && 0 === strpos( $metadata['file'], $theme_path_norm );
$style_uri = plugins_url( $style_path, $metadata['file'] );
if ( $is_theme_block ) {
$style_uri = get_theme_file_uri( str_replace( $theme_path_norm, '', $style_path_norm ) );
}
$args = array(
'handle' => sanitize_key( "{$metadata['name']}-{$style_path}" ),
'src' => $style_uri,
);
}
wp_enqueue_block_style( $metadata['name'], $args );
}
// Only return the 1st item in the array.
$metadata[ $key ] = $default_style;
}
}
return $metadata;
}
add_filter( 'block_type_metadata', '_wp_multiple_block_styles' );
/**
* Helper function that constructs a comment query vars array from the passed
* block properties.

View File

@ -166,44 +166,58 @@ class WP_Block_Type {
public $provides_context = null;
/**
* Block type editor only script handle.
* Block type editor only script handles.
*
* @since 5.0.0
* @var string|null
* @since 6.1.0
* @var string[]
*/
public $editor_script = null;
public $editor_script_handles = array();
/**
* Block type front end and editor script handle.
* Block type front end and editor script handles.
*
* @since 5.0.0
* @var string|null
* @since 6.1.0
* @var string[]
*/
public $script = null;
public $script_handles = array();
/**
* Block type front end only script handle.
* Block type front end only script handles.
*
* @since 5.9.0
* @var string|null
* @since 6.1.0
* @var string[]
*/
public $view_script = null;
public $view_script_handles = array();
/**
* Block type editor only style handle.
* Block type editor only style handles.
*
* @since 5.0.0
* @var string|null
* @since 6.1.0
* @var string[]
*/
public $editor_style = null;
public $editor_style_handles = array();
/**
* Block type front end and editor style handle.
* Block type front end and editor style handles.
*
* @since 5.0.0
* @var string|null
* @since 6.1.0
* @var string[]
*/
public $style = null;
public $style_handles = array();
/**
* Deprecated block type properties for script and style handles.
*
* @since 6.1.0
* @var string[]
*/
private $deprecated_properties = array(
'editor_script',
'script',
'view_script',
'editor_style',
'style',
);
/**
* Attributes supported by every block.
@ -228,6 +242,9 @@ class WP_Block_Type {
* @since 5.8.0 Added the `variations` property.
* @since 5.9.0 Added the `view_script` property.
* @since 6.0.0 Added the `ancestor` property.
* @since 6.1.0 Added the `editor_script_handles`, `script_handles`, `view_script_handles,
* `editor_style_handles`, and `style_handles` properties.
* Deprecated the `editor_script`, `script`, `view_script`, `editor_style`, and `style` properties.
*
* @see register_block_type()
*
@ -257,11 +274,11 @@ class WP_Block_Type {
* @type array|null $attributes Block type attributes property schemas.
* @type string[] $uses_context Context values inherited by blocks of this type.
* @type string[]|null $provides_context Context provided by blocks of this type.
* @type string|null $editor_script Block type editor only script handle.
* @type string|null $script Block type front end and editor script handle.
* @type string|null $view_script Block type front end only script handle.
* @type string|null $editor_style Block type editor only style handle.
* @type string|null $style Block type front end and editor style handle.
* @type string[] $editor_script_handles Block type editor only script handles.
* @type string[] $script_handles Block type front end and editor script handles.
* @type string[] $view_script_handles Block type front end only script handles.
* @type string[] $editor_style_handles Block type editor only style handles.
* @type string[] $style_handles Block type front end and editor style handles.
* }
*/
public function __construct( $block_type, $args = array() ) {
@ -270,6 +287,70 @@ class WP_Block_Type {
$this->set_props( $args );
}
/**
* Proxies getting values for deprecated properties for script and style handles for backward compatibility.
* Gets the value for the corresponding new property if the first item in the array provided.
*
* @since 6.1.0
*
* @param string $name Deprecated property name.
*
* @return string|null|void The value read from the new property if the first item in the array provided,
* null when value not found, or void when unknown property name provided.
*/
public function __get( $name ) {
if ( ! in_array( $name, $this->deprecated_properties ) ) {
return;
}
$new_name = $name . '_handles';
return isset( $this->{$new_name }[0] ) ? $this->{$new_name }[0] : null;
}
/**
* Proxies checking for deprecated properties for script and style handles for backward compatibility.
* Checks whether the corresponding new property has the first item in the array provided.
*
* @since 6.1.0
*
* @param string $name Deprecated property name.
*
* @return boolean Returns true when for the new property the first item in the array exists,
* or false otherwise.
*/
public function __isset( $name ) {
if ( ! in_array( $name, $this->deprecated_properties ) ) {
return false;
}
$new_name = $name . '_handles';
return isset( $this->{$new_name }[0] );
}
/**
* Proxies setting values for deprecated properties for script and style handles for backward compatibility.
* Sets the value for the corresponding new property as the first item in the array.
* It also allows setting custom properties for backward compatibility.
*
* @since 6.1.0
*
* @param string $name Property name.
* @param mixed $value Property value.
*/
public function __set( $name, $value ) {
if ( ! in_array( $name, $this->deprecated_properties ) ) {
$this->{$name} = $value;
return;
}
if ( ! is_string( $value ) ) {
return;
}
$new_name = $name . '_handles';
$this->{$new_name }[0] = $value;
}
/**
* Renders the block type output for given attributes.
*

View File

@ -260,16 +260,22 @@ class WP_Block {
$post = $global_post;
}
if ( ! empty( $this->block_type->script ) ) {
wp_enqueue_script( $this->block_type->script );
if ( ( ! empty( $this->block_type->script_handles ) ) ) {
foreach ( $this->block_type->script_handles as $script_handle ) {
wp_enqueue_script( $script_handle );
}
}
if ( ! empty( $this->block_type->view_script ) && empty( $this->block_type->render_callback ) ) {
wp_enqueue_script( $this->block_type->view_script );
if ( ! empty( $this->block_type->view_script_handles ) && empty( $this->block_type->render_callback ) ) {
foreach ( $this->block_type->view_script_handles as $view_script_handle ) {
wp_enqueue_script( $view_script_handle );
}
}
if ( ! empty( $this->block_type->style ) ) {
wp_enqueue_style( $this->block_type->style );
if ( ( ! empty( $this->block_type->style_handles ) ) ) {
foreach ( $this->block_type->style_handles as $style_handle ) {
wp_enqueue_style( $style_handle );
}
}
/**

View File

@ -4442,3 +4442,17 @@ function _get_path_to_translation_from_lang_dir( $domain ) {
return false;
}
/**
* Allows multiple block styles.
*
* @since 5.9.0
* @deprecated 6.1.0
*
* @param array $metadata Metadata for registering a block type.
* @return array Metadata for registering a block type.
*/
function _wp_multiple_block_styles( $metadata ) {
_deprecated_function( __FUNCTION__, '6.1.0' );
return $metadata;
}

View File

@ -257,7 +257,16 @@ class WP_REST_Block_Types_Controller extends WP_REST_Controller {
}
$schema = $this->get_item_schema();
$extra_fields = array(
// Fields deprecated in WordPress 6.1, but left in the schema for backwards compatibility.
$deprecated_fields = array(
'editor_script',
'script',
'view_script',
'editor_style',
'style',
);
$extra_fields = array_merge(
array(
'api_version',
'name',
'title',
@ -273,12 +282,14 @@ class WP_REST_Block_Types_Controller extends WP_REST_Controller {
'styles',
'textdomain',
'example',
'editor_script',
'script',
'view_script',
'editor_style',
'style',
'editor_script_handles',
'script_handles',
'view_script_handles',
'editor_style_handles',
'style_handles',
'variations',
),
$deprecated_fields
);
foreach ( $extra_fields as $extra_field ) {
if ( rest_is_field_included( $extra_field, $fields ) ) {
@ -437,7 +448,7 @@ class WP_REST_Block_Types_Controller extends WP_REST_Controller {
'readonly' => true,
);
$schema = array(
$this->schema = array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'block-type',
'type' => 'object',
@ -519,38 +530,53 @@ class WP_REST_Block_Types_Controller extends WP_REST_Controller {
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'editor_script' => array(
'description' => __( 'Editor script handle.' ),
'type' => array( 'string', 'null' ),
'default' => null,
'editor_script_handles' => array(
'description' => __( 'Editor script handles.' ),
'type' => array( 'array' ),
'default' => array(),
'items' => array(
'type' => 'string',
),
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'script' => array(
'description' => __( 'Public facing and editor script handle.' ),
'type' => array( 'string', 'null' ),
'default' => null,
'script_handles' => array(
'description' => __( 'Public facing and editor script handles.' ),
'type' => array( 'array' ),
'default' => array(),
'items' => array(
'type' => 'string',
),
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'view_script' => array(
'description' => __( 'Public facing script handle.' ),
'type' => array( 'string', 'null' ),
'default' => null,
'view_script_handles' => array(
'description' => __( 'Public facing script handles.' ),
'type' => array( 'array' ),
'default' => array(),
'items' => array(
'type' => 'string',
),
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'editor_style' => array(
'description' => __( 'Editor style handle.' ),
'type' => array( 'string', 'null' ),
'default' => null,
'editor_style_handles' => array(
'description' => __( 'Editor style handles.' ),
'type' => array( 'array' ),
'default' => array(),
'items' => array(
'type' => 'string',
),
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'style' => array(
'description' => __( 'Public facing and editor style handle.' ),
'type' => array( 'string', 'null' ),
'default' => null,
'style_handles' => array(
'description' => __( 'Public facing and editor style handles.' ),
'type' => array( 'array' ),
'default' => array(),
'items' => array(
'type' => 'string',
),
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
@ -667,7 +693,45 @@ class WP_REST_Block_Types_Controller extends WP_REST_Controller {
),
);
$this->schema = $schema;
// Properties deprecated in WordPress 6.1, but left in the schema for backwards compatibility.
$deprecated_properties = array(
'editor_script' => array(
'description' => __( 'Editor script handle. DEPRECATED: Use `editor_script_handles` instead.' ),
'type' => array( 'string', 'null' ),
'default' => null,
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'script' => array(
'description' => __( 'Public facing and editor script handle. DEPRECATED: Use `script_handles` instead.' ),
'type' => array( 'string', 'null' ),
'default' => null,
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'view_script' => array(
'description' => __( 'Public facing script handle. DEPRECATED: Use `view_script_handles` instead.' ),
'type' => array( 'string', 'null' ),
'default' => null,
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'editor_style' => array(
'description' => __( 'Editor style handle. DEPRECATED: Use `editor_style_handles` instead.' ),
'type' => array( 'string', 'null' ),
'default' => null,
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'style' => array(
'description' => __( 'Public facing and editor style handle. DEPRECATED: Use `style_handles` instead.' ),
'type' => array( 'string', 'null' ),
'default' => null,
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
);
$this->schema['properties'] = array_merge( $this->schema['properties'], $deprecated_properties );
return $this->add_additional_fields_schema( $this->schema );
}

View File

@ -2531,28 +2531,30 @@ function wp_enqueue_registered_block_scripts_and_styles() {
return;
}
$load_editor_scripts = is_admin() && wp_should_load_block_editor_scripts_and_styles();
$load_editor_scripts_and_styles = is_admin() && wp_should_load_block_editor_scripts_and_styles();
$block_registry = WP_Block_Type_Registry::get_instance();
foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
// Front-end styles.
if ( ! empty( $block_type->style ) ) {
wp_enqueue_style( $block_type->style );
// Front-end and editor styles.
foreach ( $block_type->style_handles as $style_handle ) {
wp_enqueue_style( $style_handle );
}
// Front-end script.
if ( ! empty( $block_type->script ) ) {
wp_enqueue_script( $block_type->script );
// Front-end and editor scripts.
foreach ( $block_type->script_handles as $script_handle ) {
wp_enqueue_script( $script_handle );
}
if ( $load_editor_scripts_and_styles ) {
// Editor styles.
if ( $load_editor_scripts && ! empty( $block_type->editor_style ) ) {
wp_enqueue_style( $block_type->editor_style );
foreach ( $block_type->editor_style_handles as $editor_style_handle ) {
wp_enqueue_style( $editor_style_handle );
}
// Editor script.
if ( $load_editor_scripts && ! empty( $block_type->editor_script ) ) {
wp_enqueue_script( $block_type->editor_script );
// Editor scripts.
foreach ( $block_type->editor_script_handles as $editor_script_handle ) {
wp_enqueue_script( $editor_script_handle );
}
}
}
}

View File

@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
$wp_version = '6.1-alpha-54154';
$wp_version = '6.1-alpha-54155';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.