From 1c7646e8463cbb5332ed15b7b7b951fbc53e1190 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Mon, 26 Sep 2022 19:18:48 +0200 Subject: [PATCH 1/7] Add `wp-inner-block` attribute --- block-hydration-experiments.php | 57 +++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/block-hydration-experiments.php b/block-hydration-experiments.php index db065620..95920139 100644 --- a/block-hydration-experiments.php +++ b/block-hydration-experiments.php @@ -53,6 +53,15 @@ function block_hydration_experiments_init() function bhe_block_wrapper($block_content, $block, $instance) { + // Append the `wp-inner-block` attribute for inner blocks of interactive blocks. + if ( isset($instance->parsed_block['isInnerBlock']) ) { + $block_content = bhe_append_block_attributes( + $instance->name, + $block_content, + 'wp-inner-block' + ); + } + $block_type = $instance->block_type; if (!block_has_support($block_type, ['view'])) { @@ -111,32 +120,60 @@ function bhe_block_wrapper($block_content, $block, $instance) esc_attr($hydration_technique) ); + // Append block wrapper attributes. + $block_content = bhe_append_block_attributes( + $instance->name, + $block_content, + $block_wrapper_attributes + ); + // The block content comes between two line breaks that seem to be included during block // serialization, corresponding to those between the block markup and the block content. // // They need to be removed here; otherwise, the preact hydration fails. $block_content = substr($block_content, 1, -1); - // Append all wp block attributes after the class attribute containing the block class name. - // Elements with that class name are supposed to be those with the wrapper attributes. - // - // We could use `WP_HTML_Walker` (see https://github.com/WordPress/gutenberg/pull/42485) in the - // future if that PR is finally merged. + return $block_content; + +} + +add_filter('render_block', 'bhe_block_wrapper', 10, 3); + +/** + * Add a flag to mark inner blocks of interactive blocks. + */ +function bhe_inner_blocks($parsed_block, $source_block, $parent_block) { + $parent_block_type = $parent_block->block_type; + + if (block_has_support($parent_block_type, ['view'])) { + $parsed_block['isInnerBlock'] = true; + } + + return $parsed_block; +} - // This replace is similar to the one used in Gutenberg (see +add_filter('render_block_data', 'bhe_inner_blocks', 10, 3); + +/** + * Append attributes after the class attribute containing the block class name. Elements with that + * class name are supposed to be those with the wrapper attributes. + * + * TODO: use `WP_HTML_Tag_Processor` (see https://github.com/WordPress/gutenberg/pull/42485) once + * the API is released. + */ +function bhe_append_block_attributes($block_name, $block_content, $attributes) { + // Generate block class name, using a replace similar to the one used in Gutenberg (see // https://github.com/WordPress/gutenberg/blob/1582c723f31ce0f728cd4dcc1af37821342eeaaa/packages/blocks/src/api/serializer.js#L41-L44). $block_classname = 'wp-block-' . preg_replace( ['/\//', '/^core-/'], ['-', ''], - $instance->name + $block_name ); // Be aware that this pattern could not cover some edge cases. $class_pattern = '/class="\s*(?:[\w\s-]\s+)*' . $block_classname . '(?:\s+[\w-]+)*\s*"/'; - $class_replacement = '$0 ' . $block_wrapper_attributes; + $class_replacement = '$0 ' . $attributes; $block_content = preg_replace( $class_pattern, $class_replacement, $block_content, 1 ); return $block_content; } - -add_filter('render_block', 'bhe_block_wrapper', 10, 3); From fe4994b561c7d82a558edc24cd630611b9ea03f7 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Mon, 26 Sep 2022 19:38:16 +0200 Subject: [PATCH 2/7] Fix PHP format --- block-hydration-experiments.php | 48 ++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/block-hydration-experiments.php b/block-hydration-experiments.php index 95920139..1892f260 100644 --- a/block-hydration-experiments.php +++ b/block-hydration-experiments.php @@ -54,7 +54,7 @@ function block_hydration_experiments_init() function bhe_block_wrapper($block_content, $block, $instance) { // Append the `wp-inner-block` attribute for inner blocks of interactive blocks. - if ( isset($instance->parsed_block['isInnerBlock']) ) { + if (isset($instance->parsed_block['isInnerBlock'])) { $block_content = bhe_append_block_attributes( $instance->name, $block_content, @@ -102,23 +102,22 @@ function bhe_block_wrapper($block_content, $block, $instance) WP_Block_Supports::$block_to_render = $previous_block_to_render; // Generate all required wrapper attributes. - $block_wrapper_attributes = - sprintf( - 'data-wp-block-type="%1$s" ' . + $block_wrapper_attributes = sprintf( + 'data-wp-block-type="%1$s" ' . 'data-wp-block-uses-block-context="%2$s" ' . 'data-wp-block-provides-block-context="%3$s" ' . 'data-wp-block-attributes="%4$s" ' . 'data-wp-block-sourced-attributes="%5$s" ' . 'data-wp-block-props="%6$s" ' . 'data-wp-block-hydration="%7$s"', - esc_attr($block['blockName']), - esc_attr(json_encode($block_type->uses_context)), - esc_attr(json_encode($block_type->provides_context)), - esc_attr(json_encode($attributes)), - esc_attr(json_encode($sourced_attributes)), - esc_attr(json_encode($block_props)), - esc_attr($hydration_technique) - ); + esc_attr($block['blockName']), + esc_attr(json_encode($block_type->uses_context)), + esc_attr(json_encode($block_type->provides_context)), + esc_attr(json_encode($attributes)), + esc_attr(json_encode($sourced_attributes)), + esc_attr(json_encode($block_props)), + esc_attr($hydration_technique) + ); // Append block wrapper attributes. $block_content = bhe_append_block_attributes( @@ -134,7 +133,6 @@ function bhe_block_wrapper($block_content, $block, $instance) $block_content = substr($block_content, 1, -1); return $block_content; - } add_filter('render_block', 'bhe_block_wrapper', 10, 3); @@ -142,7 +140,8 @@ function bhe_block_wrapper($block_content, $block, $instance) /** * Add a flag to mark inner blocks of interactive blocks. */ -function bhe_inner_blocks($parsed_block, $source_block, $parent_block) { +function bhe_inner_blocks($parsed_block, $source_block, $parent_block) +{ $parent_block_type = $parent_block->block_type; if (block_has_support($parent_block_type, ['view'])) { @@ -161,19 +160,24 @@ function bhe_inner_blocks($parsed_block, $source_block, $parent_block) { * TODO: use `WP_HTML_Tag_Processor` (see https://github.com/WordPress/gutenberg/pull/42485) once * the API is released. */ -function bhe_append_block_attributes($block_name, $block_content, $attributes) { +function bhe_append_block_attributes($block_name, $block_content, $attributes) +{ // Generate block class name, using a replace similar to the one used in Gutenberg (see // https://github.com/WordPress/gutenberg/blob/1582c723f31ce0f728cd4dcc1af37821342eeaaa/packages/blocks/src/api/serializer.js#L41-L44). - $block_classname = 'wp-block-' . preg_replace( - ['/\//', '/^core-/'], - ['-', ''], - $block_name - ); + $block_classname = + 'wp-block-' . + preg_replace(['/\//', '/^core-/'], ['-', ''], $block_name); // Be aware that this pattern could not cover some edge cases. - $class_pattern = '/class="\s*(?:[\w\s-]\s+)*' . $block_classname . '(?:\s+[\w-]+)*\s*"/'; + $class_pattern = + '/class="\s*(?:[\w\s-]\s+)*' . $block_classname . '(?:\s+[\w-]+)*\s*"/'; $class_replacement = '$0 ' . $attributes; - $block_content = preg_replace( $class_pattern, $class_replacement, $block_content, 1 ); + $block_content = preg_replace( + $class_pattern, + $class_replacement, + $block_content, + 1 + ); return $block_content; } From aed860568e770606f2ca7138c0c80e4790fdb8d9 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Tue, 27 Sep 2022 11:28:09 +0200 Subject: [PATCH 3/7] Append `wp-inner-block` to blocks without class --- block-hydration-experiments.php | 34 +++++++++++---------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/block-hydration-experiments.php b/block-hydration-experiments.php index 1892f260..36590e74 100644 --- a/block-hydration-experiments.php +++ b/block-hydration-experiments.php @@ -55,7 +55,7 @@ function bhe_block_wrapper($block_content, $block, $instance) { // Append the `wp-inner-block` attribute for inner blocks of interactive blocks. if (isset($instance->parsed_block['isInnerBlock'])) { - $block_content = bhe_append_block_attributes( + $block_content = bhe_append_attributes( $instance->name, $block_content, 'wp-inner-block' @@ -120,7 +120,7 @@ function bhe_block_wrapper($block_content, $block, $instance) ); // Append block wrapper attributes. - $block_content = bhe_append_block_attributes( + $block_content = bhe_append_attributes( $instance->name, $block_content, $block_wrapper_attributes @@ -142,9 +142,10 @@ function bhe_block_wrapper($block_content, $block, $instance) */ function bhe_inner_blocks($parsed_block, $source_block, $parent_block) { - $parent_block_type = $parent_block->block_type; - - if (block_has_support($parent_block_type, ['view'])) { + if ( + isset($parent_block) && + block_has_support($parent_block->block_type, ['view']) + ) { $parsed_block['isInnerBlock'] = true; } @@ -154,30 +155,17 @@ function bhe_inner_blocks($parsed_block, $source_block, $parent_block) add_filter('render_block_data', 'bhe_inner_blocks', 10, 3); /** - * Append attributes after the class attribute containing the block class name. Elements with that - * class name are supposed to be those with the wrapper attributes. + * Append attributes to the block wrapper element, which is assumed to be the first one. * * TODO: use `WP_HTML_Tag_Processor` (see https://github.com/WordPress/gutenberg/pull/42485) once * the API is released. */ -function bhe_append_block_attributes($block_name, $block_content, $attributes) +function bhe_append_attributes($block_name, $block_content, $attributes) { - // Generate block class name, using a replace similar to the one used in Gutenberg (see - // https://github.com/WordPress/gutenberg/blob/1582c723f31ce0f728cd4dcc1af37821342eeaaa/packages/blocks/src/api/serializer.js#L41-L44). - $block_classname = - 'wp-block-' . - preg_replace(['/\//', '/^core-/'], ['-', ''], $block_name); - // Be aware that this pattern could not cover some edge cases. - $class_pattern = - '/class="\s*(?:[\w\s-]\s+)*' . $block_classname . '(?:\s+[\w-]+)*\s*"/'; - $class_replacement = '$0 ' . $attributes; - $block_content = preg_replace( - $class_pattern, - $class_replacement, - $block_content, - 1 - ); + $pattern = '/^\s*<[^>]+/'; + $replacement = '$0 ' . $attributes; + $block_content = preg_replace($pattern, $replacement, $block_content, 1); return $block_content; } From c37fa79db7a4228e4a617f92387114ae6155dbc9 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Tue, 27 Sep 2022 13:55:02 +0200 Subject: [PATCH 4/7] Use `wp-inner-block` attr to detect inner blocks --- src/gutenberg-packages/to-vdom.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/gutenberg-packages/to-vdom.js b/src/gutenberg-packages/to-vdom.js index ee5adfa3..24944842 100644 --- a/src/gutenberg-packages/to-vdom.js +++ b/src/gutenberg-packages/to-vdom.js @@ -46,6 +46,12 @@ export default function toVdom(n) { // Walk child nodes and return vDOM children. const children = [].map.call(n.childNodes, toVdom).filter(exists); + // Create an array of inner blocks if they are found in children. + const isInnerBlock = ({ props }) => props && 'wp-inner-block' in props; + if (children.some(isInnerBlock)) { + innerBlocksFound = children.filter(isInnerBlock); + } + // Add inner blocks. if (wpBlock.type && innerBlocksFound) { wpBlock.innerBlocks = innerBlocksFound; @@ -58,11 +64,6 @@ export default function toVdom(n) { // Create vNode. Note that all `wpBlock` props should exist now to make directives work. const vNode = h(type, props, children); - // Save a renference to this vNode if it's an ` wrapper. - if (type === 'wp-inner-blocks') { - innerBlocksFound = vNode; - } - return vNode; } From ac29856634caa2b16fdfece78ff8cfb9788cb1d6 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Tue, 27 Sep 2022 14:00:00 +0200 Subject: [PATCH 5/7] Remove `wp-inner-blocks` wrapper from save --- src/gutenberg-packages/wordpress-blocks.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/gutenberg-packages/wordpress-blocks.js b/src/gutenberg-packages/wordpress-blocks.js index cd1938c9..2360c50f 100644 --- a/src/gutenberg-packages/wordpress-blocks.js +++ b/src/gutenberg-packages/wordpress-blocks.js @@ -5,17 +5,14 @@ const Wrapper = (Comp) => ({ attributes }) => ( - <> - {/* Block Context is not available during save - https://wordpress.slack.com/archives/C02QB2JS7/p1649347999484329 */} - - - - + // Block Context is not available during save + // https://wordpress.slack.com/archives/C02QB2JS7/p1649347999484329 + ); export const registerBlockType = (name, { edit, view, ...rest }) => { From ff129a899a016019be91ddc26e4f8019bf19e1d3 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Tue, 27 Sep 2022 17:02:15 +0200 Subject: [PATCH 6/7] Use `trim` instead of `substr` --- block-hydration-experiments.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block-hydration-experiments.php b/block-hydration-experiments.php index 36590e74..a358375d 100644 --- a/block-hydration-experiments.php +++ b/block-hydration-experiments.php @@ -126,11 +126,11 @@ function bhe_block_wrapper($block_content, $block, $instance) $block_wrapper_attributes ); - // The block content comes between two line breaks that seem to be included during block + // The block content comes between line breaks that seem to be included during block // serialization, corresponding to those between the block markup and the block content. // // They need to be removed here; otherwise, the preact hydration fails. - $block_content = substr($block_content, 1, -1); + $block_content = trim($block_content); return $block_content; } From 07d04e9f7b30983a59ed6b4f4aebb97dd7838025 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Tue, 27 Sep 2022 17:02:36 +0200 Subject: [PATCH 7/7] Keep nodes between inner blocks --- src/gutenberg-packages/to-vdom.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/gutenberg-packages/to-vdom.js b/src/gutenberg-packages/to-vdom.js index 24944842..79260f66 100644 --- a/src/gutenberg-packages/to-vdom.js +++ b/src/gutenberg-packages/to-vdom.js @@ -46,10 +46,13 @@ export default function toVdom(n) { // Walk child nodes and return vDOM children. const children = [].map.call(n.childNodes, toVdom).filter(exists); - // Create an array of inner blocks if they are found in children. + // Create an array of inner blocks if they are found in children. All nodes in between (e.g., + // line breaks, white spaces, etc.) are preserved in order to prevent hydration failure. const isInnerBlock = ({ props }) => props && 'wp-inner-block' in props; if (children.some(isInnerBlock)) { - innerBlocksFound = children.filter(isInnerBlock); + const first = children.findIndex(isInnerBlock); + const last = children.findLastIndex(isInnerBlock); + innerBlocksFound = children.slice(first, last + 1); } // Add inner blocks.