Commit 5687cfd67d for woocommerce

commit 5687cfd67d912fa709fc43d1b75131268f69672e
Author: Albert Juhé Lluveras <contact@albertjuhe.com>
Date:   Thu Jan 8 11:26:33 2026 +0100

    Add to Cart + Options: Avoid loading unnecessary scripts when rendering 3rd-party product types (#62681)

    * Add to Cart + Options: Avoid loading unnecessary scripts when rendering 3rd-party product types

    * Add changelog file

    * Update comments

    * PHPStan fixes

    * Fix PHPStan baseline file

    * Strengthen context access

    * Add missing 'file_exists()' to make both checks the same

    * Strengthen context access (II)

diff --git a/plugins/woocommerce/changelog/fix-add-to-cart-with-options-3rd-party-product-type-script b/plugins/woocommerce/changelog/fix-add-to-cart-with-options-3rd-party-product-type-script
new file mode 100644
index 0000000000..0e040b2cfd
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-add-to-cart-with-options-3rd-party-product-type-script
@@ -0,0 +1,4 @@
+Significance: patch
+Type: performance
+
+Add to Cart + Options: Avoid loading unnecessary scripts when rendering 3rd-party product types
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/block.json b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/block.json
index d48c99347b..23a7b17da4 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/block.json
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/block.json
@@ -19,7 +19,6 @@
 	},
 	"apiVersion": 3,
 	"$schema": "https://schemas.wp.org/trunk/block.json",
-	"viewScriptModule": "woocommerce/add-to-cart-with-options",
 	"style": "file:../woocommerce/add-to-cart-with-options-style.css",
 	"editorStyle": "file:../woocommerce/add-to-cart-with-options-editor.css"
 }
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 061394480e..81813ff2f6 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -57903,12 +57903,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/AddToCartForm.php

-		-
-			message: '#^Access to property \$context on an unknown class Automattic\\WooCommerce\\Blocks\\BlockTypes\\AddToCartWithOptions\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php
-
 		-
 			message: '#^Call to an undefined method WC_Product\:\:get_available_variations\(\)\.$#'
 			identifier: method.notFound
@@ -57939,18 +57933,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php

-		-
-			message: '#^One or more @param tags has an invalid name or invalid syntax\.$#'
-			identifier: phpDoc.parseError
-			count: 1
-			path: src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php
-
-		-
-			message: '#^PHPDoc tag @param has invalid value \(mixed string\|boolean The template part path if it exists\)\: Unexpected token "string", expected variable at offset 121 on line 5$#'
-			identifier: phpDoc.parseError
-			count: 1
-			path: src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php
-
 		-
 			message: '#^Parameter \#1 \$amount of function wc_stock_amount expects float\|int, array\|string given\.$#'
 			identifier: argument.type
@@ -57975,12 +57957,6 @@ parameters:
 			count: 4
 			path: src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php

-		-
-			message: '#^Parameter \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\AddToCartWithOptions\\AddToCartWithOptions\:\:render\(\) has invalid type Automattic\\WooCommerce\\Blocks\\BlockTypes\\AddToCartWithOptions\\WP_Block\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php
-
 		-
 			message: '#^Cannot access property \$ID on WP_Post\|null\.$#'
 			identifier: property.nonObject
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartForm.php b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartForm.php
index 1172d454b1..ff5f78321e 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartForm.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartForm.php
@@ -37,7 +37,6 @@ class AddToCartForm extends AbstractBlock {
 		return wp_parse_args( $attributes, $defaults );
 	}

-
 	/**
 	 * Enqueue assets specific to this block.
 	 * We enqueue frontend scripts only if the quantitySelectorStyle is set to 'stepper'.
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php
index 25cbbb3d38..abdbe1a2a1 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php
@@ -25,6 +25,58 @@ class AddToCartWithOptions extends AbstractBlock {
 	 */
 	protected $block_name = 'add-to-cart-with-options';

