Changeset 60931
- Timestamp:
- 10/14/2025 05:45:17 AM (8 weeks ago)
- Location:
- trunk
- Files:
-
- 6 edited
-
src/wp-includes/class-wp-dependency.php (modified) (1 diff)
-
src/wp-includes/class-wp-script-modules.php (modified) (7 diffs)
-
src/wp-includes/class-wp-scripts.php (modified) (4 diffs)
-
src/wp-includes/script-modules.php (modified) (1 diff)
-
tests/phpunit/tests/dependencies/scripts.php (modified) (2 diffs)
-
tests/phpunit/tests/script-modules/wpScriptModules.php (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/class-wp-dependency.php
r60656 r60931 51 51 * 52 52 * @since 2.6.0 53 * @var bool|string53 * @var string|false|null 54 54 */ 55 55 public $ver = false; -
trunk/src/wp-includes/class-wp-script-modules.php
r60930 r60931 41 41 */ 42 42 private $a11y_available = false; 43 44 /** 45 * Holds a mapping of dependents (as IDs) for a given script ID. 46 * Used to optimize recursive dependency tree checks. 47 * 48 * @since 6.9.0 49 * @var array<string, string[]> 50 */ 51 private $dependents_map = array(); 43 52 44 53 /** … … 271 280 272 281 /** 282 * Gets the highest fetch priority for the provided script IDs. 283 * 284 * @since 6.9.0 285 * 286 * @param string[] $ids Script module IDs. 287 * @return string Highest fetch priority for the provided script module IDs. 288 */ 289 private function get_highest_fetchpriority( array $ids ): string { 290 static $priorities = array( 291 'low', 292 'auto', 293 'high', 294 ); 295 $high_priority_index = count( $priorities ) - 1; 296 297 $highest_priority_index = 0; 298 foreach ( $ids as $id ) { 299 if ( isset( $this->registered[ $id ] ) ) { 300 $highest_priority_index = max( 301 $highest_priority_index, 302 array_search( $this->registered[ $id ]['fetchpriority'], $priorities, true ) 303 ); 304 if ( $high_priority_index === $highest_priority_index ) { 305 break; 306 } 307 } 308 } 309 310 return $priorities[ $highest_priority_index ]; 311 } 312 313 /** 273 314 * Prints the enqueued script modules using script tags with type="module" 274 315 * attributes. … … 283 324 'id' => $id . '-js-module', 284 325 ); 285 if ( 'auto' !== $script_module['fetchpriority'] ) { 286 $args['fetchpriority'] = $script_module['fetchpriority']; 326 327 $dependents = $this->get_recursive_dependents( $id ); 328 $fetchpriority = $this->get_highest_fetchpriority( array_merge( array( $id ), $dependents ) ); 329 if ( 'auto' !== $fetchpriority ) { 330 $args['fetchpriority'] = $fetchpriority; 331 } 332 if ( $fetchpriority !== $script_module['fetchpriority'] ) { 333 $args['data-wp-fetchpriority'] = $script_module['fetchpriority']; 287 334 } 288 335 wp_print_script_tag( $args ); … … 291 338 292 339 /** 293 * Prints the thestatic dependencies of the enqueued script modules using340 * Prints the static dependencies of the enqueued script modules using 294 341 * link tags with rel="modulepreload" attributes. 295 342 * … … 302 349 // Don't preload if it's marked for enqueue. 303 350 if ( ! in_array( $id, $this->queue, true ) ) { 304 echo sprintf( 305 '<link rel="modulepreload" href="%s" id="%s"%s>', 351 $enqueued_dependents = array_intersect( $this->get_recursive_dependents( $id ), $this->queue ); 352 $highest_fetchpriority = $this->get_highest_fetchpriority( $enqueued_dependents ); 353 printf( 354 '<link rel="modulepreload" href="%s" id="%s"', 306 355 esc_url( $this->get_src( $id ) ), 307 esc_attr( $id . '-js-modulepreload' ), 308 'auto' !== $script_module['fetchpriority'] ? sprintf( ' fetchpriority="%s"', esc_attr( $script_module['fetchpriority'] ) ) : '' 356 esc_attr( $id . '-js-modulepreload' ) 309 357 ); 358 if ( 'auto' !== $highest_fetchpriority ) { 359 printf( ' fetchpriority="%s"', esc_attr( $highest_fetchpriority ) ); 360 } 361 if ( $highest_fetchpriority !== $script_module['fetchpriority'] && 'auto' !== $script_module['fetchpriority'] ) { 362 printf( ' data-wp-fetchpriority="%s"', esc_attr( $script_module['fetchpriority'] ) ); 363 } 364 echo ">\n"; 310 365 } 311 366 } … … 375 430 * @return array[] List of dependencies, keyed by script module identifier. 376 431 */ 377 private function get_dependencies( array $ids, array $import_types = array( 'static', 'dynamic' ) ) {432 private function get_dependencies( array $ids, array $import_types = array( 'static', 'dynamic' ) ): array { 378 433 return array_reduce( 379 434 $ids, 380 435 function ( $dependency_script_modules, $id ) use ( $import_types ) { 381 436 $dependencies = array(); 382 foreach ( $this->registered[ $id ]['dependencies'] as $dependency ) { 383 if ( 384 in_array( $dependency['import'], $import_types, true ) && 385 isset( $this->registered[ $dependency['id'] ] ) && 386 ! isset( $dependency_script_modules[ $dependency['id'] ] ) 387 ) { 388 $dependencies[ $dependency['id'] ] = $this->registered[ $dependency['id'] ]; 437 if ( isset( $this->registered[ $id ] ) ) { 438 foreach ( $this->registered[ $id ]['dependencies'] as $dependency ) { 439 if ( 440 in_array( $dependency['import'], $import_types, true ) && 441 isset( $this->registered[ $dependency['id'] ] ) && 442 ! isset( $dependency_script_modules[ $dependency['id'] ] ) 443 ) { 444 $dependencies[ $dependency['id'] ] = $this->registered[ $dependency['id'] ]; 445 } 389 446 } 390 447 } … … 393 450 array() 394 451 ); 452 } 453 454 /** 455 * Gets all dependents of a script module. 456 * 457 * This is not recursive. 458 * 459 * @since 6.9.0 460 * 461 * @see WP_Scripts::get_dependents() 462 * 463 * @param string $id The script ID. 464 * @return string[] Script module IDs. 465 */ 466 private function get_dependents( string $id ): array { 467 // Check if dependents map for the handle in question is present. If so, use it. 468 if ( isset( $this->dependents_map[ $id ] ) ) { 469 return $this->dependents_map[ $id ]; 470 } 471 472 $dependents = array(); 473 474 // Iterate over all registered scripts, finding dependents of the script passed to this method. 475 foreach ( $this->registered as $registered_id => $args ) { 476 if ( in_array( $id, wp_list_pluck( $args['dependencies'], 'id' ), true ) ) { 477 $dependents[] = $registered_id; 478 } 479 } 480 481 // Add the module's dependents to the map to ease future lookups. 482 $this->dependents_map[ $id ] = $dependents; 483 484 return $dependents; 485 } 486 487 /** 488 * Gets all recursive dependents of a script module. 489 * 490 * @since 6.9.0 491 * 492 * @see WP_Scripts::get_dependents() 493 * 494 * @param string $id The script ID. 495 * @return string[] Script module IDs. 496 */ 497 private function get_recursive_dependents( string $id ): array { 498 $get = function ( string $id, array $checked = array() ) use ( &$get ): array { 499 500 // If by chance an unregistered script module is checked or there is a recursive dependency, return early. 501 if ( ! isset( $this->registered[ $id ] ) || isset( $checked[ $id ] ) ) { 502 return array(); 503 } 504 505 // Mark this script module as checked to guard against infinite recursion. 506 $checked[ $id ] = true; 507 508 $dependents = array(); 509 foreach ( $this->get_dependents( $id ) as $dependent ) { 510 $dependents = array_merge( 511 $dependents, 512 array( $dependent ), 513 $get( $dependent, $checked ) 514 ); 515 } 516 517 return $dependents; 518 }; 519 520 return array_unique( $get( $id ) ); 395 521 } 396 522 -
trunk/src/wp-includes/class-wp-scripts.php
r60722 r60931 128 128 * 129 129 * @since 6.3.0 130 * @var array 130 * @var array<string, string[]> 131 131 */ 132 132 private $dependents_map = array(); … … 440 440 $attr['data-wp-strategy'] = $intended_strategy; 441 441 } 442 if ( isset( $obj->extra['fetchpriority'] ) && 'auto' !== $obj->extra['fetchpriority'] && $this->is_valid_fetchpriority( $obj->extra['fetchpriority'] ) ) { 443 $attr['fetchpriority'] = $obj->extra['fetchpriority']; 444 } 442 443 // Determine fetchpriority. 444 $original_fetchpriority = isset( $obj->extra['fetchpriority'] ) ? $obj->extra['fetchpriority'] : null; 445 if ( null === $original_fetchpriority || ! $this->is_valid_fetchpriority( $original_fetchpriority ) ) { 446 $original_fetchpriority = 'auto'; 447 } 448 $actual_fetchpriority = $this->get_highest_fetchpriority_with_dependents( $handle ); 449 if ( null === $actual_fetchpriority ) { 450 // If null, it's likely this script was not explicitly enqueued, so in this case use the original priority. 451 $actual_fetchpriority = $original_fetchpriority; 452 } 453 if ( is_string( $actual_fetchpriority ) && 'auto' !== $actual_fetchpriority ) { 454 $attr['fetchpriority'] = $actual_fetchpriority; 455 } 456 if ( $original_fetchpriority !== $actual_fetchpriority ) { 457 $attr['data-wp-fetchpriority'] = $original_fetchpriority; 458 } 459 445 460 $tag = $translations . $ie_conditional_prefix . $before_script; 446 461 $tag .= wp_get_script_tag( $attr ); … … 899 914 * Gets all dependents of a script. 900 915 * 916 * This is not recursive. 917 * 901 918 * @since 6.3.0 902 919 * … … 1050 1067 1051 1068 return $eligible_strategies; 1069 } 1070 1071 /** 1072 * Gets the highest fetch priority for a given script and all of its dependent scripts. 1073 * 1074 * @since 6.9.0 1075 * @see self::filter_eligible_strategies() 1076 * @see WP_Script_Modules::get_highest_fetchpriority_with_dependents() 1077 * 1078 * @param string $handle Script module ID. 1079 * @param array<string, true> $checked Optional. An array of already checked script handles, used to avoid recursive loops. 1080 * @return string|null Highest fetch priority for the script and its dependents. 1081 */ 1082 private function get_highest_fetchpriority_with_dependents( string $handle, array $checked = array() ): ?string { 1083 // If there is a recursive dependency, return early. 1084 if ( isset( $checked[ $handle ] ) ) { 1085 return null; 1086 } 1087 1088 // Mark this handle as checked to guard against infinite recursion. 1089 $checked[ $handle ] = true; 1090 1091 // Abort if the script is not enqueued or a dependency of an enqueued script. 1092 if ( ! $this->query( $handle, 'enqueued' ) ) { 1093 return null; 1094 } 1095 1096 $fetchpriority = $this->get_data( $handle, 'fetchpriority' ); 1097 if ( ! $this->is_valid_fetchpriority( $fetchpriority ) ) { 1098 $fetchpriority = 'auto'; 1099 } 1100 1101 static $priorities = array( 1102 'low', 1103 'auto', 1104 'high', 1105 ); 1106 $high_priority_index = count( $priorities ) - 1; 1107 1108 $highest_priority_index = (int) array_search( $fetchpriority, $priorities, true ); 1109 if ( $highest_priority_index !== $high_priority_index ) { 1110 foreach ( $this->get_dependents( $handle ) as $dependent_handle ) { 1111 $dependent_priority = $this->get_highest_fetchpriority_with_dependents( $dependent_handle, $checked ); 1112 if ( is_string( $dependent_priority ) ) { 1113 $highest_priority_index = max( 1114 $highest_priority_index, 1115 (int) array_search( $dependent_priority, $priorities, true ) 1116 ); 1117 if ( $highest_priority_index === $high_priority_index ) { 1118 break; 1119 } 1120 } 1121 } 1122 } 1123 1124 return $priorities[ $highest_priority_index ]; 1052 1125 } 1053 1126 -
trunk/src/wp-includes/script-modules.php
r60704 r60931 182 182 } 183 183 184 // The Interactivity API is designed with server-side rendering as its primary goal, so all of its script modules should be loaded with low fetch priority since they should not be needed in the critical rendering path. 184 /* 185 * The Interactivity API is designed with server-side rendering as its primary goal, so all of its script modules 186 * should be loaded with low fetchpriority since they should not be needed in the critical rendering path. 187 * Also, the @wordpress/a11y script module is intended to be used as a dynamic import dependency, in which case 188 * the fetchpriority is irrelevant. See <https://make.wordpress.org/core/2024/10/14/updates-to-script-modules-in-6-7/>. 189 * However, in case it is added as a static import dependency, the fetchpriority is explicitly set to be 'low' 190 * since the module should not be involved in the critical rendering path, and if it is, its fetchpriority will 191 * be bumped to match the fetchpriority of the dependent script. 192 */ 185 193 $args = array(); 186 if ( str_starts_with( $script_module_id, '@wordpress/interactivity' ) || str_starts_with( $script_module_id, '@wordpress/block-library' ) ) { 194 if ( 195 str_starts_with( $script_module_id, '@wordpress/interactivity' ) || 196 str_starts_with( $script_module_id, '@wordpress/block-library' ) || 197 '@wordpress/a11y' === $script_module_id 198 ) { 187 199 $args['fetchpriority'] = 'low'; 188 200 } -
trunk/tests/phpunit/tests/dependencies/scripts.php
r60729 r60931 1233 1233 ) 1234 1234 ); 1235 // Note: All of these scripts have fetchpriority=high because the leaf dependent script has that fetch priority. 1235 1236 $output = get_echo( 'wp_print_scripts' ); 1236 $expected = "<script type='text/javascript' src='/main-script-d4.js' id='main-script-d4-js' defer='defer' data-wp-strategy='defer'></script>\n";1237 $expected .= "<script type='text/javascript' src='/dependent-script-d4-1.js' id='dependent-script-d4-1-js' defer='defer' data-wp-strategy='defer' ></script>\n";1238 $expected .= "<script type='text/javascript' src='/dependent-script-d4-2.js' id='dependent-script-d4-2-js' defer='defer' data-wp-strategy='async' fetchpriority=' low'></script>\n";1237 $expected = "<script type='text/javascript' src='/main-script-d4.js' id='main-script-d4-js' defer='defer' data-wp-strategy='defer' fetchpriority='high' data-wp-fetchpriority='auto'></script>\n"; 1238 $expected .= "<script type='text/javascript' src='/dependent-script-d4-1.js' id='dependent-script-d4-1-js' defer='defer' data-wp-strategy='defer' fetchpriority='high' data-wp-fetchpriority='auto'></script>\n"; 1239 $expected .= "<script type='text/javascript' src='/dependent-script-d4-2.js' id='dependent-script-d4-2-js' defer='defer' data-wp-strategy='async' fetchpriority='high' data-wp-fetchpriority='low'></script>\n"; 1239 1240 $expected .= "<script type='text/javascript' src='/dependent-script-d4-3.js' id='dependent-script-d4-3-js' defer='defer' data-wp-strategy='defer' fetchpriority='high'></script>\n"; 1240 1241 … … 1342 1343 wp_register_script( 'alias', false, array(), null, array( 'fetchpriority' => 'low' ) ); 1343 1344 $this->assertArrayNotHasKey( 'fetchpriority', wp_scripts()->registered['alias']->extra ); 1345 } 1346 1347 /** 1348 * Data provider. 1349 * 1350 * @return array<string, array{enqueues: string[], expected: string}> 1351 */ 1352 public function data_provider_to_test_fetchpriority_bumping(): array { 1353 return array( 1354 'enqueue_bajo' => array( 1355 'enqueues' => array( 'bajo' ), 1356 'expected' => '<script fetchpriority="low" id="bajo-js" src="/bajo.js" type="text/javascript"></script>', 1357 ), 1358 'enqueue_auto' => array( 1359 'enqueues' => array( 'auto' ), 1360 'expected' => ' 1361 <script type="text/javascript" src="/bajo.js" id="bajo-js" data-wp-fetchpriority="low"></script> 1362 <script type="text/javascript" src="/auto.js" id="auto-js"></script> 1363 ', 1364 ), 1365 'enqueue_alto' => array( 1366 'enqueues' => array( 'alto' ), 1367 'expected' => ' 1368 <script type="text/javascript" src="/bajo.js" id="bajo-js" fetchpriority="high" data-wp-fetchpriority="low"></script> 1369 <script type="text/javascript" src="/auto.js" id="auto-js" fetchpriority="high" data-wp-fetchpriority="auto"></script> 1370 <script type="text/javascript" src="/alto.js" id="alto-js" fetchpriority="high"></script> 1371 ', 1372 ), 1373 ); 1374 } 1375 1376 /** 1377 * Tests a higher fetchpriority on a dependent script module causes the fetchpriority of a dependency script module to be bumped. 1378 * 1379 * @ticket 61734 1380 * 1381 * @covers WP_Scripts::get_dependents 1382 * @covers WP_Scripts::get_highest_fetchpriority_with_dependents 1383 * @covers WP_Scripts::do_item 1384 * 1385 * @dataProvider data_provider_to_test_fetchpriority_bumping 1386 */ 1387 public function test_fetchpriority_bumping( array $enqueues, string $expected ) { 1388 wp_register_script( 'bajo', '/bajo.js', array(), null, array( 'fetchpriority' => 'low' ) ); 1389 wp_register_script( 'auto', '/auto.js', array( 'bajo' ), null, array( 'fetchpriority' => 'auto' ) ); 1390 wp_register_script( 'alto', '/alto.js', array( 'auto' ), null, array( 'fetchpriority' => 'high' ) ); 1391 1392 foreach ( $enqueues as $enqueue ) { 1393 wp_enqueue_script( $enqueue ); 1394 } 1395 1396 $actual = get_echo( 'wp_print_scripts' ); 1397 $this->assertEqualHTML( $expected, $actual, '<body>', "Snapshot:\n$actual" ); 1398 } 1399 1400 /** 1401 * Tests bumping fetchpriority with complex dependency graph. 1402 * 1403 * @ticket 61734 1404 * @link https://github.com/WordPress/wordpress-develop/pull/9770#issuecomment-3280065818 1405 * 1406 * @covers WP_Scripts::get_dependents 1407 * @covers WP_Scripts::get_highest_fetchpriority_with_dependents 1408 * @covers WP_Scripts::do_item 1409 */ 1410 public function test_fetchpriority_bumping_a_to_z() { 1411 wp_register_script( 'a', '/a.js', array( 'b' ), null, array( 'fetchpriority' => 'low' ) ); 1412 wp_register_script( 'b', '/b.js', array( 'c' ), null, array( 'fetchpriority' => 'auto' ) ); 1413 wp_register_script( 'c', '/c.js', array( 'd', 'e' ), null, array( 'fetchpriority' => 'auto' ) ); 1414 wp_register_script( 'd', '/d.js', array( 'z' ), null, array( 'fetchpriority' => 'high' ) ); 1415 wp_register_script( 'e', '/e.js', array(), null, array( 'fetchpriority' => 'auto' ) ); 1416 1417 wp_register_script( 'x', '/x.js', array( 'd', 'y' ), null, array( 'fetchpriority' => 'high' ) ); 1418 wp_register_script( 'y', '/y.js', array( 'z' ), null, array( 'fetchpriority' => 'auto' ) ); 1419 wp_register_script( 'z', '/z.js', array(), null, array( 'fetchpriority' => 'auto' ) ); 1420 1421 wp_enqueue_script( 'a' ); 1422 wp_enqueue_script( 'x' ); 1423 1424 $actual = get_echo( 'wp_print_scripts' ); 1425 $expected = ' 1426 <script type="text/javascript" src="/z.js" id="z-js" fetchpriority="high" data-wp-fetchpriority="auto"></script> 1427 <script type="text/javascript" src="/d.js" id="d-js" fetchpriority="high"></script> 1428 <script type="text/javascript" src="/e.js" id="e-js"></script> 1429 <script type="text/javascript" src="/c.js" id="c-js"></script> 1430 <script type="text/javascript" src="/b.js" id="b-js"></script> 1431 <script type="text/javascript" src="/a.js" id="a-js" fetchpriority="low"></script> 1432 <script type="text/javascript" src="/y.js" id="y-js" fetchpriority="high" data-wp-fetchpriority="auto"></script> 1433 <script type="text/javascript" src="/x.js" id="x-js" fetchpriority="high"></script> 1434 '; 1435 $this->assertEqualHTML( $expected, $actual, '<body>', "Snapshot:\n$actual" ); 1436 } 1437 1438 /** 1439 * Tests that printing a script without enqueueing has the same output as when it is enqueued. 1440 * 1441 * @ticket 61734 1442 * 1443 * @covers WP_Scripts::do_item 1444 * @covers WP_Scripts::do_items 1445 * @covers ::wp_default_scripts 1446 * 1447 * @dataProvider data_provider_enqueue_or_not_to_enqueue 1448 */ 1449 public function test_printing_default_script_comment_reply_enqueued_or_not_enqueued( bool $enqueue ) { 1450 $wp_scripts = wp_scripts(); 1451 wp_default_scripts( $wp_scripts ); 1452 1453 $this->assertArrayHasKey( 'comment-reply', $wp_scripts->registered ); 1454 $wp_scripts->registered['comment-reply']->ver = null; 1455 $this->assertArrayHasKey( 'fetchpriority', $wp_scripts->registered['comment-reply']->extra ); 1456 $this->assertSame( 'low', $wp_scripts->registered['comment-reply']->extra['fetchpriority'] ); 1457 $this->assertArrayHasKey( 'strategy', $wp_scripts->registered['comment-reply']->extra ); 1458 $this->assertSame( 'async', $wp_scripts->registered['comment-reply']->extra['strategy'] ); 1459 if ( $enqueue ) { 1460 wp_enqueue_script( 'comment-reply' ); 1461 $markup = get_echo( array( $wp_scripts, 'do_items' ), array( false ) ); 1462 } else { 1463 $markup = get_echo( array( $wp_scripts, 'do_items' ), array( array( 'comment-reply' ) ) ); 1464 } 1465 1466 $this->assertEqualHTML( 1467 sprintf( 1468 '<script type="text/javascript" src="%s" id="comment-reply-js" async="async" data-wp-strategy="async" fetchpriority="low"></script>', 1469 includes_url( 'js/comment-reply.js' ) 1470 ), 1471 $markup 1472 ); 1473 } 1474 1475 /** 1476 * Data provider for test_default_scripts_comment_reply_not_enqueued. 1477 * 1478 * @return array[] 1479 */ 1480 public static function data_provider_enqueue_or_not_to_enqueue(): array { 1481 return array( 1482 'not_enqueued' => array( 1483 false, 1484 ), 1485 'enqueued' => array( 1486 true, 1487 ), 1488 ); 1344 1489 } 1345 1490 -
trunk/tests/phpunit/tests/script-modules/wpScriptModules.php
r60930 r60931 68 68 $id = preg_replace( '/-js-module$/', '', (string) $p->get_attribute( 'id' ) ); 69 69 $fetchpriority = $p->get_attribute( 'fetchpriority' ); 70 $modules[ $id ] = array( 71 'url' => $p->get_attribute( 'src' ), 72 'fetchpriority' => is_string( $fetchpriority ) ? $fetchpriority : 'auto', 70 $modules[ $id ] = array_merge( 71 array( 72 'url' => $p->get_attribute( 'src' ), 73 'fetchpriority' => is_string( $fetchpriority ) ? $fetchpriority : 'auto', 74 ), 75 ...array_map( 76 static function ( $attribute_name ) use ( $p ) { 77 return array( $attribute_name => $p->get_attribute( $attribute_name ) ); 78 }, 79 $p->get_attribute_names_with_prefix( 'data-' ) 80 ) 73 81 ); 74 82 } … … 113 121 $id = preg_replace( '/-js-modulepreload$/', '', $p->get_attribute( 'id' ) ); 114 122 $fetchpriority = $p->get_attribute( 'fetchpriority' ); 115 $preloads[ $id ] = array( 116 'url' => $p->get_attribute( 'href' ), 117 'fetchpriority' => is_string( $fetchpriority ) ? $fetchpriority : 'auto', 123 $preloads[ $id ] = array_merge( 124 array( 125 'url' => $p->get_attribute( 'href' ), 126 'fetchpriority' => is_string( $fetchpriority ) ? $fetchpriority : 'auto', 127 ), 128 ...array_map( 129 static function ( $attribute_name ) use ( $p ) { 130 return array( $attribute_name => $p->get_attribute( $attribute_name ) ); 131 }, 132 $p->get_attribute_names_with_prefix( 'data-' ) 133 ) 118 134 ); 119 135 } … … 272 288 'b-dep' => array( 273 289 'url' => '/b-dep.js?ver=99.9.9', 274 'fetchpriority' => ' auto',290 'fetchpriority' => 'low', // Propagates from 'b'. 275 291 ), 276 292 'c-dep' => array( 277 'url' => '/c-static.js?ver=99.9.9', 278 'fetchpriority' => 'low', 293 'url' => '/c-static.js?ver=99.9.9', 294 'fetchpriority' => 'auto', // Not 'low' because the dependent script 'c' has a fetchpriority of 'auto'. 295 'data-wp-fetchpriority' => 'low', 279 296 ), 280 297 'c-static-dep' => array( 281 'url' => '/c-static-dep.js?ver=99.9.9', 282 'fetchpriority' => 'high', 298 'url' => '/c-static-dep.js?ver=99.9.9', 299 'fetchpriority' => 'auto', // Propagated from 'c'. 300 'data-wp-fetchpriority' => 'high', 283 301 ), 284 302 'd-static-dep' => array( … … 733 751 ), 734 752 ) 753 // Note: The default fetchpriority=auto is upgraded to high because the dependent script module 'static-dep' has a high fetch priority. 735 754 ); 736 755 $this->script_modules->register( … … 760 779 $this->assertCount( 2, $preloaded_script_modules ); 761 780 $this->assertStringStartsWith( '/static-dep.js', $preloaded_script_modules['static-dep']['url'] ); 762 $this->assertSame( ' high', $preloaded_script_modules['static-dep']['fetchpriority'] );781 $this->assertSame( 'auto', $preloaded_script_modules['static-dep']['fetchpriority'] ); // Not 'high' 763 782 $this->assertStringStartsWith( '/nested-static-dep.js', $preloaded_script_modules['nested-static-dep']['url'] ); 764 $this->assertSame( 'auto', $preloaded_script_modules['nested-static-dep']['fetchpriority'] ); 783 $this->assertSame( 'auto', $preloaded_script_modules['nested-static-dep']['fetchpriority'] ); // Auto because the enqueued script foo has the fetchpriority of auto. 765 784 $this->assertArrayNotHasKey( 'dynamic-dep', $preloaded_script_modules ); 766 785 $this->assertArrayNotHasKey( 'nested-dynamic-dep', $preloaded_script_modules ); … … 972 991 $preloaded_script_modules = $this->get_preloaded_script_modules(); 973 992 $this->assertSame( '/dep.js?ver=2.0', $preloaded_script_modules['dep']['url'] ); 974 $this->assertSame( ' high', $preloaded_script_modules['dep']['fetchpriority'] );993 $this->assertSame( 'auto', $preloaded_script_modules['dep']['fetchpriority'] ); // Because 'foo' has a priority of 'auto'. 975 994 } 976 995 … … 1345 1364 1346 1365 /** 1366 * Data provider. 1367 * 1368 * @return array<string, array{enqueues: string[], expected: array}> 1369 */ 1370 public function data_provider_to_test_fetchpriority_bumping(): array { 1371 return array( 1372 'enqueue_bajo' => array( 1373 'enqueues' => array( 'bajo' ), 1374 'expected' => array( 1375 'preload_links' => array(), 1376 'script_tags' => array( 1377 'bajo' => array( 1378 'url' => '/bajo.js', 1379 'fetchpriority' => 'high', 1380 'data-wp-fetchpriority' => 'low', 1381 ), 1382 ), 1383 'import_map' => array( 1384 'dyno' => '/dyno.js', 1385 ), 1386 ), 1387 ), 1388 'enqueue_auto' => array( 1389 'enqueues' => array( 'auto' ), 1390 'expected' => array( 1391 'preload_links' => array( 1392 'bajo' => array( 1393 'url' => '/bajo.js', 1394 'fetchpriority' => 'auto', 1395 'data-wp-fetchpriority' => 'low', 1396 ), 1397 ), 1398 'script_tags' => array( 1399 'auto' => array( 1400 'url' => '/auto.js', 1401 'fetchpriority' => 'high', 1402 'data-wp-fetchpriority' => 'auto', 1403 ), 1404 ), 1405 'import_map' => array( 1406 'bajo' => '/bajo.js', 1407 'dyno' => '/dyno.js', 1408 ), 1409 ), 1410 ), 1411 'enqueue_alto' => array( 1412 'enqueues' => array( 'alto' ), 1413 'expected' => array( 1414 'preload_links' => array( 1415 'auto' => array( 1416 'url' => '/auto.js', 1417 'fetchpriority' => 'high', 1418 ), 1419 'bajo' => array( 1420 'url' => '/bajo.js', 1421 'fetchpriority' => 'high', 1422 'data-wp-fetchpriority' => 'low', 1423 ), 1424 ), 1425 'script_tags' => array( 1426 'alto' => array( 1427 'url' => '/alto.js', 1428 'fetchpriority' => 'high', 1429 ), 1430 ), 1431 'import_map' => array( 1432 'auto' => '/auto.js', 1433 'bajo' => '/bajo.js', 1434 'dyno' => '/dyno.js', 1435 ), 1436 ), 1437 ), 1438 ); 1439 } 1440 1441 /** 1442 * Tests a higher fetchpriority on a dependent script module causes the fetchpriority of a dependency script module to be bumped. 1443 * 1444 * @ticket 61734 1445 * 1446 * @covers WP_Script_Modules::print_enqueued_script_modules 1447 * @covers WP_Script_Modules::get_dependents 1448 * @covers WP_Script_Modules::get_recursive_dependents 1449 * @covers WP_Script_Modules::get_highest_fetchpriority 1450 * @covers WP_Script_Modules::print_script_module_preloads 1451 * 1452 * @dataProvider data_provider_to_test_fetchpriority_bumping 1453 */ 1454 public function test_fetchpriority_bumping( array $enqueues, array $expected ) { 1455 $this->script_modules->register( 1456 'dyno', 1457 '/dyno.js', 1458 array(), 1459 null, 1460 array( 'fetchpriority' => 'low' ) // This won't show up anywhere since it is a dynamic import dependency. 1461 ); 1462 1463 $this->script_modules->register( 1464 'bajo', 1465 '/bajo.js', 1466 array( 1467 array( 1468 'id' => 'dyno', 1469 'import' => 'dynamic', 1470 ), 1471 ), 1472 null, 1473 array( 'fetchpriority' => 'low' ) 1474 ); 1475 1476 $this->script_modules->register( 1477 'auto', 1478 '/auto.js', 1479 array( 1480 array( 1481 'id' => 'bajo', 1482 'import' => 'static', 1483 ), 1484 ), 1485 null, 1486 array( 'fetchpriority' => 'auto' ) 1487 ); 1488 $this->script_modules->register( 1489 'alto', 1490 '/alto.js', 1491 array( 'auto' ), 1492 null, 1493 array( 'fetchpriority' => 'high' ) 1494 ); 1495 1496 foreach ( $enqueues as $enqueue ) { 1497 $this->script_modules->enqueue( $enqueue ); 1498 } 1499 1500 $actual = array( 1501 'preload_links' => $this->get_preloaded_script_modules(), 1502 'script_tags' => $this->get_enqueued_script_modules(), 1503 'import_map' => $this->get_import_map(), 1504 ); 1505 $this->assertSame( 1506 $expected, 1507 $actual, 1508 "Snapshot:\n" . var_export( $actual, true ) 1509 ); 1510 } 1511 1512 /** 1513 * Tests bumping fetchpriority with complex dependency graph. 1514 * 1515 * @ticket 61734 1516 * @link https://github.com/WordPress/wordpress-develop/pull/9770#issuecomment-3280065818 1517 * 1518 * @covers WP_Script_Modules::print_enqueued_script_modules 1519 * @covers WP_Script_Modules::get_dependents 1520 * @covers WP_Script_Modules::get_recursive_dependents 1521 * @covers WP_Script_Modules::get_highest_fetchpriority 1522 * @covers WP_Script_Modules::print_script_module_preloads 1523 */ 1524 public function test_fetchpriority_bumping_a_to_z() { 1525 wp_register_script_module( 'a', '/a.js', array( 'b' ), null, array( 'fetchpriority' => 'low' ) ); 1526 wp_register_script_module( 'b', '/b.js', array( 'c' ), null, array( 'fetchpriority' => 'auto' ) ); 1527 wp_register_script_module( 'c', '/c.js', array( 'd', 'e' ), null, array( 'fetchpriority' => 'auto' ) ); 1528 wp_register_script_module( 'd', '/d.js', array( 'z' ), null, array( 'fetchpriority' => 'high' ) ); 1529 wp_register_script_module( 'e', '/e.js', array(), null, array( 'fetchpriority' => 'auto' ) ); 1530 1531 wp_register_script_module( 'x', '/x.js', array( 'd', 'y' ), null, array( 'fetchpriority' => 'high' ) ); 1532 wp_register_script_module( 'y', '/y.js', array( 'z' ), null, array( 'fetchpriority' => 'auto' ) ); 1533 wp_register_script_module( 'z', '/z.js', array(), null, array( 'fetchpriority' => 'auto' ) ); 1534 1535 // The fetch priorities are derived from these enqueued dependents. 1536 wp_enqueue_script_module( 'a' ); 1537 wp_enqueue_script_module( 'x' ); 1538 1539 $actual = get_echo( array( wp_script_modules(), 'print_script_module_preloads' ) ); 1540 $actual .= get_echo( array( wp_script_modules(), 'print_enqueued_script_modules' ) ); 1541 $expected = ' 1542 <link rel="modulepreload" href="/b.js" id="b-js-modulepreload" fetchpriority="low"> 1543 <link rel="modulepreload" href="/c.js" id="c-js-modulepreload" fetchpriority="low"> 1544 <link rel="modulepreload" href="/d.js" id="d-js-modulepreload" fetchpriority="high"> 1545 <link rel="modulepreload" href="/e.js" id="e-js-modulepreload" fetchpriority="low"> 1546 <link rel="modulepreload" href="/z.js" id="z-js-modulepreload" fetchpriority="high"> 1547 <link rel="modulepreload" href="/y.js" id="y-js-modulepreload" fetchpriority="high"> 1548 <script type="module" src="/a.js" id="a-js-module" fetchpriority="low"></script> 1549 <script type="module" src="/x.js" id="x-js-module" fetchpriority="high"></script> 1550 '; 1551 $this->assertEqualHTML( $expected, $actual, '<body>', "Snapshot:\n$actual" ); 1552 } 1553 1554 /** 1555 * Tests bumping fetchpriority with complex dependency graph. 1556 * 1557 * @ticket 61734 1558 * @link https://github.com/WordPress/wordpress-develop/pull/9770#issuecomment-3284266884 1559 * 1560 * @covers WP_Script_Modules::print_enqueued_script_modules 1561 * @covers WP_Script_Modules::get_dependents 1562 * @covers WP_Script_Modules::get_recursive_dependents 1563 * @covers WP_Script_Modules::get_highest_fetchpriority 1564 * @covers WP_Script_Modules::print_script_module_preloads 1565 */ 1566 public function test_fetchpriority_propagation() { 1567 // The high fetchpriority for this module will be disregarded because its enqueued dependent has a non-high priority. 1568 wp_register_script_module( 'a', '/a.js', array( 'd', 'e' ), null, array( 'fetchpriority' => 'high' ) ); 1569 wp_register_script_module( 'b', '/b.js', array( 'e' ), null ); 1570 wp_register_script_module( 'c', '/c.js', array( 'e', 'f' ), null ); 1571 wp_register_script_module( 'd', '/d.js', array(), null ); 1572 // The low fetchpriority for this module will be disregarded because its enqueued dependent has a non-low priority. 1573 wp_register_script_module( 'e', '/e.js', array(), null, array( 'fetchpriority' => 'low' ) ); 1574 wp_register_script_module( 'f', '/f.js', array(), null ); 1575 1576 wp_register_script_module( 'x', '/x.js', array( 'a' ), null, array( 'fetchpriority' => 'low' ) ); 1577 wp_register_script_module( 'y', '/y.js', array( 'b' ), null, array( 'fetchpriority' => 'auto' ) ); 1578 wp_register_script_module( 'z', '/z.js', array( 'c' ), null, array( 'fetchpriority' => 'high' ) ); 1579 1580 wp_enqueue_script_module( 'x' ); 1581 wp_enqueue_script_module( 'y' ); 1582 wp_enqueue_script_module( 'z' ); 1583 1584 $actual = get_echo( array( wp_script_modules(), 'print_script_module_preloads' ) ); 1585 $actual .= get_echo( array( wp_script_modules(), 'print_enqueued_script_modules' ) ); 1586 $expected = ' 1587 <link rel="modulepreload" href="/a.js" id="a-js-modulepreload" fetchpriority="low" data-wp-fetchpriority="high"> 1588 <link rel="modulepreload" href="/d.js" id="d-js-modulepreload" fetchpriority="low"> 1589 <link rel="modulepreload" href="/e.js" id="e-js-modulepreload" fetchpriority="high" data-wp-fetchpriority="low"> 1590 <link rel="modulepreload" href="/b.js" id="b-js-modulepreload"> 1591 <link rel="modulepreload" href="/c.js" id="c-js-modulepreload" fetchpriority="high"> 1592 <link rel="modulepreload" href="/f.js" id="f-js-modulepreload" fetchpriority="high"> 1593 <script type="module" src="/x.js" id="x-js-module" fetchpriority="low"></script> 1594 <script type="module" src="/y.js" id="y-js-module"></script> 1595 <script type="module" src="/z.js" id="z-js-module" fetchpriority="high"></script> 1596 '; 1597 $this->assertEqualHTML( $expected, $actual, '<body>', "Snapshot:\n$actual" ); 1598 } 1599 1600 /** 1601 * Tests that default script modules are printed as expected. 1602 * 1603 * @covers ::wp_default_script_modules 1604 * @covers WP_Script_Modules::print_script_module_preloads 1605 * @covers WP_Script_Modules::print_enqueued_script_modules 1606 */ 1607 public function test_default_script_modules() { 1608 wp_default_script_modules(); 1609 wp_enqueue_script_module( '@wordpress/a11y' ); 1610 wp_enqueue_script_module( '@wordpress/block-library/navigation/view' ); 1611 1612 $actual = get_echo( array( wp_script_modules(), 'print_script_module_preloads' ) ); 1613 $actual .= get_echo( array( wp_script_modules(), 'print_enqueued_script_modules' ) ); 1614 1615 $actual = $this->normalize_markup_for_snapshot( $actual ); 1616 1617 $expected = ' 1618 <link rel="modulepreload" href="/wp-includes/js/dist/script-modules/interactivity/debug.min.js" id="@wordpress/interactivity-js-modulepreload" fetchpriority="low"> 1619 <script type="module" src="/wp-includes/js/dist/script-modules/a11y/index.min.js" id="@wordpress/a11y-js-module" fetchpriority="low"></script> 1620 <script type="module" src="/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js" id="@wordpress/block-library/navigation/view-js-module" fetchpriority="low"></script> 1621 '; 1622 $this->assertEqualHTML( $expected, $actual, '<body>', "Snapshot:\n$actual" ); 1623 } 1624 1625 /** 1626 * Tests that a dependent with high priority for default script modules with a low fetch priority are printed as expected. 1627 * 1628 * @covers ::wp_default_script_modules 1629 * @covers WP_Script_Modules::print_script_module_preloads 1630 * @covers WP_Script_Modules::print_enqueued_script_modules 1631 */ 1632 public function test_dependent_of_default_script_modules() { 1633 wp_default_script_modules(); 1634 wp_enqueue_script_module( 1635 'super-important', 1636 '/super-important-module.js', 1637 array( '@wordpress/a11y', '@wordpress/block-library/navigation/view' ), 1638 null, 1639 array( 'fetchpriority' => 'high' ) 1640 ); 1641 1642 $actual = get_echo( array( wp_script_modules(), 'print_script_module_preloads' ) ); 1643 $actual .= get_echo( array( wp_script_modules(), 'print_enqueued_script_modules' ) ); 1644 1645 $actual = $this->normalize_markup_for_snapshot( $actual ); 1646 1647 $expected = ' 1648 <link rel="modulepreload" href="/wp-includes/js/dist/script-modules/a11y/index.min.js" id="@wordpress/a11y-js-modulepreload" fetchpriority="high" data-wp-fetchpriority="low"> 1649 <link rel="modulepreload" href="/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js" id="@wordpress/block-library/navigation/view-js-modulepreload" fetchpriority="high" data-wp-fetchpriority="low"> 1650 <link rel="modulepreload" href="/wp-includes/js/dist/script-modules/interactivity/debug.min.js" id="@wordpress/interactivity-js-modulepreload" fetchpriority="high" data-wp-fetchpriority="low"> 1651 <script type="module" src="/super-important-module.js" id="super-important-js-module" fetchpriority="high"></script> 1652 '; 1653 $this->assertEqualHTML( $expected, $actual, '<body>', "Snapshot:\n$actual" ); 1654 } 1655 1656 /** 1657 * Normalizes markup for snapshot. 1658 * 1659 * @param string $markup Markup. 1660 * @return string Normalized markup. 1661 */ 1662 private function normalize_markup_for_snapshot( string $markup ): string { 1663 $processor = new WP_HTML_Tag_Processor( $markup ); 1664 $clean_url = static function ( string $url ): string { 1665 $url = preg_replace( '#^https?://[^/]+#', '', $url ); 1666 return remove_query_arg( 'ver', $url ); 1667 }; 1668 while ( $processor->next_tag() ) { 1669 if ( 'LINK' === $processor->get_tag() && is_string( $processor->get_attribute( 'href' ) ) ) { 1670 $processor->set_attribute( 'href', $clean_url( $processor->get_attribute( 'href' ) ) ); 1671 } elseif ( 'SCRIPT' === $processor->get_tag() && is_string( $processor->get_attribute( 'src' ) ) ) { 1672 $processor->set_attribute( 'src', $clean_url( $processor->get_attribute( 'src' ) ) ); 1673 } 1674 } 1675 return $processor->get_updated_html(); 1676 } 1677 1678 /** 1347 1679 * Tests that directly manipulating the queue works as expected. 1348 1680 *
Note: See TracChangeset
for help on using the changeset viewer.