Skip to content

Commit 3901a29

Browse files
committed
Script Loader: Emit notices when enqueueing a script, style, or script module with missing dependencies.
Developed in #10545 Follow-up to [60999]. Props deepakprajapati, westonruter. See #63486. Fixes #64229. git-svn-id: https://develop.svn.wordpress.org/trunk@61323 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 3f93553 commit 3901a29

File tree

7 files changed

+197
-4
lines changed

7 files changed

+197
-4
lines changed

src/wp-includes/class-wp-dependencies.php

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ class WP_Dependencies {
104104
*/
105105
private $queued_before_register = array();
106106

107+
/**
108+
* List of handles for dependencies encountered which themselves have missing dependencies.
109+
*
110+
* A dependency handle is added to this list when it is discovered to have missing dependencies. At this time, a
111+
* warning is emitted with {@see _doing_it_wrong()}. The handle is then added to this list, so that duplicate
112+
* warnings don't occur.
113+
*
114+
* @since 7.0.0
115+
* @var string[]
116+
*/
117+
private $dependencies_with_missing_dependencies = array();
118+
107119
/**
108120
* Processes the items and dependencies.
109121
*
@@ -199,10 +211,22 @@ public function all_deps( $handles, $recursion = false, $group = false ) {
199211
continue;
200212
}
201213

202-
$keep_going = true;
214+
$keep_going = true;
215+
$missing_dependencies = array();
216+
if ( isset( $this->registered[ $handle ] ) && count( $this->registered[ $handle ]->deps ) > 0 ) {
217+
$missing_dependencies = array_diff( $this->registered[ $handle ]->deps, array_keys( $this->registered ) );
218+
}
203219
if ( ! isset( $this->registered[ $handle ] ) ) {
204220
$keep_going = false; // Item doesn't exist.
205-
} elseif ( $this->registered[ $handle ]->deps && array_diff( $this->registered[ $handle ]->deps, array_keys( $this->registered ) ) ) {
221+
} elseif ( count( $missing_dependencies ) > 0 ) {
222+
if ( ! in_array( $handle, $this->dependencies_with_missing_dependencies, true ) ) {
223+
_doing_it_wrong(
224+
get_class( $this ) . '::add',
225+
$this->get_dependency_warning_message( $handle, $missing_dependencies ),
226+
'7.0.0'
227+
);
228+
$this->dependencies_with_missing_dependencies[] = $handle;
229+
}
206230
$keep_going = false; // Item requires dependencies that don't exist.
207231
} elseif ( $this->registered[ $handle ]->deps && ! $this->all_deps( $this->registered[ $handle ]->deps, true, $new_group ) ) {
208232
$keep_going = false; // Item requires dependencies that don't exist.
@@ -535,4 +559,22 @@ public function get_etag( $load ) {
535559
*/
536560
return 'W/"' . md5( $etag ) . '"';
537561
}
562+
563+
/**
564+
* Gets a dependency warning message for a handle.
565+
*
566+
* @since 7.0.0
567+
*
568+
* @param string $handle Handle with missing dependencies.
569+
* @param string[] $missing_dependency_handles Missing dependency handles.
570+
* @return string Formatted, localized warning message.
571+
*/
572+
protected function get_dependency_warning_message( $handle, $missing_dependency_handles ) {
573+
return sprintf(
574+
/* translators: 1: Handle, 2: Comma-separated list of missing dependency handles. */
575+
__( 'The handle "%1$s" was enqueued with dependencies that are not registered: %2$s.' ),
576+
$handle,
577+
implode( ', ', $missing_dependency_handles )
578+
);
579+
}
538580
}

src/wp-includes/class-wp-script-modules.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ class WP_Script_Modules {
7070
'high',
7171
);
7272

