Commit 6a6312f06c for woocommerce

commit 6a6312f06cbff80ff1df79fb490d8ab7b2b5cade
Author: Lucio Giannotta <lucio.giannotta@a8c.com>
Date:   Tue Dec 23 02:08:05 2025 -0500

    Fix wp-editor script being enqueued on widgets screen (#62327) (#62438)

    Replace static imports from @wordpress/editor with string-based store
    selectors to avoid adding wp-editor as a script dependency. This fixes
    the PHP notice on WordPress 5.8+ block-based widgets editor where
    wp-editor should not be loaded.

    Also, added a mock store registration for tests. Created a reusable test
    utility for mocking the core/editor store.

    This is needed because we use string-based store selectors instead of
    importing the store from @wordpress/editor (to avoid wp-editor dependency).

diff --git a/plugins/woocommerce/changelog/62438-wooplug-5970-improve-wp-editor-script-enqueue-to-avoid-wordpress-php b/plugins/woocommerce/changelog/62438-wooplug-5970-improve-wp-editor-script-enqueue-to-avoid-wordpress-php
new file mode 100644
index 0000000000..a5e720e345
--- /dev/null
+++ b/plugins/woocommerce/changelog/62438-wooplug-5970-improve-wp-editor-script-enqueue-to-avoid-wordpress-php
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fixed enqueuing of the whole `wordpress/editor` script where unnecessary.
\ No newline at end of file
diff --git a/plugins/woocommerce/client/admin/client/wp-admin-scripts/command-palette-analytics/index.js b/plugins/woocommerce/client/admin/client/wp-admin-scripts/command-palette-analytics/index.js
index af45b858b6..90d01a74aa 100644
--- a/plugins/woocommerce/client/admin/client/wp-admin-scripts/command-palette-analytics/index.js
+++ b/plugins/woocommerce/client/admin/client/wp-admin-scripts/command-palette-analytics/index.js
@@ -7,7 +7,6 @@ import { useEffect } from '@wordpress/element';
 import { registerPlugin } from '@wordpress/plugins';
 import { addQueryArgs } from '@wordpress/url';
 import { useSelect } from '@wordpress/data';
-import { store as editorStore } from '@wordpress/editor';

 /**
  * Internal dependencies
@@ -35,8 +34,9 @@ const registerWooCommerceAnalyticsCommand = ( { label, path, origin } ) => {

 const WooCommerceAnalyticsCommands = () => {
 	const { editedPostType } = useSelect( ( select ) => {
+		const editor = select( 'core/editor' );
 		return {
-			editedPostType: select( editorStore ).getCurrentPostType(),
+			editedPostType: editor?.getCurrentPostType?.() ?? null,
 		};
 	} );
 	const origin = editedPostType ? editedPostType + '-editor' : null;
diff --git a/plugins/woocommerce/client/admin/client/wp-admin-scripts/command-palette/index.js b/plugins/woocommerce/client/admin/client/wp-admin-scripts/command-palette/index.js
index 1462f93277..a093fd818c 100644
--- a/plugins/woocommerce/client/admin/client/wp-admin-scripts/command-palette/index.js
+++ b/plugins/woocommerce/client/admin/client/wp-admin-scripts/command-palette/index.js
@@ -6,7 +6,6 @@ import { __, sprintf } from '@wordpress/i18n';
 import { box, plus, settings } from '@wordpress/icons';
 import { useEffect, useMemo, useRef } from '@wordpress/element';
 import { dispatch, useSelect } from '@wordpress/data';
-import { store as editorStore } from '@wordpress/editor';
 import { store as coreStore } from '@wordpress/core-data';
 import { addQueryArgs } from '@wordpress/url';
 import { recordEvent, queueRecordEvent } from '@woocommerce/tracks';
@@ -41,8 +40,9 @@ const registerWooCommerceSettingsCommand = ( { label, tab, origin } ) => {
 // https://github.com/WordPress/gutenberg/blob/8863b49b7e686f555e8b8adf70cc588c4feebfbf/packages/core-commands/src/site-editor-navigation-commands.js#L36C7-L36C44
 function useProductCommandLoader( { search } ) {
 	const { editedPostType } = useSelect( ( select ) => {
+		const editor = select( 'core/editor' );
 		return {
-			editedPostType: select( editorStore ).getCurrentPostType(),
+			editedPostType: editor?.getCurrentPostType?.() ?? null,
 		};
 	} );
 	const origin = editedPostType ? editedPostType + '-editor' : null;
@@ -122,8 +122,9 @@ function useProductCommandLoader( { search } ) {

 const WooCommerceCommands = () => {
 	const { editedPostType } = useSelect( ( select ) => {
+		const editor = select( 'core/editor' );
 		return {
-			editedPostType: select( editorStore ).getCurrentPostType(),
+			editedPostType: editor?.getCurrentPostType?.() ?? null,
 		};
 	} );
 	const origin = editedPostType ? editedPostType + '-editor' : null;
diff --git a/plugins/woocommerce/client/blocks/assets/js/atomic/blocks/product-elements/shared/use-is-descendent-of-single-product-template.tsx b/plugins/woocommerce/client/blocks/assets/js/atomic/blocks/product-elements/shared/use-is-descendent-of-single-product-template.tsx
index 8c5d887a23..bbdd08bb35 100644
--- a/plugins/woocommerce/client/blocks/assets/js/atomic/blocks/product-elements/shared/use-is-descendent-of-single-product-template.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/atomic/blocks/product-elements/shared/use-is-descendent-of-single-product-template.tsx
@@ -1,14 +1,15 @@
 /**
  * External dependencies
  */
-import { store as editorStore } from '@wordpress/editor';
 import { useSelect } from '@wordpress/data';
 import { isString } from '@woocommerce/types';
+import { CORE_EDITOR_STORE } from '@woocommerce/utils';

 export const useIsDescendentOfSingleProductTemplate = () => {
 	const isDescendentOfSingleProductTemplate = useSelect( ( select ) => {
+		const editor = select( CORE_EDITOR_STORE );
 		// @ts-expect-error getEditedPostSlug is not typed
-		const postSlug = select( editorStore ).getEditedPostSlug();
+		const postSlug = editor?.getEditedPostSlug?.();

 		return isString( postSlug )
 			? postSlug.includes( 'single-product' )
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/test/block.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/test/block.tsx
index f87e460a02..b012912a0a 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/test/block.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/test/block.tsx
@@ -43,13 +43,10 @@ import Taxes from '../inner-blocks/checkout-order-summary-taxes/frontend';
 import { defaultCartState } from '../../../data/cart/default-state';
 import Checkout from '../block';

-jest.mock( '@wordpress/data', () => {
-	const wpData = jest.requireActual( 'wordpress-data-wp-6-7' );
-	return {
-		__esModule: true,
-		...wpData,
-	};
-} );
+jest.mock( '@wordpress/data', () =>
+	// eslint-disable-next-line @typescript-eslint/no-var-requires -- Must use require due to Jest mock hoisting
+	require( '@woocommerce/blocks-test-utils/mock-editor-store' ).mockWordPressDataWithEditorStore()
+);

 jest.mock( '@wordpress/compose', () => ( {
 	...jest.requireActual( '@wordpress/compose' ),
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/test/editor-integration.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/test/editor-integration.tsx
index 0531d74847..fdba1e57c5 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/test/editor-integration.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/test/editor-integration.tsx
@@ -7,6 +7,11 @@ import { type BlockAttributes } from '@wordpress/blocks';
 import { getByLabelText, getByRole } from '@testing-library/dom';
 import { userEvent } from '@testing-library/user-event';

+jest.mock( '@wordpress/data', () =>
+	// eslint-disable-next-line @typescript-eslint/no-var-requires -- Must use require due to Jest mock hoisting
+	require( '@woocommerce/blocks-test-utils/mock-editor-store' ).mockWordPressDataWithEditorStore()
+);
+
 /**
  * Internal dependencies
  */
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/classic-template/index.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/classic-template/index.tsx
index 2f8861bfe5..f294459bd4 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/classic-template/index.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/classic-template/index.tsx
@@ -20,7 +20,7 @@ import { useDispatch, useSelect } from '@wordpress/data';
 import { useEffect, useState } from '@wordpress/element';
 import { store as noticesStore } from '@wordpress/notices';
 import { useEntityRecord } from '@wordpress/core-data';
-import { store as editorStore } from '@wordpress/editor';
+import { CORE_EDITOR_STORE } from '@woocommerce/utils';

 /**
  * Internal dependencies
@@ -184,7 +184,7 @@ const Edit = ( {
 	const { currentPostId } = useSelect( ( sel ) => {
 		return {
 			// @ts-expect-error getCurrentPostId is not typed
-			currentPostId: sel( editorStore ).getCurrentPostId(),
+			currentPostId: sel( CORE_EDITOR_STORE )?.getCurrentPostId?.() ?? 0,
 		};
 	}, [] );

diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/email-content/edit.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/email-content/edit.tsx
index bcaff96ec2..fe4814caf2 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/email-content/edit.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/email-content/edit.tsx
@@ -6,11 +6,10 @@ import { useState } from '@wordpress/element';
 import { useSelect } from '@wordpress/data';
 import ServerSideRender from '@wordpress/server-side-render';
 import { useBlockProps } from '@wordpress/block-editor';
+import { CORE_EDITOR_STORE } from '@woocommerce/utils';
 import {
 	__experimentalText as Text, // eslint-disable-line
 } from '@wordpress/components';
-// eslint-disable-next-line @woocommerce/dependency-group
-import { store as editorStore } from '@wordpress/editor';

 /**
  * Internal dependencies
@@ -41,7 +40,7 @@ export default function Edit() {
 	const blockProps = useBlockProps();
 	const { postId } = useSelect(
 		( select ) => ( {
-			postId: select( editorStore ).getCurrentPostId?.() ?? 0,
+			postId: select( CORE_EDITOR_STORE )?.getCurrentPostId?.() ?? 0,
 		} ),
 		[]
 	);
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/tracks-utils.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/tracks-utils.ts
index b5d1aa9a65..96c55ff733 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/tracks-utils.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/tracks-utils.ts
@@ -2,7 +2,7 @@
  * External dependencies
  */
 import { useSelect } from '@wordpress/data';
-import { store as editorStore } from '@wordpress/editor';
+import { CORE_EDITOR_STORE } from '@woocommerce/utils';

 enum Locations {
 	SINGLE_PRODUCT = 'single-product',
@@ -31,9 +31,10 @@ const templateSlugToTemplateMap: {

 export const useTracksLocation = ( templateSlug: string | undefined ) => {
 	const postType = useSelect( ( select ) => {
+		const editor = select( CORE_EDITOR_STORE );
 		// @ts-expect-error Type definitions are missing
 		// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/wordpress__blocks/store/selectors.d.ts
-		return select( editorStore ).getCurrentPostType();
+		return editor?.getCurrentPostType?.();
 	}, [] );

 	if ( postType === Locations.PAGE || postType === Locations.POST ) {
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/utils.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/utils.tsx
index 97dfbc57cc..b2e46209c1 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/utils.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/utils.tsx
@@ -5,8 +5,8 @@ import { store as blockEditorStore } from '@wordpress/block-editor';
 import { addFilter } from '@wordpress/hooks';
 import { select, useSelect, useDispatch } from '@wordpress/data';
 import { store as coreDataStore } from '@wordpress/core-data';
-import { store as editorStore } from '@wordpress/editor';
 import type { BlockEditProps, Block } from '@wordpress/blocks';
+import { CORE_EDITOR_STORE } from '@woocommerce/utils';
 import {
 	useEffect,
 	useLayoutEffect,
@@ -85,7 +85,8 @@ const isInProductArchive = () => {
 	];

 	// @ts-expect-error getEditedPostSlug is not typed
-	const currentTemplateId = select( editorStore ).getEditedPostSlug();
+	const currentTemplateId =
+		select( CORE_EDITOR_STORE )?.getEditedPostSlug?.();

 	/**
 	 * Set inherit value when Product Collection block is first added to the page.
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/product-query.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/product-query.tsx
index 5ef7c49e88..83f18e49ff 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/product-query.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/product-query.tsx
@@ -10,12 +10,11 @@ import { __ } from '@wordpress/i18n';
 import { stacks } from '@woocommerce/icons';
 import { getSettingWithCoercion } from '@woocommerce/settings';
 import { select, subscribe } from '@wordpress/data';
-import { store as editorStore } from '@wordpress/editor';
 import {
 	QueryBlockAttributes,
 	ProductQueryBlockQuery,
 } from '@woocommerce/blocks/product-query/types';
-import { isSiteEditorPage } from '@woocommerce/utils';
+import { isSiteEditorPage, CORE_EDITOR_STORE } from '@woocommerce/utils';
 import { isNumber, isString } from '@woocommerce/types';

 /**
@@ -73,7 +72,7 @@ let currentTemplateSlug: string | undefined;
 subscribe( () => {
 	const previousTemplateSlug = currentTemplateSlug;
 	// @ts-expect-error getEditedPostSlug is not typed
-	currentTemplateSlug = select( editorStore )?.getEditedPostSlug();
+	currentTemplateSlug = select( CORE_EDITOR_STORE )?.getEditedPostSlug?.();
 	if ( previousTemplateSlug === currentTemplateSlug ) {
 		return;
 	}
diff --git a/plugins/woocommerce/client/blocks/assets/js/editor-components/default-notice/index.tsx b/plugins/woocommerce/client/blocks/assets/js/editor-components/default-notice/index.tsx
index 0ef002e501..6bea20c78c 100644
--- a/plugins/woocommerce/client/blocks/assets/js/editor-components/default-notice/index.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/editor-components/default-notice/index.tsx
@@ -2,12 +2,13 @@
  * External dependencies
  */
 import { __ } from '@wordpress/i18n';
-import { store as editorStore } from '@wordpress/editor';
 import triggerFetch from '@wordpress/api-fetch';
 import { store as coreStore } from '@wordpress/core-data';
 import { Notice } from '@wordpress/components';
 import { useSelect, useDispatch } from '@wordpress/data';
 import { CHECKOUT_PAGE_ID, CART_PAGE_ID } from '@woocommerce/block-settings';
+import { CORE_EDITOR_STORE } from '@woocommerce/utils';
+
 import {
 	useCallback,
 	useState,
@@ -30,17 +31,16 @@ export function DefaultNotice( { block }: { block: string } ) {

 	// Everything below works the same for Cart/Checkout
 	const { saveEntityRecord } = useDispatch( coreStore );
-	const { editPost, savePost } = useDispatch( editorStore );
+	const { editPost, savePost } = useDispatch( CORE_EDITOR_STORE );
 	const { slug, postPublished, currentPostId } = useSelect( ( select ) => {
 		const { getEntityRecord } = select( coreStore );
-		const { isCurrentPostPublished, getCurrentPostId } =
-			select( editorStore );
+		const editor = select( CORE_EDITOR_STORE );
 		return {
 			slug:
 				getEntityRecord( 'postType', 'page', ORIGINAL_PAGE_ID )?.slug ||
 				block,
-			postPublished: isCurrentPostPublished(),
-			currentPostId: getCurrentPostId(),
+			postPublished: editor?.isCurrentPostPublished?.() ?? false,
+			currentPostId: editor?.getCurrentPostId?.() ?? 0,
 		};
 	}, [] );
 	const [ settingStatus, setStatus ] = useState( 'pristine' );
diff --git a/plugins/woocommerce/client/blocks/assets/js/utils/index.ts b/plugins/woocommerce/client/blocks/assets/js/utils/index.ts
index b54bdf78ea..8274da5dfa 100644
--- a/plugins/woocommerce/client/blocks/assets/js/utils/index.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/utils/index.ts
@@ -11,6 +11,7 @@ export * from './shared-attributes';
 export * from './is-site-editor-page';
 export * from './is-widget-editor-page';
 export * from './trim-words';
+export * from './wordpress-stores';
 export * from './find-block';
 export * from './get-unique-id';
 export * from './html-entities';
diff --git a/plugins/woocommerce/client/blocks/assets/js/utils/is-site-editor-page.ts b/plugins/woocommerce/client/blocks/assets/js/utils/is-site-editor-page.ts
index 60dd090ac4..1b5b457b96 100644
--- a/plugins/woocommerce/client/blocks/assets/js/utils/is-site-editor-page.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/utils/is-site-editor-page.ts
@@ -2,11 +2,15 @@
  * External dependencies
  */
 import { select } from '@wordpress/data';
-import { store as editorStore } from '@wordpress/editor';
+
+/**
+ * Internal dependencies
+ */
+import { CORE_EDITOR_STORE } from './wordpress-stores';

 export const isSiteEditorPage = (): boolean => {
-	// @ts-expect-error getCurrentPostType is not typed.
-	const editedPostType = select( editorStore )?.getCurrentPostType();
+	const editor = select( CORE_EDITOR_STORE );
+	const editedPostType = editor?.getCurrentPostType?.();

 	return (
 		editedPostType === 'wp_template' ||
diff --git a/plugins/woocommerce/client/blocks/assets/js/utils/wordpress-stores.ts b/plugins/woocommerce/client/blocks/assets/js/utils/wordpress-stores.ts
new file mode 100644
index 0000000000..24000d566b
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/assets/js/utils/wordpress-stores.ts
@@ -0,0 +1,10 @@
+/**
+ * WordPress store names for use with @wordpress/data select/dispatch.
+ *
+ * Using string-based store names instead of importing store objects avoids
+ * adding unnecessary script dependencies. For example, importing
+ * `store as editorStore from '@wordpress/editor'` would enqueue the whole
+ * wp-editor package as a dependency.
+ */
+
+export const CORE_EDITOR_STORE = 'core/editor' as const;
diff --git a/plugins/woocommerce/client/blocks/tests/js/jest.config.json b/plugins/woocommerce/client/blocks/tests/js/jest.config.json
index 9ae089ce81..512de9d3b0 100644
--- a/plugins/woocommerce/client/blocks/tests/js/jest.config.json
+++ b/plugins/woocommerce/client/blocks/tests/js/jest.config.json
@@ -36,6 +36,7 @@
 		"@woocommerce/resource-previews": "assets/js/previews",
 		"@woocommerce/shared-context": "assets/js/shared/context",
 		"@woocommerce/shared-hocs": "assets/js/shared/hocs",
+		"@woocommerce/blocks-test-utils/(.*)$": "tests/utils/$1",
 		"@woocommerce/blocks-test-utils": "tests/utils",
 		"@woocommerce/types": "assets/js/types",
 		"@woocommerce/utils": "assets/js/utils",
diff --git a/plugins/woocommerce/client/blocks/tests/utils/mock-editor-store.ts b/plugins/woocommerce/client/blocks/tests/utils/mock-editor-store.ts
new file mode 100644
index 0000000000..6965c40de8
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/tests/utils/mock-editor-store.ts
@@ -0,0 +1,35 @@
+/**
+ * Jest mock factory for @wordpress/data that registers a mock core/editor store.
+ *
+ * This is needed because we use string-based store selectors ('core/editor')
+ * instead of importing the store from @wordpress/editor (which would add
+ * wp-editor as a script dependency). The import side-effect would normally
+ * register the store, but since we avoid the import, we need to register
+ * a mock in tests.
+ *
+ * Usage (must use require, not import, due to Jest hoisting):
+ * ```
+ * jest.mock( '@wordpress/data', () =>
+ *     require( '@woocommerce/blocks-test-utils/mock-editor-store' ).mockWordPressDataWithEditorStore()
+ * );
+ * ```
+ */
+export const mockWordPressDataWithEditorStore = () => {
+	// Use require to avoid issues with Jest's module system
+	// eslint-disable-next-line @typescript-eslint/no-var-requires
+	const wpData = require( 'wordpress-data-wp-6-7' );
+	const mockEditorStore = wpData.createReduxStore( 'core/editor', {
+		reducer: () => ( {} ),
+		selectors: {
+			getCurrentPostId: () => null,
+			getCurrentPostType: () => null,
+			getCurrentPost: () => null,
+			isCurrentPostPublished: () => false,
+		},
+	} );
+	wpData.register( mockEditorStore );
+	return {
+		__esModule: true,
+		...wpData,
+	};
+};