Commit ae00acca5f4 for woocommerce

commit ae00acca5f40ca499f6def4fd60b017b3b6a6d21
Author: Luigi Teschio <gigitux@gmail.com>
Date:   Tue Jul 28 10:21:34 2026 +0200

    Consolidate Woo blocks editor assets into shared bundles (#66200)

    * Consolidate Woo blocks editor assets

    * restructure code

    * add documentation

    * improve naming

    * lint code

    * improve performance test

    * fix test

    * fix build configuration

    * re-run test

    * fix css

    * fix build

    * lint code

    * relocate in the right folder

    * fix build

    * fix webpack

    * fix crash build

    * avoid restructuring

    * WIP

    * fix c&c blocks

    * add side effect

    * fix import

    * fix block.json

    * clean up changes

    * clean up changes

    * clean up code

    * fix baseline

    * externalize packages

    * fix warning

    * refresh pnpm-lock

    * improve bundle

    * restore pnpm-lock

    * improve logic

    * update docs

    * restore the previous name for the CSS bundle

    * fix lint

    * avoid redunant get_script_data

    * address feedback

    * fix path for is_built function

    * update changelog

    * use str_starts_with

    * add deprecation warning

    * update comment

    * optimize bundle

    * Restore Blocks shared package bundles

    * deprecate less packages possible

    * implement feature flag

    * clean up code

    * improve CI

    * update webpack configuration

    * clean up code

    * lint code

    * restore changes

    * restore changes

    * improve conf

    * clean up changes

    * simplify webpack conf

    * clean up code

    * improve documentation

    * remove old changelog

    * fix css bundle

    * improve optimization

    * update readme

    * remove declare type

    * improve logic

    * update documentation

    * fix deps

diff --git a/plugins/woocommerce/changelog/performance-unify-blocks-editor-assets b/plugins/woocommerce/changelog/performance-unify-blocks-editor-assets
new file mode 100644
index 00000000000..5d58f84022d
--- /dev/null
+++ b/plugins/woocommerce/changelog/performance-unify-blocks-editor-assets
@@ -0,0 +1,4 @@
+Significance: major
+Type: performance
+
+Unify Woo blocks editor scripts and editor styles into shared editor assets.
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart-coupons.tsx b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart-coupons.tsx
index 5ec82c8e90d..3fbbc471ecc 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart-coupons.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart-coupons.tsx
@@ -10,9 +10,9 @@ import { server, http, HttpResponse } from '@woocommerce/test-utils/msw';
 import { useStoreCartCoupons } from '../use-store-cart-coupons';

 // Mock the resolvers to avoid actual API calls on cart data store setup.
-jest.mock( '../../../../../data/cart/resolvers', () => {
+jest.mock( '@woocommerce/block-data/cart/resolvers', () => {
 	return {
-		...jest.requireActual( '../../../../../data/cart/resolvers' ),
+		...jest.requireActual( '@woocommerce/block-data/cart/resolvers' ),
 		getCartData: jest
 			.fn()
 			.mockResolvedValue(
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart-item-quantity.js b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart-item-quantity.js
index 89b4e7c109d..84df7c4da17 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart-item-quantity.js
+++ b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart-item-quantity.js
@@ -16,10 +16,20 @@ jest.mock( '../use-store-cart', () => ( {
 	useStoreCart: jest.fn(),
 } ) );

-jest.mock( '@woocommerce/block-data', () => ( {
-	__esModule: true,
-	...jest.requireActual( '@woocommerce/block-data' ),
-} ) );
+jest.mock( '@woocommerce/block-data', () => {
+	const cart = jest.requireActual( '@woocommerce/block-data/cart' );
+	const checkout = jest.requireActual( '@woocommerce/block-data/checkout' );
+	const utils = jest.requireActual( '@woocommerce/block-data/utils' );
+
+	return {
+		__esModule: true,
+		CART_STORE_KEY: cart.CART_STORE_KEY,
+		CHECKOUT_STORE_KEY: checkout.CHECKOUT_STORE_KEY,
+		cartStore: cart.store,
+		checkoutStore: checkout.store,
+		processErrorResponse: utils.processErrorResponse,
+	};
+} );

 // Make debounce instantaneous.
 jest.mock( 'use-debounce', () => ( {
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart.jsx b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart.jsx
index 778b04fde5d..ea9f7d0a58c 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart.jsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart.jsx
@@ -15,10 +15,17 @@ jest.mock( '../../../providers/editor-context', () => ( {
 	useEditorContext: jest.fn(),
 } ) );

-jest.mock( '@woocommerce/block-data', () => ( {
-	...jest.requireActual( '@woocommerce/block-data' ),
-	__esModule: true,
-} ) );
+jest.mock( '@woocommerce/block-data', () => {
+	const constants = jest.requireActual( '@woocommerce/block-data/constants' );
+	const cart = jest.requireActual( '@woocommerce/block-data/cart' );
+
+	return {
+		__esModule: true,
+		...constants,
+		CART_STORE_KEY: cart.CART_STORE_KEY,
+		cartStore: cart.store,
+	};
+} );

 describe( 'useStoreCart', () => {
 	let registry;
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/tsconfig.json b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/tsconfig.json
index c5b9d46d020..363ec666e1e 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/tsconfig.json
+++ b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/tsconfig.json
@@ -11,7 +11,7 @@
 		"../providers/cart-checkout/shipping/index.js",
 		"../../components/cart-checkout/totals/shipping",
 		"../../../editor-components/utils/*",
-		"../../../data/index.ts"
+		"../../../data/**/*"
 	],
 	"exclude": [ "**/test/**" ]
 }
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.tsx
index fc70ecccec6..750676fa111 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.tsx
@@ -21,14 +21,14 @@ import {
 	ConfigOf,
 	CurriedSelectorsOf,
 } from '@wordpress/data/build-types/types';
+import { paymentStore } from '@woocommerce/block-data';

 /**
  * Internal dependencies
  */
+import type { PaymentStoreDescriptor } from '@woocommerce/block-data/payment';
 import PaymentMethodErrorBoundary from './payment-method-error-boundary';
-import { STORE_KEY as PAYMENT_STORE_KEY } from '../../../data/payment/constants';
 import { useExpressPaymentContext } from '../../cart-checkout-shared/payment-methods/express-payment/express-payment-context';
-import type { PaymentStoreDescriptor } from '../../../data/payment';
 import { useExpressPaymentFocus } from './use-express-payment-focus';

 const ExpressPaymentMethods = () => {
@@ -48,7 +48,7 @@ const ExpressPaymentMethods = () => {
 	const { activePaymentMethod, paymentMethodData } = useSelect(
 		( select ) => {
 			const store = select(
-				PAYMENT_STORE_KEY
+				paymentStore
 			) as CurriedSelectorsOf< PaymentStoreDescriptor >;
 			return {
 				activePaymentMethod: store.getActivePaymentMethod(),
@@ -63,7 +63,7 @@ const ExpressPaymentMethods = () => {
 		__internalSetPaymentError,
 		__internalSetPaymentMethodData,
 		__internalSetExpressPaymentError,
-	} = useDispatch( PAYMENT_STORE_KEY ) as ActionCreatorsOf<
+	} = useDispatch( paymentStore ) as ActionCreatorsOf<
 		ConfigOf< PaymentStoreDescriptor >
 	>;
 	const { paymentMethods } = useExpressPaymentMethods();
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart-checkout-shared/payment-methods/saved-payment-method-options.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/cart-checkout-shared/payment-methods/saved-payment-method-options.tsx
index 972f26cdf5d..c92833c462b 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart-checkout-shared/payment-methods/saved-payment-method-options.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/cart-checkout-shared/payment-methods/saved-payment-method-options.tsx
@@ -20,8 +20,8 @@ import { isNull } from '@woocommerce/types';
 /**
  * Internal dependencies
  */
-import { getCanMakePaymentArg } from '../../../data/payment/utils/check-payment-methods';
-import { CustomerPaymentMethodConfiguration } from '../../../data/payment/types';
+import { getCanMakePaymentArg } from '@woocommerce/block-data/payment/utils/check-payment-methods';
+import { CustomerPaymentMethodConfiguration } from '@woocommerce/block-data/payment/types';

 /**
  * Returns the option object for a cc or echeck saved payment method token.
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/taxonomy-filter/test/block.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/taxonomy-filter/test/block.ts
index 260fa0df8e9..4a1df0c205a 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/taxonomy-filter/test/block.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/taxonomy-filter/test/block.ts
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import { type BlockAttributes } from '@wordpress/blocks';
+import { BlockAttributes } from '@wordpress/blocks';
 import '@testing-library/jest-dom';
 import { act, fireEvent, screen, within } from '@testing-library/react';

@@ -50,7 +50,7 @@ jest.mock( '@woocommerce/settings', () => {
 } );

 // Mock WooCommerce schema selectors to prevent namespace errors
-jest.mock( '../../../../../data/schema/selectors', () => ( {
+jest.mock( '@woocommerce/block-data/schema/selectors', () => ( {
 	getRoute: jest.fn( () => null ),
 	getRoutes: jest.fn( () => ( {
 		'/wc/store/v1': {},
diff --git a/plugins/woocommerce/client/blocks/assets/js/data/collections/resolvers.js b/plugins/woocommerce/client/blocks/assets/js/data/collections/resolvers.js
index 823e83635c2..df7b96c4876 100644
--- a/plugins/woocommerce/client/blocks/assets/js/data/collections/resolvers.js
+++ b/plugins/woocommerce/client/blocks/assets/js/data/collections/resolvers.js
@@ -8,7 +8,7 @@ import { addQueryArgs } from '@wordpress/url';
  * Internal dependencies
  */
 import { receiveCollection, receiveCollectionError } from './actions';
-import { STORE_KEY as SCHEMA_STORE_KEY } from '../schema/constants';
+import { SCHEMA_STORE_KEY } from '../schema';
 import { STORE_KEY, DEFAULT_EMPTY_ARRAY } from './constants';
 import { apiFetchWithHeadersControl } from '../shared-controls';

diff --git a/plugins/woocommerce/client/blocks/assets/js/dependency-detection/utils.ts b/plugins/woocommerce/client/blocks/assets/js/dependency-detection/utils.ts
index 5d529e963f7..8875ca7bc32 100644
--- a/plugins/woocommerce/client/blocks/assets/js/dependency-detection/utils.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/dependency-detection/utils.ts
@@ -28,6 +28,7 @@ export interface WcGlobalExportsMap {
 	blocksComponents: 'wc-blocks-components';
 	wcTypes: 'wc-types';
 	sanitize: 'wc-sanitize';
+	wcEntities: 'wc-entities';
 }

 /**
diff --git a/plugins/woocommerce/client/blocks/bin/webpack-config-block-editor-unified-assets.js b/plugins/woocommerce/client/blocks/bin/webpack-config-block-editor-unified-assets.js
new file mode 100644
index 00000000000..1a5edb9cdba
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/bin/webpack-config-block-editor-unified-assets.js
@@ -0,0 +1,342 @@
+/**
+ * External dependencies
+ */
+const path = require( 'path' );
+const { omit } = require( 'lodash' );
+const cssnano = require( 'cssnano' );
+const postcss = require( 'postcss' );
+const ProgressBarPlugin = require( 'progress-bar-webpack-plugin' );
+const webpack = require( 'webpack' );
+
+/**
+ * Internal dependencies
+ */
+const { getEntryConfig } = require( './webpack-entries' );
+const {
+	editorStyleEntries,
+	styleEntries,
+} = require( './webpack-interactivity-entries' );
+const {
+	NODE_ENV,
+	getProgressBarPluginConfig,
+	getResolve,
+	requestToExternal,
+	requestToHandle,
+} = require( './webpack-helpers' );
+const { getSharedPlugins, getStylingConfig } = require( './webpack-configs' );
+const { sharedOptimizationConfig } = require( './webpack-shared-config' );
+
+const ROOT_DIR = path.resolve( __dirname, '../../../../../' );
+// Blocks' webpack writes directly to the WooCommerce plugin's
+// `assets/client/blocks/` so PHP can enqueue files from their final location
+// without an intermediate rsync step.
+const BUILD_DIR = path.resolve( __dirname, '../../../assets/client/blocks' );
+const BABEL_CACHE_DIR = path.join(
+	ROOT_DIR,
+	'node_modules/.cache/babel-loader'
+);
+const isProduction = NODE_ENV === 'production';
+const UNIFIED_EDITOR_STYLE_HANDLE = 'wc-block-library-style';
+const UNIFIED_EDITOR_STYLE_PATTERN = /^wc-block-library-style(?:-rtl)?\.css$/;
+const OPTIMIZE_UNIFIED_EDITOR_STYLES_PLUGIN =
+	'OptimizeUnifiedEditorStylesPlugin';
+
+const editorExternalPackages = [
+	'@woocommerce/block-data',
+	'@woocommerce/blocks-checkout',
+	'@woocommerce/blocks-checkout-events',
+	'@woocommerce/blocks-components',
+	'@woocommerce/blocks-registry',
+	'@woocommerce/data',
+	'@woocommerce/entities',
+	'@woocommerce/price-format',
+	'@woocommerce/sanitize',
+	'@woocommerce/settings',
+	'@woocommerce/shared-context',
+	'@woocommerce/shared-hocs',
+	'@woocommerce/types',
+];
+
+const shouldBundleWooPackage = ( request ) =>
+	request.startsWith( '@woocommerce/' ) &&
+	! editorExternalPackages.includes( request );
+
+const requestToUnifiedEditorExternal = ( request ) => {
+	if ( shouldBundleWooPackage( request ) ) {
+		return false;
+	}
+
+	return requestToExternal( request );
+};
+
+const requestToUnifiedEditorHandle = ( request ) => {
+	if ( shouldBundleWooPackage( request ) ) {
+		return false;
+	}
+
+	return requestToHandle( request );
+};
+
+const getUnifiedEditorPackageAliases = () => ( {
+	'@woocommerce/block-data': path.resolve( __dirname, `../assets/js/data` ),
+	'@woocommerce/blocks-checkout': path.resolve(
+		__dirname,
+		`../packages/checkout`
+	),
+	'@woocommerce/blocks-checkout-events': path.resolve(
+		__dirname,
+		`../assets/js/events`
+	),
+	'@woocommerce/blocks-components': path.resolve(
+		__dirname,
+		`../packages/components`
+	),
+	'@woocommerce/blocks-registry': path.resolve(
+		__dirname,
+		`../assets/js/blocks-registry`
+	),
+	'@woocommerce/data': path.resolve(
+		__dirname,
+		`../../../../../packages/js/data/src/index.ts`
+	),
+	'@woocommerce/price-format': path.resolve(
+		__dirname,
+		`../packages/prices`
+	),
+	'@woocommerce/sanitize': path.resolve(
+		__dirname,
+		`../../../../../packages/js/sanitize/src/index.ts`
+	),
+	'@woocommerce/settings': path.resolve(
+		__dirname,
+		`../assets/js/settings/shared`
+	),
+	'@woocommerce/shared-context': path.resolve(
+		__dirname,
+		`../assets/js/shared/context/`
+	),
+	'@woocommerce/shared-hocs': path.resolve(
+		__dirname,
+		`../assets/js/shared/hocs/`
+	),
+} );
+
+/**
+ * Optimize the combined editor styles after CSS extraction and RTL generation.
+ *
+ * The inherited styling configuration runs PostCSS for every Sass entry before
+ * MiniCssExtractPlugin combines those entries. When multiple entries import the
+ * same shared styles, each entry is minified independently and the duplicate
+ * rules remain in the final combined stylesheet.
+ *
+ * This plugin runs cssnano once more at Webpack's optimize-size stage, after
+ * WebpackRTLPlugin has emitted its derived RTL asset. Processing at that point
+ * allows cssnano to remove duplicates across the complete LTR and RTL bundles
+ * rather than only within individual Sass entries.
+ *
+ * Only the unified editor stylesheet filenames are processed so legacy block
+ * styles and other build outputs remain unchanged. The plugin is added to the
+ * production configuration only; development builds avoid the additional work
+ * to preserve fast rebuilds and their existing source output.
+ */
+class OptimizeUnifiedEditorStylesPlugin {
+	/**
+	 * Apply the plugin.
+	 *
+	 * @param {webpack.Compiler} compiler Webpack compiler.
+	 */
+	apply( compiler ) {
+		compiler.hooks.thisCompilation.tap(
+			OPTIMIZE_UNIFIED_EDITOR_STYLES_PLUGIN,
+			( compilation ) => {
+				compilation.hooks.processAssets.tapPromise(
+					{
+						name: OPTIMIZE_UNIFIED_EDITOR_STYLES_PLUGIN,
+						stage: webpack.Compilation
+							.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
+					},
+					async ( assets ) => {
+						await Promise.all(
+							Object.entries( assets )
+								.filter( ( [ assetName ] ) =>
+									UNIFIED_EDITOR_STYLE_PATTERN.test(
+										assetName
+									)
+								)
+								.map( async ( [ assetName, asset ] ) => {
+									const result = await postcss( [
+										cssnano,
+									] ).process( asset.source().toString(), {
+										from: undefined,
+										map: false,
+									} );
+
+									compilation.updateAsset(
+										assetName,
+										new webpack.sources.RawSource(
+											result.css
+										)
+									);
+								} )
+						);
+					}
+				);
+			}
+		);
+	}
+}
+
+/**
+ * Reuse the established styling entry graph so the unified bundle includes
+ * transitive and nonstandard SCSS imports as the legacy per-block build does.
+ *
+ * @param {string[]} exclude Entry names to exclude.
+ * @return {Object} Unified styling entries.
+ */
+const getUnifiedEditorStyleEntries = ( exclude = [] ) =>
+	omit(
+		{
+			'wc-block-library-style-source': [
+				...Object.values( getEntryConfig( 'styling', exclude ) ).flat(),
+				// Interactivity styles are emitted by a separate frontend build,
+				// so they are excluded from the standard styling entry graph.
+				...Object.values( styleEntries ).flat(),
+				...Object.values( editorStyleEntries ).flat(),
+			],
+		},
+		exclude
+	);
+
+/**
+ * Build config for unified Blocks editor scripts.
+ *
+ * @param {Object} options Build options.
+ */
+const getUnifiedMainConfig = ( options = {} ) => {
+	const { alias, resolvePlugins = [] } = options;
+	const resolve = getResolve( {
+		alias: {
+			...getUnifiedEditorPackageAliases(),
+			...alias,
+		},
+		resolvePlugins,
+	} );
+
+	return {
+		entry: omit(
+			{
+				'wc-block-library': Object.values(
+					getEntryConfig( 'main' )
+				).flat(),
+			},
+			options.exclude || []
+		),
+		output: {
+			devtoolNamespace: 'wc',
+			path: BUILD_DIR,
+			// Keep the filename stable for WordPress translations while using
+			// the query string to invalidate cached chunks after a build.
+			chunkFilename: `wc-block-library-[name].js?ver=[contenthash]`,
+			filename: `[name].js`,
+			uniqueName: 'webpackWcBlocksUnifiedMainJsonp',
+		},
+		module: {
+			rules: [
+				{
+					test: /\.(j|t)sx?$/,
+					exclude: [ /[\/\\](node_modules|build|docs|vendor)[\/\\]/ ],
+					use: {
+						loader: 'babel-loader',
+						options: {
+							presets: [ '@wordpress/babel-preset-default' ],
+							plugins: [
+								isProduction
+									? require.resolve(
+											'babel-plugin-transform-react-remove-prop-types'
+									  )
+									: false,
+							].filter( Boolean ),
+							cacheDirectory: BABEL_CACHE_DIR,
+							cacheCompression: false,
+						},
+					},
+				},
+				{
+					test: /\.s[c|a]ss$/,
+					use: {
+						loader: 'ignore-loader',
+					},
+				},
+			],
+		},
+		optimization: {
+			...sharedOptimizationConfig,
+			splitChunks: false,
+		},
+		plugins: [
+			...getSharedPlugins( {
+				bundleAnalyzerReportTitle: 'Unified editor',
+				dependencyRequestToExternal: requestToUnifiedEditorExternal,
+				dependencyRequestToHandle: requestToUnifiedEditorHandle,
+			} ),
+			new ProgressBarPlugin(
+				getProgressBarPluginConfig( 'Unified editor' )
+			),
+			new webpack.optimize.LimitChunkCountPlugin( {
+				maxChunks: 1,
+			} ),
+		],
+		resolve: {
+			...resolve,
+			extensions: [ '.js', '.jsx', '.ts', '.tsx' ],
+		},
+	};
+};
+
+/**
+ * Build config for unified Blocks editor styles.
+ *
+ * @param {Object} options Build options.
+ */
+const getUnifiedStylingConfig = ( options = {} ) => {
+	const stylingConfig = getStylingConfig( options );
+
+	return {
+		...stylingConfig,
+		entry: getUnifiedEditorStyleEntries( options.exclude || [] ),
+		output: {
+			...stylingConfig.output,
+			uniqueName: 'webpackWcBlocksUnifiedStylingJsonp',
+		},
+		optimization: {
+			...stylingConfig.optimization,
+			splitChunks: {
+				...stylingConfig.optimization.splitChunks,
+				cacheGroups: {
+					// JavaScript is traversed only to discover stylesheet imports.
+					// Do not emit the inherited JavaScript split chunks.
+					default: false,
+					defaultVendors: false,
+					editorStyle: {
+						test: ( module = {} ) => {
+							return module.type.includes( 'css' );
+						},
+						name: UNIFIED_EDITOR_STYLE_HANDLE,
+						chunks: 'all',
+						enforce: true,
+						priority: 10,
+					},
+				},
+			},
+		},
+		plugins: [
+			...stylingConfig.plugins,
+			isProduction && new OptimizeUnifiedEditorStylesPlugin(),
+		].filter( Boolean ),
+	};
+};
+
+module.exports = {
+	getUnifiedMainConfig,
+	getUnifiedStylingConfig,
+};
diff --git a/plugins/woocommerce/client/blocks/bin/webpack-configs.js b/plugins/woocommerce/client/blocks/bin/webpack-configs.js
index 527fedcf83b..3622b78085f 100644
--- a/plugins/woocommerce/client/blocks/bin/webpack-configs.js
+++ b/plugins/woocommerce/client/blocks/bin/webpack-configs.js
@@ -52,6 +52,8 @@ let initialBundleAnalyzerPort = 8888;
 const getSharedPlugins = ( {
 	bundleAnalyzerReportTitle,
 	checkCircularDeps = true,
+	dependencyRequestToExternal = requestToExternal,
+	dependencyRequestToHandle = requestToHandle,
 } ) =>
 	[
 		CHECK_CIRCULAR_DEPS === 'true' && checkCircularDeps !== false
@@ -72,8 +74,8 @@ const getSharedPlugins = ( {
 			injectPolyfill: true,
 			combineAssets: ASSET_CHECK,
 			outputFormat: ASSET_CHECK ? 'json' : 'php',
-			requestToExternal,
-			requestToHandle,
+			requestToExternal: dependencyRequestToExternal,
+			requestToHandle: dependencyRequestToHandle,
 		} ),
 		// Substitute the `__i18n_text_domain__` identifier used by the
 		// @woocommerce/email-editor package with the WooCommerce text
@@ -884,6 +886,7 @@ const getCartAndCheckoutFrontendConfig = ( options = {} ) => {
 };

 module.exports = {
+	getSharedPlugins,
 	getCoreConfig,
 	getFrontConfig,
 	getMainConfig,
diff --git a/plugins/woocommerce/client/blocks/bin/webpack-helpers.js b/plugins/woocommerce/client/blocks/bin/webpack-helpers.js
index 41aa08d30d9..6d3958d5490 100644
--- a/plugins/woocommerce/client/blocks/bin/webpack-helpers.js
+++ b/plugins/woocommerce/client/blocks/bin/webpack-helpers.js
@@ -8,9 +8,12 @@ const NODE_ENV = process.env.NODE_ENV || 'development';
 const CHECK_CIRCULAR_DEPS = process.env.CHECK_CIRCULAR_DEPS || false;
 const ASSET_CHECK = process.env.ASSET_CHECK === 'true';

-// See also @woocommerce/dependency-extraction-webpack-plugin/assets/packages. It will backfill any missing
-// mapping here and any duplicates are because of switched between Woo and WordPress versions of the plugin.
-// As of 2026 it's Woo version to address pnpm peer dependencies related issues to support filesystem cache.
+// See also @woocommerce/dependency-extraction-webpack-plugin/assets/packages and
+// docs/internal-developers/enqueueable-packages/README.md. They should stay in sync with this map.
+// The dependency extraction plugin will backfill any missing mapping here. Duplicates exist because
+// this file has switched between Woo and WordPress versions of the plugin.
+// As of 2026, it uses the Woo version to address pnpm peer dependency issues and support
+// filesystem cache.
 const wcDepMap = {
 	'@woocommerce/tracks': false, // Bundle; do not externalize
 	'@woocommerce/blocks-registry': [ 'wc', 'wcBlocksRegistry' ],
@@ -25,6 +28,7 @@ const wcDepMap = {
 	'@woocommerce/blocks-components': [ 'wc', 'blocksComponents' ],
 	'@woocommerce/types': [ 'wc', 'wcTypes' ],
 	'@woocommerce/sanitize': [ 'wc', 'sanitize' ],
+	'@woocommerce/entities': [ 'wc', 'wcEntities' ],
 };
 const wcHandleMap = {
 	'@woocommerce/tracks': false, // Bundle; no PHP handle needed
@@ -40,6 +44,7 @@ const wcHandleMap = {
 	'@woocommerce/blocks-components': 'wc-blocks-components',
 	'@woocommerce/types': 'wc-types',
 	'@woocommerce/sanitize': 'wc-sanitize',
+	'@woocommerce/entities': 'wc-entities',
 };

 const getAlias = ( options = {} ) => {
@@ -74,6 +79,14 @@ const getAlias = ( options = {} ) => {
 			__dirname,
 			`../assets/js/${ pathPart }base/utils/`
 		),
+		'@woocommerce/block-data': path.resolve(
+			__dirname,
+			`../assets/js/data`
+		),
+		'@woocommerce/blocks-components': path.resolve(
+			__dirname,
+			`../packages/components`
+		),
 		'@woocommerce/blocks': path.resolve(
 			__dirname,
 			`../assets/js/${ pathPart }/blocks`
diff --git a/plugins/woocommerce/client/blocks/docs/internal-developers/block-client-apis/README.md b/plugins/woocommerce/client/blocks/docs/internal-developers/block-client-apis/README.md
index cbcc6763e1a..c5450aabfb5 100644
--- a/plugins/woocommerce/client/blocks/docs/internal-developers/block-client-apis/README.md
+++ b/plugins/woocommerce/client/blocks/docs/internal-developers/block-client-apis/README.md
@@ -2,17 +2,18 @@

 This folder contains documentation for API interfaces for Blocks. In _most cases_, these docs describe APIs and interfaces that are _internal_ only, and thus are provided to assist with developing the blocks in this repository. Documentation will tend to focus on high level overviews.

-| Document                                           | Description                                                                                         |
-| -------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
-| [Checkout API interface](checkout/checkout-api.md) | This doc goes into some detail about some of the API specifics for the checkout block architecture. |
-| [Notices](notices.md)                              | Explains how the notices system works and which methods are available to add an remove them.        |
+| Document | Description |
+| --- | --- |
+| [Checkout API interface](checkout/checkout-api.md) | Describes API details for the Checkout block architecture. |
+| [Unified editor assets](../enqueueable-packages/README.md) | Explains the default and experimental editor asset configurations, external package boundaries, and compatibility handles. |
+| [Notices](notices.md) | Explains how the notices system works and which methods can add and remove notices. |

 Some of the resources from this directory were migrated to WooCommerce Developer Documentation:

--   [Checkout Flow and Events](https://developer.woocommerce.com/docs/cart-and-checkout-checkout-flow-and-events/)
+- [Checkout Flow and Events](https://developer.woocommerce.com/docs/cart-and-checkout-checkout-flow-and-events/)

 For more details about extensibility points in the blocks, you can reference the extensibility docs:

--   [Data Store](../../third-party-developers/extensibility/data-store/README.md)
--   [Hooks](../../third-party-developers/extensibility/hooks/README.md)
--   [Extending the Store API](../../third-party-developers/extensibility/rest-api/README.md)
+- [Data Store](../../third-party-developers/extensibility/data-store/README.md)
+- [Hooks](../../third-party-developers/extensibility/hooks/README.md)
+- [Extending the Store API](../../third-party-developers/extensibility/rest-api/README.md)
diff --git a/plugins/woocommerce/client/blocks/docs/internal-developers/enqueueable-packages/README.md b/plugins/woocommerce/client/blocks/docs/internal-developers/enqueueable-packages/README.md
new file mode 100644
index 00000000000..6efb16d3024
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/docs/internal-developers/enqueueable-packages/README.md
@@ -0,0 +1,60 @@
+# WooCommerce Blocks editor assets
+
+WooCommerce includes an experimental configuration that replaces its per-block editor scripts and styles with shared editor assets. The experiment is disabled by default, so existing per-block assets and handles continue to work unchanged.
+
+Enable **Unified block editor assets** under **WooCommerce > Settings > Advanced > Features** to test the shared configuration. The feature flag is `block_editor_unified_assets`, its setting is stored in `woocommerce_feature_block_editor_unified_assets_enabled`, and changes take effect on the next request.
+
+## Asset configurations
+
+### Default
+
+When the experiment is disabled, WooCommerce uses:
+
+- Per-block scripts such as `wc-cart-block` and `wc-checkout-block`.
+- Per-block styles declared by block metadata.
+- The `wc-blocks-vendors` and `wc-blocks` bundles.
+- The `wc-blocks-editor-style` stylesheet.
+
+### Unified
+
+When the experiment is enabled, WooCommerce block types use:
+
+| Handle | Type | What it contains |
+| --- | --- | --- |
+| `wc-block-library` | Script | WooCommerce block editor entry points and internal package imports. |
+| `wc-block-library-style` | Stylesheet | Editor styles for blocks shipped by WooCommerce. Styles registered by third-party blocks remain separate. |
+
+These assets replace WooCommerce's per-block editor assets. They do not combine every script loaded by the editor: shared WordPress dependencies and the enqueueable WooCommerce packages below remain external.
+
+## External package dependencies
+
+The generated asset file for `wc-block-library` declares packages that remain separate scripts:
+
+| Package import | Script handle | Global | Why it stays external |
+| --- | --- | --- | --- |
+| `@woocommerce/block-data` | `wc-blocks-data-store` | `wc.wcBlocksData` | Registers WooCommerce Blocks data stores once and shares them with editor extensions and checkout APIs. |
+| `@woocommerce/blocks-checkout` | `wc-blocks-checkout` | `wc.blocksCheckout` | Shares checkout filters, slot/fill APIs, and checkout registry helpers with extensions. |
+| `@woocommerce/blocks-checkout-events` | `wc-blocks-checkout-events` | `wc.blocksCheckoutEvents` | Shares the checkout lifecycle event emitter between subscribers and emitters. |
+| `@woocommerce/blocks-components` | `wc-blocks-components` | `wc.blocksComponents` | Shares components used by WooCommerce blocks and extensions. |
+| `@woocommerce/blocks-registry` | `wc-blocks-registry` | `wc.wcBlocksRegistry` | Keeps block, payment method, and product collection registrations in shared registries. |
+| `@woocommerce/data` | `wc-store-data` | `wc.data` | Shares WooCommerce Admin data stores without re-registering them from the editor bundle. |
+| `@woocommerce/entities` | `wc-entities` | `wc.wcEntities` (deprecated) | Registers WooCommerce entities once. The global helper exports remain temporarily for backward compatibility and must not be used as a public extension API. |
+| `@woocommerce/price-format` | `wc-price-format` | `wc.priceFormat` | Shares price and currency formatting across editor, frontend, and extension code. |
+| `@woocommerce/sanitize` | `wc-sanitize` | `wc.sanitize` | Shares HTML sanitization with the Components and Checkout package bundles. |
+| `@woocommerce/settings` | `wc-settings` | `wc.wcSettings` | Shares one runtime settings object across the editor and package bundles. |
+| `@woocommerce/shared-context` | `wc-blocks-shared-context` | `wc.wcBlocksSharedContext` | Shares React contexts across separately built scripts. |
+| `@woocommerce/shared-hocs` | `wc-blocks-shared-hocs` | `wc.wcBlocksSharedHocs` | Shares higher-order components across separately built scripts. |
+| `@woocommerce/types` | `wc-types` | `wc.wcTypes` | Shares runtime type guards used by the external package bundles. |
+
+This list is defined by `editorExternalPackages` in `bin/webpack-config-block-editor-unified-assets.js`. Other `@woocommerce/*` imports are bundled into `wc-block-library` for the unified editor build. Their standalone handles may still be registered for frontend assets or third-party scripts.
+
+`wc-blocks-middleware` is registered separately and loaded by both `wc-block-library` and `wc-blocks-data-store`. It does not map to an enqueueable `@woocommerce/*` package.
+
+### Cart and Checkout frontend chunks
+
+The `wc-blocks-checkout` and `wc-blocks-components` package bundles are built with the Cart and Checkout frontend configuration. Their generated asset metadata depends on:
+
+- `wc-cart-checkout-base`
+- `wc-cart-checkout-vendors`
+
+WordPress therefore loads `wc-cart-checkout-base-frontend.js` and `wc-cart-checkout-vendors-frontend.js` when the unified editor needs either package. The `-frontend` suffix identifies the build configuration that produced the files; it does not restrict them to storefront requests. Removing these dependencies without changing how the packages are built would leave required modules unavailable.
diff --git a/plugins/woocommerce/client/blocks/tests/js/config/global-mocks.js b/plugins/woocommerce/client/blocks/tests/js/config/global-mocks.js
index b53055f5755..f79cd52c7a2 100644
--- a/plugins/woocommerce/client/blocks/tests/js/config/global-mocks.js
+++ b/plugins/woocommerce/client/blocks/tests/js/config/global-mocks.js
@@ -390,6 +390,6 @@ jest.mock( 'client-zip', () => ( {
  * store may be registered in the test environment without an actual editor
  * context. Individual tests can override this mock if needed.
  */
-jest.mock( '../../../assets/js/data/utils/is-editor', () => ( {
+jest.mock( '@woocommerce/block-data/utils/is-editor', () => ( {
 	isEditor: jest.fn().mockReturnValue( false ),
 } ) );
diff --git a/plugins/woocommerce/client/blocks/tests/js/jest.config.js b/plugins/woocommerce/client/blocks/tests/js/jest.config.js
index 259d2b9fa11..7f8d9e7b512 100644
--- a/plugins/woocommerce/client/blocks/tests/js/jest.config.js
+++ b/plugins/woocommerce/client/blocks/tests/js/jest.config.js
@@ -74,6 +74,7 @@ module.exports = {
 		'@woocommerce/base-hocs(.*)$': 'assets/js/base/hocs/$1',
 		'@woocommerce/base-hooks(.*)$': 'assets/js/base/hooks/$1',
 		'@woocommerce/base-utils(.*)$': 'assets/js/base/utils',
+		'@woocommerce/block-data/(.*)$': 'assets/js/data/$1',
 		'@woocommerce/block-data': 'assets/js/data',
 		'@woocommerce/resource-previews': 'assets/js/previews',
 		'@woocommerce/shared-context': 'assets/js/shared/context',
diff --git a/plugins/woocommerce/client/blocks/tsconfig.base.json b/plugins/woocommerce/client/blocks/tsconfig.base.json
index b5192f38bf3..08ee775ad3c 100644
--- a/plugins/woocommerce/client/blocks/tsconfig.base.json
+++ b/plugins/woocommerce/client/blocks/tsconfig.base.json
@@ -85,6 +85,9 @@
 			"@woocommerce/block-data": [
 				"assets/js/data"
 			],
+			"@woocommerce/block-data/*": [
+				"assets/js/data/*"
+			],
 			"@woocommerce/block-hocs": [
 				"assets/js/hocs"
 			],
diff --git a/plugins/woocommerce/client/blocks/tsconfig.json b/plugins/woocommerce/client/blocks/tsconfig.json
index 3771cbe9813..6e2aa1c19c3 100644
--- a/plugins/woocommerce/client/blocks/tsconfig.json
+++ b/plugins/woocommerce/client/blocks/tsconfig.json
@@ -4,9 +4,13 @@
 		"./typings/**/*",
 		"./assets/js/**/*",
 		"./assets/js/**/*.json",
+		"./assets/js/blocks-registry/**/*",
 		"./packages/checkout/**/*",
 		"./packages/components/**/*",
+		"./assets/js/entities/**/*",
 		"./packages/prices/**/*",
+		"./assets/js/data/**/*",
+		"./assets/js/middleware/**/*",
 		"./assets/js/blocks/**/block.json",
 		"./assets/js/atomic/blocks/**/block.json",
 		"./assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/**/block.json",
diff --git a/plugins/woocommerce/client/blocks/webpack.config.js b/plugins/woocommerce/client/blocks/webpack.config.js
index e428ee262ab..4ccbdc832be 100644
--- a/plugins/woocommerce/client/blocks/webpack.config.js
+++ b/plugins/woocommerce/client/blocks/webpack.config.js
@@ -17,6 +17,10 @@ const {
 	getStylingConfig,
 	getCartAndCheckoutFrontendConfig,
 } = require( './bin/webpack-configs.js' );
+const {
+	getUnifiedMainConfig,
+	getUnifiedStylingConfig,
+} = require( './bin/webpack-config-block-editor-unified-assets.js' );

 const interactivityBlocksConfig = require( './bin/webpack-config-interactive-blocks.js' );
 const dependencyDetectionConfig = require( './bin/webpack-config-dependency-detection.js' );
@@ -90,6 +94,13 @@ const MainConfig = {
 	...getMainConfig( { alias: getAlias() } ),
 };

+// Unified Blocks config enabled at runtime by a WooCommerce feature flag.
+const UnifiedMainConfig = {
+	...sharedConfig,
+	cache: getCacheConfig( 'unified-main', [] ),
+	...getUnifiedMainConfig( { alias: getAlias() } ),
+};
+
 // Frontend config for scripts used in the store itself.
 const FrontendConfig = {
 	...sharedConfig,
@@ -124,9 +135,14 @@ const StylingConfig = {
 	...getStylingConfig( { alias: getAlias() } ),
 };

-/**
- * Config to generate the site editor scripts.
- */
+// Unified editor styles enabled at runtime by a WooCommerce feature flag.
+const UnifiedStylingConfig = {
+	...sharedConfig,
+	cache: getCacheConfig( 'unified-styling', [] ),
+	...getUnifiedStylingConfig( { alias: getAlias() } ),
+};
+
+// Scripts used exclusively in the Site Editor by the legacy asset path.
 const SiteEditorConfig = {
 	...sharedConfig,
 	cache: getCacheConfig( 'site-editor', [] ),
@@ -157,11 +173,13 @@ module.exports = [
 	CartAndCheckoutFrontendConfig,
 	CoreConfig,
 	MainConfig,
+	UnifiedMainConfig,
 	FrontendConfig,
 	ExtensionsConfig,
 	PaymentsConfig,
 	SiteEditorConfig,
 	StylingConfig,
+	UnifiedStylingConfig,
 	InteractivityBlocksConfig,
 	DependencyDetectionConfig,
 ];
diff --git a/plugins/woocommerce/package.json b/plugins/woocommerce/package.json
index cb1b571e900..37b6ede4386 100644
--- a/plugins/woocommerce/package.json
+++ b/plugins/woocommerce/package.json
@@ -78,6 +78,7 @@
 		"test:e2e:default": "pnpm test:e2e:install && pnpm test:e2e:with-env default",
 		"test:e2e:install": "pnpm playwright install chromium",
 		"test:e2e:blocks": "node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON --test tests/e2e/utils/blocks/*.test.mjs && pnpm playwright test --config=tests/e2e/playwright.config.ts --project=blocks-chromium",
+		"test:e2e:blocks:unified-editor-assets": "pnpm wp-env:e2e run cli wp option update woocommerce_feature_block_editor_unified_assets_enabled yes && pnpm test:e2e:blocks",
 		"test:e2e:blocks:performance": "pnpm playwright test --config=tests/e2e/playwright.performance.config.ts --project=blocks-performance",
 		"test:e2e:email-update-propagation:pr": "pnpm test:e2e:default --project=core-serial --project=core-parallel --grep @pr tests/email-editor/update-propagation",
 		"test:e2e:email-update-propagation:nightly": "pnpm test:e2e:default --project=core-serial --project=core-parallel tests/email-editor/update-propagation",
@@ -97,6 +98,7 @@
 		"test:php:watch": "sh ./client/blocks/bin/copy-blocks-json.sh && ./vendor/bin/phpunit-watcher watch",
 		"test:metrics": "USE_WP_ENV=1 pnpm playwright test --config=tests/metrics/playwright.config.js",
 		"test:metrics:ci": "../../.github/workflows/scripts/run-metrics.sh",
+		"test:metrics:ci:unified-editor-assets": "pnpm wp-env:e2e run cli wp option update woocommerce_feature_block_editor_unified_assets_enabled yes && pnpm test:metrics:ci",
 		"test:php:env": "sh ./client/blocks/bin/copy-blocks-json.sh && pnpm wp-env:test run --env-cwd='wp-content/plugins/woocommerce' cli tests/php/bin/run-phpunit.sh -c phpunit.xml --verbose",
 		"test:php:env:hpos-off": "sh ./client/blocks/bin/copy-blocks-json.sh && pnpm wp-env:test run --env-cwd='wp-content/plugins/woocommerce' cli env DISABLE_HPOS=1 tests/php/bin/run-phpunit.sh -c phpunit.xml --verbose",
 		"test:php:env:watch": "sh ./client/blocks/bin/copy-blocks-json.sh && pnpm wp-env:test run --env-cwd='wp-content/plugins/woocommerce' cli vendor/bin/phpunit-watcher watch --verbose",
@@ -780,6 +782,38 @@
 						"resultsPath": "../../tools/compare-perf/artifacts/"
 					}
 				},
+				{
+					"name": "Metrics - unified editor assets",
+					"testType": "performance",
+					"command": "test:metrics:ci:unified-editor-assets",
+					"optional": true,
+					"changes": [
+						"composer.json",
+						"composer.lock",
+						"includes/**/*.php",
+						"patterns/**/*.php",
+						"src/**/*.php",
+						"templates/**/*.php",
+						"templates/**/*.html",
+						"tests/metrics/**",
+						".wp-env.e2e.json"
+					],
+					"onlyForDependencies": [
+						"@woocommerce/admin-library",
+						"@woocommerce/block-library",
+						"@woocommerce/classic-assets"
+					],
+					"testEnv": {
+						"start": "env:perf"
+					},
+					"events": [
+						"pull_request"
+					],
+					"report": {
+						"resultsBlobName": "core-metrics-unified-editor-assets-report",
+						"resultsPath": "../../tools/compare-perf/artifacts/"
+					}
+				},
 				{
 					"name": "Blocks e2e tests",
 					"testType": "e2e",
@@ -837,6 +871,58 @@
 						"allure": true
 					}
 				},
+				{
+					"name": "Blocks e2e tests - unified editor assets",
+					"testType": "e2e",
+					"usesSharedPluginBuild": true,
+					"command": "test:e2e:blocks:unified-editor-assets",
+					"shardingArguments": [
+						"--shard=1/10",
+						"--shard=2/10",
+						"--shard=3/10",
+						"--shard=4/10",
+						"--shard=5/10",
+						"--shard=6/10",
+						"--shard=7/10",
+						"--shard=8/10",
+						"--shard=9/10",
+						"--shard=10/10"
+					],
+					"changes": [
+						"patterns/**/*.php",
+						"includes/**/*.php",
+						"src/Blocks/**/*.php",
+						"src/Internal/Features/BlockEditorUnifiedAssets.php",
+						"templates/**/*.php",
+						"templates/**/*.html",
+						"tests/e2e/bin/blocks/**",
+						"tests/e2e/fixtures/blocks-setup.ts",
+						"tests/e2e/playwright.config.ts",
+						"tests/e2e/content-templates/blocks/**",
+						"tests/e2e/test-data/blocks/**",
+						"tests/e2e/test-plugins/blocks/**",
+						"tests/e2e/tests/blocks/**",
+						"tests/e2e/themes/blocks/**",
+						"tests/e2e/utils/blocks/**"
+					],
+					"onlyForDependencies": [
+						"@woocommerce/block-library"
+					],
+					"testEnv": {
+						"start": "env:start:blocks",
+						"config": {
+							"phpVersion": "7.4"
+						}
+					},
+					"events": [
+						"nightly-checks"
+					],
+					"report": {
+						"resultsBlobName": "blocks-e2e-unified-editor-assets-report",
+						"resultsPath": "tests/e2e/test-results",
+						"allure": true
+					}
+				},
 				{
 					"name": "Blocks e2e tests - WP pre-release",
 					"optional": true,
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index d2a79439e1f..6a2ce350f6c 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -49641,18 +49641,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/AbstractBlock.php

-		-
-			message: '#^Parameter \#1 \$arr1 of function array_merge expects array, array\|string given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/AbstractBlock.php
-
-		-
-			message: '#^Parameter \#1 \$arr1 of function array_merge expects array, array\|string\|null given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/AbstractBlock.php
-
 		-
 			message: '#^Parameter \#1 \$file_or_folder of function register_block_type_from_metadata expects string, string\|true given\.$#'
 			identifier: argument.type
@@ -49665,36 +49653,12 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/AbstractBlock.php

-		-
-			message: '#^Parameter \#1 \$handle of method Automattic\\WooCommerce\\Blocks\\Assets\\Api\:\:register_script\(\) expects string, array\|string given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/AbstractBlock.php
-
-		-
-			message: '#^Parameter \#1 \$handle of method Automattic\\WooCommerce\\Blocks\\Assets\\Api\:\:register_script\(\) expects string, array\|string\|null given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/AbstractBlock.php
-
 		-
 			message: '#^Parameter \#1 \$object_or_string of function is_a expects object, array\|WP_Block given\.$#'
 			identifier: argument.type
 			count: 1
 			path: src/Blocks/BlockTypes/AbstractBlock.php

-		-
-			message: '#^Parameter \#1 \$relative_src of method Automattic\\WooCommerce\\Blocks\\Assets\\Api\:\:get_script_data\(\) expects string, array\|string given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/AbstractBlock.php
-
-		-
-			message: '#^Parameter \#1 \$relative_src of method Automattic\\WooCommerce\\Blocks\\Assets\\Api\:\:get_script_data\(\) expects string, array\|string\|null given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/AbstractBlock.php
-
 		-
 			message: '#^Parameter \#1 \$request of method WP_REST_Server\:\:get_namespace_index\(\) expects WP_REST_Request, array\<string, string\> given\.$#'
 			identifier: argument.type
@@ -49719,18 +49683,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/AbstractBlock.php

-		-
-			message: '#^Parameter \#2 \$relative_src of method Automattic\\WooCommerce\\Blocks\\Assets\\Api\:\:register_script\(\) expects string, array\|string given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/AbstractBlock.php
-
-		-
-			message: '#^Parameter \#2 \$relative_src of method Automattic\\WooCommerce\\Blocks\\Assets\\Api\:\:register_script\(\) expects string, array\|string\|null given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/AbstractBlock.php
-
 		-
 			message: '#^Parameter \#3 \$block of method Automattic\\WooCommerce\\Blocks\\BlockTypes\\AbstractBlock\:\:enqueue_assets\(\) expects WP_Block, WP_Block\|null given\.$#'
 			identifier: argument.type
@@ -50247,12 +50199,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/Cart.php

-		-
-			message: '#^PHPDoc tag @return has invalid value \(array\|string;\)\: Unexpected token ";", expected TOKEN_HORIZONTAL_WS at offset 148 on line 5$#'
-			identifier: phpDoc.parseError
-			count: 1
-			path: src/Blocks/BlockTypes/Cart.php
-
 		-
 			message: '#^Parameter \#1 \$arr1 of function array_merge expects array, array\<string\>\|null given\.$#'
 			identifier: argument.type
@@ -50397,12 +50343,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/Checkout.php

-		-
-			message: '#^PHPDoc tag @return has invalid value \(array\|string;\)\: Unexpected token ";", expected TOKEN_HORIZONTAL_WS at offset 148 on line 5$#'
-			identifier: phpDoc.parseError
-			count: 1
-			path: src/Blocks/BlockTypes/Checkout.php
-
 		-
 			message: '#^Parameter \#1 \$arr1 of function array_merge expects array, array\<string\>\|null given\.$#'
 			identifier: argument.type
@@ -50925,12 +50865,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/MiniCart.php

-		-
-			message: '#^PHPDoc tag @return has invalid value \(array\|string;\)\: Unexpected token ";", expected TOKEN_HORIZONTAL_WS at offset 148 on line 5$#'
-			identifier: phpDoc.parseError
-			count: 1
-			path: src/Blocks/BlockTypes/MiniCart.php
-
 		-
 			message: '#^Parameter \#1 \$content of function do_blocks expects string, string\|false given\.$#'
 			identifier: argument.type
@@ -51051,12 +50985,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/MiniCartContents.php

-		-
-			message: '#^PHPDoc tag @return has invalid value \(array\|string;\)\: Unexpected token ";", expected TOKEN_HORIZONTAL_WS at offset 152 on line 6$#'
-			identifier: phpDoc.parseError
-			count: 1
-			path: src/Blocks/BlockTypes/MiniCartContents.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Blocks\\BlockTypes\\MiniCartFooterBlock\:\:enqueue_data\(\) has no return type specified\.$#'
 			identifier: missingType.return
diff --git a/plugins/woocommerce/src/Blocks/AssetsController.php b/plugins/woocommerce/src/Blocks/AssetsController.php
index ef3e4499d66..5f650bee231 100644
--- a/plugins/woocommerce/src/Blocks/AssetsController.php
+++ b/plugins/woocommerce/src/Blocks/AssetsController.php
@@ -6,6 +6,7 @@ namespace Automattic\WooCommerce\Blocks;
 use Automattic\Jetpack\Constants;
 use Automattic\WooCommerce\Blocks\Assets\Api as AssetApi;
 use Automattic\WooCommerce\Blocks\Utils\Utils;
+use Automattic\WooCommerce\Internal\Features\BlockEditorUnifiedAssets;

 /**
  * AssetsController class.
@@ -72,18 +73,22 @@ final class AssetsController {
 		$this->register_style( 'wc-blocks-packages-style', plugins_url( $this->api->get_block_asset_build_path( 'packages-style', 'css' ), dirname( __DIR__ ) ), array(), 'all', true );
 		$this->register_style( 'wc-blocks-style', plugins_url( $this->api->get_block_asset_build_path( 'wc-blocks', 'css' ), dirname( __DIR__ ) ), array(), 'all', true );
 		$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 );
+		if ( BlockEditorUnifiedAssets::is_enabled() ) {
+			$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 );
+		}

 		$this->api->register_script( 'wc-types', $this->api->get_block_asset_build_path( 'wc-types' ), array(), false );
 		$this->api->register_script( 'wc-entities', 'assets/client/blocks/wc-entities.js', array(), false );
 		$this->api->register_script( 'wc-blocks-middleware', 'assets/client/blocks/wc-blocks-middleware.js', array(), false );
 		$this->api->register_script( 'wc-blocks-data-store', 'assets/client/blocks/wc-blocks-data.js', array( 'wc-blocks-middleware' ) );
-		$this->api->register_script( 'wc-blocks-vendors', $this->api->get_block_asset_build_path( 'wc-blocks-vendors' ), array(), false );
 		$this->api->register_script( 'wc-blocks-registry', 'assets/client/blocks/wc-blocks-registry.js', array(), false );
-		$this->api->register_script( 'wc-blocks', $this->api->get_block_asset_build_path( 'wc-blocks' ), array( 'wc-blocks-vendors' ), false );
 		$this->api->register_script( 'wc-blocks-shared-context', 'assets/client/blocks/wc-blocks-shared-context.js' );
 		$this->api->register_script( 'wc-blocks-shared-hocs', 'assets/client/blocks/wc-blocks-shared-hocs.js', array(), false );
+		$this->api->register_script( 'wc-blocks-components', 'assets/client/blocks/blocks-components.js' );
+		$this->register_editor_scripts();

-		// The price package is shared externally so has no blocks prefix.
+		// Keep price-format as a dedicated shared package: editor and frontend/runtime assets depend on it, and
+		// externalizing it avoids duplicating price formatting helpers across bundles.
 		$this->api->register_script( 'wc-price-format', 'assets/client/blocks/price-format.js', array(), false );

 		// Vendor scripts for blocks frontends (not including cart and checkout).
@@ -94,7 +99,6 @@ final class AssetsController {
 		$this->api->register_script( 'wc-cart-checkout-base', $this->api->get_block_asset_build_path( 'wc-cart-checkout-base-frontend' ), array(), true );
 		$this->api->register_script( 'wc-blocks-checkout', 'assets/client/blocks/blocks-checkout.js' );
 		$this->api->register_script( 'wc-blocks-checkout-events', 'assets/client/blocks/blocks-checkout-events.js' );
-		$this->api->register_script( 'wc-blocks-components', 'assets/client/blocks/blocks-components.js' );
 		$this->api->register_script( 'wc-schema-parser', 'assets/client/blocks/wc-schema-parser.js', array(), false );

 		// Sanitize.
@@ -116,6 +120,65 @@ final class AssetsController {
 		);
 	}

+	/**
+	 * Register scripts for the active block editor asset configuration.
+	 */
+	private function register_editor_scripts(): void {
+		if ( ! BlockEditorUnifiedAssets::is_enabled() ) {
+			$this->api->register_script( 'wc-blocks-vendors', $this->api->get_block_asset_build_path( 'wc-blocks-vendors' ), array(), false );
+			$this->api->register_script( 'wc-blocks', $this->api->get_block_asset_build_path( 'wc-blocks' ), array( 'wc-blocks-vendors' ), false );
+			return;
+		}
+
+		$this->api->register_script(
+			'wc-block-library',
+			$this->api->get_block_asset_build_path( 'wc-block-library' ),
+			array( 'wc-blocks-middleware', 'wc-entities' ),
+			true
+		);
+		$this->register_deprecated_script_handles();
+	}
+
+	/**
+	 * Register deprecated script handles for backward compatibility.
+	 */
+	private function register_deprecated_script_handles(): void {
+		wp_register_script( 'wc-blocks-vendors', false, array(), $this->api->wc_version, true );
+		wp_register_script( 'wc-blocks', false, array( 'wc-blocks-vendors' ), $this->api->wc_version, true );
+
+		$this->add_deprecated_script_handle_warnings(
+			array(
+				'wc-blocks-vendors',
+				'wc-blocks',
+			)
+		);
+	}
+
+	/**
+	 * Add console warnings for deprecated script handles.
+	 *
+	 * @param array $handles Deprecated script handles.
+	 */
+	private function add_deprecated_script_handle_warnings( array $handles ): void {
+		foreach ( $handles as $handle ) {
+			$message = wp_json_encode(
+				sprintf(
+					'[WooCommerce] The "%s" script handle is deprecated and kept for backward compatibility only. See the enqueueable packages documentation for the supported WooCommerce Blocks package handles: https://github.com/woocommerce/woocommerce/tree/trunk/plugins/woocommerce/client/blocks/docs/internal-developers/enqueueable-packages',
+					$handle
+				)
+			);
+
+			if ( false === $message ) {
+				continue;
+			}
+
+			$this->api->add_inline_script(
+				$handle,
+				sprintf( 'console.warn( %s );', $message )
+			);
+		}
+	}
+
 	/**
 	 * Defines resource hints to help speed up the loading of some critical blocks.
 	 *
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/AbstractBlock.php b/plugins/woocommerce/src/Blocks/BlockTypes/AbstractBlock.php
index 3b76c0849b7..43057813918 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/AbstractBlock.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/AbstractBlock.php
@@ -1,4 +1,5 @@
 <?php
+
 namespace Automattic\WooCommerce\Blocks\BlockTypes;

 use Automattic\WooCommerce\Admin\Features\Features;
@@ -6,6 +7,7 @@ use Automattic\WooCommerce\Blocks\Assets\Api as AssetApi;
 use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
 use Automattic\WooCommerce\Blocks\Integrations\IntegrationRegistry;
 use Automattic\WooCommerce\Enums\ProductStatus;
+use Automattic\WooCommerce\Internal\Features\BlockEditorUnifiedAssets;
 use Automattic\WooCommerce\Internal\Utilities\ProductUtil;
 use WP_Block;

@@ -146,36 +148,98 @@ abstract class AbstractBlock {
 	 * This registers the scripts; it does not enqueue them.
 	 */
 	protected function register_block_type_assets() {
-		if ( null !== $this->get_block_type_editor_script() ) {
-			$data     = $this->asset_api->get_script_data( $this->get_block_type_editor_script( 'path' ) );
-			$has_i18n = in_array( 'wp-i18n', $data['dependencies'], true );
+		$editor_script = $this->get_block_type_editor_script();
+		if ( is_array( $editor_script ) ) {
+			$this->register_editor_script_asset( $editor_script );
+		}

-			$this->asset_api->register_script(
-				$this->get_block_type_editor_script( 'handle' ),
-				$this->get_block_type_editor_script( 'path' ),
+		$frontend_script = $this->get_block_type_script();
+		if ( is_array( $frontend_script ) ) {
+			$this->register_frontend_script_asset( $frontend_script );
+		}
+	}
+
+	/**
+	 * Register the editor script asset.
+	 *
+	 * @param array $editor_script The editor script data.
+	 * @return void
+	 */
+	private function register_editor_script_asset( $editor_script ) {
+		$handle       = (string) $editor_script['handle'];
+		$path         = (string) $editor_script['path'];
+		$dependencies = array_values(
+			array_unique(
 				array_merge(
-					$this->get_block_type_editor_script( 'dependencies' ),
+					(array) $editor_script['dependencies'],
 					$this->integration_registry->get_all_registered_editor_script_handles()
-				),
-				$has_i18n
-			);
-		}
-		if ( null !== $this->get_block_type_script() ) {
-			$data     = $this->asset_api->get_script_data( $this->get_block_type_script( 'path' ) );
+				)
+			)
+		);
+
+		if ( wp_script_is( $handle, 'registered' ) ) {
+			$this->add_script_dependencies( $handle, $dependencies );
+		} else {
+			$data     = $this->asset_api->get_script_data( $path );
 			$has_i18n = in_array( 'wp-i18n', $data['dependencies'], true );

 			$this->asset_api->register_script(
-				$this->get_block_type_script( 'handle' ),
-				$this->get_block_type_script( 'path' ),
-				array_merge(
-					$this->get_block_type_script( 'dependencies' ),
-					$this->integration_registry->get_all_registered_script_handles()
-				),
+				$handle,
+				$path,
+				$dependencies,
 				$has_i18n
 			);
 		}
 	}

+	/**
+	 * Register the frontend script asset.
+	 *
+	 * @param array $frontend_script The frontend script data.
+	 * @return void
+	 */
+	private function register_frontend_script_asset( $frontend_script ) {
+		$handle       = (string) $frontend_script['handle'];
+		$path         = (string) $frontend_script['path'];
+		$dependencies = array_merge(
+			(array) $frontend_script['dependencies'],
+			$this->integration_registry->get_all_registered_script_handles()
+		);
+		$data         = $this->asset_api->get_script_data( $path );
+		$has_i18n     = in_array( 'wp-i18n', $data['dependencies'], true );
+
+		$this->asset_api->register_script(
+			$handle,
+			$path,
+			$dependencies,
+			$has_i18n
+		);
+	}
+
+	/**
+	 * Add dependencies to a registered script.
+	 *
+	 * @param string $handle       The script handle.
+	 * @param array  $dependencies The dependencies to add.
+	 * @return void
+	 */
+	private function add_script_dependencies( $handle, $dependencies ) {
+		$wp_scripts = wp_scripts();
+
+		if ( ! isset( $wp_scripts->registered[ $handle ] ) ) {
+			return;
+		}
+
+		$wp_scripts->registered[ $handle ]->deps = array_values(
+			array_unique(
+				array_merge(
+					$wp_scripts->registered[ $handle ]->deps,
+					$dependencies
+				)
+			)
+		);
+	}
+
 	/**
 	 * Injects Chunk Translations into the page so translations work for lazy loaded components.
 	 *
@@ -302,11 +366,17 @@ abstract class AbstractBlock {
 	 * @return array|string
 	 */
 	protected function get_block_type_editor_script( $key = null ) {
-		$script = [
-			'handle'       => 'wc-' . $this->block_name . '-block',
-			'path'         => $this->asset_api->get_block_asset_build_path( $this->block_name ),
-			'dependencies' => [ 'wc-blocks' ],
-		];
+		$script = BlockEditorUnifiedAssets::is_enabled()
+			? array(
+				'handle'       => 'wc-block-library',
+				'path'         => $this->asset_api->get_block_asset_build_path( 'wc-block-library' ),
+				'dependencies' => array(),
+			)
+			: array(
+				'handle'       => 'wc-' . $this->block_name . '-block',
+				'path'         => $this->asset_api->get_block_asset_build_path( $this->block_name ),
+				'dependencies' => array( 'wc-blocks' ),
+			);
 		return $key ? $script[ $key ] : $script;
 	}

@@ -317,7 +387,7 @@ abstract class AbstractBlock {
 	 * @return string|null
 	 */
 	protected function get_block_type_editor_style() {
-		return 'wc-blocks-editor-style';
+		return BlockEditorUnifiedAssets::is_enabled() ? 'wc-block-library-style' : 'wc-blocks-editor-style';
 	}

 	/**
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/Cart.php b/plugins/woocommerce/src/Blocks/BlockTypes/Cart.php
index 5b6d27a60af..aea17c68d7d 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/Cart.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/Cart.php
@@ -110,21 +110,6 @@ class Cart extends AbstractBlock {
 		);
 	}

-	/**
-	 * Get the editor script handle for this block type.
-	 *
-	 * @param string $key Data to get, or default to everything.
-	 * @return array|string;
-	 */
-	protected function get_block_type_editor_script( $key = null ) {
-		$script = [
-			'handle'       => 'wc-' . $this->block_name . '-block',
-			'path'         => $this->asset_api->get_block_asset_build_path( $this->block_name ),
-			'dependencies' => [ 'wc-blocks' ],
-		];
-		return $key ? $script[ $key ] : $script;
-	}
-
 	/**
 	 * Get the frontend script handle for this block type.
 	 *
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/Checkout.php b/plugins/woocommerce/src/Blocks/BlockTypes/Checkout.php
index ce2792016fb..5d48876aaf4 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/Checkout.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/Checkout.php
@@ -159,21 +159,6 @@ class Checkout extends AbstractBlock {
 		);
 	}

-	/**
-	 * Get the editor script handle for this block type.
-	 *
-	 * @param string $key Data to get, or default to everything.
-	 * @return array|string;
-	 */
-	protected function get_block_type_editor_script( $key = null ) {
-		$script = [
-			'handle'       => 'wc-' . $this->block_name . '-block',
-			'path'         => $this->asset_api->get_block_asset_build_path( $this->block_name ),
-			'dependencies' => [ 'wc-blocks' ],
-		];
-		return $key ? $script[ $key ] : $script;
-	}
-
 	/**
 	 * Get the frontend script handle for this block type.
 	 *
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/CouponCode.php b/plugins/woocommerce/src/Blocks/BlockTypes/CouponCode.php
index daaa050e8df..079dfc0fcd9 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/CouponCode.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/CouponCode.php
@@ -46,21 +46,6 @@ class CouponCode extends AbstractBlock {
 		'letter-spacing'   => '1px',
 	);

-	/**
-	 * Get the editor script handle for this block type.
-	 *
-	 * @param string|null $key Data to get. Valid keys: "handle", "path", "dependencies".
-	 * @return array|string|null
-	 */
-	protected function get_block_type_editor_script( $key = null ) {
-		$script = array(
-			'handle'       => 'wc-' . $this->block_name . '-block',
-			'path'         => $this->asset_api->get_block_asset_build_path( $this->block_name ),
-			'dependencies' => array( 'wc-blocks' ),
-		);
-		return null === $key ? $script : ( $script[ $key ] ?? null );
-	}
-
 	/**
 	 * Get the frontend style handle for this block type.
 	 *
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/EmailContent.php b/plugins/woocommerce/src/Blocks/BlockTypes/EmailContent.php
index 33e590b3824..2151bfc871d 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/EmailContent.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/EmailContent.php
@@ -27,21 +27,6 @@ class EmailContent extends AbstractBlock {
 		return null;
 	}

-	/**
-	 * Get the editor script handle for this block type.
-	 *
-	 * @param string $key Data to get, or default to everything.
-	 * @return array|string
-	 */
-	protected function get_block_type_editor_script( $key = null ) {
-		$script = [
-			'handle'       => 'wc-' . $this->block_name . '-block',
-			'path'         => $this->asset_api->get_block_asset_build_path( $this->block_name ),
-			'dependencies' => [ 'wc-blocks' ],
-		];
-		return $key ? $script[ $key ] : $script;
-	}
-
 	/**
 	 * Get the frontend script handle for this block type.
 	 *
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/MiniCart.php b/plugins/woocommerce/src/Blocks/BlockTypes/MiniCart.php
index 98e9bb03e52..fce4b28a622 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/MiniCart.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/MiniCart.php
@@ -131,21 +131,6 @@ class MiniCart extends AbstractBlock {
 		return $parsed_hooked_block;
 	}

-	/**
-	 * Get the editor script handle for this block type.
-	 *
-	 * @param string $key Data to get, or default to everything.
-	 * @return array|string;
-	 */
-	protected function get_block_type_editor_script( $key = null ) {
-		$script = array(
-			'handle'       => 'wc-' . $this->block_name . '-block',
-			'path'         => $this->asset_api->get_block_asset_build_path( $this->block_name ),
-			'dependencies' => array( 'wc-blocks' ),
-		);
-		return $key ? $script[ $key ] : $script;
-	}
-
 	/**
 	 * Get the frontend script handle for this block type.
 	 *
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/MiniCartContents.php b/plugins/woocommerce/src/Blocks/BlockTypes/MiniCartContents.php
index 695b99e0071..18d7d9a5c97 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/MiniCartContents.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/MiniCartContents.php
@@ -16,22 +16,6 @@ class MiniCartContents extends AbstractBlock {
 	 */
 	protected $block_name = 'mini-cart-contents';

-	/**
-	 * Get the editor script handle for this block type.
-	 *
-	 * @param string $key Data to get, or default to everything.
-	 *
-	 * @return array|string;
-	 */
-	protected function get_block_type_editor_script( $key = null ) {
-		$script = [
-			'handle'       => 'wc-' . $this->block_name . '-block',
-			'path'         => $this->asset_api->get_block_asset_build_path( $this->block_name ),
-			'dependencies' => [ 'wc-blocks' ],
-		];
-		return $key ? $script[ $key ] : $script;
-	}
-
 	/**
 	 * Get the frontend script handle for this block type.
 	 *
diff --git a/plugins/woocommerce/src/Blocks/BlockTypesController.php b/plugins/woocommerce/src/Blocks/BlockTypesController.php
index ec886d56cf6..f59b01ed6a6 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypesController.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypesController.php
@@ -9,6 +9,7 @@ use Automattic\WooCommerce\Blocks\Integrations\IntegrationRegistry;
 use Automattic\WooCommerce\Blocks\BlockTypes\Cart;
 use Automattic\WooCommerce\Blocks\BlockTypes\Checkout;
 use Automattic\WooCommerce\Blocks\BlockTypes\MiniCartContents;
+use Automattic\WooCommerce\Internal\Features\BlockEditorUnifiedAssets;
 use Automattic\WooCommerce\Internal\ShopperLists\ShopperListsController;

 /**
@@ -62,6 +63,7 @@ final class BlockTypesController {
 		add_filter( 'render_block', array( $this, 'add_data_attributes' ), 10, 2 );
 		add_action( 'woocommerce_login_form_end', array( $this, 'redirect_to_field' ) );
 		add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_legacy_widgets_with_block_equivalent' ) );
+		add_filter( 'block_type_metadata_settings', array( $this, 'use_single_block_editor_style' ), 10, 2 );
 		add_filter( 'register_block_type_args', array( $this, 'enqueue_block_style_for_classic_themes' ), 10, 2 );
 		add_filter( 'block_core_breadcrumbs_post_type_settings', array( $this, 'set_product_breadcrumbs_preferred_taxonomy' ), 10, 3 );
 		add_filter( 'block_core_breadcrumbs_items', array( $this, 'apply_woocommerce_breadcrumb_filters' ), 10, 1 );
@@ -585,7 +587,64 @@ final class BlockTypesController {
 	}

 	/**
-	 * Set the preferred taxonomy and term for product breadcrumbs.
+	 * Use one shared editor stylesheet for WooCommerce blocks.
+	 *
+	 * WordPress loads `style` handles in both the frontend and editor. WooCommerce
+	 * keeps those per-block handles for frontend performance, but removes them in
+	 * admin so the block editor loads the combined stylesheet only.
+	 *
+	 * @internal
+	 *
+	 * @param array $settings Block settings.
+	 * @param array $metadata Block metadata.
+	 *
+	 * @return array Block settings.
+	 */
+	public function use_single_block_editor_style( $settings, $metadata ) {
+		if (
+			! BlockEditorUnifiedAssets::is_enabled() ||
+			! is_admin() ||
+			! $this->is_woocommerce_block_metadata( $metadata ) ) {
+			return $settings;
+		}
+
+		$settings['style_handles']        = array();
+		$settings['style']                = array();
+		$settings['editor_style_handles'] = array( 'wc-block-library-style' );
+		$settings['editor_style']         = array( 'wc-block-library-style' );
+
+		return $settings;
+	}
+
+	/**
+	 * Check whether block metadata belongs to a block bundled with WooCommerce.
+	 *
+	 * @param array $metadata Block metadata.
+	 *
+	 * @return bool Whether the metadata file is in the WooCommerce blocks directory.
+	 */
+	private function is_woocommerce_block_metadata( $metadata ) {
+		static $blocks_path = null;
+
+		if ( null === $blocks_path ) {
+			$resolved_path = realpath( WC_ABSPATH . 'assets/client/blocks' );
+			$blocks_path   = false === $resolved_path
+				? ''
+				: trailingslashit( wp_normalize_path( $resolved_path ) );
+		}
+
+		if ( '' === $blocks_path || empty( $metadata['file'] ) ) {
+			return false;
+		}
+
+		return str_starts_with(
+			wp_normalize_path( $metadata['file'] ),
+			$blocks_path
+		);
+	}
+
+	/**
+	 * Set the preferred taxonomy and term for the breadcrumbs block on the product post type.
 	 *
 	 * @internal
 	 *
diff --git a/plugins/woocommerce/src/Blocks/DependencyDetection.php b/plugins/woocommerce/src/Blocks/DependencyDetection.php
index 719517b9dbb..324731ace2c 100644
--- a/plugins/woocommerce/src/Blocks/DependencyDetection.php
+++ b/plugins/woocommerce/src/Blocks/DependencyDetection.php
@@ -53,6 +53,7 @@ final class DependencyDetection {
 		'blocksComponents'      => 'wc-blocks-components',
 		'wcTypes'               => 'wc-types',
 		'sanitize'              => 'wc-sanitize',
+		'wcEntities'            => 'wc-entities',
 	);

 	/**
diff --git a/plugins/woocommerce/src/Blocks/Domain/Bootstrap.php b/plugins/woocommerce/src/Blocks/Domain/Bootstrap.php
index bbbad044980..b19c178e88b 100644
--- a/plugins/woocommerce/src/Blocks/Domain/Bootstrap.php
+++ b/plugins/woocommerce/src/Blocks/Domain/Bootstrap.php
@@ -37,6 +37,7 @@ use Automattic\WooCommerce\StoreApi\SchemaController;
 use Automattic\WooCommerce\StoreApi\StoreApi;
 use Automattic\WooCommerce\Blocks\Shipping\ShippingController;
 use Automattic\WooCommerce\Blocks\TemplateOptions;
+use Automattic\WooCommerce\Internal\Features\BlockEditorUnifiedAssets;


 /**
@@ -170,8 +171,10 @@ class Bootstrap {
 	 * @return bool
 	 */
 	protected function is_built() {
+		$editor_asset = BlockEditorUnifiedAssets::is_enabled() ? 'wc-block-library.js' : 'featured-product.js';
+
 		return file_exists(
-			$this->package->get_path( 'assets/client/blocks/featured-product.js' )
+			$this->package->get_path( 'assets/client/blocks/' . $editor_asset )
 		);
 	}

diff --git a/plugins/woocommerce/src/Blocks/Payments/Api.php b/plugins/woocommerce/src/Blocks/Payments/Api.php
index 095c3285c43..82a36757438 100644
--- a/plugins/woocommerce/src/Blocks/Payments/Api.php
+++ b/plugins/woocommerce/src/Blocks/Payments/Api.php
@@ -7,6 +7,7 @@ use Automattic\WooCommerce\Blocks\Payments\Integrations\BankTransfer;
 use Automattic\WooCommerce\Blocks\Payments\Integrations\CashOnDelivery;
 use Automattic\WooCommerce\Blocks\Payments\Integrations\Cheque;
 use Automattic\WooCommerce\Blocks\Payments\Integrations\PayPal;
+use Automattic\WooCommerce\Internal\Features\BlockEditorUnifiedAssets;

 /**
  *  The Api class provides an interface to payment method registration.
@@ -59,10 +60,33 @@ class Api {
 	 * @return array
 	 */
 	public function add_payment_method_script_dependencies( $dependencies, $handle ) {
-		if ( ! in_array( $handle, [ 'wc-checkout-block', 'wc-checkout-block-frontend', 'wc-cart-block', 'wc-cart-block-frontend' ], true ) ) {
+		if ( ! in_array( $handle, $this->get_cart_checkout_script_handles(), true ) ) {
 			return $dependencies;
 		}
-		return array_merge( $dependencies, $this->payment_method_registry->get_all_active_payment_method_script_dependencies() );
+		return array_values(
+			array_unique(
+				array_merge(
+					$dependencies,
+					$this->payment_method_registry->get_all_active_payment_method_script_dependencies()
+				)
+			)
+		);
+	}
+
+	/**
+	 * Get Cart and Checkout script handles for the active editor asset configuration.
+	 *
+	 * @return string[] Script handles.
+	 */
+	private function get_cart_checkout_script_handles(): array {
+		$editor_handles = BlockEditorUnifiedAssets::is_enabled()
+			? array( 'wc-block-library' )
+			: array( 'wc-checkout-block', 'wc-cart-block' );
+
+		return array_merge(
+			$editor_handles,
+			array( 'wc-cart-block-frontend', 'wc-checkout-block-frontend' )
+		);
 	}

 	/**
@@ -160,7 +184,7 @@ class Api {
 						sprintf( 'console.error( "%s" );', $error_message )
 					);

-					$cart_checkout_scripts = [ 'wc-cart-block', 'wc-cart-block-frontend', 'wc-checkout-block', 'wc-checkout-block-frontend' ];
+					$cart_checkout_scripts = $this->get_cart_checkout_script_handles();
 					foreach ( $cart_checkout_scripts as $script_handle ) {
 						if (
 							! array_key_exists( $script_handle, $wp_scripts->registered ) ||
diff --git a/plugins/woocommerce/src/Internal/Features/BlockEditorUnifiedAssets.php b/plugins/woocommerce/src/Internal/Features/BlockEditorUnifiedAssets.php
new file mode 100644
index 00000000000..ecb9328212a
--- /dev/null
+++ b/plugins/woocommerce/src/Internal/Features/BlockEditorUnifiedAssets.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * BlockEditorUnifiedAssets class file.
+ */
+
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Internal\Features;
+
+use Automattic\WooCommerce\Utilities\FeaturesUtil;
+
+/**
+ * Feature gate for unified WooCommerce block editor assets.
+ */
+final class BlockEditorUnifiedAssets {
+
+	/**
+	 * Feature Name.
+	 */
+	public const FEATURE_NAME = 'block_editor_unified_assets';
+
+	/**
+	 * Option that stores whether the feature is enabled.
+	 */
+	public const OPTION_NAME = 'woocommerce_feature_block_editor_unified_assets_enabled';
+
+	/**
+	 * Check whether unified block editor assets are enabled.
+	 *
+	 * @return bool True when unified block editor assets are enabled.
+	 *
+	 * @since 11.1.0
+	 */
+	public static function is_enabled(): bool {
+		return FeaturesUtil::feature_is_enabled( self::FEATURE_NAME );
+	}
+}
diff --git a/plugins/woocommerce/src/Internal/Features/FeaturesController.php b/plugins/woocommerce/src/Internal/Features/FeaturesController.php
index a12b1d51c7a..4b0c0a13b67 100644
--- a/plugins/woocommerce/src/Internal/Features/FeaturesController.php
+++ b/plugins/woocommerce/src/Internal/Features/FeaturesController.php
@@ -295,7 +295,7 @@ class FeaturesController {
 		$tracking_enabled                 = WC_Site_Tracking::is_tracking_enabled();

 		$legacy_features = array(
-			'analytics'                          => array(
+			'analytics'                            => array(
 				'name'                         => __( 'WooCommerce Analytics', 'woocommerce' ),
 				'description'                  => __( 'Enable WooCommerce Analytics to track your store\'s key metrics and view them in a detailed dashboard. All data stays within your store.', 'woocommerce' ),
 				'option_key'                   => Analytics::TOGGLE_OPTION_NAME,
@@ -305,7 +305,7 @@ class FeaturesController {
 				'skip_compatibility_checks'    => true,
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			ProductMediaGallery::FEATURE_ID      => array(
+			ProductMediaGallery::FEATURE_ID        => array(
 				'name'                         => __( 'Product gallery videos', 'woocommerce' ),
 				'description'                  => __( 'Enable videos in product galleries.', 'woocommerce' ),
 				'option_key'                   => ProductMediaGallery::ENABLE_OPTION_NAME,
@@ -315,14 +315,14 @@ class FeaturesController {
 				'skip_compatibility_checks'    => true,
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			'cart_checkout_blocks'               => array(
+			'cart_checkout_blocks'                 => array(
 				'name'                         => __( 'Cart & Checkout Blocks', 'woocommerce' ),
 				'description'                  => __( 'Optimize for faster checkout', 'woocommerce' ),
 				'is_experimental'              => false,
 				'disable_ui'                   => true,
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			'rate_limit_checkout'                => array(
+			'rate_limit_checkout'                  => array(
 				'name'                         => __( 'Rate limit Checkout', 'woocommerce' ),
 				'description'                  => sprintf(
 					// translators: %s is the URL to the rate limiting documentation.
@@ -335,7 +335,7 @@ class FeaturesController {
 				'skip_compatibility_checks'    => true,
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			'marketplace'                        => array(
+			'marketplace'                          => array(
 				'name'                         => __( 'Marketplace', 'woocommerce' ),
 				'description'                  => __(
 					'New, faster way to find extensions and themes for your WooCommerce store',
@@ -351,7 +351,7 @@ class FeaturesController {
 			),
 			// Marked as a legacy feature to avoid compatibility checks, which aren't really relevant to this feature.
 			// https://github.com/woocommerce/woocommerce/pull/39701#discussion_r1376976959.
-			'order_attribution'                  => array(
+			'order_attribution'                    => array(
 				'name'                         => __( 'Order Attribution', 'woocommerce' ),
 				'description'                  => __(
 					'Enable this feature to track and credit channels and campaigns that contribute to orders on your site',
@@ -363,7 +363,7 @@ class FeaturesController {
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 				'is_experimental'              => false,
 			),
-			'site_visibility_badge'              => array(
+			'site_visibility_badge'                => array(
 				'name'                         => __( 'Site visibility badge', 'woocommerce' ),
 				'description'                  => __(
 					'Enable the site visibility badge in the WordPress admin bar',
@@ -376,7 +376,7 @@ class FeaturesController {
 				'is_experimental'              => false,
 				'disabled'                     => false,
 			),
-			'hpos_fts_indexes'                   => array(
+			'hpos_fts_indexes'                     => array(
 				'name'                         => __( 'HPOS Full text search indexes', 'woocommerce' ),
 				'description'                  => __(
 					'Create and use full text search indexes for orders. This feature only works with high-performance order storage.',
@@ -388,7 +388,7 @@ class FeaturesController {
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 				'option_key'                   => CustomOrdersTableController::HPOS_FTS_INDEX_OPTION,
 			),
-			'hpos_datastore_caching'             => array(
+			'hpos_datastore_caching'               => array(
 				'name'                         => __( 'HPOS Data Caching', 'woocommerce' ),
 				'description'                  => __(
 					'Enable order data caching in the datastore. This feature only works with high-performance order storage and is recommended for stores using object caching.',
@@ -401,7 +401,7 @@ class FeaturesController {
 				'disable_ui'                   => false,
 				'option_key'                   => CustomOrdersTableController::HPOS_DATASTORE_CACHING_ENABLED_OPTION,
 			),
-			'remote_logging'                     => array(
+			'remote_logging'                       => array(
 				'name'                         => __( 'Remote Logging', 'woocommerce' ),
 				'description'                  => sprintf(
 					/* translators: %1$s: opening link tag, %2$s: closing link tag */
@@ -436,7 +436,7 @@ class FeaturesController {
 					},
 				),
 			),
-			'deferred_transactional_emails'      => array(
+			'deferred_transactional_emails'        => array(
 				'name'                         => __( 'Deferred emails', 'woocommerce' ),
 				'description'                  => __(
 					'Send transactional emails asynchronously via Action Scheduler instead of during the current request.',
@@ -446,7 +446,7 @@ class FeaturesController {
 				'enabled_by_default'           => false,
 				'is_experimental'              => false,
 			),
-			'customer_review_request'            => array(
+			'customer_review_request'              => array(
 				'name'                         => __( 'Customer review request (beta)', 'woocommerce' ),
 				'description'                  => __(
 					'Send customers a transactional email after order completion inviting them to review the products they bought, and host the per-order Review Order landing page.',
@@ -458,7 +458,7 @@ class FeaturesController {
 				'enabled_by_default'           => false,
 				'is_experimental'              => false,
 			),
-			'order_withdrawal'                   => array(
+			'order_withdrawal'                     => array(
 				'name'                         => __( 'Order withdrawal', 'woocommerce' ),
 				'description'                  => __( 'Enable the public order withdrawal endpoint for stakeholder testing.', 'woocommerce' ),
 				'enabled_by_default'           => false,
@@ -466,7 +466,7 @@ class FeaturesController {
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 				'is_experimental'              => true,
 			),
-			'abandoned_cart_recovery'            => array(
+			'abandoned_cart_recovery'              => array(
 				'name'                         => __( 'Abandoned cart recovery', 'woocommerce' ),
 				'description'                  => __(
 					'Send a reminder email to shoppers who didn\'t finish checking out.',
@@ -477,7 +477,7 @@ class FeaturesController {
 				'enabled_by_default'           => false,
 				'is_experimental'              => true,
 			),
-			'email_improvements'                 => array(
+			'email_improvements'                   => array(
 				'name'                         => __( 'Email improvements', 'woocommerce' ),
 				'description'                  => __(
 					'Enable modern email design for transactional emails',
@@ -497,7 +497,7 @@ class FeaturesController {
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 				'is_experimental'              => false,
 			),
-			'blueprint'                          => array(
+			'blueprint'                            => array(
 				'name'                         => __( 'Blueprint (beta)', 'woocommerce' ),
 				'description'                  => __(
 					'Enable blueprint to import and export settings in bulk',
@@ -518,7 +518,7 @@ class FeaturesController {
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 				'is_experimental'              => false,
 			),
-			'block_email_editor'                 => array(
+			'block_email_editor'                   => array(
 				'name'                         => __( 'Block Email Editor (alpha)', 'woocommerce' ),
 				'description'                  => __(
 					'Enable the block-based email editor for transactional emails.',
@@ -550,7 +550,7 @@ class FeaturesController {
 				'skip_compatibility_checks'    => true,
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			'wc-visual-attribute'                => array(
+			'wc-visual-attribute'                  => array(
 				'name'                         => __( 'Color swatches for attributes', 'woocommerce' ),
 				'description'                  => __(
 					'Add color swatches to product attribute values.',
@@ -563,7 +563,7 @@ class FeaturesController {
 				'skip_compatibility_checks'    => true,
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			'point_of_sale'                      => array(
+			'point_of_sale'                        => array(
 				'name'                         => __( 'Point of Sale', 'woocommerce' ),
 				'description'                  => __(
 					'Enable Point of Sale functionality in the WooCommerce mobile apps.',
@@ -577,7 +577,7 @@ class FeaturesController {
 				'deprecated_since'             => '11.0.0',
 				'deprecated_value'             => true,
 			),
-			'point_of_sale_staff'                => array(
+			'point_of_sale_staff'                  => array(
 				'name'                         => __( 'POS staff', 'woocommerce' ),
 				'description'                  => __(
 					'Experimental: POS staff management, roles, and order attribution.',
@@ -591,7 +591,7 @@ class FeaturesController {
 				'skip_compatibility_checks'    => true,
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			'fulfillments'                       => array(
+			'fulfillments'                         => array(
 				'name'                         => __( 'Order Fulfillments', 'woocommerce' ),
 				'description'                  => __(
 					'Enable the Order Fulfillments feature to manage order fulfillment and shipping.',
@@ -602,7 +602,7 @@ class FeaturesController {
 				'is_experimental'              => false,
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			'mcp_integration'                    => array(
+			'mcp_integration'                      => array(
 				'name'                         => __( 'WooCommerce MCP', 'woocommerce' ),
 				'description'                  => $this->get_mcp_integration_description(),
 				'enabled_by_default'           => false,
@@ -611,7 +611,7 @@ class FeaturesController {
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 				'is_legacy'                    => false,
 			),
-			'destroy-empty-sessions'             => array(
+			'destroy-empty-sessions'               => array(
 				'name'                         => __( 'Clear Customer Sessions When Empty', 'woocommerce' ),
 				'description'                  => __(
 					'[Performance] Removes session cookies for non-logged in customers when session data is empty, improving page caching performance. May cause compatibility issues with extensions that depend on the session cookie without using session data.',
@@ -622,7 +622,7 @@ class FeaturesController {
 				'disable_ui'                   => false,
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			'agentic_checkout'                   => array(
+			'agentic_checkout'                     => array(
 				'name'                         => __( 'Agentic Checkout API', 'woocommerce' ),
 				'description'                  => __(
 					'Enable the Agentic Checkout API for AI-powered checkout experiences (e.g., ChatGPT). This adds REST API endpoints that allow AI agents to create and manage checkout sessions.',
@@ -634,7 +634,7 @@ class FeaturesController {
 				'skip_compatibility_checks'    => true,
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			'dual_code_graphql_api'              => array(
+			'dual_code_graphql_api'                => array(
 				'name'                         => __( 'Dual Code & GraphQL API', 'woocommerce' ),
 				'description'                  => __(
 					'Experimental code-first API for WooCommerce with automatic GraphQL endpoint generation. Requires PHP 8.1 or later.',
@@ -646,7 +646,7 @@ class FeaturesController {
 				'skip_compatibility_checks'    => true,
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			PushNotifications::FEATURE_NAME      => array(
+			PushNotifications::FEATURE_NAME        => array(
 				'name'                         => __( 'Push Notifications', 'woocommerce' ),
 				'description'                  => __(
 					'Enable push notifications for the WooCommerce mobile apps to receive order notifications and store updates.',
@@ -660,7 +660,7 @@ class FeaturesController {
 				'deprecated_since'             => '10.9.2',
 				'deprecated_value'             => true,
 			),
-			'rest_api_caching'                   => array(
+			'rest_api_caching'                     => array(
 				'name'                         => __( 'REST API Caching', 'woocommerce' ),
 				'description'                  => sprintf(
 					/* translators: %1$s and %2$s are opening and closing <a> tags */
@@ -674,7 +674,7 @@ class FeaturesController {
 				'skip_compatibility_checks'    => true,
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			'cart_save_for_later'                => array(
+			'cart_save_for_later'                  => array(
 				'name'                         => __( 'Save for Later in Cart', 'woocommerce' ),
 				'description'                  => __(
 					'Let shoppers save cart items to a list to purchase later.',
@@ -687,7 +687,7 @@ class FeaturesController {
 				'option_key'                   => 'woocommerce_cart_save_for_later_enabled',
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			'product_wishlist'                   => array(
+			'product_wishlist'                     => array(
 				'name'                         => __( 'Wishlists', 'woocommerce' ),
 				'description'                  => __(
 					'Let shoppers save products to a wishlist from product pages. Requires the Add to Cart + Options block on the single-product template.',
@@ -698,7 +698,7 @@ class FeaturesController {
 				'option_key'                   => 'woocommerce_product_wishlist_enabled',
 				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
 			),
-			ProductCacheController::FEATURE_NAME => array(
+			ProductCacheController::FEATURE_NAME   => array(
 				'name'                         => __( 'Cache Product Objects', 'woocommerce' ),
 				'description'                  => __(
 					'[Performance] Speeds up your store by caching product objects during each request, preventing duplicate product loads. Can improve page load times on product-heavy pages.',
@@ -709,6 +709,15 @@ class FeaturesController {
 				'is_experimental'              => true,
 				'disable_ui'                   => false,
 			),
+			BlockEditorUnifiedAssets::FEATURE_NAME => array(
+				'name'                         => __( 'Unified block editor assets', 'woocommerce' ),
+				'description'                  => __( 'Load WooCommerce block editor scripts and styles from shared bundles to improve editor performance.', 'woocommerce' ),
+				'option_key'                   => BlockEditorUnifiedAssets::OPTION_NAME,
+				'default_plugin_compatibility' => FeaturePluginCompatibility::COMPATIBLE,
+				'enabled_by_default'           => false,
+				'is_experimental'              => true,
+				'disable_ui'                   => false,
+			),
 		);

 		if ( ! $tracking_enabled ) {
@@ -1905,8 +1914,9 @@ class FeaturesController {

 		$incompatible_features_count = count( $incompatible_features );
 		if ( $incompatible_features_count > 0 ) {
-			$columns_count      = $wp_list_table->get_column_count();
-			$is_active          = true; // For now we are showing active plugins in the "Incompatible with..." view.
+			$columns_count = $wp_list_table->get_column_count();
+			$is_active     = true;
+			// For now we are showing active plugins in the "Incompatible with..." view.
 			$is_active_class    = $is_active ? 'active' : 'inactive';
 			$is_active_td_style = $is_active ? " style='border-left: 4px solid #72aee6;'" : '';