Commit 3641f2bbda for woocommerce

commit 3641f2bbdaa13257bdaf2a9be6dc67f2fc6a6f36
Author: Sam Seay <samueljseay@gmail.com>
Date:   Thu Dec 11 16:30:17 2025 +1300

    Hydrate cartItem state server side to avoid Preact warning (#62155)

diff --git a/plugins/woocommerce/changelog/62155-dev-hydrate-mini-cart-cart-item-state b/plugins/woocommerce/changelog/62155-dev-hydrate-mini-cart-cart-item-state
new file mode 100644
index 0000000000..c9e97e99ed
--- /dev/null
+++ b/plugins/woocommerce/changelog/62155-dev-hydrate-mini-cart-cart-item-state
@@ -0,0 +1,4 @@
+Significance: minor
+Type: fix
+
+Fix a bug where Preact warns in development about inconsistent rendering between SSR content and client.
\ No newline at end of file
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index a8e4010e42..40c1b79a54 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -29700,6 +29700,12 @@ parameters:
 			count: 1
 			path: includes/data-stores/class-wc-product-data-store-cpt.php

+		-
+			message: '#^Call to function is_callable\(\) with ''_prime_post_caches'' will always evaluate to true\.$#'
+			identifier: function.alreadyNarrowedType
+			count: 1
+			path: includes/data-stores/class-wc-product-grouped-data-store-cpt.php
+
 		-
 			message: '#^Method WC_Product\:\:get_children\(\) invoked with 1 parameter, 0 required\.$#'
 			identifier: arguments.count
@@ -71247,12 +71253,6 @@ parameters:
 			count: 1
 			path: src/Blocks/AssetsController.php

-		-
-			message: '#^Constant WOOCOMMERCE_VERSION not found\.$#'
-			identifier: constant.notFound
-			count: 2
-			path: src/Blocks/AssetsController.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Blocks\\AssetsController\:\:enqueue_wc_entities\(\) has no return type specified\.$#'
 			identifier: missingType.return
@@ -73654,19 +73654,19 @@ parameters:
 			path: src/Blocks/BlockTypes/MiniCartItemsBlock.php

 		-
-			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\MiniCartProductsTableBlock\:\:render_experimental_iapi_markup\(\) should return string but returns string\|false\.$#'
+			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\MiniCartProductsTableBlock\:\:render\(\) should return string but returns string\|false\.$#'
 			identifier: return.type
 			count: 1
 			path: src/Blocks/BlockTypes/MiniCartProductsTableBlock.php

 		-
-			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\MiniCartProductsTableBlock\:\:render_experimental_iapi_product_details_item_markup\(\) should return string but returns string\|false\.$#'
+			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\MiniCartProductsTableBlock\:\:render_product_details_item_markup\(\) should return string but returns string\|false\.$#'
 			identifier: return.type
 			count: 1
 			path: src/Blocks/BlockTypes/MiniCartProductsTableBlock.php

 		-
-			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\MiniCartProductsTableBlock\:\:render_experimental_iapi_product_details_markup\(\) should return string but returns string\|false\.$#'
+			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\MiniCartProductsTableBlock\:\:render_product_details_markup\(\) should return string but returns string\|false\.$#'
 			identifier: return.type
 			count: 1
 			path: src/Blocks/BlockTypes/MiniCartProductsTableBlock.php
@@ -73677,12 +73677,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/MiniCartProductsTableBlock.php

-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\MiniCartProductsTableBlock\:\:render_experimental_iapi_markup\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/MiniCartProductsTableBlock.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\MiniCartShoppingButtonBlock\:\:render_experimental_iapi_markup\(\) should return string but returns string\|false\.$#'
 			identifier: return.type
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/MiniCartProductsTableBlock.php b/plugins/woocommerce/src/Blocks/BlockTypes/MiniCartProductsTableBlock.php
index 20d821dd37..e1a1405989 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/MiniCartProductsTableBlock.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/MiniCartProductsTableBlock.php
@@ -1,8 +1,6 @@
 <?php
 namespace Automattic\WooCommerce\Blocks\BlockTypes;

-use Automattic\WooCommerce\Admin\Features\Features;
-
 /**
  * MiniCartProductsTableBlock class.
  */
@@ -24,28 +22,31 @@ class MiniCartProductsTableBlock extends AbstractInnerBlock {
 	 * @return string Rendered block type output.
 	 */
 	protected function render( $attributes, $content, $block ) {
-		if ( Features::is_enabled( 'experimental-iapi-mini-cart' ) ) {
-			return $this->render_experimental_iapi_markup( $attributes, $content, $block );
-		}
-
-		return $content;
-	}
-
-	/**
-	 * Render experimental iAPI block markup.
-	 *
-	 * @param array    $attributes Block attributes.
-	 * @param string   $content    Block content.
-	 * @param WP_Block $block      Block instance.
-	 * @return string Rendered block type output.
-	 */
-	protected function render_experimental_iapi_markup( $attributes, $content, $block ) {
 		$screen_reader_text = __( 'Products in cart', 'woocommerce' );
 		$remove_item_label  = __( 'Remove item', 'woocommerce' );
 		$head_product_label = __( 'Product', 'woocommerce' );
 		$head_details_label = __( 'Details', 'woocommerce' );
 		$head_total_label   = __( 'Total', 'woocommerce' );

+		wp_interactivity_state(
+			$this->get_full_block_name(),
+			array(
+				'cartItem' => function () {
+					$context = wp_interactivity_get_context( 'woocommerce' );
+					$cart_state = wp_interactivity_state( 'woocommerce' );
+					$item_key = $context['cartItem']['key'];
+
+					foreach ( $cart_state['cart']['items'] as $item ) {
+						if ( $item['key'] === $item_key ) {
+							return $item;
+						}
+					}
+
+					return null;
+				},
+			)
+		);
+
 		// translators: %s is the name of the product in cart.
 		$reduce_quantity_label = __( 'Reduce quantity of %s', 'woocommerce' );

@@ -181,8 +182,8 @@ class MiniCartProductsTableBlock extends AbstractInnerBlock {
 										<div data-wp-watch="callbacks.itemShortDescription" >
 											<div class="wc-block-components-product-metadata__description"></div>
 										</div>
-										<?php echo $this->render_experimental_iapi_product_details_markup( 'item_data' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
-										<?php echo $this->render_experimental_iapi_product_details_markup( 'variation' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+										<?php echo $this->render_product_details_markup( 'item_data' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+										<?php echo $this->render_product_details_markup( 'variation' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
 									</div>
 									<div class="wc-block-cart-item__quantity">
 										<div class="wc-block-components-quantity-selector" data-wp-bind--hidden="state.cartItem.sold_individually">
@@ -263,7 +264,7 @@ class MiniCartProductsTableBlock extends AbstractInnerBlock {
 	 * @param string $property The property to render in the product details markup.
 	 * @return string Rendered product details output.
 	 */
-	protected function render_experimental_iapi_product_details_markup( $property ) {
+	protected function render_product_details_markup( $property ) {
 		$context = array( 'dataProperty' => $property );

 		// If the property is item_data, so not a variation, we need to skip the text directive.
@@ -276,7 +277,7 @@ class MiniCartProductsTableBlock extends AbstractInnerBlock {
 			class="wc-block-components-product-details"
 			data-wp-bind--hidden="state.shouldHideSingleProductDetails"
 		>
-			<?php echo $this->render_experimental_iapi_product_details_item_markup( 'div', $is_item_data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+			<?php echo $this->render_product_details_item_markup( 'div', $is_item_data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
 		</div>
 		<ul
 			<?php echo wp_interactivity_data_wp_context( $context ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
@@ -287,7 +288,7 @@ class MiniCartProductsTableBlock extends AbstractInnerBlock {
 				data-wp-each--item-data="state.cartItem.<?php echo esc_attr( $property ); ?>"
 				data-wp-each-key="state.cartItemDataKey"
 			>
-				<?php echo $this->render_experimental_iapi_product_details_item_markup( 'li', $is_item_data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+				<?php echo $this->render_product_details_item_markup( 'li', $is_item_data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
 			</template>
 		</ul>
 		<?php
@@ -301,7 +302,7 @@ class MiniCartProductsTableBlock extends AbstractInnerBlock {
 	 * @param bool   $is_item_data Whether the item is of item_data type.
 	 * @return string Rendered product detail item output based on item type.
 	 */
-	private function render_experimental_iapi_product_details_item_markup( $tag_name, $is_item_data = false ) {
+	private function render_product_details_item_markup( $tag_name, $is_item_data = false ) {
 		ob_start();
 		?>
 	<<?php echo tag_escape( $tag_name ); ?>