Commit 58465aa777 for wordpress.org

commit 58465aa7771e146d436e61c9903d9d39f2cc08a6
Author: Weston Ruter <weston@xwp.co>
Date:   Sun Nov 30 00:56:32 2025 +0000

    Script Loader: Emit notices when enqueueing a script, style, or script module with missing dependencies.

    Developed in https://github.com/WordPress/wordpress-develop/pull/10545

    Follow-up to [60999].

    Props deepakprajapati, westonruter.
    See #63486.
    Fixes #64229.

    Built from https://develop.svn.wordpress.org/trunk@61323


    git-svn-id: http://core.svn.wordpress.org/trunk@60635 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-includes/class-wp-dependencies.php b/wp-includes/class-wp-dependencies.php
index 057fd8059e..60c117d2f6 100644
--- a/wp-includes/class-wp-dependencies.php
+++ b/wp-includes/class-wp-dependencies.php
@@ -104,6 +104,18 @@ class WP_Dependencies {
 	 */
 	private $queued_before_register = array();

+	/**
+	 * List of handles for dependencies encountered which themselves have missing dependencies.
+	 *
+	 * A dependency handle is added to this list when it is discovered to have missing dependencies. At this time, a
+	 * warning is emitted with {@see _doing_it_wrong()}. The handle is then added to this list, so that duplicate
+	 * warnings don't occur.
+	 *
+	 * @since 7.0.0
+	 * @var string[]
+	 */
+	private $dependencies_with_missing_dependencies = array();
+
 	/**
 	 * Processes the items and dependencies.
 	 *
@@ -199,10 +211,22 @@ class WP_Dependencies {
 				continue;
 			}

-			$keep_going = true;
+			$keep_going           = true;
+			$missing_dependencies = array();
+			if ( isset( $this->registered[ $handle ] ) && count( $this->registered[ $handle ]->deps ) > 0 ) {
+				$missing_dependencies = array_diff( $this->registered[ $handle ]->deps, array_keys( $this->registered ) );
+			}
 			if ( ! isset( $this->registered[ $handle ] ) ) {
 				$keep_going = false; // Item doesn't exist.
-			} elseif ( $this->registered[ $handle ]->deps && array_diff( $this->registered[ $handle ]->deps, array_keys( $this->registered ) ) ) {
+			} elseif ( count( $missing_dependencies ) > 0 ) {
+				if ( ! in_array( $handle, $this->dependencies_with_missing_dependencies, true ) ) {
+					_doing_it_wrong(
+						get_class( $this ) . '::add',
+						$this->get_dependency_warning_message( $handle, $missing_dependencies ),
+						'7.0.0'
+					);
+					$this->dependencies_with_missing_dependencies[] = $handle;
+				}
 				$keep_going = false; // Item requires dependencies that don't exist.
 			} elseif ( $this->registered[ $handle ]->deps && ! $this->all_deps( $this->registered[ $handle ]->deps, true, $new_group ) ) {
 				$keep_going = false; // Item requires dependencies that don't exist.
@@ -535,4 +559,22 @@ class WP_Dependencies {
 		 */
 		return 'W/"' . md5( $etag ) . '"';
 	}
+
+	/**
+	 * Gets a dependency warning message for a handle.
+	 *
+	 * @since 7.0.0
+	 *
+	 * @param string   $handle                     Handle with missing dependencies.
+	 * @param string[] $missing_dependency_handles Missing dependency handles.
+	 * @return string Formatted, localized warning message.
+	 */
+	protected function get_dependency_warning_message( $handle, $missing_dependency_handles ) {
+		return sprintf(
+			/* translators: 1: Handle, 2: Comma-separated list of missing dependency handles. */
+			__( 'The handle "%1$s" was enqueued with dependencies that are not registered: %2$s.' ),
+			$handle,
+			implode( ', ', $missing_dependency_handles )
+		);
+	}
 }
diff --git a/wp-includes/class-wp-script-modules.php b/wp-includes/class-wp-script-modules.php
index 6a919e2625..e05e1900a2 100644
--- a/wp-includes/class-wp-script-modules.php
+++ b/wp-includes/class-wp-script-modules.php
@@ -70,6 +70,17 @@ class WP_Script_Modules {
 		'high',
 	);