73+
/**
74+
* List of IDs for script modules encountered which have missing dependencies.
75+
*
76+
* An ID is added to this list when it is discovered to have missing dependencies. At this time, a warning is
77+
* emitted with {@see _doing_it_wrong()}. The ID is then added to this list, so that duplicate warnings don't occur.
78+
*
79+
* @since 7.0.0
80+
* @var string[]
81+
*/
82+
private $modules_with_missing_dependencies = array();
83+
7384
/**
7485
* Registers the script module if no script module with that script module
7586
* identifier has already been registered.
@@ -722,7 +733,22 @@ private function sort_item_dependencies( string $id, array $import_types, array
722733
}
723734

724735
// If the item requires dependencies that do not exist, fail.
725-
if ( count( array_diff( $dependency_ids, array_keys( $this->registered ) ) ) > 0 ) {
736+
$missing_dependencies = array_diff( $dependency_ids, array_keys( $this->registered ) );
737+
if ( count( $missing_dependencies ) > 0 ) {
738+
if ( ! in_array( $id, $this->modules_with_missing_dependencies, true ) ) {
739+
_doing_it_wrong(
740+
get_class( $this ) . '::register',
741+
sprintf(
742+
/* translators: 1: Script module ID, 2: Comma-separated list of missing dependency IDs. */
743+
__( 'The script module with the ID "%1$s" was enqueued with dependencies that are not registered: %2$s.' ),
744+
$id,
745+
implode( ', ', $missing_dependencies )
746+
),
747+
'7.0.0'
748+
);
749+
$this->modules_with_missing_dependencies[] = $id;
750+
}
751+
726752
return false;
727753
}
728754

src/wp-includes/class-wp-scripts.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,4 +1164,22 @@ public function reset() {
11641164
$this->ext_version = '';
11651165
$this->ext_handles = '';
11661166
}
1167+
1168+
/**
1169+
* Gets a script-specific dependency warning message.
1170+
*
1171+
* @since 7.0.0
1172+
*
1173+
* @param string $handle Script handle with missing dependencies.
1174+
* @param string[] $missing_dependency_handles Missing dependency handles.
1175+
* @return string Formatted, localized warning message.
1176+
*/
1177+
protected function get_dependency_warning_message( $handle, $missing_dependency_handles ) {
1178+
return sprintf(
1179+
/* translators: 1: Script handle, 2: Comma-separated list of missing dependency handles. */
1180+
__( 'The script with the handle "%1$s" was enqueued with dependencies that are not registered: %2$s.' ),
1181+
$handle,
1182+
implode( ', ', $missing_dependency_handles )
1183+
);
1184+
}
11671185
}

src/wp-includes/class-wp-styles.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,4 +493,22 @@ public function reset() {
493493
$this->concat_version = '';
494494
$this->print_html = '';
495495
}
496+
497+
/**
498+
* Gets a style-specific dependency warning message.
499+
*
500+
* @since 7.0.0
501+
*
502+
* @param string $handle Style handle with missing dependencies.
503+
* @param string[] $missing_dependency_handles Missing dependency handles.
504+
* @return string Formatted, localized warning message.
505+
*/
506+
protected function get_dependency_warning_message( $handle, $missing_dependency_handles ) {
507+
return sprintf(
508+
/* translators: 1: Style handle, 2: Comma-separated list of missing dependency handles. */
509+
__( 'The style with the handle "%1$s" was enqueued with dependencies that are not registered: %2$s.' ),
510+
$handle,
511+
implode( ', ', $missing_dependency_handles )
512+
);
513+
}
496514
}

tests/phpunit/tests/dependencies/scripts.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4093,4 +4093,33 @@ public function test_print_translations_no_display_no_sourceurl() {
40934093
$translations_script_data = $wp_scripts->print_translations( 'test-example', false );
40944094
$this->assertStringNotContainsStringIgnoringCase( 'sourceURL=', $translations_script_data );
40954095
}
4096+
4097+
/**
4098+
* Tests that WP_Scripts emits a _doing_it_wrong() notice for missing dependencies.
4099+
*
4100+
* @ticket 64229
4101+
* @covers WP_Dependencies::all_deps
4102+
*/
4103+
public function test_wp_scripts_doing_it_wrong_for_missing_dependencies() {
4104+
$expected_incorrect_usage = 'WP_Scripts::add';
4105+
$this->setExpectedIncorrectUsage( $expected_incorrect_usage );
4106+
4107+
wp_register_script( 'registered-dep', '/registered-dep.js' );
4108+
wp_enqueue_script( 'main', '/main.js', array( 'registered-dep', 'missing-dep' ) );
4109+
4110+
$markup = get_echo( 'wp_print_scripts' );
4111+
$this->assertStringNotContainsString( 'main.js', $markup, 'Expected script to be absent.' );
4112+
4113+
$this->assertArrayHasKey(
4114+
$expected_incorrect_usage,
4115+
$this->caught_doing_it_wrong,
4116+
"Expected $expected_incorrect_usage to trigger a _doing_it_wrong() notice for missing dependency."
4117+
);
4118+
4119+
$this->assertStringContainsString(
4120+
'The script with the handle "main" was enqueued with dependencies that are not registered: missing-dep',
4121+
$this->caught_doing_it_wrong[ $expected_incorrect_usage ],
4122+
'Expected _doing_it_wrong() notice to indicate missing dependencies for enqueued script.'
4123+
);
4124+
}
40964125
}