+	/**
+	 * Get the template part path for a product type.
+	 *
+	 * @param string $product_type The product type.
+	 * @return string|bool The template part path if it exists, false otherwise.
+	 */
+	protected function get_template_part_path( $product_type ) {
+		if ( in_array( $product_type, array( ProductType::SIMPLE, ProductType::EXTERNAL, ProductType::VARIABLE, ProductType::GROUPED ), true ) ) {
+			return Package::get_path() . 'templates/' . BlockTemplateUtils::DIRECTORY_NAMES['TEMPLATE_PARTS'] . '/' . $product_type . '-product-add-to-cart-with-options.html';
+		}
+
+		/**
+		 * Experimental filter for extensions to register a block template part
+		 * for a product type.
+		 *
+		 * @since 9.9.0
+		 * @param string|boolean $template_part_path The template part path if it exists
+		 * @param string $product_type The product type
+		 */
+		return apply_filters( '__experimental_woocommerce_' . $product_type . '_add_to_cart_with_options_block_template_part', false, $product_type );
+	}
+
+	/**
+	 * Enqueue assets specific to this block.
+	 * We enqueue frontend scripts only if the product type has a block template
+	 * part (that's WC core product types and extensions that migrated to block
+	 * templates).
+	 *
+	 * @param array     $attributes Block attributes.
+	 * @param string    $content Block content.
+	 * @param \WP_Block $block Block instance.
+	 *
+	 * @return void
+	 */
+	protected function enqueue_assets( $attributes, $content, $block ) {
+		$product_id = ( is_object( $block ) && property_exists( $block, 'context' ) && is_array( $block->context ) && array_key_exists( 'postId', $block->context ) ) ? $block->context['postId'] : null;
+
+		if ( isset( $product_id ) ) {
+			$rendered_product = wc_get_product( $product_id );
+
+			if ( $rendered_product instanceof \WC_Product ) {
+				$template_part_path = $this->get_template_part_path( $rendered_product->get_type() );
+
+				if ( is_string( $template_part_path ) && '' !== $template_part_path && file_exists( $template_part_path ) ) {
+					wp_enqueue_script_module( 'woocommerce/add-to-cart-with-options' );
+				}
+			}
+		}
+
+		parent::enqueue_assets( $attributes, $content, $block );
+	}
+
 	/**
 	 * Extra data passed through from server to client for block.
 	 *
@@ -122,16 +174,16 @@ class AddToCartWithOptions extends AbstractBlock {
 	/**
 	 * Render the block.
 	 *
-	 * @param array    $attributes Block attributes.
-	 * @param string   $content Block content.
-	 * @param WP_Block $block Block instance.
+	 * @param array     $attributes Block attributes.
+	 * @param string    $content Block content.
+	 * @param \WP_Block $block Block instance.
 	 *
-	 * @return string | void Rendered block output.
+	 * @return string|void Rendered block output.
 	 */
 	protected function render( $attributes, $content, $block ) {
 		global $product;

-		$product_id = $block->context['postId'];
+		$product_id = ( is_object( $block ) && property_exists( $block, 'context' ) && is_array( $block->context ) && array_key_exists( 'postId', $block->context ) ) ? $block->context['postId'] : null;

 		if ( ! isset( $product_id ) ) {
 			return '';
@@ -148,21 +200,6 @@ class AddToCartWithOptions extends AbstractBlock {
 		// For variations, we display the simple product form.
 		$product_type = ProductType::VARIATION === $product->get_type() ? ProductType::SIMPLE : $product->get_type();

-		$slug = $product_type . '-product-add-to-cart-with-options';
-
-		if ( in_array( $product_type, array( ProductType::SIMPLE, ProductType::EXTERNAL, ProductType::VARIABLE, ProductType::GROUPED ), true ) ) {
-			$template_part_path = Package::get_path() . 'templates/' . BlockTemplateUtils::DIRECTORY_NAMES['TEMPLATE_PARTS'] . '/' . $slug . '.html';
-		} else {
-			/**
-			 * Filter to declare product type's cart block template is supported.
-			 *
-			 * @since 9.9.0
-			 * @param mixed string|boolean The template part path if it exists
-			 * @param string $product_type The product type
-			 */
-			$template_part_path = apply_filters( '__experimental_woocommerce_' . $product_type . '_add_to_cart_with_options_block_template_part', false, $product_type );
-		}
-
 		$classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes, array(), array( 'extra_classes' ) );
 		$classes            = implode(
 			' ',
@@ -174,8 +211,10 @@ class AddToCartWithOptions extends AbstractBlock {
 			)
 		);

-		if ( is_string( $template_part_path ) && file_exists( $template_part_path ) ) {
+		$template_part_path = $this->get_template_part_path( $product_type );

+		if ( is_string( $template_part_path ) && '' !== $template_part_path && file_exists( $template_part_path ) ) {
+			$slug                   = $product_type . '-product-add-to-cart-with-options';
 			$template_part_contents = '';
 			// Determine if we need to load the template part from the DB, the theme or WooCommerce in that order.
 			$templates_from_db = BlockTemplateUtils::get_block_templates_from_db( array( $slug ), 'wp_template_part' );