+	/**
+	 * List of IDs for script modules encountered which have missing dependencies.
+	 *
+	 * An ID is added to this list when it is discovered to have missing dependencies. At this time, a warning is
+	 * emitted with {@see _doing_it_wrong()}. The ID is then added to this list, so that duplicate warnings don't occur.
+	 *
+	 * @since 7.0.0
+	 * @var string[]
+	 */
+	private $modules_with_missing_dependencies = array();
+
 	/**
 	 * Registers the script module if no script module with that script module
 	 * identifier has already been registered.
@@ -722,7 +733,22 @@ class WP_Script_Modules {
 		}

 		// If the item requires dependencies that do not exist, fail.
-		if ( count( array_diff( $dependency_ids, array_keys( $this->registered ) ) ) > 0 ) {
+		$missing_dependencies = array_diff( $dependency_ids, array_keys( $this->registered ) );
+		if ( count( $missing_dependencies ) > 0 ) {
+			if ( ! in_array( $id, $this->modules_with_missing_dependencies, true ) ) {
+				_doing_it_wrong(
+					get_class( $this ) . '::register',
+					sprintf(
+						/* translators: 1: Script module ID, 2: Comma-separated list of missing dependency IDs. */
+						__( 'The script module with the ID "%1$s" was enqueued with dependencies that are not registered: %2$s.' ),
+						$id,
+						implode( ', ', $missing_dependencies )
+					),
+					'7.0.0'
+				);
+				$this->modules_with_missing_dependencies[] = $id;
+			}
+
 			return false;
 		}

diff --git a/wp-includes/class-wp-scripts.php b/wp-includes/class-wp-scripts.php
index b15bd3f8e9..a30b09249f 100644
--- a/wp-includes/class-wp-scripts.php
+++ b/wp-includes/class-wp-scripts.php
@@ -1164,4 +1164,22 @@ JS;
 		$this->ext_version    = '';
 		$this->ext_handles    = '';
 	}
+
+	/**
+	 * Gets a script-specific dependency warning message.
+	 *
+	 * @since 7.0.0
+	 *
+	 * @param string   $handle                     Script handle with missing dependencies.
+	 * @param string[] $missing_dependency_handles Missing dependency handles.
+	 * @return string Formatted, localized warning message.
+	 */
+	protected function get_dependency_warning_message( $handle, $missing_dependency_handles ) {
+		return sprintf(
+			/* translators: 1: Script handle, 2: Comma-separated list of missing dependency handles. */
+			__( 'The script with the handle "%1$s" was enqueued with dependencies that are not registered: %2$s.' ),
+			$handle,
+			implode( ', ', $missing_dependency_handles )
+		);
+	}
 }
diff --git a/wp-includes/class-wp-styles.php b/wp-includes/class-wp-styles.php
index 9d6d5b5dd2..67fb3a0fd4 100644
--- a/wp-includes/class-wp-styles.php
+++ b/wp-includes/class-wp-styles.php
@@ -493,4 +493,22 @@ class WP_Styles extends WP_Dependencies {
 		$this->concat_version = '';
 		$this->print_html     = '';
 	}
+
+	/**
+	 * Gets a style-specific dependency warning message.
+	 *
+	 * @since 7.0.0
+	 *
+	 * @param string   $handle                     Style handle with missing dependencies.
+	 * @param string[] $missing_dependency_handles Missing dependency handles.
+	 * @return string Formatted, localized warning message.
+	 */
+	protected function get_dependency_warning_message( $handle, $missing_dependency_handles ) {
+		return sprintf(
+			/* translators: 1: Style handle, 2: Comma-separated list of missing dependency handles. */
+			__( 'The style with the handle "%1$s" was enqueued with dependencies that are not registered: %2$s.' ),
+			$handle,
+			implode( ', ', $missing_dependency_handles )
+		);
+	}
 }
diff --git a/wp-includes/version.php b/wp-includes/version.php
index d0d453c4ac..b20c7790a4 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.0-alpha-61322';
+$wp_version = '7.0-alpha-61323';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.