diff --git a/.gitignore b/.gitignore index 2eca5aa47e..01699a4815 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ wp-tests-config.php /src/wp-includes/css/dist /src/wp-includes/css/*.min.css /src/wp-includes/css/*-rtl.css +/src/wp-includes/blocks/**/*.css /packagehash.txt # Files and folders that get created in wp-content diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index c0658dd35f..2d33580b0e 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -157,24 +157,48 @@ function register_block_style_handle( $metadata, $field_name ) { if ( empty( $metadata[ $field_name ] ) ) { return false; } + $is_core_block = isset( $metadata['file'] ) && 0 === strpos( $metadata['file'], ABSPATH . WPINC ); + if ( $is_core_block && ! 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 ( $style_handle === $style_path ) { + + if ( $style_handle === $style_path && ! $is_core_block ) { return $style_handle; } + $style_uri = plugins_url( $style_path, $metadata['file'] ); + if ( $is_core_block ) { + $style_path = "style$suffix.css"; + $style_uri = includes_url( 'blocks/' . str_replace( 'core/', '', $metadata['name'] ) . "/style$suffix.css" ); + } + $style_handle = generate_block_asset_handle( $metadata['name'], $field_name ); $block_dir = dirname( $metadata['file'] ); $style_file = realpath( "$block_dir/$style_path" ); + $version = file_exists( $style_file ) ? filemtime( $style_file ) : false; $result = wp_register_style( $style_handle, - plugins_url( $style_path, $metadata['file'] ), + $style_uri, array(), - filemtime( $style_file ) + $version ); if ( file_exists( str_replace( '.css', '-rtl.css', $style_file ) ) ) { wp_style_add_data( $style_handle, 'rtl', 'replace' ); } + if ( file_exists( $style_file ) ) { + wp_style_add_data( $style_handle, 'path', $style_file ); + } + + $rtl_file = str_replace( "$suffix.css", "-rtl$suffix.css", $style_file ); + if ( is_rtl() && file_exists( $rtl_file ) ) { + wp_style_add_data( $style_handle, 'path', $rtl_file ); + } return $result ? $style_handle : false; } @@ -928,3 +952,26 @@ function block_has_support( $block_type, $feature, $default = false ) { return true === $block_support || is_array( $block_support ); } + +/** + * Checks whether separate assets should be loaded for core blocks. + * + * @since 5.8 + * + * @return bool + */ +function should_load_separate_core_block_assets() { + if ( is_admin() || is_feed() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { + return false; + } + /** + * Determine if separate styles & scripts will be loaded for blocks on-render or not. + * + * @since 5.8.0 + * + * @param bool $load_separate_styles Whether separate styles will be loaded or not. + * + * @return bool Whether separate styles will be loaded or not. + */ + return apply_filters( 'separate_core_block_assets', false ); +} diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 9df2bad2c3..7a7bfeab0f 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -551,6 +551,9 @@ add_filter( 'customize_controls_print_styles', 'wp_resource_hints', 1 ); add_action( 'wp_default_styles', 'wp_default_styles' ); add_filter( 'style_loader_src', 'wp_style_loader_src', 10, 2 ); +add_action( 'wp_head', 'wp_maybe_inline_styles', 1 ); // Run for styles enqueued in
. +add_action( 'wp_footer', 'wp_maybe_inline_styles', 1 ); // Run for late-loaded styles in the footer. + // Taxonomy. add_action( 'init', 'create_initial_taxonomies', 0 ); // Highest priority. add_action( 'change_locale', 'create_initial_taxonomies' ); diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 81225dbb6d..704c59e652 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -1502,7 +1502,9 @@ function wp_default_styles( $styles ) { } $styles->add( 'wp-editor-font', $fonts_url ); // No longer used in core as of 5.7. - $styles->add( 'wp-block-library-theme', "/wp-includes/css/dist/block-library/theme$suffix.css" ); + $block_library_theme_path = "/wp-includes/css/dist/block-library/theme$suffix.css"; + $styles->add( 'wp-block-library-theme', $block_library_theme_path ); + $styles->add_data( 'wp-block-library-theme', 'path', ABSPATH . $block_library_theme_path ); $styles->add( 'wp-reset-editor-styles', @@ -1571,7 +1573,11 @@ function wp_default_styles( $styles ) { $handle = 'wp-' . $package; $path = "/wp-includes/css/dist/$package/style$suffix.css"; + if ( 'block-library' === $package && should_load_separate_core_block_assets() ) { + $path = "/wp-includes/css/dist/$package/common$suffix.css"; + } $styles->add( $handle, $path, $dependencies ); + $styles->add_data( $handle, 'path', ABSPATH . $path ); } // RTL CSS. @@ -2277,6 +2283,10 @@ function wp_should_load_block_editor_scripts_and_styles() { function wp_enqueue_registered_block_scripts_and_styles() { global $current_screen; + if ( should_load_separate_core_block_assets() ) { + return; + } + $load_editor_scripts = is_admin() && wp_should_load_block_editor_scripts_and_styles(); $block_registry = WP_Block_Type_Registry::get_instance(); @@ -2495,3 +2505,80 @@ function wp_get_inline_script_tag( $javascript, $attributes = array() ) { function wp_print_inline_script_tag( $javascript, $attributes = array() ) { echo wp_get_inline_script_tag( $javascript, $attributes ); } + +/** + * Allow small styles to be inlined. + * This improves performance and sustainability, and is opt-in. + * + * Stylesheets can opt-in by adding `path` data using `wp_style_add_data`, and defining the file's absolute path. + * wp_style_add_data( $style_handle, 'path', $file_path ); + * + * @since 5.8.0 + * + * @return void + */ +function wp_maybe_inline_styles() { + + $total_inline_limit = 20000; + /** + * The maximum size of inlined styles in bytes. + * + * @param int $total_inline_limit The file-size threshold, in bytes. Defaults to 20000. + * @return int The file-size threshold, in bytes. + */ + $total_inline_limit = apply_filters( 'styles_inline_size_limit', $total_inline_limit ); + + global $wp_styles; + $styles = array(); + + // Build an array of styles that have a path defined. + foreach ( $wp_styles->queue as $handle ) { + if ( wp_styles()->get_data( $handle, 'path' ) && file_exists( $wp_styles->registered[ $handle ]->extra['path'] ) ) { + $styles[] = array( + 'handle' => $handle, + 'path' => $wp_styles->registered[ $handle ]->extra['path'], + 'size' => filesize( $wp_styles->registered[ $handle ]->extra['path'] ), + ); + } + } + + if ( ! empty( $styles ) ) { + // Reorder styles array based on size. + usort( + $styles, + function( $a, $b ) { + return ( $a['size'] <= $b['size'] ) ? -1 : 1; + } + ); + + /** + * The total inlined size. + * + * On each iteration of the loop, if a style gets added inline the value of this var increases + * to reflect the total size of inlined styles. + */ + $total_inline_size = 0; + + // Loop styles. + foreach ( $styles as $style ) { + + // Size check. Since styles are ordered by size, we can break the loop. + if ( $total_inline_size + $style['size'] > $total_inline_limit ) { + break; + } + + // Get the styles if we don't already have them. + $style['css'] = file_get_contents( $style['path'] ); + + // Set `src` to `false` and add styles inline. + $wp_styles->registered[ $style['handle'] ]->src = false; + if ( empty( $wp_styles->registered[ $style['handle'] ]->extra['after'] ) ) { + $wp_styles->registered[ $style['handle'] ]->extra['after'] = array(); + } + array_unshift( $wp_styles->registered[ $style['handle'] ]->extra['after'], $style['css'] ); + + // Add the styles size to the $total_inline_size var. + $total_inline_size += (int) $style['size']; + } + } +} diff --git a/tests/phpunit/tests/blocks/register.php b/tests/phpunit/tests/blocks/register.php index bedcf799e4..8369a8b524 100644 --- a/tests/phpunit/tests/blocks/register.php +++ b/tests/phpunit/tests/blocks/register.php @@ -270,6 +270,12 @@ class WP_Test_Block_Register extends WP_UnitTestCase { $this->assertSame( 'unit-tests-test-block-style', $result ); $this->assertSame( 'replace', wp_styles()->get_data( 'unit-tests-test-block-style', 'rtl' ) ); + + // @ticket 50328 + $this->assertSame( + wp_normalize_path( realpath( DIR_TESTDATA . '/blocks/notice/block.css' ) ), + wp_normalize_path( wp_styles()->get_data( 'unit-tests-test-block-style', 'path' ) ) + ); } /** @@ -366,6 +372,12 @@ class WP_Test_Block_Register extends WP_UnitTestCase { $this->assertSame( 'tests-notice-script', $result->script ); $this->assertSame( 'tests-notice-editor-style', $result->editor_style ); $this->assertSame( 'tests-notice-style', $result->style ); + + // @ticket 50328 + $this->assertSame( + wp_normalize_path( realpath( DIR_TESTDATA . '/blocks/notice/block.css' ) ), + wp_normalize_path( wp_styles()->get_data( 'unit-tests-test-block-style', 'path' ) ) + ); } /** diff --git a/tests/phpunit/tests/dependencies/styles.php b/tests/phpunit/tests/dependencies/styles.php index 1ffddc95f6..c02a8cc7dd 100644 --- a/tests/phpunit/tests/dependencies/styles.php +++ b/tests/phpunit/tests/dependencies/styles.php @@ -420,4 +420,44 @@ CSS; wp_common_block_scripts_and_styles(); $this->assertTrue( wp_style_is( 'wp-block-library-theme' ) ); } + + /** + * Tests that the main "style.css" file gets enqueued when the site doesn't opt-in to separate_core_block_assets. + * + * @ticket 50263 + */ + function test_common_block_styles_for_viewing_without_split_styles() { + add_filter( 'separate_core_block_assets', '__return_false' ); + wp_default_styles( $GLOBALS['wp_styles'] ); + + $this->assertSame( + $GLOBALS['wp_styles']->registered['wp-block-library']->src, + '/' . WPINC . '/css/dist/block-library/style.css' + ); + } + + /** + * Tests that the "common.css" file gets enqueued when the site opts-in to separate_core_block_assets. + * + * @ticket 50263 + */ + function test_common_block_styles_for_viewing_with_split_styles() { + add_filter( 'separate_core_block_assets', '__return_false' ); + wp_default_styles( $GLOBALS['wp_styles'] ); + + $this->assertSame( + $GLOBALS['wp_styles']->registered['wp-block-library']->src, + '/' . WPINC . '/css/dist/block-library/style.css' + ); + } + + function test_block_styles_for_viewing_with_split_styles() { + add_filter( 'separate_core_block_assets', '__return_true' ); + wp_default_styles( $GLOBALS['wp_styles'] ); + + $this->assertSame( + $GLOBALS['wp_styles']->registered['wp-block-library']->src, + '/' . WPINC . '/css/dist/block-library/common.css' + ); + } } diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 0610a45c19..44cbd90405 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -3788,6 +3788,164 @@ mockedApiResponse.Schema = { } ] }, + "/wp/v2/blocks/(?P