PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 11.1.0-beta.2
WooCommerce v11.1.0-beta.2
11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / src / Blocks / AssetsController.php
woocommerce / src / Blocks Last commit date
AI 1 year ago AIContent 2 weeks ago Assets 1 month ago BlockTypes 2 weeks ago Domain 2 weeks ago Images 1 year ago Integrations 2 years ago Patterns 2 weeks ago Payments 2 weeks ago Registry 2 years ago SharedStores 3 months ago Shipping 2 weeks ago Templates 3 weeks ago Utils 1 week ago Assets.php 2 years ago AssetsController.php 2 weeks ago BlockPatterns.php 2 weeks ago BlockTemplatesController.php 9 months ago BlockTemplatesRegistry.php 2 weeks ago BlockTypesController.php 2 weeks ago CoreBreadcrumbsCompatibility.php 2 weeks ago DependencyDetection.php 2 weeks ago InboxNotifications.php 2 years ago Installer.php 3 months ago Library.php 2 years ago Options.php 2 years ago Package.php 1 year ago QueryFilters.php 1 month ago TemplateOptions.php 1 year ago
AssetsController.php
578 lines
1 <?php
2 declare( strict_types = 1 );
3
4 namespace Automattic\WooCommerce\Blocks;
5
6 use Automattic\Jetpack\Constants;
7 use Automattic\WooCommerce\Blocks\Assets\Api as AssetApi;
8 use Automattic\WooCommerce\Blocks\Utils\Utils;
9 use Automattic\WooCommerce\Internal\Features\BlockEditorUnifiedAssets;
10
11 /**
12 * AssetsController class.
13 *
14 * @since 5.0.0
15 * @internal
16 */
17 final class AssetsController {
18
19 /**
20 * Asset API interface for various asset registration.
21 *
22 * @var AssetApi
23 */
24 private $api;
25
26 /**
27 * Constructor.
28 *
29 * @param AssetApi $asset_api Asset API interface for various asset registration.
30 */
31 public function __construct( AssetApi $asset_api ) {
32 $this->api = $asset_api;
33 $this->init();
34 }
35
36 /**
37 * Initialize class features.
38 */
39 protected function init() { // phpcs:ignore WooCommerce.Functions.InternalInjectionMethod.MissingPublic
40 add_action( 'init', array( $this, 'register_assets' ) );
41 add_action( 'init', array( $this, 'register_script_modules' ) );
42 add_filter( 'wp_resource_hints', array( $this, 'add_resource_hints' ), 10, 2 );
43 add_action( 'body_class', array( $this, 'add_theme_body_class' ), 1 );
44 add_action( 'admin_body_class', array( $this, 'add_theme_body_class' ), 1 );
45 add_action( 'admin_enqueue_scripts', array( $this, 'update_block_style_dependencies' ), 20 );
46 add_action( 'wp_enqueue_scripts', array( $this, 'update_block_settings_dependencies' ), 100 );
47 add_action( 'admin_enqueue_scripts', array( $this, 'update_block_settings_dependencies' ), 100 );
48 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_wc_entities' ), 100 );
49 add_filter( 'js_do_concat', array( $this, 'skip_boost_minification_for_cart_checkout' ), 10, 2 );
50 }
51
52 /**
53 * Register script modules.
54 */
55 public function register_script_modules() {
56 // Right now we only have one script modules build for supported interactivity API powered block front-ends.
57 // We generate a combined asset file for that via DependencyExtractionWebpackPlugin to make registration more
58 // efficient.
59 $asset_data = $this->api->get_asset_data(
60 $this->api->get_block_asset_build_path( 'interactivity-blocks-frontend-assets', 'php' )
61 );
62
63 foreach ( $asset_data as $handle => $data ) {
64 $handle_without_js = str_replace( '.js', '', $handle );
65 wp_register_script_module( $handle_without_js, plugins_url( $this->api->get_block_asset_build_path( $handle_without_js ), dirname( __DIR__ ) ), $data['dependencies'], $data['version'] );
66 }
67 }
68
69 /**
70 * Register block scripts & styles.
71 */
72 public function register_assets() {
73 $this->register_style( 'wc-blocks-packages-style', plugins_url( $this->api->get_block_asset_build_path( 'packages-style', 'css' ), dirname( __DIR__ ) ), array(), 'all', true );
74 $this->register_style( 'wc-blocks-style', plugins_url( $this->api->get_block_asset_build_path( 'wc-blocks', 'css' ), dirname( __DIR__ ) ), array(), 'all', true );
75 $this->register_style( 'wc-blocks-editor-style', plugins_url( $this->api->get_block_asset_build_path( 'wc-blocks-editor-style', 'css' ), dirname( __DIR__ ) ), array( 'wp-edit-blocks' ), 'all', true );
76 if ( BlockEditorUnifiedAssets::is_enabled() ) {
77 $this->register_style( 'wc-block-library-style', plugins_url( $this->api->get_block_asset_build_path( 'wc-block-library-style', 'css' ), dirname( __DIR__ ) ), array( 'wp-edit-blocks' ), 'all', true );
78 }
79
80 $this->api->register_script( 'wc-types', $this->api->get_block_asset_build_path( 'wc-types' ), array(), false );
81 $this->api->register_script( 'wc-entities', 'assets/client/blocks/wc-entities.js', array(), false );
82 $this->api->register_script( 'wc-blocks-middleware', 'assets/client/blocks/wc-blocks-middleware.js', array(), false );
83 $this->api->register_script( 'wc-blocks-data-store', 'assets/client/blocks/wc-blocks-data.js', array( 'wc-blocks-middleware' ) );
84 $this->api->register_script( 'wc-blocks-registry', 'assets/client/blocks/wc-blocks-registry.js', array(), false );
85 $this->api->register_script( 'wc-blocks-shared-context', 'assets/client/blocks/wc-blocks-shared-context.js' );
86 $this->api->register_script( 'wc-blocks-shared-hocs', 'assets/client/blocks/wc-blocks-shared-hocs.js', array(), false );
87 $this->api->register_script( 'wc-blocks-components', 'assets/client/blocks/blocks-components.js' );
88 $this->register_editor_scripts();
89
90 // Keep price-format as a dedicated shared package: editor and frontend/runtime assets depend on it, and
91 // externalizing it avoids duplicating price formatting helpers across bundles.
92 $this->api->register_script( 'wc-price-format', 'assets/client/blocks/price-format.js', array(), false );
93
94 // Vendor scripts for blocks frontends (not including cart and checkout).
95 $this->api->register_script( 'wc-blocks-frontend-vendors', $this->api->get_block_asset_build_path( 'wc-blocks-frontend-vendors-frontend' ), array(), true );
96
97 // Cart and checkout frontend scripts.
98 $this->api->register_script( 'wc-cart-checkout-vendors', $this->api->get_block_asset_build_path( 'wc-cart-checkout-vendors-frontend' ), array(), true );
99 $this->api->register_script( 'wc-cart-checkout-base', $this->api->get_block_asset_build_path( 'wc-cart-checkout-base-frontend' ), array(), true );
100 $this->api->register_script( 'wc-blocks-checkout', 'assets/client/blocks/blocks-checkout.js' );
101 $this->api->register_script( 'wc-blocks-checkout-events', 'assets/client/blocks/blocks-checkout-events.js' );
102 $this->api->register_script( 'wc-schema-parser', 'assets/client/blocks/wc-schema-parser.js', array(), false );
103
104 // Sanitize.
105 $this->api->register_script(
106 'wc-sanitize',
107 'assets/client/admin/sanitize/index.js',
108 array()
109 );
110
111 wp_add_inline_script(
112 'wc-blocks-middleware',
113 "
114 var wcBlocksMiddlewareConfig = {
115 storeApiNonce: '" . esc_js( wp_create_nonce( 'wc_store_api' ) ) . "',
116 wcStoreApiNonceTimestamp: '" . esc_js( time() ) . "'
117 };
118 ",
119 'before'
120 );
121 }
122
123 /**
124 * Register scripts for the active block editor asset configuration.
125 */
126 private function register_editor_scripts(): void {
127 if ( ! BlockEditorUnifiedAssets::is_enabled() ) {
128 $this->api->register_script( 'wc-blocks-vendors', $this->api->get_block_asset_build_path( 'wc-blocks-vendors' ), array(), false );
129 $this->api->register_script( 'wc-blocks', $this->api->get_block_asset_build_path( 'wc-blocks' ), array( 'wc-blocks-vendors' ), false );
130 return;
131 }
132
133 $this->api->register_script(
134 'wc-block-library',
135 $this->api->get_block_asset_build_path( 'wc-block-library' ),
136 array( 'wc-blocks-middleware', 'wc-entities' ),
137 true
138 );
139
140 $this->register_deprecated_script_handles();
141 }
142
143 /**
144 * Register deprecated script handles for backward compatibility.
145 */
146 private function register_deprecated_script_handles(): void {
147 $script_dependencies = array(
148 'wc-blocks-vendors' => array(),
149 'wc-blocks' => array( 'wc-blocks-vendors', 'wc-block-library' ),
150 );
151
152 if ( is_admin() ) {
153 // Discovering legacy block assets requires expensive filesystem checks, so only run this in admin requests.
154 $build_path = WC_ABSPATH . 'assets/client/blocks/';
155 $asset_files = glob( $build_path . '*.asset.php' );
156
157 foreach ( false === $asset_files ? array() : $asset_files as $asset_file ) {
158 $script_name = basename( $asset_file, '.asset.php' );
159
160 if (
161 in_array( $script_name, array( 'wc-block-library', 'wc-blocks' ), true ) ||
162 ! file_exists( $build_path . $script_name . '.js' )
163 ) {
164 continue;
165 }
166
167 // The file comes from WooCommerce's generated assets directory, not user input.
168 // nosemgrep audit.php.lang.security.file.inclusion-arg.
169 $asset_data = require $asset_file;
170
171 if (
172 ! is_array( $asset_data ) ||
173 ! in_array( 'wp-blocks', (array) ( $asset_data['dependencies'] ?? array() ), true )
174 ) {
175 continue;
176 }
177
178 $legacy_handle = 'wc-' . $script_name . '-block';
179 $script_dependencies[ $legacy_handle ] = array( 'wc-blocks' );
180 }
181 }
182
183 foreach ( $script_dependencies as $handle => $dependencies ) {
184 wp_register_script( $handle, false, $dependencies, $this->api->wc_version, true );
185 }
186
187 $this->add_deprecated_script_handle_warnings( array_keys( $script_dependencies ) );
188 }
189
190 /**
191 * Add console warnings for deprecated script handles.
192 *
193 * @param array $handles Deprecated script handles.
194 */
195 private function add_deprecated_script_handle_warnings( array $handles ): void {
196 foreach ( $handles as $handle ) {
197 $message = wp_json_encode(
198 sprintf(
199 '[WooCommerce] The "%s" script handle is a deprecated compatibility placeholder and does not load a script. See the enqueueable packages documentation: https://github.com/woocommerce/woocommerce/tree/trunk/plugins/woocommerce/client/blocks/docs/internal-developers/enqueueable-packages',
200 $handle
201 )
202 );
203
204 if ( false === $message ) {
205 continue;
206 }
207
208 $this->api->add_inline_script(
209 $handle,
210 sprintf( 'console.warn( %s );', $message )
211 );
212 }
213 }
214
215 /**
216 * Defines resource hints to help speed up the loading of some critical blocks.
217 *
218 * These will not impact page loading times negatively because they are loaded once the current page is idle.
219 *
220 * @param array $urls URLs to print for resource hints. Each URL is an array of resource attributes, or a URL string.
221 * @param string $relation_type The relation type the URLs are printed. Possible values: preconnect, dns-prefetch, prefetch, prerender.
222 * @return array URLs to print for resource hints.
223 */
224 public function add_resource_hints( $urls, $relation_type ) {
225 if ( ! in_array( $relation_type, array( 'prefetch', 'prerender' ), true ) || is_admin() ) {
226 return $urls;
227 }
228
229 // We only need to prefetch when the cart has contents.
230 $cart = wc()->cart;
231
232 if ( ! $cart instanceof \WC_Cart || 0 === $cart->get_cart_contents_count() ) {
233 return $urls;
234 }
235
236 if ( 'prefetch' === $relation_type ) {
237 $urls = array_merge(
238 $urls,
239 $this->get_prefetch_resource_hints()
240 );
241 }
242
243 if ( 'prerender' === $relation_type ) {
244 $urls = array_merge(
245 $urls,
246 $this->get_prerender_resource_hints()
247 );
248 }
249
250 return $urls;
251 }
252
253 /**
254 * Get resource hints during prefetch requests.
255 *
256 * @return array Array of URLs.
257 */
258 private function get_prefetch_resource_hints() {
259 $urls = array();
260
261 // Core page IDs.
262 $cart_page_id = wc_get_page_id( 'cart' );
263 $checkout_page_id = wc_get_page_id( 'checkout' );
264
265 // Checks a specific page (by ID) to see if it contains the named block.
266 $has_block_cart = $cart_page_id && has_block( 'woocommerce/cart', $cart_page_id );
267 $has_block_checkout = $checkout_page_id && has_block( 'woocommerce/checkout', $checkout_page_id );
268
269 // Checks the current page to see if it contains the named block.
270 $is_block_cart = has_block( 'woocommerce/cart' );
271 $is_block_checkout = has_block( 'woocommerce/checkout' );
272
273 if ( $has_block_cart && ! $is_block_cart ) {
274 $urls = array_merge( $urls, $this->get_block_asset_resource_hints( 'cart-frontend' ) );
275 }
276
277 if ( $has_block_checkout && ! $is_block_checkout ) {
278 $urls = array_merge( $urls, $this->get_block_asset_resource_hints( 'checkout-frontend' ) );
279 }
280
281 return $urls;
282 }
283
284 /**
285 * Get resource hints during prerender requests.
286 *
287 * @return array Array of URLs.
288 */
289 private function get_prerender_resource_hints() {
290 $urls = array();
291 $is_block_cart = has_block( 'woocommerce/cart' );
292
293 if ( ! $is_block_cart ) {
294 return $urls;
295 }
296
297 $checkout_page_id = wc_get_page_id( 'checkout' );
298 $checkout_page_url = $checkout_page_id ? get_permalink( $checkout_page_id ) : '';
299
300 if ( $checkout_page_url ) {
301 $urls[] = $checkout_page_url;
302 }
303
304 return $urls;
305 }
306
307 /**
308 * Get the block asset resource hints in the cache or null if not found.
309 *
310 * @return array|null Array of resource hints.
311 */
312 private function get_block_asset_resource_hints_cache() {
313 if ( wp_is_development_mode( 'plugin' ) ) {
314 return null;
315 }
316
317 $cache = get_transient( 'woocommerce_block_asset_resource_hints' );
318
319 $current_version = array(
320 'woocommerce' => Constants::get_constant( 'WC_VERSION' ),
321 'wordpress' => get_bloginfo( 'version' ),
322 'site_url' => site_url(),
323 );
324
325 if ( isset( $cache['version'] ) && $cache['version'] === $current_version ) {
326 return $cache['files'];
327 }
328
329 return null;
330 }
331
332 /**
333 * Set the block asset resource hints in the cache.
334 *
335 * @param string $filename File name.
336 * @param array $data Array of resource hints.
337 */
338 private function set_block_asset_resource_hints_cache( $filename, $data ) {
339 $cache = $this->get_block_asset_resource_hints_cache();
340 $updated = array(
341 'files' => $cache ?? array(),
342 'version' => array(
343 'woocommerce' => Constants::get_constant( 'WC_VERSION' ),
344 'wordpress' => get_bloginfo( 'version' ),
345 'site_url' => site_url(),
346 ),
347 );
348
349 $updated['files'][ $filename ] = $data;
350 set_transient( 'woocommerce_block_asset_resource_hints', $updated, WEEK_IN_SECONDS );
351 }
352
353 /**
354 * Get resource hint for a block by name.
355 *
356 * @param string $filename Block filename.
357 * @return array
358 */
359 private function get_block_asset_resource_hints( $filename = '' ) {
360 if ( ! $filename ) {
361 return array();
362 }
363
364 $cached = $this->get_block_asset_resource_hints_cache();
365
366 if ( isset( $cached[ $filename ] ) ) {
367 return $cached[ $filename ];
368 }
369
370 $script_data = $this->api->get_script_data(
371 $this->api->get_block_asset_build_path( $filename )
372 );
373 $resources = array_merge(
374 array( esc_url( add_query_arg( 'ver', $script_data['version'], $script_data['src'] ) ) ),
375 $this->get_script_dependency_src_array( $script_data['dependencies'] )
376 );
377
378 $data = array_map(
379 function ( $src ) {
380 return array(
381 'href' => $src,
382 'as' => 'script',
383 );
384 },
385 array_unique( array_filter( $resources ) )
386 );
387
388 $this->set_block_asset_resource_hints_cache( $filename, $data );
389
390 return $data;
391 }
392
393 /**
394 * Get the src of all script dependencies (handles).
395 *
396 * @param array $dependencies Array of dependency handles.
397 * @return string[] Array of src strings.
398 */
399 private function get_script_dependency_src_array( array $dependencies ) {
400 $wp_scripts = wp_scripts();
401
402 $found_dependencies = array();
403 $this->gather_script_dependency_handles( $dependencies, $wp_scripts, $found_dependencies );
404
405 $src = array();
406 foreach ( $found_dependencies as $handle => $unused ) {
407 $script_src = $wp_scripts->registered[ $handle ]->src;
408 if ( ! is_string( $script_src ) ) {
409 // Skip srcless dependencies (e.g. meta-packages), which have no URL to hint.
410 continue;
411 }
412 $src[] = esc_url( add_query_arg( 'ver', $wp_scripts->registered[ $handle ]->ver, Utils::get_absolute_script_url( $script_src ) ) );
413 }
414 return $src;
415 }
416
417 /**
418 * Recursively gather all unique script dependency handles from a starting list.
419 *
420 * Traverses the dependency graph for each input handle, collecting any found handles
421 * and their nested dependencies in the provided array. Used internally to build a
422 * complete, deduplicated set of handles for further processing (e.g., mapping to src URLs).
423 *
424 * @param array $dependencies Array of initial script handles to process.
425 * @param \WP_Scripts $wp_scripts WP_Scripts instance containing all registered scripts.
426 * @param array $found_dependencies Reference to array in which discovered handles are stored.
427 *
428 * @return void
429 */
430 private function gather_script_dependency_handles( array $dependencies, \WP_Scripts $wp_scripts, &$found_dependencies = array() ) {
431 foreach ( $dependencies as $handle ) {
432 if ( isset( $wp_scripts->registered[ $handle ] ) && ! isset( $found_dependencies[ $handle ] ) ) {
433 $found_dependencies[ $handle ] = true;
434 if ( ! empty( $wp_scripts->registered[ $handle ]->deps ) ) {
435 $this->gather_script_dependency_handles( $wp_scripts->registered[ $handle ]->deps, $wp_scripts, $found_dependencies );
436 }
437 }
438 }
439 }
440
441 /**
442 * Skip Jetpack Boost minification on older versions of Jetpack Boost where it causes issues.
443 *
444 * @param mixed $do_concat Whether to concatenate the script or not.
445 * @param mixed $handle The script handle.
446 * @return mixed
447 */
448 public function skip_boost_minification_for_cart_checkout( $do_concat, $handle ) {
449 $boost_is_outdated = defined( 'JETPACK_BOOST_VERSION' ) && version_compare( JETPACK_BOOST_VERSION, '3.4.2', '<' );
450 $scripts_to_ignore = array(
451 'wc-cart-checkout-vendors',
452 'wc-cart-checkout-base',
453 );
454
455 return $boost_is_outdated && in_array( $handle, $scripts_to_ignore, true ) ? false : $do_concat;
456 }
457
458 /**
459 * Add body classes to the frontend and within admin.
460 *
461 * @param string|array $classes Array or string of CSS classnames.
462 * @return string|array Modified classnames.
463 */
464 public function add_theme_body_class( $classes ) {
465 $class = 'theme-' . get_template();
466
467 if ( is_array( $classes ) ) {
468 $classes[] = $class;
469 } else {
470 $classes .= ' ' . $class . ' ';
471 }
472
473 return $classes;
474 }
475
476 /**
477 * Get the file modified time as a cache buster if we're in dev mode.
478 *
479 * @param string $file Local path to the file.
480 * @return string The cache buster value to use for the given file.
481 */
482 protected function get_file_version( $file ) {
483 if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG && file_exists( \Automattic\WooCommerce\Blocks\Package::get_path() . $file ) ) {
484 return filemtime( \Automattic\WooCommerce\Blocks\Package::get_path() . $file );
485 }
486 return $this->api->wc_version;
487 }
488
489 /**
490 * Registers a style according to `wp_register_style`.
491 *
492 * @param string $handle Name of the stylesheet. Should be unique.
493 * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory.
494 * @param array $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array.
495 * @param string $media Optional. The media for which this stylesheet has been defined. Default 'all'. Accepts media types like
496 * 'all', 'print' and 'screen', or media queries like '(orientation: portrait)' and '(max-width: 640px)'.
497 * @param boolean $rtl Optional. Whether or not to register RTL styles.
498 */
499 protected function register_style( $handle, $src, $deps = array(), $media = 'all', $rtl = false ) {
500 $filename = str_replace( plugins_url( '/', dirname( __DIR__ ) ), '', $src );
501 $ver = self::get_file_version( $filename );
502
503 wp_register_style( $handle, $src, $deps, $ver, $media );
504
505 if ( $rtl ) {
506 wp_style_add_data( $handle, 'rtl', 'replace' );
507 }
508 }
509
510 /**
511 * Update block style dependencies after they have been registered.
512 */
513 public function update_block_style_dependencies() {
514 $wp_styles = wp_styles();
515 $style = $wp_styles->query( 'wc-blocks-style', 'registered' );
516
517 if ( ! $style ) {
518 return;
519 }
520
521 // In WC < 5.5, `woocommerce-general` is not registered in block editor
522 // screens, so we don't add it as a dependency if it's not registered.
523 // In WC >= 5.5, `woocommerce-general` is registered on `admin_enqueue_scripts`,
524 // so we need to check if it's registered here instead of on `init`.
525 if (
526 wp_style_is( 'woocommerce-general', 'registered' ) &&
527 ! in_array( 'woocommerce-general', $style->deps, true )
528 ) {
529 $style->deps[] = 'woocommerce-general';
530 }
531 }
532
533 /**
534 * Fix scripts with wc-settings dependency.
535 *
536 * The wc-settings script only works correctly when enqueued in the footer. This is to give blocks etc time to
537 * register their settings data before it's printed.
538 *
539 * This code will look at registered scripts, and if they have a wc-settings dependency, force them to print in the
540 * footer instead of the header.
541 *
542 * This only supports packages known to require wc-settings!
543 *
544 * @see https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/5052
545 */
546 public function update_block_settings_dependencies() {
547 $wp_scripts = wp_scripts();
548 $known_packages = array( 'wc-settings', 'wc-blocks-checkout', 'wc-price-format' );
549
550 foreach ( $wp_scripts->registered as $handle => $script ) {
551 // scripts that are loaded in the footer has extra->group = 1.
552 if ( array_intersect( $known_packages, $script->deps ) && ! isset( $script->extra['group'] ) ) {
553 // Append the script to footer.
554 $wp_scripts->add_data( $handle, 'group', 1 );
555 // Show a warning.
556 $error_handle = 'wc-settings-dep-in-header';
557 $used_deps = implode( ', ', array_intersect( $known_packages, $script->deps ) );
558 $error_message = "Scripts that have a dependency on [$used_deps] must be loaded in the footer, {$handle} was registered to load in the header, but has been switched to load in the footer instead. See https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5059";
559 // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.NotInFooter,WordPress.WP.EnqueuedResourceParameters.MissingVersion
560 wp_register_script( $error_handle, '' );
561 wp_enqueue_script( $error_handle );
562 wp_add_inline_script(
563 $error_handle,
564 sprintf( 'console.warn( "%s" );', $error_message )
565 );
566
567 }
568 }
569 }
570
571 /**
572 * Enqueue the wc-entities script.
573 */
574 public function enqueue_wc_entities() {
575 wp_enqueue_script( 'wc-entities' );
576 }
577 }
578