Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - Allow different block-styles loading methods #27716

Closed
wants to merge 12 commits into from
218 changes: 218 additions & 0 deletions lib/client-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -663,3 +663,221 @@ function gutenberg_extend_block_editor_settings_with_fse_theme_flag( $settings )
return $settings;
}
add_filter( 'block_editor_settings', 'gutenberg_extend_block_editor_settings_with_fse_theme_flag' );

/**
* Filters the block being rendered in render_block(), before it's processed.
*
* @param array $parsed_block The block being rendered.
*
* @return array
*/
function gutenberg_modify_render_block_data_assets_loading( $parsed_block ) {

// Don't run while in the editor or wp-admin.
if ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
return $parsed_block;
}

// Performance improvement:
// Static var to avoid printing the same style multiple times.
static $processed;
if ( ! $processed ) {
$processed = array();
}

// Early return if this block-type has already been processed, or is null.
if ( isset( $parsed_block['blockName'] ) && ( ! $parsed_block['blockName'] || in_array( $parsed_block['blockName'], $processed, true ) ) ) {
return $parsed_block;
}

// Get the registered block-type.
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $parsed_block['blockName'] );

// Early return if there is no style defined.
if ( ! $block_type || empty( $block_type->style ) ) {
return $parsed_block;
}

$method = 'enqueue';
/**
* The load method.
*
* @param string $param The loading method.
* @param WP_Block_Type $block_type The block-type.
*
* @return string
*/
$method = apply_filters( 'gutenberg_block_type_assets_load_method', $method, $block_type );

switch ( $method ) {
case 'inline':
/**
* Inline styles on render_block.
*/
add_filter(
'render_block',
/**
* Print inline styles for blocks that need it.
*
* @param string $html The block HTML contents.
*
* @return string Returns the HTML.
*/
function( $html ) use ( $block_type ) {
global $wp_styles;

// Early return if we can't locate the style.
if ( ! isset( $wp_styles->registered[ $block_type->style ] ) ) {
return $html;
}

$block_style = $wp_styles->registered[ $block_type->style ];
$path = str_replace( trailingslashit( site_url() ), trailingslashit( ABSPATH ), $block_style->src );
if ( file_exists( $path ) ) {
$block_css = file_get_contents( $path );
// Add styles that were added via wp_add_inline_style().
if ( is_array( $block_style->extra ) && isset( $block_style->extra['after'] ) ) {
$block_css .= implode( '', $block_style->extra['after'] );
}

// If SCRIPT_DEBUG is not enabled, then minify styles.
if ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) {
$block_css = gutenberg_minify_styles( $block_css );
}

$block_style_el = '<style id="' . $block_type->style . '-css">' . $block_css . '</style>';

// If the block doesn't have any html then echo the style directly,
// otherwise append the styles to the block-contents.
if ( empty( trim( $html ) ) ) {
echo $block_style_el;
} else {
$html .= $block_style_el;
}

// Remove the stylesheet from $wp_styles.
unset( $wp_styles->registered[ $block_type->style ] );
}
return $html;
},
10
);
break;

case 'inline-footer':
/**
* Inline styles in the footer.
*/
add_action(
'wp_footer',
function() use ( $block_type ) {
global $wp_styles;

$style = $wp_styles->registered[ $block_type->style ];
$path = str_replace( trailingslashit( site_url() ), trailingslashit( ABSPATH ), $style->src );

if ( isset( $wp_styles->registered[ $block_type->style ] ) && file_exists( $path ) ) {
echo '<style id="' . esc_attr( $block_type->style ) . '-css">';
include $path;

// Add styles that were added via wp_add_inline_style().
if ( is_array( $style->extra ) && isset( $style->extra['after'] ) ) {
echo implode( '', $style->extra['after'] );
}

echo '</style>';

// Remove the stylesheet from $wp_styles.
unset( $wp_styles->registered[ $block_type->style ] );
}
},
1
);
break;

case 'async-head':
// Avoid adding the script more than once.
static $script_added;
global $wp_styles;

if ( ! $script_added ) {
/**
* Injects the styles in <head> via JS.
* Styles get added as <link> elements.
*/
?>
<script id="wp-enqueue-style-script">
function wpEnqueueStyle( handle, src, deps, ver, media ) {

// Early exit if element already exists.
if ( document.getElementById( handle + '-css' ) ) {
return;
}

// Create the element.
var style = document.createElement( 'link' ),
isFirst = ! window.wpEnqueueStyleLastInjectedEl,
injectEl = isFirst ? document.head : document.getElementById( window.wpEnqueueStyleLastInjectedEl ),
injectPos = isFirst ? 'afterbegin' : 'afterend';

// Add element props for the stylesheet.
style.id = handle + '-css';
style.rel = 'stylesheet';
style.href = src;
if ( ver ) {
style.href += 0 < style.href.indexOf( '?' ) ? '&ver=' + ver : '?ver=' + ver;
}
style.media = media ? media : 'all';

// Set the global var so we know where to add the next style.
// This helps us preserve priorities and inject styles one after the other instead of reversed.
window.wpEnqueueStyleLastInjectedEl = handle + '-css';

// Inject the element.
injectEl.insertAdjacentElement( injectPos, style );
}
</script>
<?php
$script_added = true;
}

$style = $wp_styles->registered[ $block_type->style ];
// Call wpEnqueueStyle() to inject style in <head>.
echo "<script>wpEnqueueStyle('{$style->handle}', '{$style->src}', [], '{$style->ver}', '{$style->args}')</script>";

// Add styles that were added via wp_add_inline_style().
if ( is_array( $style->extra ) && isset( $style->extra['after'] ) ) {
echo '<style id="' . esc_attr( $block_type->style ) . '-css">';
echo implode( '', $style->extra['after'] );
echo '</style>';
}

// Remove the stylesheet from $wp_styles.
unset( $wp_styles->registered[ $block_type->style ] );
break;

default: // No need to do anything.
break;
}

// Add this block-type to the $processed array
// to avoid re-processing it next time we encounter the same block-type.
$processed[] = $parsed_block['blockName'];

return $parsed_block;
}
add_filter( 'render_block_data', 'gutenberg_modify_render_block_data_assets_loading' );

/**
* Removes whitespace and comments from CSS.
*
* @param string $css The CSS to be minified.
* @return string The minified CSS.
*/
function gutenberg_minify_styles( $css ) {
$re1 = '(?sx)("(?:[^"\\\\]++|\\\\.)*+"|\'(?:[^\'\\\\]++|\\\\.)*+\')|/\\* (?> .*? \\*/ )';
$re2 = '(?six)("(?:[^"\\\\]++|\\\\.)*+"|\'(?:[^\'\\\\]++|\\\\.)*+\')|\\s*+ ; \\s*+ ( } ) \\s*+|\\s*+ ( [*$~^|]?+= | [{};,>~+-] | !important\\b ) \\s*+|( [[(:] ) \\s++|\\s++ ( [])] )|\\s++ ( : ) \\s*+(?!(?>[^{}"\']++|"(?:[^"\\\\]++|\\\\.)*+"|\'(?:[^\'\\\\]++|\\\\.)*+\')*+{)|^ \\s++ | \\s++ \\z|(\\s)\\s+';
$css = preg_replace( "%$re1%", '$1', $css );

return preg_replace( "%$re2%", '$1$2$3$4$5$6$7', $css );
}