Changeset 60999
- Timestamp:
- 10/21/2025 12:43:51 AM (5 weeks ago)
- Location:
- trunk
- Files:
-
- 4 edited
-
src/wp-includes/blocks.php (modified) (1 diff)
-
src/wp-includes/class-wp-script-modules.php (modified) (22 diffs)
-
src/wp-includes/script-modules.php (modified) (4 diffs)
-
tests/phpunit/tests/script-modules/wpScriptModules.php (modified) (37 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/blocks.php
r60939 r60999 183 183 ) { 184 184 $args['fetchpriority'] = 'low'; 185 $args['in_footer'] = true; 185 186 } 186 187 -
trunk/src/wp-includes/class-wp-script-modules.php
r60951 r60999 19 19 * 20 20 * @since 6.5.0 21 * @var array []21 * @var array<string, array<string, mixed>> 22 22 */ 23 23 private $registered = array(); … … 30 30 */ 31 31 private $queue = array(); 32 33 /** 34 * Holds the script module identifiers that have been printed. 35 * 36 * @since 6.9.0 37 * @var string[] 38 */ 39 private $done = array(); 32 40 33 41 /** … … 50 58 */ 51 59 private $dependents_map = array(); 60 61 /** 62 * Holds the valid values for fetchpriority. 63 * 64 * @since 6.9.0 65 * @var string[] 66 */ 67 private $priorities = array( 68 'low', 69 'auto', 70 'high', 71 ); 52 72 53 73 /** … … 85 105 * Optional. An array of additional args. Default empty array. 86 106 * 107 * @type bool $in_footer Whether to print the script module in the footer. Only relevant to block themes. Default 'false'. Optional. 87 108 * @type 'auto'|'low'|'high' $fetchpriority Fetch priority. Default 'auto'. Optional. 88 109 * } 89 110 */ 90 111 public function register( string $id, string $src, array $deps = array(), $version = false, array $args = array() ) { 112 if ( '' === $id ) { 113 _doing_it_wrong( __METHOD__, __( 'Non-empty string required for id.' ), '6.9.0' ); 114 return; 115 } 116 91 117 if ( ! isset( $this->registered[ $id ] ) ) { 92 118 $dependencies = array(); … … 111 137 } 112 138 139 $in_footer = isset( $args['in_footer'] ) && (bool) $args['in_footer']; 140 113 141 $fetchpriority = 'auto'; 114 142 if ( isset( $args['fetchpriority'] ) ) { … … 133 161 'version' => $version, 134 162 'dependencies' => $dependencies, 163 'in_footer' => $in_footer, 135 164 'fetchpriority' => $fetchpriority, 136 165 ); … … 158 187 */ 159 188 private function is_valid_fetchpriority( $priority ): bool { 160 return in_array( $priority, array( 'auto', 'low', 'high' ), true );189 return in_array( $priority, $this->priorities, true ); 161 190 } 162 191 … … 190 219 191 220 $this->registered[ $id ]['fetchpriority'] = $priority; 221 return true; 222 } 223 224 /** 225 * Sets whether a script module should be printed in the footer. 226 * 227 * This is only relevant in block themes. 228 * 229 * @since 6.9.0 230 * 231 * @param string $id Script module identifier. 232 * @param bool $in_footer Whether to print in the footer. 233 * @return bool Whether setting the printing location was successful. 234 */ 235 public function set_in_footer( string $id, bool $in_footer ): bool { 236 if ( ! isset( $this->registered[ $id ] ) ) { 237 return false; 238 } 239 $this->registered[ $id ]['in_footer'] = $in_footer; 192 240 return true; 193 241 } … … 229 277 * Optional. An array of additional args. Default empty array. 230 278 * 279 * @type bool $in_footer Whether to print the script module in the footer. Only relevant to block themes. Default 'false'. Optional. 231 280 * @type 'auto'|'low'|'high' $fetchpriority Fetch priority. Default 'auto'. Optional. 232 281 * } 233 282 */ 234 283 public function enqueue( string $id, string $src = '', array $deps = array(), $version = false, array $args = array() ) { 284 if ( '' === $id ) { 285 _doing_it_wrong( __METHOD__, __( 'Non-empty string required for id.' ), '6.9.0' ); 286 return; 287 } 288 235 289 if ( ! in_array( $id, $this->queue, true ) ) { 236 290 $this->queue[] = $id; … … 275 329 */ 276 330 public function add_hooks() { 277 $position = wp_is_block_theme() ? 'wp_head' : 'wp_footer'; 331 $is_block_theme = wp_is_block_theme(); 332 $position = $is_block_theme ? 'wp_head' : 'wp_footer'; 278 333 add_action( $position, array( $this, 'print_import_map' ) ); 279 add_action( $position, array( $this, 'print_enqueued_script_modules' ) ); 334 if ( $is_block_theme ) { 335 /* 336 * Modules can only be printed in the head for block themes because only with 337 * block themes will import map be fully populated by modules discovered by 338 * rendering the block template. In classic themes, modules are enqueued during 339 * template rendering, thus the import map must be printed in the footer, 340 * followed by all enqueued modules. 341 */ 342 add_action( 'wp_head', array( $this, 'print_head_enqueued_script_modules' ) ); 343 } 344 add_action( 'wp_footer', array( $this, 'print_enqueued_script_modules' ) ); 280 345 add_action( $position, array( $this, 'print_script_module_preloads' ) ); 281 346 … … 296 361 * 297 362 * @param string[] $ids Script module IDs. 298 * @return stringHighest fetch priority for the provided script module IDs.363 * @return 'auto'|'low'|'high' Highest fetch priority for the provided script module IDs. 299 364 */ 300 365 private function get_highest_fetchpriority( array $ids ): string { 301 static $priorities = array( 302 'low', 303 'auto', 304 'high', 305 ); 306 $high_priority_index = count( $priorities ) - 1; 366 static $high_priority_index = null; 367 if ( null === $high_priority_index ) { 368 $high_priority_index = count( $this->priorities ) - 1; 369 } 307 370 308 371 $highest_priority_index = 0; 309 372 foreach ( $ids as $id ) { 310 373 if ( isset( $this->registered[ $id ] ) ) { 311 $highest_priority_index = max(374 $highest_priority_index = (int) max( 312 375 $highest_priority_index, 313 array_search( $this->registered[ $id ]['fetchpriority'], $priorities, true )376 (int) array_search( $this->registered[ $id ]['fetchpriority'], $this->priorities, true ) 314 377 ); 315 378 if ( $high_priority_index === $highest_priority_index ) { … … 319 382 } 320 383 321 return $priorities[ $highest_priority_index ]; 322 } 323 324 /** 325 * Prints the enqueued script modules using script tags with type="module" 384 return $this->priorities[ $highest_priority_index ]; 385 } 386 387 /** 388 * Prints the enqueued script modules in head. 389 * 390 * This is only used in block themes. 391 * 392 * @since 6.9.0 393 */ 394 public function print_head_enqueued_script_modules() { 395 foreach ( $this->get_sorted_dependencies( $this->queue ) as $id ) { 396 if ( 397 isset( $this->registered[ $id ] ) && 398 ! $this->registered[ $id ]['in_footer'] 399 ) { 400 // If any dependency is set to be printed in footer, skip printing this module in head. 401 $dependencies = $this->get_dependencies( array( $id ) ); 402 foreach ( $dependencies as $dependency_id ) { 403 if ( 404 in_array( $dependency_id, $this->queue, true ) && 405 isset( $this->registered[ $dependency_id ] ) && 406 $this->registered[ $dependency_id ]['in_footer'] 407 ) { 408 continue 2; 409 } 410 } 411 $this->print_script_module( $id ); 412 } 413 } 414 } 415 416 /** 417 * Prints the enqueued script modules in footer. 418 * 419 * @since 6.5.0 420 */ 421 public function print_enqueued_script_modules() { 422 foreach ( $this->get_sorted_dependencies( $this->queue ) as $id ) { 423 $this->print_script_module( $id ); 424 } 425 } 426 427 /** 428 * Prints the enqueued script module using script tags with type="module" 326 429 * attributes. 327 430 * 328 * @since 6.5.0 329 */ 330 public function print_enqueued_script_modules() { 331 foreach ( $this->get_marked_for_enqueue() as $id => $script_module ) { 332 $args = array( 333 'type' => 'module', 334 'src' => $this->get_src( $id ), 335 'id' => $id . '-js-module', 336 ); 337 338 $dependents = $this->get_recursive_dependents( $id ); 339 $fetchpriority = $this->get_highest_fetchpriority( array_merge( array( $id ), $dependents ) ); 340 if ( 'auto' !== $fetchpriority ) { 341 $args['fetchpriority'] = $fetchpriority; 342 } 343 if ( $fetchpriority !== $script_module['fetchpriority'] ) { 344 $args['data-wp-fetchpriority'] = $script_module['fetchpriority']; 345 } 346 wp_print_script_tag( $args ); 347 } 431 * @since 6.9.0 432 * 433 * @param string $id The script module identifier. 434 */ 435 private function print_script_module( string $id ) { 436 if ( in_array( $id, $this->done, true ) || ! in_array( $id, $this->queue, true ) ) { 437 return; 438 } 439 440 $this->done[] = $id; 441 442 $src = $this->get_src( $id ); 443 if ( '' === $src ) { 444 return; 445 } 446 447 $attributes = array( 448 'type' => 'module', 449 'src' => $src, 450 'id' => $id . '-js-module', 451 ); 452 453 $script_module = $this->registered[ $id ]; 454 $dependents = $this->get_recursive_dependents( $id ); 455 $fetchpriority = $this->get_highest_fetchpriority( array_merge( array( $id ), $dependents ) ); 456 if ( 'auto' !== $fetchpriority ) { 457 $attributes['fetchpriority'] = $fetchpriority; 458 } 459 if ( $fetchpriority !== $script_module['fetchpriority'] ) { 460 $attributes['data-wp-fetchpriority'] = $script_module['fetchpriority']; 461 } 462 wp_print_script_tag( $attributes ); 348 463 } 349 464 … … 357 472 */ 358 473 public function print_script_module_preloads() { 359 foreach ( $this->get_dependencies( array_unique( $this->queue ), array( 'static' ) ) as $id => $script_module ) { 474 $dependency_ids = $this->get_sorted_dependencies( $this->queue, array( 'static' ) ); 475 foreach ( $dependency_ids as $id ) { 360 476 // Don't preload if it's marked for enqueue. 361 if ( ! in_array( $id, $this->queue, true ) ) { 362 $enqueued_dependents = array_intersect( $this->get_recursive_dependents( $id ), $this->queue ); 363 $highest_fetchpriority = $this->get_highest_fetchpriority( $enqueued_dependents ); 364 printf( 365 '<link rel="modulepreload" href="%s" id="%s"', 366 esc_url( $this->get_src( $id ) ), 367 esc_attr( $id . '-js-modulepreload' ) 368 ); 369 if ( 'auto' !== $highest_fetchpriority ) { 370 printf( ' fetchpriority="%s"', esc_attr( $highest_fetchpriority ) ); 371 } 372 if ( $highest_fetchpriority !== $script_module['fetchpriority'] && 'auto' !== $script_module['fetchpriority'] ) { 373 printf( ' data-wp-fetchpriority="%s"', esc_attr( $script_module['fetchpriority'] ) ); 374 } 375 echo ">\n"; 376 } 477 if ( in_array( $id, $this->queue, true ) ) { 478 continue; 479 } 480 481 $src = $this->get_src( $id ); 482 if ( '' === $src ) { 483 continue; 484 } 485 486 $enqueued_dependents = array_intersect( $this->get_recursive_dependents( $id ), $this->queue ); 487 $highest_fetchpriority = $this->get_highest_fetchpriority( $enqueued_dependents ); 488 printf( 489 '<link rel="modulepreload" href="%s" id="%s"', 490 esc_url( $src ), 491 esc_attr( $id . '-js-modulepreload' ) 492 ); 493 if ( 'auto' !== $highest_fetchpriority ) { 494 printf( ' fetchpriority="%s"', esc_attr( $highest_fetchpriority ) ); 495 } 496 if ( $highest_fetchpriority !== $this->registered[ $id ]['fetchpriority'] && 'auto' !== $this->registered[ $id ]['fetchpriority'] ) { 497 printf( ' data-wp-fetchpriority="%s"', esc_attr( $this->registered[ $id ]['fetchpriority'] ) ); 498 } 499 echo ">\n"; 377 500 } 378 501 } … … 387 510 if ( ! empty( $import_map['imports'] ) ) { 388 511 wp_print_inline_script_tag( 389 wp_json_encode( $import_map, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),512 (string) wp_json_encode( $import_map, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), 390 513 array( 391 514 'type' => 'importmap', … … 406 529 private function get_import_map(): array { 407 530 $imports = array(); 408 foreach ( $this->get_dependencies( array_unique( $this->queue ) ) as $id => $script_module ) { 409 $imports[ $id ] = $this->get_src( $id ); 531 foreach ( $this->get_dependencies( $this->queue ) as $id ) { 532 $src = $this->get_src( $id ); 533 if ( '' !== $src ) { 534 $imports[ $id ] = $src; 535 } 410 536 } 411 537 return array( 'imports' => $imports ); … … 414 540 /** 415 541 * Retrieves the list of script modules marked for enqueue. 542 * 543 * Even though this is a private method and is unused in core, there are ecosystem plugins accessing it via the 544 * Reflection API. The ecosystem should rather use {@see self::get_queue()}. 416 545 * 417 546 * @since 6.5.0 … … 427 556 428 557 /** 429 * Retrieves all the dependencies for the given script module identifiers, 430 * filtered by import types. 558 * Retrieves all the dependencies for the given script module identifiers, filtered by import types. 431 559 * 432 560 * It will consolidate an array containing a set of unique dependencies based … … 438 566 * @param string[] $ids The identifiers of the script modules for which to gather dependencies. 439 567 * @param string[] $import_types Optional. Import types of dependencies to retrieve: 'static', 'dynamic', or both. 440 * Default is both.441 * @return array[] List of dependencies, keyed by script module identifier.568 * Default is both. 569 * @return string[] List of IDs for script module dependencies. 442 570 */ 443 571 private function get_dependencies( array $ids, array $import_types = array( 'static', 'dynamic' ) ): array { 444 return array_reduce( 445 $ids, 446 function ( $dependency_script_modules, $id ) use ( $import_types ) { 447 $dependencies = array(); 448 if ( isset( $this->registered[ $id ] ) ) { 449 foreach ( $this->registered[ $id ]['dependencies'] as $dependency ) { 450 if ( 451 in_array( $dependency['import'], $import_types, true ) && 452 isset( $this->registered[ $dependency['id'] ] ) && 453 ! isset( $dependency_script_modules[ $dependency['id'] ] ) 454 ) { 455 $dependencies[ $dependency['id'] ] = $this->registered[ $dependency['id'] ]; 456 } 457 } 572 $all_dependencies = array(); 573 $id_queue = $ids; 574 575 while ( ! empty( $id_queue ) ) { 576 $id = array_shift( $id_queue ); 577 if ( ! isset( $this->registered[ $id ] ) ) { 578 continue; 579 } 580 581 foreach ( $this->registered[ $id ]['dependencies'] as $dependency ) { 582 if ( 583 ! isset( $all_dependencies[ $dependency['id'] ] ) && 584 in_array( $dependency['import'], $import_types, true ) && 585 isset( $this->registered[ $dependency['id'] ] ) 586 ) { 587 $all_dependencies[ $dependency['id'] ] = true; 588 589 // Add this dependency to the list to get dependencies for. 590 $id_queue[] = $dependency['id']; 458 591 } 459 return array_merge( $dependency_script_modules, $dependencies, $this->get_dependencies( array_keys( $dependencies ), $import_types ) );460 },461 array() 462 );592 } 593 } 594 595 return array_keys( $all_dependencies ); 463 596 } 464 597 … … 507 640 */ 508 641 private function get_recursive_dependents( string $id ): array { 509 $get = function ( string $id, array $checked = array() ) use ( &$get ): array { 510 511 // If by chance an unregistered script module is checked or there is a recursive dependency, return early. 512 if ( ! isset( $this->registered[ $id ] ) || isset( $checked[ $id ] ) ) { 513 return array(); 514 } 515 516 // Mark this script module as checked to guard against infinite recursion. 517 $checked[ $id ] = true; 518 519 $dependents = array(); 520 foreach ( $this->get_dependents( $id ) as $dependent ) { 521 $dependents = array_merge( 522 $dependents, 523 array( $dependent ), 524 $get( $dependent, $checked ) 525 ); 526 } 527 528 return $dependents; 529 }; 530 531 return array_unique( $get( $id ) ); 642 $dependents = array(); 643 $id_queue = array( $id ); 644 $processed = array(); 645 646 while ( ! empty( $id_queue ) ) { 647 $current_id = array_shift( $id_queue ); 648 649 // Skip unregistered or already-processed script modules. 650 if ( ! isset( $this->registered[ $current_id ] ) || isset( $processed[ $current_id ] ) ) { 651 continue; 652 } 653 654 // Mark as processed to guard against infinite loops from circular dependencies. 655 $processed[ $current_id ] = true; 656 657 // Find the direct dependents of the current script. 658 foreach ( $this->get_dependents( $current_id ) as $dependent_id ) { 659 // Only add the dependent if we haven't found it before. 660 if ( ! isset( $dependents[ $dependent_id ] ) ) { 661 $dependents[ $dependent_id ] = true; 662 663 // Add dependency to the queue. 664 $id_queue[] = $dependent_id; 665 } 666 } 667 } 668 669 return array_keys( $dependents ); 670 } 671 672 /** 673 * Sorts the given script module identifiers based on their dependencies. 674 * 675 * It will return a list of script module identifiers sorted in the order 676 * they should be printed, so that dependencies are printed before the script 677 * modules that depend on them. 678 * 679 * @since 6.9.0 680 * 681 * @param string[] $ids The identifiers of the script modules to sort. 682 * @param string[] $import_types Optional. Import types of dependencies to retrieve: 'static', 'dynamic', or both. 683 * Default is both. 684 * @return string[] Sorted list of script module identifiers. 685 */ 686 private function get_sorted_dependencies( array $ids, array $import_types = array( 'static', 'dynamic' ) ): array { 687 $sorted = array(); 688 689 foreach ( $ids as $id ) { 690 $this->sort_item_dependencies( $id, $import_types, $sorted ); 691 } 692 693 return array_unique( $sorted ); 694 } 695 696 /** 697 * Recursively sorts the dependencies for a single script module identifier. 698 * 699 * @since 6.9.0 700 * 701 * @param string $id The identifier of the script module to sort. 702 * @param string[] $import_types Optional. Import types of dependencies to retrieve: 'static', 'dynamic', or both. 703 * @param string[] &$sorted The array of sorted identifiers, passed by reference. 704 * @return bool True on success, false on failure (e.g., missing dependency). 705 */ 706 private function sort_item_dependencies( string $id, array $import_types, array &$sorted ): bool { 707 // If already processed, don't do it again. 708 if ( in_array( $id, $sorted, true ) ) { 709 return true; 710 } 711 712 // If the item doesn't exist, fail. 713 if ( ! isset( $this->registered[ $id ] ) ) { 714 return false; 715 } 716 717 $dependency_ids = array(); 718 foreach ( $this->registered[ $id ]['dependencies'] as $dependency ) { 719 if ( in_array( $dependency['import'], $import_types, true ) ) { 720 $dependency_ids[] = $dependency['id']; 721 } 722 } 723 724 // If the item requires dependencies that do not exist, fail. 725 if ( count( array_diff( $dependency_ids, array_keys( $this->registered ) ) ) > 0 ) { 726 return false; 727 } 728 729 // Recursively process dependencies. 730 foreach ( $dependency_ids as $dependency_id ) { 731 if ( ! $this->sort_item_dependencies( $dependency_id, $import_types, $sorted ) ) { 732 // A dependency failed to resolve, so this branch fails. 733 return false; 734 } 735 } 736 737 // All dependencies are sorted, so we can now add the current item. 738 $sorted[] = $id; 739 740 return true; 532 741 } 533 742 … … 552 761 $src = $script_module['src']; 553 762 554 if ( false === $script_module['version'] ) { 555 $src = add_query_arg( 'ver', get_bloginfo( 'version' ), $src ); 556 } elseif ( null !== $script_module['version'] ) { 557 $src = add_query_arg( 'ver', $script_module['version'], $src ); 763 if ( '' !== $src ) { 764 if ( false === $script_module['version'] ) { 765 $src = add_query_arg( 'ver', get_bloginfo( 'version' ), $src ); 766 } elseif ( null !== $script_module['version'] ) { 767 $src = add_query_arg( 'ver', $script_module['version'], $src ); 768 } 558 769 } 559 770 … … 567 778 */ 568 779 $src = apply_filters( 'script_module_loader_src', $src, $id ); 780 if ( ! is_string( $src ) ) { 781 $src = ''; 782 } 569 783 570 784 return $src; … … 682 896 683 897 wp_print_inline_script_tag( 684 wp_json_encode(898 (string) wp_json_encode( 685 899 $data, 686 900 $json_encode_flags -
trunk/src/wp-includes/script-modules.php
r60931 r60999 65 65 * Optional. An array of additional args. Default empty array. 66 66 * 67 * @type bool $in_footer Whether to print the script module in the footer. Only relevant to block themes. Default 'false'. Optional. 67 68 * @type 'auto'|'low'|'high' $fetchpriority Fetch priority. Default 'auto'. Optional. 68 69 * } … … 108 109 * Optional. An array of additional args. Default empty array. 109 110 * 111 * @type bool $in_footer Whether to print the script module in the footer. Only relevant to block themes. Default 'false'. Optional. 110 112 * @type 'auto'|'low'|'high' $fetchpriority Fetch priority. Default 'auto'. Optional. 111 113 * } … … 184 186 /* 185 187 * 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 case188 * the fetchpriority is irrelevant. See <https://make.wordpress.org/core/2024/10/14/updates-to-script-modules-in-6-7/>.188 * should be loaded with low fetchpriority and printed in the footer since they should not be needed in the 189 * critical rendering path. Also, the @wordpress/a11y script module is intended to be used as a dynamic import 190 * dependency, in which case the fetchpriority is irrelevant. See <https://make.wordpress.org/core/2024/10/14/updates-to-script-modules-in-6-7/>. 189 191 * However, in case it is added as a static import dependency, the fetchpriority is explicitly set to be 'low' 190 192 * since the module should not be involved in the critical rendering path, and if it is, its fetchpriority will … … 198 200 ) { 199 201 $args['fetchpriority'] = 'low'; 202 $args['in_footer'] = true; 200 203 } 201 204 -
trunk/tests/phpunit/tests/script-modules/wpScriptModules.php
r60951 r60999 57 57 */ 58 58 public function get_enqueued_script_modules(): array { 59 $modules = array(); 60 61 $p = new WP_HTML_Tag_Processor( get_echo( array( $this->script_modules, 'print_enqueued_script_modules' ) ) ); 62 while ( $p->next_tag( array( 'tag' => 'SCRIPT' ) ) ) { 63 $this->assertSame( 'module', $p->get_attribute( 'type' ) ); 64 $this->assertIsString( $p->get_attribute( 'id' ) ); 65 $this->assertIsString( $p->get_attribute( 'src' ) ); 66 $this->assertStringEndsWith( '-js-module', $p->get_attribute( 'id' ) ); 67 68 $id = preg_replace( '/-js-module$/', '', (string) $p->get_attribute( 'id' ) ); 69 $fetchpriority = $p->get_attribute( 'fetchpriority' ); 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 ) 81 ); 82 } 59 $get_modules = function ( string $html, bool $in_footer ): array { 60 $modules = array(); 61 $p = new WP_HTML_Tag_Processor( $html ); 62 while ( $p->next_tag( array( 'tag' => 'SCRIPT' ) ) ) { 63 $this->assertSame( 'module', $p->get_attribute( 'type' ) ); 64 $this->assertIsString( $p->get_attribute( 'id' ) ); 65 $this->assertIsString( $p->get_attribute( 'src' ) ); 66 $this->assertStringEndsWith( '-js-module', $p->get_attribute( 'id' ) ); 67 68 $id = preg_replace( '/-js-module$/', '', (string) $p->get_attribute( 'id' ) ); 69 $fetchpriority = $p->get_attribute( 'fetchpriority' ); 70 $modules[ $id ] = array_merge( 71 array( 72 'url' => $p->get_attribute( 'src' ), 73 'fetchpriority' => is_string( $fetchpriority ) ? $fetchpriority : 'auto', 74 'in_footer' => $in_footer, 75 ), 76 ...array_map( 77 static function ( $attribute_name ) use ( $p ) { 78 return array( $attribute_name => $p->get_attribute( $attribute_name ) ); 79 }, 80 $p->get_attribute_names_with_prefix( 'data-' ) 81 ) 82 ); 83 } 84 return $modules; 85 }; 86 87 $modules = array_merge( 88 $get_modules( get_echo( array( $this->script_modules, 'print_head_enqueued_script_modules' ) ), false ), 89 $get_modules( get_echo( array( $this->script_modules, 'print_enqueued_script_modules' ) ), true ) 90 ); 83 91 84 92 return $modules; … … 148 156 149 157 /** 158 * Test wp_register_script_module() with empty ID. 159 * 160 * @ticket 63486 161 * 162 * @expectedIncorrectUsage WP_Script_Modules::register 163 * 164 * @covers ::wp_register_script_module 165 * @covers WP_Script_Modules::register 166 */ 167 public function test_register_with_empty_id() { 168 wp_register_script_module( '', '/null-and-void.js' ); 169 $this->assertArrayNotHasKey( '', $this->get_registered_script_modules( wp_script_modules() ) ); 170 } 171 172 /** 173 * Test wp_enqueue_script_module() with empty ID. 174 * 175 * @ticket 63486 176 * 177 * @expectedIncorrectUsage WP_Script_Modules::enqueue 178 * 179 * @covers ::wp_enqueue_script_module 180 * @covers WP_Script_Modules::enqueue 181 */ 182 public function test_enqueue_with_empty_id() { 183 wp_enqueue_script_module( '', '/null-and-void.js' ); 184 $this->assertArrayNotHasKey( '', $this->get_registered_script_modules( wp_script_modules() ) ); 185 $this->assertNotContains( '', wp_script_modules()->get_queue() ); 186 } 187 188 /** 150 189 * Tests various ways of registering, enqueueing, dequeuing, and deregistering a script module. 151 190 * … … 153 192 * 154 193 * @ticket 56313 194 * @ticket 63486 155 195 * 156 196 * @dataProvider data_test_register_and_enqueue_script_module … … 164 204 * @covers ::wp_deregister_script_module() 165 205 * @covers WP_Script_Modules::deregister() 206 * @covers WP_Script_Modules::get_queue() 207 * @covers WP_Script_Modules::get_marked_for_enqueue() 166 208 * @covers WP_Script_Modules::set_fetchpriority() 209 * @covers WP_Script_Modules::print_head_enqueued_script_modules() 167 210 * @covers WP_Script_Modules::print_enqueued_script_modules() 168 211 * @covers WP_Script_Modules::print_import_map() … … 180 223 } 181 224 }; 225 226 $reflection_class = new ReflectionClass( wp_script_modules() ); 227 $get_marked_for_enqueue = $reflection_class->getMethod( 'get_marked_for_enqueue' ); 228 if ( PHP_VERSION_ID < 80100 ) { 229 $get_marked_for_enqueue->setAccessible( true ); 230 } 182 231 183 232 $register_and_enqueue = static function ( ...$args ) use ( $use_global_function, $only_enqueue ) { … … 201 250 // Minimal args. 202 251 $register_and_enqueue( 'a', '/a.js' ); 252 $this->assertSame( array( 'a' ), wp_script_modules()->get_queue(), 'Expected queue to match.' ); 253 $marked_for_enqueue = $get_marked_for_enqueue->invoke( wp_script_modules() ); 254 $this->assertSame( wp_script_modules()->get_queue(), array_keys( $marked_for_enqueue ), 'Expected get_queue() to match keys returned by get_marked_for_enqueue().' ); 255 $this->assertIsArray( $marked_for_enqueue['a'], 'Expected script module "a" to have an array entry.' ); 256 $this->assertSame( '/a.js', $marked_for_enqueue['a']['src'], 'Expected script module "a" to have the given src.' ); 203 257 204 258 // One Dependency. … … 206 260 $register_and_enqueue( 'b', '/b.js', array( 'b-dep' ) ); 207 261 $this->assertTrue( wp_script_modules()->set_fetchpriority( 'b', 'low' ) ); 262 $this->assertSame( array( 'a', 'b' ), wp_script_modules()->get_queue() ); 208 263 209 264 // Two dependencies with different formats and a false version. … … 222 277 false 223 278 ); 279 $this->assertSame( array( 'a', 'b', 'c' ), wp_script_modules()->get_queue() ); 224 280 225 281 // Two dependencies, one imported statically and the other dynamically, with a null version. … … 241 297 null 242 298 ); 299 $this->assertSame( array( 'a', 'b', 'c', 'd' ), wp_script_modules()->get_queue() ); 243 300 244 301 // No dependencies, with a string version version. … … 249 306 '1.0.0' 250 307 ); 308 $this->assertSame( array( 'a', 'b', 'c', 'd', 'e' ), wp_script_modules()->get_queue() ); 251 309 252 310 // No dependencies, with a string version and fetch priority. … … 258 316 array( 'fetchpriority' => 'auto' ) 259 317 ); 318 $this->assertSame( array( 'a', 'b', 'c', 'd', 'e', 'f' ), wp_script_modules()->get_queue() ); 260 319 261 320 // No dependencies, with a string version and fetch priority of low. … … 267 326 array( 'fetchpriority' => 'low' ) 268 327 ); 328 $this->assertSame( array( 'a', 'b', 'c', 'd', 'e', 'f', 'g' ), wp_script_modules()->get_queue() ); 269 329 270 330 // No dependencies, with a string version and fetch priority of high. … … 276 336 array( 'fetchpriority' => 'high' ) 277 337 ); 338 $this->assertSame( array( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' ), wp_script_modules()->get_queue() ); 339 340 // Register and enqueue something which we'll dequeue right away. 341 $register_and_enqueue( 342 'i', 343 '/i.js', 344 array(), 345 '3.0.0' 346 ); 347 $this->assertSame( array( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ), wp_script_modules()->get_queue() ); 348 349 // Register and enqueue something which we'll deregister right away. 350 $register_and_enqueue( 351 'j', 352 '/j.js', 353 array(), 354 '3.0.0' 355 ); 356 $this->assertSame( array( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' ), wp_script_modules()->get_queue() ); 357 358 // Make sure unregister functions work. 359 $deregister_id = 'j'; 360 $this->assertArrayHasKey( 'j', $this->get_registered_script_modules( $this->script_modules ) ); 361 if ( $use_global_function ) { 362 wp_deregister_script_module( $deregister_id ); 363 } else { 364 wp_script_modules()->deregister( $deregister_id ); 365 } 366 $this->assertArrayNotHasKey( 'j', $this->get_registered_script_modules( $this->script_modules ) ); 367 $this->assertSame( array( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ), wp_script_modules()->get_queue() ); 368 369 // Make sure dequeue functions work. 370 $dequeue_id = 'i'; 371 $this->assertArrayHasKey( 'i', $this->get_registered_script_modules( $this->script_modules ) ); 372 if ( $use_global_function ) { 373 wp_dequeue_script_module( $dequeue_id ); 374 } else { 375 wp_script_modules()->dequeue( $dequeue_id ); 376 } 377 $this->assertArrayHasKey( 'i', $this->get_registered_script_modules( $this->script_modules ) ); 378 $this->assertSame( array( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' ), wp_script_modules()->get_queue() ); 278 379 279 380 $actual = array( … … 309 410 'url' => '/a.js?ver=99.9.9', 310 411 'fetchpriority' => 'auto', 412 'in_footer' => false, 311 413 ), 312 414 'b' => array( 313 415 'url' => '/b.js?ver=99.9.9', 314 416 'fetchpriority' => 'low', 417 'in_footer' => false, 315 418 ), 316 419 'c' => array( 317 420 'url' => '/c.js?ver=99.9.9', 318 421 'fetchpriority' => 'auto', 422 'in_footer' => false, 319 423 ), 320 424 'd' => array( 321 425 'url' => '/d.js', 322 426 'fetchpriority' => 'auto', 427 'in_footer' => false, 323 428 ), 324 429 'e' => array( 325 430 'url' => '/e.js?ver=1.0.0', 326 431 'fetchpriority' => 'auto', 432 'in_footer' => false, 327 433 ), 328 434 'f' => array( 329 435 'url' => '/f.js?ver=2.0.0', 330 436 'fetchpriority' => 'auto', 437 'in_footer' => false, 331 438 ), 332 439 'g' => array( 333 440 'url' => '/g.js?ver=2.0.0', 334 441 'fetchpriority' => 'low', 442 'in_footer' => false, 335 443 ), 336 444 'h' => array( 337 445 'url' => '/h.js?ver=3.0.0', 338 446 'fetchpriority' => 'high', 447 'in_footer' => false, 339 448 ), 340 449 ), … … 346 455 'd-dynamic-dep' => '/d-dynamic-dep.js?ver=99.9.9', 347 456 ), 348 ),349 $actual,350 "Snapshot:\n" . var_export( $actual, true )351 );352 353 // Dequeue the first half of the scripts.354 foreach ( array( 'a', 'b', 'c', 'd' ) as $id ) {355 if ( $use_global_function ) {356 wp_dequeue_script_module( $id );357 } else {358 wp_script_modules()->dequeue( $id );359 }360 }361 362 $actual = array(363 'preload_links' => $this->get_preloaded_script_modules(),364 'script_tags' => $this->get_enqueued_script_modules(),365 'import_map' => $this->get_import_map(),366 );367 $this->assertSame(368 array(369 'preload_links' => array(),370 'script_tags' => array(371 'e' => array(372 'url' => '/e.js?ver=1.0.0',373 'fetchpriority' => 'auto',374 ),375 'f' => array(376 'url' => '/f.js?ver=2.0.0',377 'fetchpriority' => 'auto',378 ),379 'g' => array(380 'url' => '/g.js?ver=2.0.0',381 'fetchpriority' => 'low',382 ),383 'h' => array(384 'url' => '/h.js?ver=3.0.0',385 'fetchpriority' => 'high',386 ),387 ),388 'import_map' => array(),389 ),390 $actual,391 "Snapshot:\n" . var_export( $actual, true )392 );393 394 // Unregister the remaining scripts.395 foreach ( array( 'e', 'f', 'g', 'h' ) as $id ) {396 if ( $use_global_function ) {397 wp_dequeue_script_module( $id );398 } else {399 wp_script_modules()->dequeue( $id );400 }401 }402 403 $actual = array(404 'preload_links' => $this->get_preloaded_script_modules(),405 'script_tags' => $this->get_enqueued_script_modules(),406 'import_map' => $this->get_import_map(),407 );408 $this->assertSame(409 array(410 'preload_links' => array(),411 'script_tags' => array(),412 'import_map' => array(),413 457 ), 414 458 $actual, … … 443 487 * 444 488 * @ticket 56313 489 * @ticket 63486 445 490 * 446 491 * @covers WP_Script_Modules::register() 447 492 * @covers WP_Script_Modules::enqueue() 493 * @covers WP_Script_Modules::print_head_enqueued_script_modules() 448 494 * @covers WP_Script_Modules::print_enqueued_script_modules() 449 495 * @covers WP_Script_Modules::set_fetchpriority() 496 * @covers WP_Script_Modules::set_in_footer() 450 497 */ 451 498 public function test_wp_enqueue_script_module() { 452 499 $this->script_modules->register( 'foo', '/foo.js' ); 453 $this->script_modules->register( 'bar', '/bar.js', array(), false, array( 'fetchpriority' => 'high' ) ); 500 $this->script_modules->register( 501 'bar', 502 '/bar.js', 503 array(), 504 false, 505 array( 506 'fetchpriority' => 'high', 507 'in_footer' => true, 508 ) 509 ); 454 510 $this->script_modules->register( 'baz', '/baz.js' ); 455 511 $this->assertTrue( $this->script_modules->set_fetchpriority( 'baz', 'low' ) ); 512 $this->assertTrue( $this->script_modules->set_in_footer( 'baz', true ) ); 456 513 $this->script_modules->enqueue( 'foo' ); 457 514 $this->script_modules->enqueue( 'bar' ); … … 463 520 $this->assertStringStartsWith( '/foo.js', $enqueued_script_modules['foo']['url'] ); 464 521 $this->assertSame( 'auto', $enqueued_script_modules['foo']['fetchpriority'] ); 522 $this->assertFalse( $enqueued_script_modules['foo']['in_footer'] ); 465 523 $this->assertStringStartsWith( '/bar.js', $enqueued_script_modules['bar']['url'] ); 466 524 $this->assertSame( 'high', $enqueued_script_modules['bar']['fetchpriority'] ); 525 $this->assertTrue( $enqueued_script_modules['bar']['in_footer'] ); 467 526 $this->assertStringStartsWith( '/baz.js', $enqueued_script_modules['baz']['url'] ); 468 527 $this->assertSame( 'low', $enqueued_script_modules['baz']['fetchpriority'] ); 528 $this->assertTrue( $enqueued_script_modules['baz']['in_footer'] ); 529 } 530 531 /** 532 * Tests that no script is printed for a script without a src. 533 * 534 * @ticket 63486 535 * 536 * @covers WP_Script_Modules::register() 537 * @covers WP_Script_Modules::enqueue() 538 * @covers WP_Script_Modules::print_head_enqueued_script_modules() 539 * @covers WP_Script_Modules::print_enqueued_script_modules() 540 * @covers WP_Script_Modules::get_src() 541 */ 542 public function test_wp_enqueue_script_module_with_empty_src() { 543 wp_enqueue_script_module( 'with-src', '/src.js' ); 544 wp_register_script_module( 'without-src', '' ); 545 wp_register_script_module( 'without-src-but-filtered', '' ); 546 wp_enqueue_script_module( 'without-src' ); 547 wp_enqueue_script_module( 'without-src-but-filtered' ); 548 $this->assertSame( array( 'with-src', 'without-src', 'without-src-but-filtered' ), wp_script_modules()->get_queue() ); 549 add_filter( 550 'script_module_loader_src', 551 static function ( $src, $id ) { 552 if ( 'without-src-but-filtered' === $id ) { 553 $src = '/was-empty-but-added-via-filter.js'; 554 } 555 return $src; 556 }, 557 10, 558 2 559 ); 560 $actual = get_echo( array( wp_script_modules(), 'print_enqueued_script_modules' ) ); 561 $this->assertEqualHTML( 562 ' 563 <script type="module" src="https://core.trac.wordpress.org/src.js?ver=6.9-alpha-60093-src" id="with-src-js-module"></script> 564 <script type="module" src="https://core.trac.wordpress.org/was-empty-but-added-via-filter.js" id="without-src-but-filtered-js-module"></script> 565 ', 566 $actual, 567 '<body>', 568 "Expected only one SCRIPT tag to be printed. Snapshot:\n$actual" 569 ); 469 570 } 470 571 … … 473 574 * 474 575 * @ticket 56313 576 * @ticket 63486 475 577 * 476 578 * @covers WP_Script_Modules::register() 477 579 * @covers WP_Script_Modules::enqueue() 478 580 * @covers WP_Script_Modules::dequeue() 581 * @covers WP_Script_Modules::print_head_enqueued_script_modules() 479 582 * @covers WP_Script_Modules::print_enqueued_script_modules() 480 583 */ … … 571 674 * 572 675 * @ticket 56313 676 * @ticket 63486 573 677 * 574 678 * @covers WP_Script_Modules::register() 575 679 * @covers WP_Script_Modules::enqueue() 680 * @covers WP_Script_Modules::print_head_enqueued_script_modules() 576 681 * @covers WP_Script_Modules::print_enqueued_script_modules() 577 682 */ … … 594 699 * 595 700 * @ticket 56313 701 * @ticket 63486 596 702 * 597 703 * @covers WP_Script_Modules::register() 598 704 * @covers WP_Script_Modules::enqueue() 599 705 * @covers WP_Script_Modules::dequeue() 706 * @covers WP_Script_Modules::print_head_enqueued_script_modules() 600 707 * @covers WP_Script_Modules::print_enqueued_script_modules() 601 708 */ … … 961 1068 * 962 1069 * @ticket 56313 1070 * @ticket 63486 963 1071 * 964 1072 * @covers WP_Script_Modules::register() 965 1073 * @covers WP_Script_Modules::enqueue() 1074 * @covers WP_Script_Modules::print_head_enqueued_script_modules() 966 1075 * @covers WP_Script_Modules::print_enqueued_script_modules() 967 1076 * @covers WP_Script_Modules::print_import_map() … … 999 1108 * 1000 1109 * @ticket 56313 1110 * @ticket 63486 1001 1111 * 1002 1112 * @covers WP_Script_Modules::enqueue() 1113 * @covers WP_Script_Modules::print_head_enqueued_script_modules() 1003 1114 * @covers WP_Script_Modules::print_enqueued_script_modules() 1004 1115 */ … … 1017 1128 * 1018 1129 * @ticket 56313 1130 * @ticket 63486 1019 1131 * 1020 1132 * @covers WP_Script_Modules::enqueue() 1133 * @covers WP_Script_Modules::print_head_enqueued_script_modules() 1021 1134 * @covers WP_Script_Modules::print_enqueued_script_modules() 1022 1135 */ … … 1036 1149 * 1037 1150 * @ticket 56313 1151 * @ticket 63486 1038 1152 * 1039 1153 * @covers WP_Script_Modules::enqueue() 1154 * @covers WP_Script_Modules::print_head_enqueued_script_modules() 1040 1155 * @covers WP_Script_Modules::print_enqueued_script_modules() 1041 1156 */ … … 1062 1177 * 1063 1178 * @ticket 56313 1179 * @ticket 63486 1064 1180 * 1065 1181 * @covers WP_Script_Modules::register() 1066 1182 * @covers WP_Script_Modules::enqueue() 1183 * @covers WP_Script_Modules::print_head_enqueued_script_modules() 1067 1184 * @covers WP_Script_Modules::print_enqueued_script_modules() 1068 1185 * @covers WP_Script_Modules::print_import_map() … … 1316 1433 1317 1434 /** 1435 * Tests ways of setting in_footer. 1436 * 1437 * @ticket 63486 1438 * @ticket 63486 1439 * 1440 * @covers ::wp_register_script_module 1441 * @covers ::wp_enqueue_script_module 1442 * @covers WP_Script_Modules::set_in_footer 1443 */ 1444 public function test_in_footer_methods() { 1445 wp_register_script_module( 'default', '/default.js', array(), null ); 1446 wp_enqueue_script_module( 'default' ); 1447 1448 wp_register_script_module( 'in-footer-via-register', '/in-footer-via-register.js', array(), null, array( 'in_footer' => true ) ); 1449 wp_enqueue_script_module( 'in-footer-via-register' ); 1450 1451 wp_enqueue_script_module( 'in-footer-via-enqueue', '/in-footer-via-enqueue.js', array(), null, array( 'in_footer' => true ) ); 1452 1453 wp_enqueue_script_module( 'not-in-footer-via-enqueue', '/not-in-footer-via-enqueue.js', array(), null, array( 'in_footer' => false ) ); 1454 1455 wp_enqueue_script_module( 'in-footer-via-override', '/in-footer-via-override.js' ); 1456 wp_script_modules()->set_in_footer( 'in-footer-via-override', true ); 1457 1458 wp_enqueue_script_module( 'not-in-footer-via-override', '/not-in-footer-via-override.js', array(), null, array( 'in_footer' => true ) ); 1459 wp_script_modules()->set_in_footer( 'not-in-footer-via-override', false ); 1460 1461 $actual_head = get_echo( array( wp_script_modules(), 'print_head_enqueued_script_modules' ) ); 1462 $actual_footer = get_echo( array( wp_script_modules(), 'print_enqueued_script_modules' ) ); 1463 1464 $this->assertEqualHTML( 1465 $actual_head, 1466 ' 1467 <script type="module" src="https://core.trac.wordpress.org/default.js" id="default-js-module"></script> 1468 <script type="module" src="https://core.trac.wordpress.org/not-in-footer-via-enqueue.js" id="not-in-footer-via-enqueue-js-module"></script> 1469 <script type="module" src="https://core.trac.wordpress.org/not-in-footer-via-override.js" id="not-in-footer-via-override-js-module"></script> 1470 ', 1471 '<body>', 1472 "Expected equal script modules in the HEAD. Snapshot:\n$actual_head" 1473 ); 1474 $this->assertEqualHTML( 1475 $actual_footer, 1476 ' 1477 <script type="module" src="https://core.trac.wordpress.org/in-footer-via-register.js" id="in-footer-via-register-js-module"></script> 1478 <script type="module" src="https://core.trac.wordpress.org/in-footer-via-enqueue.js" id="in-footer-via-enqueue-js-module"></script> 1479 <script type="module" src="https://core.trac.wordpress.org/in-footer-via-override.js?ver=6.9-alpha-60093-src" id="in-footer-via-override-js-module"></script> 1480 ', 1481 '<body>', 1482 "Expected equal script modules in the footer. Snapshot:\n$actual_footer" 1483 ); 1484 } 1485 1486 /** 1318 1487 * Tests that a script module with an invalid fetchpriority value gets a value of auto. 1319 1488 * … … 1378 1547 'url' => '/bajo.js', 1379 1548 'fetchpriority' => 'high', 1549 'in_footer' => false, 1380 1550 'data-wp-fetchpriority' => 'low', 1381 1551 ), … … 1400 1570 'url' => '/auto.js', 1401 1571 'fetchpriority' => 'high', 1572 'in_footer' => false, 1402 1573 'data-wp-fetchpriority' => 'auto', 1403 1574 ), … … 1413 1584 'expected' => array( 1414 1585 'preload_links' => array( 1415 'auto' => array(1416 'url' => '/auto.js',1417 'fetchpriority' => 'high',1418 ),1419 1586 'bajo' => array( 1420 1587 'url' => '/bajo.js', … … 1422 1589 'data-wp-fetchpriority' => 'low', 1423 1590 ), 1591 'auto' => array( 1592 'url' => '/auto.js', 1593 'fetchpriority' => 'high', 1594 ), 1424 1595 ), 1425 1596 'script_tags' => array( … … 1427 1598 'url' => '/alto.js', 1428 1599 'fetchpriority' => 'high', 1600 'in_footer' => false, 1429 1601 ), 1430 1602 ), … … 1540 1712 $actual .= get_echo( array( wp_script_modules(), 'print_enqueued_script_modules' ) ); 1541 1713 $expected = ' 1542 <link rel="modulepreload" href="https://core.trac.wordpress.org/b.js" id="b-js-modulepreload" fetchpriority="low"> 1543 <link rel="modulepreload" href="https://core.trac.wordpress.org/c.js" id="c-js-modulepreload" fetchpriority="low"> 1714 <link rel="modulepreload" href="https://core.trac.wordpress.org/z.js" id="z-js-modulepreload" fetchpriority="high"> 1544 1715 <link rel="modulepreload" href="https://core.trac.wordpress.org/d.js" id="d-js-modulepreload" fetchpriority="high"> 1545 1716 <link rel="modulepreload" href="https://core.trac.wordpress.org/e.js" id="e-js-modulepreload" fetchpriority="low"> 1546 <link rel="modulepreload" href="https://core.trac.wordpress.org/z.js" id="z-js-modulepreload" fetchpriority="high"> 1717 <link rel="modulepreload" href="https://core.trac.wordpress.org/c.js" id="c-js-modulepreload" fetchpriority="low"> 1718 <link rel="modulepreload" href="https://core.trac.wordpress.org/b.js" id="b-js-modulepreload" fetchpriority="low"> 1547 1719 <link rel="modulepreload" href="https://core.trac.wordpress.org/y.js" id="y-js-modulepreload" fetchpriority="high"> 1548 1720 <script type="module" src="https://core.trac.wordpress.org/a.js" id="a-js-module" fetchpriority="low"></script> … … 1585 1757 $actual .= get_echo( array( wp_script_modules(), 'print_enqueued_script_modules' ) ); 1586 1758 $expected = ' 1587 <link rel="modulepreload" href="https://core.trac.wordpress.org/a.js" id="a-js-modulepreload" fetchpriority="low" data-wp-fetchpriority="high">1588 1759 <link rel="modulepreload" href="https://core.trac.wordpress.org/d.js" id="d-js-modulepreload" fetchpriority="low"> 1589 1760 <link rel="modulepreload" href="https://core.trac.wordpress.org/e.js" id="e-js-modulepreload" fetchpriority="high" data-wp-fetchpriority="low"> 1761 <link rel="modulepreload" href="https://core.trac.wordpress.org/a.js" id="a-js-modulepreload" fetchpriority="low" data-wp-fetchpriority="high"> 1590 1762 <link rel="modulepreload" href="https://core.trac.wordpress.org/b.js" id="b-js-modulepreload"> 1763 <link rel="modulepreload" href="https://core.trac.wordpress.org/f.js" id="f-js-modulepreload" fetchpriority="high"> 1591 1764 <link rel="modulepreload" href="https://core.trac.wordpress.org/c.js" id="c-js-modulepreload" fetchpriority="high"> 1592 <link rel="modulepreload" href="https://core.trac.wordpress.org/f.js" id="f-js-modulepreload" fetchpriority="high">1593 1765 <script type="module" src="https://core.trac.wordpress.org/x.js" id="x-js-module" fetchpriority="low"></script> 1594 1766 <script type="module" src="https://core.trac.wordpress.org/y.js" id="y-js-module"></script> … … 1601 1773 * Tests that default script modules are printed as expected. 1602 1774 * 1775 * @ticket 63486 1776 * 1603 1777 * @covers ::wp_default_script_modules 1604 1778 * @covers WP_Script_Modules::print_script_module_preloads 1779 * @covers WP_Script_Modules::print_head_enqueued_script_modules 1605 1780 * @covers WP_Script_Modules::print_enqueued_script_modules 1606 1781 */ … … 1610 1785 wp_enqueue_script_module( '@wordpress/block-library/navigation/view' ); 1611 1786 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="https://core.trac.wordpress.org/wp-includes/js/dist/script-modules/interactivity/debug.min.js" id="@wordpress/interactivity-js-modulepreload" fetchpriority="low"> 1619 <script type="module" src="https://core.trac.wordpress.org/wp-includes/js/dist/script-modules/a11y/index.min.js" id="@wordpress/a11y-js-module" fetchpriority="low"></script> 1620 <script type="module" src="https://core.trac.wordpress.org/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" ); 1787 $actual_preloads = $this->normalize_markup_for_snapshot( get_echo( array( wp_script_modules(), 'print_script_module_preloads' ) ) ); 1788 $this->assertEqualHTML( 1789 ' 1790 <link rel="modulepreload" href="https://core.trac.wordpress.org/wp-includes/js/dist/script-modules/interactivity/debug.min.js" id="@wordpress/interactivity-js-modulepreload" fetchpriority="low"> 1791 ', 1792 $actual_preloads, 1793 '<body>', 1794 "Snapshot:\n$actual_preloads" 1795 ); 1796 1797 $actual_head_script_modules = $this->normalize_markup_for_snapshot( get_echo( array( wp_script_modules(), 'print_head_enqueued_script_modules' ) ) ); 1798 $this->assertEqualHTML( 1799 '', 1800 $actual_head_script_modules, 1801 '<body>', 1802 "Snapshot:\n$actual_head_script_modules" 1803 ); 1804 1805 $actual_footer_script_modules = $this->normalize_markup_for_snapshot( get_echo( array( wp_script_modules(), 'print_enqueued_script_modules' ) ) ); 1806 $this->assertEqualHTML( 1807 ' 1808 <script type="module" src="https://core.trac.wordpress.org/wp-includes/js/dist/script-modules/a11y/index.min.js" id="@wordpress/a11y-js-module" fetchpriority="low"></script> 1809 <script type="module" src="https://core.trac.wordpress.org/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js" id="@wordpress/block-library/navigation/view-js-module" fetchpriority="low"></script> 1810 ', 1811 $actual_footer_script_modules, 1812 '<body>', 1813 "Snapshot:\n$actual_footer_script_modules" 1814 ); 1623 1815 } 1624 1816 … … 1647 1839 $expected = ' 1648 1840 <link rel="modulepreload" href="https://core.trac.wordpress.org/wp-includes/js/dist/script-modules/a11y/index.min.js" id="@wordpress/a11y-js-modulepreload" fetchpriority="high" data-wp-fetchpriority="low"> 1841 <link rel="modulepreload" href="https://core.trac.wordpress.org/wp-includes/js/dist/script-modules/interactivity/debug.min.js" id="@wordpress/interactivity-js-modulepreload" fetchpriority="high" data-wp-fetchpriority="low"> 1649 1842 <link rel="modulepreload" href="https://core.trac.wordpress.org/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="https://core.trac.wordpress.org/wp-includes/js/dist/script-modules/interactivity/debug.min.js" id="@wordpress/interactivity-js-modulepreload" fetchpriority="high" data-wp-fetchpriority="low">1651 1843 <script type="module" src="https://core.trac.wordpress.org/super-important-module.js" id="super-important-js-module" fetchpriority="high"></script> 1652 1844 '; … … 1731 1923 ); 1732 1924 } 1925 1926 /** 1927 * Tests various ways of printing and dependency ordering of script modules. 1928 * 1929 * This ensures that the global function aliases pass all the same parameters as the class methods. 1930 * 1931 * @ticket 63486 1932 * 1933 * @dataProvider data_test_register_and_enqueue_script_module 1934 * 1935 * @covers ::wp_register_script_module() 1936 * @covers WP_Script_Modules::register() 1937 * @covers ::wp_enqueue_script_module() 1938 * @covers WP_Script_Modules::enqueue() 1939 * @covers ::wp_dequeue_script_module() 1940 * @covers WP_Script_Modules::dequeue() 1941 * @covers ::wp_deregister_script_module() 1942 * @covers WP_Script_Modules::deregister() 1943 * @covers WP_Script_Modules::set_fetchpriority() 1944 * @covers WP_Script_Modules::print_head_enqueued_script_modules() 1945 * @covers WP_Script_Modules::print_enqueued_script_modules() 1946 * @covers WP_Script_Modules::print_import_map() 1947 * @covers WP_Script_Modules::print_script_module_preloads() 1948 */ 1949 public function test_script_module_printing_and_dependency_ordering( bool $use_global_function, bool $only_enqueue ) { 1950 global $wp_version; 1951 $wp_version = '99.9.9'; 1952 1953 $register = static function ( ...$args ) use ( $use_global_function ) { 1954 if ( $use_global_function ) { 1955 wp_register_script_module( ...$args ); 1956 } else { 1957 wp_script_modules()->register( ...$args ); 1958 } 1959 }; 1960 1961 $register_and_enqueue = static function ( ...$args ) use ( $use_global_function, $only_enqueue ) { 1962 if ( $use_global_function ) { 1963 if ( $only_enqueue ) { 1964 wp_enqueue_script_module( ...$args ); 1965 } else { 1966 wp_register_script_module( ...$args ); 1967 wp_enqueue_script_module( $args[0] ); 1968 } 1969 } else { 1970 if ( $only_enqueue ) { 1971 wp_script_modules()->enqueue( ...$args ); 1972 } else { 1973 wp_script_modules()->register( ...$args ); 1974 wp_script_modules()->enqueue( $args[0] ); 1975 } 1976 } 1977 }; 1978 1979 $deregister = static function ( array $ids ) use ( $use_global_function ) { 1980 foreach ( $ids as $id ) { 1981 if ( $use_global_function ) { 1982 wp_deregister_script_module( $id ); 1983 } else { 1984 wp_script_modules()->deregister( $id ); 1985 } 1986 } 1987 }; 1988 1989 // Test script module is placed in footer when in_footer is true. 1990 $register_and_enqueue( 'a', '/a.js', array(), '1.0.0', array( 'in_footer' => true ) ); 1991 1992 $actual = array( 1993 'preload_links' => $this->get_preloaded_script_modules(), 1994 'script_tags' => $this->get_enqueued_script_modules(), 1995 'import_map' => $this->get_import_map(), 1996 ); 1997 $this->assertSame( 1998 array( 1999 'preload_links' => array(), 2000 'script_tags' => array( 2001 'a' => array( 2002 'url' => '/a.js?ver=1.0.0', 2003 'fetchpriority' => 'auto', 2004 'in_footer' => true, 2005 ), 2006 ), 2007 'import_map' => array(), 2008 ), 2009 $actual, 2010 "Snapshot:\n" . var_export( $actual, true ) 2011 ); 2012 2013 $deregister( array( 'a' ) ); 2014 2015 // Test that dependant also gets placed in footer when its dependency is in footer. 2016 $register_and_enqueue( 'b', '/b.js', array(), '1.0.0', array( 'in_footer' => true ) ); 2017 $register_and_enqueue( 'c', '/c.js', array( 'b' ), '1.0.0' ); 2018 2019 $actual = array( 2020 'preload_links' => $this->get_preloaded_script_modules(), 2021 'script_tags' => $this->get_enqueued_script_modules(), 2022 'import_map' => $this->get_import_map(), 2023 ); 2024 $this->assertSame( 2025 array( 2026 'preload_links' => array(), 2027 'script_tags' => array( 2028 'b' => array( 2029 'url' => '/b.js?ver=1.0.0', 2030 'fetchpriority' => 'auto', 2031 'in_footer' => true, 2032 ), 2033 'c' => array( 2034 'url' => '/c.js?ver=1.0.0', 2035 'fetchpriority' => 'auto', 2036 'in_footer' => true, 2037 ), 2038 ), 2039 'import_map' => array( 2040 'b' => '/b.js?ver=1.0.0', 2041 ), 2042 ), 2043 $actual, 2044 "Snapshot:\n" . var_export( $actual, true ) 2045 ); 2046 2047 $deregister( array( 'b', 'c ' ) ); 2048 2049 // Test that registered dependency in footer doesn't place dependant in footer. 2050 $register( 'd', '/d.js', array(), '1.0.0', array( 'in_footer' => true ) ); 2051 $register_and_enqueue( 'e', '/e.js', array( 'd' ), '1.0.0' ); 2052 2053 $actual = array( 2054 'preload_links' => $this->get_preloaded_script_modules(), 2055 'script_tags' => $this->get_enqueued_script_modules(), 2056 'import_map' => $this->get_import_map(), 2057 ); 2058 $this->assertSame( 2059 array( 2060 'preload_links' => array( 2061 'd' => array( 2062 'url' => '/d.js?ver=1.0.0', 2063 'fetchpriority' => 'auto', 2064 ), 2065 ), 2066 'script_tags' => array( 2067 'e' => array( 2068 'url' => '/e.js?ver=1.0.0', 2069 'fetchpriority' => 'auto', 2070 'in_footer' => false, 2071 ), 2072 ), 2073 'import_map' => array( 2074 'd' => '/d.js?ver=1.0.0', 2075 ), 2076 ), 2077 $actual, 2078 "Snapshot:\n" . var_export( $actual, true ) 2079 ); 2080 2081 $deregister( array( 'd', 'e' ) ); 2082 2083 // Test if one of the dependency is in footer, the dependant and other dependant dependencies are also placed in footer. 2084 $register_and_enqueue( 'f', '/f.js', array(), '1.0.0' ); 2085 $register_and_enqueue( 'g', '/g.js', array( 'f' ), '1.0.0', array( 'in_footer' => true ) ); 2086 $register_and_enqueue( 'h', '/h.js', array( 'g' ), '1.0.0' ); 2087 $register_and_enqueue( 'i', '/i.js', array( 'h' ), '1.0.0' ); 2088 2089 $actual = array( 2090 'preload_links' => $this->get_preloaded_script_modules(), 2091 'script_tags' => $this->get_enqueued_script_modules(), 2092 'import_map' => $this->get_import_map(), 2093 ); 2094 2095 $this->assertSame( 2096 array( 2097 'preload_links' => array(), 2098 'script_tags' => array( 2099 'f' => array( 2100 'url' => '/f.js?ver=1.0.0', 2101 'fetchpriority' => 'auto', 2102 'in_footer' => false, 2103 ), 2104 'g' => array( 2105 'url' => '/g.js?ver=1.0.0', 2106 'fetchpriority' => 'auto', 2107 'in_footer' => true, 2108 ), 2109 'h' => array( 2110 'url' => '/h.js?ver=1.0.0', 2111 'fetchpriority' => 'auto', 2112 'in_footer' => true, 2113 ), 2114 'i' => array( 2115 'url' => '/i.js?ver=1.0.0', 2116 'fetchpriority' => 'auto', 2117 'in_footer' => true, 2118 ), 2119 ), 2120 'import_map' => array( 2121 'f' => '/f.js?ver=1.0.0', 2122 'g' => '/g.js?ver=1.0.0', 2123 'h' => '/h.js?ver=1.0.0', 2124 ), 2125 ), 2126 $actual, 2127 "Snapshot:\n" . var_export( $actual, true ) 2128 ); 2129 2130 $deregister( array( 'f', 'g', 'h', 'i' ) ); 2131 2132 // Test dependency ordering when all scripts modules are enqueued in head. 2133 // Expected order: j, k, l, m. 2134 $register_and_enqueue( 'm', '/m.js', array( 'j', 'l' ), '1.0.0' ); 2135 $register_and_enqueue( 'k', '/k.js', array( 'j' ), '1.0.0' ); 2136 $register_and_enqueue( 'l', '/l.js', array( 'k' ), '1.0.0' ); 2137 $register_and_enqueue( 'j', '/j.js', array(), '1.0.0' ); 2138 2139 $actual = array( 2140 'preload_links' => $this->get_preloaded_script_modules(), 2141 'script_tags' => $this->get_enqueued_script_modules(), 2142 'import_map' => $this->get_import_map(), 2143 ); 2144 2145 $this->assertSame( 2146 array( 2147 'preload_links' => array(), 2148 'script_tags' => array( 2149 'j' => array( 2150 'url' => '/j.js?ver=1.0.0', 2151 'fetchpriority' => 'auto', 2152 'in_footer' => false, 2153 ), 2154 'k' => array( 2155 'url' => '/k.js?ver=1.0.0', 2156 'fetchpriority' => 'auto', 2157 'in_footer' => false, 2158 ), 2159 'l' => array( 2160 'url' => '/l.js?ver=1.0.0', 2161 'fetchpriority' => 'auto', 2162 'in_footer' => false, 2163 ), 2164 'm' => array( 2165 'url' => '/m.js?ver=1.0.0', 2166 'fetchpriority' => 'auto', 2167 'in_footer' => false, 2168 ), 2169 ), 2170 'import_map' => array( 2171 'j' => '/j.js?ver=1.0.0', 2172 'l' => '/l.js?ver=1.0.0', 2173 'k' => '/k.js?ver=1.0.0', 2174 ), 2175 ), 2176 $actual, 2177 "Snapshot:\n" . var_export( $actual, true ) 2178 ); 2179 2180 $deregister( array( 'j', 'k', 'l', 'm' ) ); 2181 2182 // Test dependency ordering when scripts modules are enqueued in both head and footer. 2183 // Expected order: q, n, o, p, r. 2184 $register_and_enqueue( 'n', '/n.js', array( 'q' ), '1.0.0' ); 2185 $register_and_enqueue( 'q', '/q.js', array(), '1.0.0' ); 2186 $register_and_enqueue( 'o', '/o.js', array( 'n' ), '1.0.0', array( 'in_footer' => true ) ); 2187 $register_and_enqueue( 'r', '/r.js', array( 'q', 'o', 'p' ), '1.0.0' ); 2188 $register_and_enqueue( 'p', '/p.js', array(), '1.0.0', array( 'in_footer' => true ) ); 2189 2190 $actual = array( 2191 'preload_links' => $this->get_preloaded_script_modules(), 2192 'script_tags' => $this->get_enqueued_script_modules(), 2193 'import_map' => $this->get_import_map(), 2194 ); 2195 2196 $this->assertSame( 2197 array( 2198 'preload_links' => array(), 2199 'script_tags' => array( 2200 'q' => array( 2201 'url' => '/q.js?ver=1.0.0', 2202 'fetchpriority' => 'auto', 2203 'in_footer' => false, 2204 ), 2205 'n' => array( 2206 'url' => '/n.js?ver=1.0.0', 2207 'fetchpriority' => 'auto', 2208 'in_footer' => false, 2209 ), 2210 'o' => array( 2211 'url' => '/o.js?ver=1.0.0', 2212 'fetchpriority' => 'auto', 2213 'in_footer' => true, 2214 ), 2215 'p' => array( 2216 'url' => '/p.js?ver=1.0.0', 2217 'fetchpriority' => 'auto', 2218 'in_footer' => true, 2219 ), 2220 'r' => array( 2221 'url' => '/r.js?ver=1.0.0', 2222 'fetchpriority' => 'auto', 2223 'in_footer' => true, 2224 ), 2225 ), 2226 'import_map' => array( 2227 'q' => '/q.js?ver=1.0.0', 2228 'n' => '/n.js?ver=1.0.0', 2229 'o' => '/o.js?ver=1.0.0', 2230 'p' => '/p.js?ver=1.0.0', 2231 ), 2232 ), 2233 $actual, 2234 "Snapshot:\n" . var_export( $actual, true ) 2235 ); 2236 } 2237 2238 /** 2239 * Tests various ways of printing and dependency ordering of script modules. 2240 * 2241 * @ticket 63486 2242 * 2243 * @dataProvider data_test_register_and_enqueue_script_module 2244 * 2245 * @covers ::wp_register_script_module() 2246 * @covers WP_Script_Modules::register() 2247 * @covers ::wp_enqueue_script_module() 2248 * @covers WP_Script_Modules::enqueue() 2249 * @covers ::wp_dequeue_script_module() 2250 * @covers WP_Script_Modules::dequeue() 2251 * @covers ::wp_deregister_script_module() 2252 * @covers WP_Script_Modules::deregister() 2253 * @covers WP_Script_Modules::set_fetchpriority() 2254 * @covers WP_Script_Modules::print_head_enqueued_script_modules() 2255 * @covers WP_Script_Modules::print_enqueued_script_modules() 2256 * @covers WP_Script_Modules::print_import_map() 2257 * @covers WP_Script_Modules::print_script_module_preloads() 2258 */ 2259 public function test_static_import_dependency_with_dynamic_imports_depending_on_static_import_dependency() { 2260 $get_dependency = function ( string $id, string $import ): array { 2261 return compact( 'id', 'import' ); 2262 }; 2263 2264 wp_register_script_module( 'enqueued', '/enqueued.js', array( $get_dependency( 'static1', 'static' ) ), null ); 2265 wp_register_script_module( 'static1', '/static1.js', array( $get_dependency( 'dynamic1', 'dynamic' ) ), null ); 2266 wp_register_script_module( 'dynamic1', '/dynamic1.js', array( $get_dependency( 'static2', 'static' ) ), null ); 2267 wp_register_script_module( 'static2', '/static2.js', array(), null ); 2268 2269 wp_enqueue_script_module( 'enqueued' ); 2270 $import_map = $this->get_import_map(); 2271 $preload_links = get_echo( array( wp_script_modules(), 'print_script_module_preloads' ) ); 2272 $script_modules = get_echo( array( wp_script_modules(), 'print_enqueued_script_modules' ) ); 2273 2274 $this->assertEquals( 2275 array( 2276 'static1' => '/static1.js', 2277 'dynamic1' => '/dynamic1.js', 2278 'static2' => '/static2.js', 2279 ), 2280 $import_map, 2281 "Expected import map to match snapshot:\n" . var_export( $import_map, true ) 2282 ); 2283 $this->assertEqualHTML( 2284 ' 2285 <link rel="modulepreload" href="https://core.trac.wordpress.org/static1.js" id="static1-js-modulepreload"> 2286 ', 2287 $preload_links, 2288 '<body>', 2289 "Expected preload links to match snapshot:\n$preload_links" 2290 ); 2291 $this->assertEqualHTML( 2292 ' 2293 <script type="module" src="https://core.trac.wordpress.org/enqueued.js" id="enqueued-js-module"></script> 2294 ', 2295 $script_modules, 2296 '<body>', 2297 "Expected script modules to match snapshot:\n$script_modules" 2298 ); 2299 } 1733 2300 }
Note: See TracChangeset
for help on using the changeset viewer.