Skip to content

Commit

Permalink
Interactivity API: Backport fixes from Core (#59903)
Browse files Browse the repository at this point in the history
* Add mode to directive processing

* Add #59886 wp-develop fix

* Add #6251 fix

* Fix linter

* Include #6261 wp-develop

* Fix spacing

Co-authored-by: c4rl0sbr4v0 <cbravobernal@git.wordpress.org>
Co-authored-by: SantosGuillamot <santosguillamot@git.wordpress.org>
Co-authored-by: sirreal <jonsurrell@git.wordpress.org>
  • Loading branch information
4 people committed Mar 15, 2024
1 parent 69b2229 commit fca6dc1
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -141,20 +141,36 @@ public function config( string $store_namespace, array $config = array() ): arra
* @since 6.5.0
*/
public function print_client_interactivity_data() {
$store = array();
$has_state = ! empty( $this->state_data );
$has_config = ! empty( $this->config_data );
if ( empty( $this->state_data ) && empty( $this->config_data ) ) {
return;
}

$interactivity_data = array();

if ( $has_state || $has_config ) {
if ( $has_config ) {
$store['config'] = $this->config_data;
$config = array();
foreach ( $this->config_data as $key => $value ) {
if ( ! empty( $value ) ) {
$config[ $key ] = $value;
}
if ( $has_state ) {
$store['state'] = $this->state_data;
}
if ( ! empty( $config ) ) {
$interactivity_data['config'] = $config;
}

$state = array();
foreach ( $this->state_data as $key => $value ) {
if ( ! empty( $value ) ) {
$state[ $key ] = $value;
}
}
if ( ! empty( $state ) ) {
$interactivity_data['state'] = $state;
}

if ( ! empty( $interactivity_data ) ) {
wp_print_inline_script_tag(
wp_json_encode(
$store,
$interactivity_data,
JSON_HEX_TAG | JSON_HEX_AMP
),
array(
Expand Down Expand Up @@ -205,6 +221,9 @@ public function add_hooks() {
* @return string The processed HTML content. It returns the original content when the HTML contains unbalanced tags.
*/
public function process_directives( string $html ): string {
if ( ! str_contains( $html, 'data-wp-' ) ) {
return $html;
}
$context_stack = array();
$namespace_stack = array();
$result = $this->process_directives_args( $html, $context_stack, $namespace_stack );
Expand Down Expand Up @@ -286,34 +305,41 @@ private function process_directives_args( string $html, array &$context_stack, a
}
}
/*
* If the matching opener tag didn't have any directives, it can skip the
* processing.
*/
* If the matching opener tag didn't have any directives, it can skip the
* processing.
*/
if ( 0 === count( $directives_prefixes ) ) {
continue;
}

/*
* Sorts the attributes by the order of the `directives_processor` array
* and checks what directives are present in this element. The processing
* order is reversed for tag closers.
*/
$directives_prefixes = array_intersect(
$p->is_tag_closer()
? $directive_processor_prefixes_reversed
: $directive_processor_prefixes,
$directives_prefixes
// Directive processing might be different depending on if it is entering the tag or exiting it.
$modes = array(
'enter' => ! $p->is_tag_closer(),
'exit' => $p->is_tag_closer() || ! $p->has_and_visits_its_closer_tag(),
);
foreach ( $modes as $mode => $should_run ) {
if ( ! $should_run ) {
continue;
}

// Executes the directive processors present in this element.
foreach ( $directives_prefixes as $directive_prefix ) {
$func = is_array( self::$directive_processors[ $directive_prefix ] )
? self::$directive_processors[ $directive_prefix ]
: array( $this, self::$directive_processors[ $directive_prefix ] );
call_user_func_array(
$func,
array( $p, &$context_stack, &$namespace_stack, &$tag_stack )
/*
* Sorts the attributes by the order of the `directives_processor` array
* and checks what directives are present in this element.
*/
$existing_directives_prefixes = array_intersect(
'enter' === $mode ? $directive_processor_prefixes : $directive_processor_prefixes_reversed,
$directives_prefixes
);
foreach ( $existing_directives_prefixes as $directive_prefix ) {
$func = is_array( self::$directive_processors[ $directive_prefix ] )
? self::$directive_processors[ $directive_prefix ]
: array( $this, self::$directive_processors[ $directive_prefix ] );

call_user_func_array(
$func,
array( $p, $mode, &$context_stack, &$namespace_stack, &$tag_stack )
);
}
}
}

Expand Down Expand Up @@ -466,12 +492,13 @@ function ( $matches ) {
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
*/
private function data_wp_interactive_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack ) {
// In closing tags, it removes the last namespace from the stack.
if ( $p->is_tag_closer() ) {
private function data_wp_interactive_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
// When exiting tags, it removes the last namespace from the stack.
if ( 'exit' === $mode ) {
array_pop( $namespace_stack );
return;
}
Expand Down Expand Up @@ -510,12 +537,13 @@ private function data_wp_interactive_processor( WP_Interactivity_API_Directives_
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
*/
private function data_wp_context_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack ) {
// In closing tags, it removes the last context from the stack.
if ( $p->is_tag_closer() ) {
private function data_wp_context_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
// When exiting tags, it removes the last context from the stack.
if ( 'exit' === $mode ) {
array_pop( $context_stack );
return;
}
Expand Down Expand Up @@ -556,11 +584,12 @@ private function data_wp_context_processor( WP_Interactivity_API_Directives_Proc
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
*/
private function data_wp_bind_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack ) {
if ( ! $p->is_tag_closer() ) {
private function data_wp_bind_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
if ( 'enter' === $mode ) {
$all_bind_directives = $p->get_attribute_names_with_prefix( 'data-wp-bind--' );

foreach ( $all_bind_directives as $attribute_name ) {
Expand All @@ -572,15 +601,21 @@ private function data_wp_bind_processor( WP_Interactivity_API_Directives_Process
$attribute_value = $p->get_attribute( $attribute_name );
$result = $this->evaluate( $attribute_value, end( $namespace_stack ), end( $context_stack ) );

if ( null !== $result && ( false !== $result || '-' === $bound_attribute[4] ) ) {
if ( null !== $result && (
false !== $result ||
( strlen( $bound_attribute ) > 5 && '-' === $bound_attribute[4] )
) ) {
/*
* If the result of the evaluation is a boolean and the attribute is
* `aria-` or `data-, convert it to a string "true" or "false". It
* follows the exact same logic as Preact because it needs to
* replicate what Preact will later do in the client:
* https://github.com/preactjs/preact/blob/ea49f7a0f9d1ff2c98c0bdd66aa0cbc583055246/src/diff/props.js#L131C24-L136
*/
if ( is_bool( $result ) && '-' === $bound_attribute[4] ) {
if (
is_bool( $result ) &&
( strlen( $bound_attribute ) > 5 && '-' === $bound_attribute[4] )
) {
$result = $result ? 'true' : 'false';
}
$p->set_attribute( $bound_attribute, $result );
Expand All @@ -600,11 +635,12 @@ private function data_wp_bind_processor( WP_Interactivity_API_Directives_Process
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
*/
private function data_wp_class_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack ) {
if ( ! $p->is_tag_closer() ) {
private function data_wp_class_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
if ( 'enter' === $mode ) {
$all_class_directives = $p->get_attribute_names_with_prefix( 'data-wp-class--' );

foreach ( $all_class_directives as $attribute_name ) {
Expand Down Expand Up @@ -634,11 +670,12 @@ private function data_wp_class_processor( WP_Interactivity_API_Directives_Proces
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
*/
private function data_wp_style_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack ) {
if ( ! $p->is_tag_closer() ) {
private function data_wp_style_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
if ( 'enter' === $mode ) {
$all_style_attributes = $p->get_attribute_names_with_prefix( 'data-wp-style--' );

foreach ( $all_style_attributes as $attribute_name ) {
Expand Down Expand Up @@ -726,11 +763,12 @@ private function merge_style_property( string $style_attribute_value, string $st
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
*/
private function data_wp_text_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack ) {
if ( ! $p->is_tag_closer() ) {
private function data_wp_text_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
if ( 'enter' === $mode ) {
$attribute_value = $p->get_attribute( 'data-wp-text' );
$result = $this->evaluate( $attribute_value, end( $namespace_stack ), end( $context_stack ) );

Expand Down Expand Up @@ -823,10 +861,11 @@ class="screen-reader-text"
*
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
*/
private function data_wp_router_region_processor( WP_Interactivity_API_Directives_Processor $p ) {
if ( ! $p->is_tag_closer() && ! $this->has_processed_router_region ) {
private function data_wp_router_region_processor( WP_Interactivity_API_Directives_Processor $p, string $mode ) {
if ( 'enter' === $mode && ! $this->has_processed_router_region ) {
$this->has_processed_router_region = true;

// Initialize the `core/router` store.
Expand Down Expand Up @@ -862,12 +901,13 @@ private function data_wp_router_region_processor( WP_Interactivity_API_Directive
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $context_stack The reference to the context stack.
* @param array $namespace_stack The reference to the store namespace stack.
* @param array $tag_stack The reference to the tag stack.
*/
private function data_wp_each_processor( WP_Interactivity_API_Directives_Processor $p, array &$context_stack, array &$namespace_stack, array &$tag_stack ) {
if ( ! $p->is_tag_closer() && 'TEMPLATE' === $p->get_tag() ) {
private function data_wp_each_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack, array &$tag_stack ) {
if ( 'enter' === $mode && 'TEMPLATE' === $p->get_tag() ) {
$attribute_name = $p->get_attribute_names_with_prefix( 'data-wp-each' )[0];
$extracted_suffix = $this->extract_prefix_and_suffix( $attribute_name );
$item_name = isset( $extracted_suffix[1] ) ? $this->kebab_to_camel_case( $extracted_suffix[1] ) : 'item';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function wp_interactivity_process_directives_of_interactive_blocks( array $parse
* Uses a priority of 100 to ensure that other filters can edit $parsed_block
* without crashing the SSR.
*/
add_filter( 'render_block_data', 'wp_interactivity_process_directives_of_interactive_blocks', 100, 1 );
add_filter( 'render_block_data', 'wp_interactivity_process_directives_of_interactive_blocks', 100 );
}

if ( ! function_exists( 'wp_interactivity' ) ) {
Expand Down

0 comments on commit fca6dc1

Please sign in to comment.