tests/phpunit/tests/dependencies/styles.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,4 +814,36 @@ public function test_source_url_with_concat() {
814814

815815
$this->assertEqualHTML( $expected, $printed );
816816
}
817+
818+
/**
819+
* Tests that WP_Styles emits a _doing_it_wrong() notice for missing dependencies.
820+
*
821+
* @ticket 64229
822+
* @covers WP_Dependencies::all_deps
823+
*/
824+
public function test_wp_style_doing_it_wrong_for_missing_dependencies() {
825+
$expected_incorrect_usage = 'WP_Styles::add';
826+
$this->setExpectedIncorrectUsage( $expected_incorrect_usage );
827+
828+
wp_enqueue_style(
829+
'main-style',
830+
'/main-style.css',
831+
array( 'missing-style-dep' )
832+
);
833+
834+
$markup = get_echo( 'wp_print_styles' );
835+
$this->assertStringNotContainsString( 'main-style.css', $markup, 'Expected style to be absent.' );
836+
837+
$this->assertArrayHasKey(
838+
$expected_incorrect_usage,
839+
$this->caught_doing_it_wrong,
840+
"Expected $expected_incorrect_usage to trigger a _doing_it_wrong() notice for missing dependency."
841+
);
842+
843+
$this->assertStringContainsString(
844+
'The style with the handle "main-style" was enqueued with dependencies that are not registered: missing-style-dep',
845+
$this->caught_doing_it_wrong[ $expected_incorrect_usage ],
846+
'Expected _doing_it_wrong() notice to indicate missing dependencies for enqueued styles.'
847+
);
848+
}
817849
}

tests/phpunit/tests/script-modules/wpScriptModules.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2059,7 +2059,7 @@ public function test_script_module_printing_and_dependency_ordering( bool $use_g
20592059
"Snapshot:\n" . var_export( $actual, true )
20602060
);
20612061

2062-
$deregister( array( 'b', 'c ' ) );
2062+
$deregister( array( 'b', 'c' ) );
20632063

20642064
// Test that registered dependency in footer doesn't place dependant in footer.
20652065
$register( 'd', '/d.js', array(), '1.0.0', array( 'in_footer' => true ) );
@@ -2312,4 +2312,32 @@ public function test_static_import_dependency_with_dynamic_imports_depending_on_
23122312
"Expected script modules to match snapshot:\n$script_modules"
23132313
);
23142314
}
2315+
2316+
/**
2317+
* Tests that a missing script module dependency triggers a _doing_it_wrong() notice.
2318+
*
2319+
* @ticket 64229
2320+
* @covers WP_Script_Modules::sort_item_dependencies
2321+
*/
2322+
public function test_missing_script_module_dependency_triggers_incorrect_usage() {
2323+
$expected_incorrect_usage = 'WP_Script_Modules::register';
2324+
$this->setExpectedIncorrectUsage( $expected_incorrect_usage );
2325+
2326+
$this->script_modules->enqueue( 'main-module', '/main-module.js', array( 'missing-mod-dep' ) );
2327+
2328+
$markup = get_echo( array( $this->script_modules, 'print_enqueued_script_modules' ) );
2329+
$this->assertStringNotContainsString( 'main-module.js', $markup, 'Expected script module to be absent.' );
2330+
2331+
$this->assertArrayHasKey(
2332+
$expected_incorrect_usage,
2333+
$this->caught_doing_it_wrong,
2334+
'Expected WP_Script_Modules::register to be reported via doing_it_wrong().'
2335+
);
2336+
2337+
// Assert the message mentions the missing dependency handle.
2338+
$this->assertStringContainsString(
2339+
'The script module with the ID "main-module" was enqueued with dependencies that are not registered: missing-mod-dep',
2340+
$this->caught_doing_it_wrong[ $expected_incorrect_usage ]
2341+
);
2342+
}
23152343
}

0 commit comments

Comments
 (0)