From 0f560023039d79a61e15a7103b93eea26554cd6b Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 11 May 2023 20:25:17 +0400 Subject: [PATCH] Plugin: Use bundled files for enqueuing global styles (#50310) * Plugin: Use bundled files for enqueuing global styles * Override the '__experimentalFeatures' * Recreate global styles for block editors * Move global styles custom CSS handlers * Move _gutenberg_clean_theme_json_caches * Override global styles custom CSS properties --- lib/block-editor-settings.php | 82 ++++++ .../wordpress-6.2/block-editor-settings.php | 31 --- lib/compat/wordpress-6.2/default-filters.php | 7 - .../get-global-styles-and-settings.php | 170 ------------- lib/compat/wordpress-6.2/script-loader.php | 22 -- lib/global-styles-and-settings.php | 233 ++++++++++++++++++ lib/load.php | 4 +- lib/script-loader.php | 94 +++++++ 8 files changed, 412 insertions(+), 231 deletions(-) create mode 100644 lib/block-editor-settings.php delete mode 100644 lib/compat/wordpress-6.2/block-editor-settings.php create mode 100644 lib/global-styles-and-settings.php create mode 100644 lib/script-loader.php diff --git a/lib/block-editor-settings.php b/lib/block-editor-settings.php new file mode 100644 index 00000000000000..c0f514da8d85ef --- /dev/null +++ b/lib/block-editor-settings.php @@ -0,0 +1,82 @@ + 'variables', + '__unstableType' => 'presets', + 'isGlobalStyles' => true, + ), + array( + 'css' => 'presets', + '__unstableType' => 'presets', + 'isGlobalStyles' => true, + ), + ); + foreach ( $presets as $preset_style ) { + $actual_css = gutenberg_get_global_stylesheet( array( $preset_style['css'] ) ); + if ( '' !== $actual_css ) { + $preset_style['css'] = $actual_css; + $global_styles[] = $preset_style; + } + } + + if ( wp_theme_has_theme_json() ) { + $block_classes = array( + 'css' => 'styles', + '__unstableType' => 'theme', + 'isGlobalStyles' => true, + ); + $actual_css = gutenberg_get_global_stylesheet( array( $block_classes['css'] ) ); + if ( '' !== $actual_css ) { + $block_classes['css'] = $actual_css; + $global_styles[] = $block_classes; + } + + /* + * Add the custom CSS as a separate stylesheet so any invalid CSS + * entered by users does not break other global styles. + */ + $global_styles[] = array( + 'css' => gutenberg_get_global_styles_custom_css(), + '__unstableType' => 'user', + 'isGlobalStyles' => true, + ); + } else { + // If there is no `theme.json` file, ensure base layout styles are still available. + $block_classes = array( + 'css' => 'base-layout-styles', + '__unstableType' => 'base-layout', + 'isGlobalStyles' => true, + ); + $actual_css = gutenberg_get_global_stylesheet( array( $block_classes['css'] ) ); + if ( '' !== $actual_css ) { + $block_classes['css'] = $actual_css; + $global_styles[] = $block_classes; + } + } + + $settings['styles'] = array_merge( $global_styles, get_block_editor_theme_styles() ); + + // Copied from get_block_editor_settings() at wordpress-develop/block-editor.php. + $settings['__experimentalFeatures'] = gutenberg_get_global_settings(); + + return $settings; +} +add_filter( 'block_editor_settings_all', 'gutenberg_get_block_editor_settings', PHP_INT_MAX ); diff --git a/lib/compat/wordpress-6.2/block-editor-settings.php b/lib/compat/wordpress-6.2/block-editor-settings.php deleted file mode 100644 index 593a8b7e6b55f2..00000000000000 --- a/lib/compat/wordpress-6.2/block-editor-settings.php +++ /dev/null @@ -1,31 +0,0 @@ - gutenberg_get_global_styles_custom_css(), - '__unstableType' => 'user', - 'isGlobalStyles' => true, - ); - } - - // Copied from get_block_editor_settings() at wordpress-develop/block-editor.php. - $settings['__experimentalFeatures'] = gutenberg_get_global_settings(); - - return $settings; -} - -add_filter( 'block_editor_settings_all', 'gutenberg_get_block_editor_settings_6_2', PHP_INT_MAX ); diff --git a/lib/compat/wordpress-6.2/default-filters.php b/lib/compat/wordpress-6.2/default-filters.php index 6a5bcb3b75b051..7e797af3e43b1f 100644 --- a/lib/compat/wordpress-6.2/default-filters.php +++ b/lib/compat/wordpress-6.2/default-filters.php @@ -17,13 +17,6 @@ * @package gutenberg */ -/** - * When backporting to core, the existing filters hooked to WP_Theme_JSON_Resolver::clean_cached_data() - * need to be removed. - */ -add_action( 'start_previewing_theme', '_gutenberg_clean_theme_json_caches' ); -add_action( 'switch_theme', '_gutenberg_clean_theme_json_caches' ); - /** * This is a temporary fix to ensure that the block editor styles are enqueued * in the order the iframe expects. diff --git a/lib/compat/wordpress-6.2/get-global-styles-and-settings.php b/lib/compat/wordpress-6.2/get-global-styles-and-settings.php index e02a0466a0b98f..eb1481a3808b68 100644 --- a/lib/compat/wordpress-6.2/get-global-styles-and-settings.php +++ b/lib/compat/wordpress-6.2/get-global-styles-and-settings.php @@ -59,176 +59,6 @@ function wp_theme_has_theme_json_clean_cache() { } } -/** - * Gets the global styles custom css from theme.json. - * - * @return string - */ -function gutenberg_get_global_styles_custom_css() { - // Ignore cache when `WP_DEBUG` is enabled, so it doesn't interfere with the theme developers workflow. - $can_use_cached = ! WP_DEBUG; - $cache_key = 'gutenberg_get_global_custom_css'; - $cache_group = 'theme_json'; - if ( $can_use_cached ) { - $cached = wp_cache_get( $cache_key, $cache_group ); - if ( $cached ) { - return $cached; - } - } - - if ( ! wp_theme_has_theme_json() ) { - return ''; - } - - $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data(); - $stylesheet = $tree->get_custom_css(); - - if ( $can_use_cached ) { - wp_cache_set( $cache_key, $stylesheet, $cache_group ); - } - - return $stylesheet; -} - -/** - * Returns the stylesheet resulting of merging core, theme, and user data. - * - * @param array $types Types of styles to load. Optional. - * It accepts as values: 'variables', 'presets', 'styles', 'base-layout-styles. - * If empty, it'll load the following: - * - for themes without theme.json: 'variables', 'presets', 'base-layout-styles'. - * - for themes with theme.json: 'variables', 'presets', 'styles'. - * - * @return string Stylesheet. - */ -function gutenberg_get_global_stylesheet( $types = array() ) { - // Ignore cache when `WP_DEBUG` is enabled, so it doesn't interfere with the theme developers workflow. - $can_use_cached = empty( $types ) && ! WP_DEBUG; - $cache_key = 'gutenberg_get_global_stylesheet'; - $cache_group = 'theme_json'; - if ( $can_use_cached ) { - $cached = wp_cache_get( $cache_key, $cache_group ); - if ( $cached ) { - return $cached; - } - } - $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data(); - $supports_theme_json = wp_theme_has_theme_json(); - if ( empty( $types ) && ! $supports_theme_json ) { - $types = array( 'variables', 'presets', 'base-layout-styles' ); - } elseif ( empty( $types ) ) { - $types = array( 'variables', 'presets', 'styles' ); - } - - /* - * If variables are part of the stylesheet, - * we add them. - * - * This is so themes without a theme.json still work as before 5.9: - * they can override the default presets. - * See https://core.trac.wordpress.org/ticket/54782 - */ - $styles_variables = ''; - if ( in_array( 'variables', $types, true ) ) { - /* - * We only use the default, theme, and custom origins. - * This is because styles for blocks origin are added - * at a later phase (render cycle) so we only render the ones in use. - * @see wp_add_global_styles_for_blocks - */ - $origins = array( 'default', 'theme', 'custom' ); - $styles_variables = $tree->get_stylesheet( array( 'variables' ), $origins ); - $types = array_diff( $types, array( 'variables' ) ); - } - - /* - * For the remaining types (presets, styles), we do consider origins: - * - * - themes without theme.json: only the classes for the presets defined by core - * - themes with theme.json: the presets and styles classes, both from core and the theme - */ - $styles_rest = ''; - if ( ! empty( $types ) ) { - /* - * We only use the default, theme, and custom origins. - * This is because styles for blocks origin are added - * at a later phase (render cycle) so we only render the ones in use. - * @see wp_add_global_styles_for_blocks - */ - $origins = array( 'default', 'theme', 'custom' ); - if ( ! $supports_theme_json ) { - $origins = array( 'default' ); - } - $styles_rest = $tree->get_stylesheet( $types, $origins ); - } - $stylesheet = $styles_variables . $styles_rest; - if ( $can_use_cached ) { - wp_cache_set( $cache_key, $stylesheet, $cache_group ); - } - return $stylesheet; -} - -/** - * Function to get the settings resulting of merging core, theme, and user data. - * - * @param array $path Path to the specific setting to retrieve. Optional. - * If empty, will return all settings. - * @param array $context { - * Metadata to know where to retrieve the $path from. Optional. - * - * @type string $block_name Which block to retrieve the settings from. - * If empty, it'll return the settings for the global context. - * @type string $origin Which origin to take data from. - * Valid values are 'all' (core, theme, and user) or 'base' (core and theme). - * If empty or unknown, 'all' is used. - * } - * - * @return array The settings to retrieve. - */ -function gutenberg_get_global_settings( $path = array(), $context = array() ) { - if ( ! empty( $context['block_name'] ) ) { - $new_path = array( 'blocks', $context['block_name'] ); - foreach ( $path as $subpath ) { - $new_path[] = $subpath; - } - $path = $new_path; - } - - // This is the default value when no origin is provided or when it is 'all'. - $origin = 'custom'; - if ( - ! wp_theme_has_theme_json() || - ( isset( $context['origin'] ) && 'base' === $context['origin'] ) - ) { - $origin = 'theme'; - } - - $cache_group = 'theme_json'; - $cache_key = 'gutenberg_get_global_settings_' . $origin; - $settings = wp_cache_get( $cache_key, $cache_group ); - - if ( false === $settings || WP_DEBUG ) { - $settings = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $origin )->get_settings(); - wp_cache_set( $cache_key, $settings, $cache_group ); - } - - return _wp_array_get( $settings, $path, $settings ); -} - -/** - * Private function to clean the caches used by gutenberg_get_global_settings method. - * - * @access private - */ -function _gutenberg_clean_theme_json_caches() { - wp_cache_delete( 'wp_theme_has_theme_json', 'theme_json' ); - wp_cache_delete( 'gutenberg_get_global_stylesheet', 'theme_json' ); - wp_cache_delete( 'gutenberg_get_global_settings_custom', 'theme_json' ); - wp_cache_delete( 'gutenberg_get_global_settings_theme', 'theme_json' ); - wp_cache_delete( 'gutenberg_get_global_custom_css', 'theme_json' ); - WP_Theme_JSON_Resolver_Gutenberg::clean_cached_data(); -} - /** * Tell the cache mechanisms not to persist theme.json data across requests. * The data stored under this cache group: diff --git a/lib/compat/wordpress-6.2/script-loader.php b/lib/compat/wordpress-6.2/script-loader.php index 142cbf4e1477b3..6ed69244726d33 100644 --- a/lib/compat/wordpress-6.2/script-loader.php +++ b/lib/compat/wordpress-6.2/script-loader.php @@ -152,25 +152,3 @@ function( $settings ) { }, 100 ); - -/** - * Enqueues the global styles custom css. - * - * @since 6.2.0 - */ -function gutenberg_enqueue_global_styles_custom_css() { - if ( ! wp_is_block_theme() ) { - return; - } - - // Don't enqueue Customizer's custom CSS separately. - remove_action( 'wp_head', 'wp_custom_css_cb', 101 ); - - $custom_css = wp_get_custom_css(); - $custom_css .= gutenberg_get_global_styles_custom_css(); - - if ( ! empty( $custom_css ) ) { - wp_add_inline_style( 'global-styles', $custom_css ); - } -} -add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_global_styles_custom_css' ); diff --git a/lib/global-styles-and-settings.php b/lib/global-styles-and-settings.php new file mode 100644 index 00000000000000..b239080f298c2c --- /dev/null +++ b/lib/global-styles-and-settings.php @@ -0,0 +1,233 @@ +get_stylesheet( array( 'variables' ), $origins ); + $types = array_diff( $types, array( 'variables' ) ); + } + + /* + * For the remaining types (presets, styles), we do consider origins: + * + * - themes without theme.json: only the classes for the presets defined by core + * - themes with theme.json: the presets and styles classes, both from core and the theme + */ + $styles_rest = ''; + if ( ! empty( $types ) ) { + /* + * We only use the default, theme, and custom origins. + * This is because styles for blocks origin are added + * at a later phase (render cycle) so we only render the ones in use. + * @see wp_add_global_styles_for_blocks + */ + $origins = array( 'default', 'theme', 'custom' ); + if ( ! $supports_theme_json ) { + $origins = array( 'default' ); + } + $styles_rest = $tree->get_stylesheet( $types, $origins ); + } + $stylesheet = $styles_variables . $styles_rest; + if ( $can_use_cached ) { + wp_cache_set( $cache_key, $stylesheet, $cache_group ); + } + return $stylesheet; +} + +/** + * Function to get the settings resulting of merging core, theme, and user data. + * + * @param array $path Path to the specific setting to retrieve. Optional. + * If empty, will return all settings. + * @param array $context { + * Metadata to know where to retrieve the $path from. Optional. + * + * @type string $block_name Which block to retrieve the settings from. + * If empty, it'll return the settings for the global context. + * @type string $origin Which origin to take data from. + * Valid values are 'all' (core, theme, and user) or 'base' (core and theme). + * If empty or unknown, 'all' is used. + * } + * + * @return array The settings to retrieve. + */ +function gutenberg_get_global_settings( $path = array(), $context = array() ) { + if ( ! empty( $context['block_name'] ) ) { + $new_path = array( 'blocks', $context['block_name'] ); + foreach ( $path as $subpath ) { + $new_path[] = $subpath; + } + $path = $new_path; + } + + // This is the default value when no origin is provided or when it is 'all'. + $origin = 'custom'; + if ( + ! wp_theme_has_theme_json() || + ( isset( $context['origin'] ) && 'base' === $context['origin'] ) + ) { + $origin = 'theme'; + } + + $cache_group = 'theme_json'; + $cache_key = 'gutenberg_get_global_settings_' . $origin; + $settings = wp_cache_get( $cache_key, $cache_group ); + + if ( false === $settings || WP_DEBUG ) { + $settings = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $origin )->get_settings(); + wp_cache_set( $cache_key, $settings, $cache_group ); + } + + return _wp_array_get( $settings, $path, $settings ); +} + +/** + * Gets the global styles custom css from theme.json. + * + * @return string + */ +function gutenberg_get_global_styles_custom_css() { + // Ignore cache when `WP_DEBUG` is enabled, so it doesn't interfere with the theme developers workflow. + $can_use_cached = ! WP_DEBUG; + $cache_key = 'gutenberg_get_global_custom_css'; + $cache_group = 'theme_json'; + if ( $can_use_cached ) { + $cached = wp_cache_get( $cache_key, $cache_group ); + if ( $cached ) { + return $cached; + } + } + + if ( ! wp_theme_has_theme_json() ) { + return ''; + } + + $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data(); + $stylesheet = $tree->get_custom_css(); + + if ( $can_use_cached ) { + wp_cache_set( $cache_key, $stylesheet, $cache_group ); + } + + return $stylesheet; +} + +/** + * Adds global style rules to the inline style for each block. + * + * @return void + */ +function gutenberg_add_global_styles_for_blocks() { + $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data(); + $block_nodes = $tree->get_styles_block_nodes(); + foreach ( $block_nodes as $metadata ) { + $block_css = $tree->get_styles_for_block( $metadata ); + + if ( ! wp_should_load_separate_core_block_assets() ) { + wp_add_inline_style( 'global-styles', $block_css ); + continue; + } + + $stylesheet_handle = 'global-styles'; + if ( isset( $metadata['name'] ) ) { + /* + * These block styles are added on block_render. + * This hooks inline CSS to them so that they are loaded conditionally + * based on whether or not the block is used on the page. + */ + if ( str_starts_with( $metadata['name'], 'core/' ) ) { + $block_name = str_replace( 'core/', '', $metadata['name'] ); + $stylesheet_handle = 'wp-block-' . $block_name; + } + wp_add_inline_style( $stylesheet_handle, $block_css ); + } + + // The likes of block element styles from theme.json do not have $metadata['name'] set. + if ( ! isset( $metadata['name'] ) && ! empty( $metadata['path'] ) ) { + $result = array_values( + array_filter( + $metadata['path'], + function ( $item ) { + if ( strpos( $item, 'core/' ) !== false ) { + return true; + } + return false; + } + ) + ); + if ( isset( $result[0] ) ) { + if ( str_starts_with( $result[0], 'core/' ) ) { + $block_name = str_replace( 'core/', '', $result[0] ); + $stylesheet_handle = 'wp-block-' . $block_name; + } + wp_add_inline_style( $stylesheet_handle, $block_css ); + } + } + } +} + +/** + * Private function to clean the caches used by gutenberg_get_global_settings method. + * + * @access private + */ +function _gutenberg_clean_theme_json_caches() { + wp_cache_delete( 'wp_theme_has_theme_json', 'theme_json' ); + wp_cache_delete( 'gutenberg_get_global_stylesheet', 'theme_json' ); + wp_cache_delete( 'gutenberg_get_global_settings_custom', 'theme_json' ); + wp_cache_delete( 'gutenberg_get_global_settings_theme', 'theme_json' ); + wp_cache_delete( 'gutenberg_get_global_custom_css', 'theme_json' ); + WP_Theme_JSON_Resolver_Gutenberg::clean_cached_data(); +} +add_action( 'start_previewing_theme', '_gutenberg_clean_theme_json_caches' ); +add_action( 'switch_theme', '_gutenberg_clean_theme_json_caches' ); diff --git a/lib/load.php b/lib/load.php index 37dc2305678940..8883f66c267941 100644 --- a/lib/load.php +++ b/lib/load.php @@ -74,7 +74,6 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-6.2/default-filters.php'; require __DIR__ . '/compat/wordpress-6.2/site-editor.php'; require __DIR__ . '/compat/wordpress-6.2/block-editor.php'; -require __DIR__ . '/compat/wordpress-6.2/block-editor-settings.php'; require __DIR__ . '/compat/wordpress-6.2/theme.php'; require __DIR__ . '/compat/wordpress-6.2/widgets.php'; require __DIR__ . '/compat/wordpress-6.2/menu.php'; @@ -128,11 +127,14 @@ function gutenberg_is_experiment_enabled( $name ) { } // Plugin specific code. +require __DIR__ . '/script-loader.php'; +require __DIR__ . '/global-styles-and-settings.php'; require __DIR__ . '/class-wp-theme-json-data-gutenberg.php'; require __DIR__ . '/class-wp-theme-json-gutenberg.php'; require __DIR__ . '/class-wp-theme-json-resolver-gutenberg.php'; require __DIR__ . '/class-wp-duotone-gutenberg.php'; require __DIR__ . '/blocks.php'; +require __DIR__ . '/block-editor-settings.php'; require __DIR__ . '/client-assets.php'; require __DIR__ . '/demo.php'; require __DIR__ . '/experiments-page.php'; diff --git a/lib/script-loader.php b/lib/script-loader.php new file mode 100644 index 00000000000000..2b5c7bb08a676f --- /dev/null +++ b/lib/script-loader.php @@ -0,0 +1,94 @@ +