Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
dbcd32c
Output script modules with fetchpriority=low
westonruter May 19, 2025
5207c27
Fix duplicate array keys in data_special_chars_script_encoding
westonruter May 19, 2025
088366d
Add ability to set fetch priority for script modules
westonruter May 19, 2025
9eab85d
Add fetchpriority support for non-module scripts
westonruter May 19, 2025
d9c4037
Set fetchpriority=low on comment-reply script
westonruter May 19, 2025
4f82a4f
Use auto as default fetchpriority for script modules
westonruter May 19, 2025
f2cd9be
Avoid printing fetchpriority attribute when auto
westonruter May 19, 2025
ff4fd36
fixup! Use auto as default fetchpriority for script modules
westonruter May 19, 2025
9edbe4c
Use HTML Tag Processor to parse import map
westonruter May 19, 2025
021fe74
Ensure parity in args between class methods and global function aliases
westonruter May 19, 2025
34ef7fa
Use fetch priority low by default for Interactivity API view script m…
westonruter May 20, 2025
e54274a
Add missing since tag
westonruter May 21, 2025
cc1d909
Account for full block.json schema when checking for interactivity
westonruter May 21, 2025
4197eb2
Merge branch 'trunk' of https://github.com/WordPress/wordpress-develo…
westonruter Jun 20, 2025
74a49e1
Remove PHPStan annotations for commit
westonruter Jun 20, 2025
75fb879
Merge branch 'trunk' of https://github.com/WordPress/wordpress-develo…
westonruter Jul 29, 2025
0e9994a
Merge branch 'trunk' of https://github.com/WordPress/wordpress-develo…
westonruter Aug 26, 2025
06e9cac
Remove TODO comments which have been filed in https://github.com/Word…
westonruter Aug 26, 2025
f91c61d
Merge branch 'trunk' of https://github.com/WordPress/wordpress-develo…
westonruter Sep 1, 2025
4596644
Add validation for fetchpriority set on scripts
westonruter Sep 2, 2025
2172e9e
Allow empty string for set_fetchpriority(), return bool, and add tests
westonruter Sep 2, 2025
535669d
Add missing type for args param
westonruter Sep 2, 2025
fe366e2
Improve typing for is_delayed_stragegy
westonruter Sep 2, 2025
f704f5e
Merge branch 'trunk' of https://github.com/WordPress/wordpress-develo…
westonruter Sep 2, 2025
38150dc
Replace n.e.x.t with 6.9.0
westonruter Sep 2, 2025
f05ca72
Ensure reflection property is accessible
westonruter Sep 2, 2025
607ce0c
Use static data provider methods
westonruter Sep 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add fetchpriority support for non-module scripts
  • Loading branch information
westonruter committed May 19, 2025
commit 9eab85dc4b0c2d6dbf13356d38bb5f9ea248ad4a
3 changes: 3 additions & 0 deletions src/wp-includes/class-wp-scripts.php
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,9 @@ public function do_item( $handle, $group = false ) {
if ( $intended_strategy ) {
$attr['data-wp-strategy'] = $intended_strategy;
}
if ( isset( $obj->extra['fetchpriority'] ) && 'auto' !== $obj->extra['fetchpriority'] ) {
$attr['fetchpriority'] = $obj->extra['fetchpriority'];
}
$tag = $translations . $ie_conditional_prefix . $before_script;
$tag .= wp_get_script_tag( $attr );
$tag .= $after_script . $ie_conditional_suffix;
Expand Down
16 changes: 12 additions & 4 deletions src/wp-includes/functions.wp-scripts.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,9 @@ function wp_add_inline_script( $handle, $data, $position = 'after' ) {
* Optional. An array of additional script loading strategies. Default empty array.
* Otherwise, it may be a boolean in which case it determines whether the script is printed in the footer. Default false.
*
* @type string $strategy Optional. If provided, may be either 'defer' or 'async'.
* @type bool $in_footer Optional. Whether to print the script in the footer. Default 'false'.
* @type string $strategy Optional. If provided, may be either 'defer' or 'async'.
* @type bool $in_footer Optional. Whether to print the script in the footer. Default 'false'.
* @type string $fetchpriority Optional. The fetch priority for the script. Default 'auto'.
* }
* @return bool Whether the script has been registered. True on success, false on failure.
*/
Expand All @@ -193,6 +194,9 @@ function wp_register_script( $handle, $src, $deps = array(), $ver = false, $args
if ( ! empty( $args['strategy'] ) ) {
$wp_scripts->add_data( $handle, 'strategy', $args['strategy'] );
}
if ( ! empty( $args['fetchpriority'] ) ) {
$wp_scripts->add_data( $handle, 'fetchpriority', $args['fetchpriority'] );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fetchpriority is checked against an allowlist for modules. I don't see that for scripts. Did I overlook that or should it be included?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sirreal In the case of classic scripts, the fetchpriority is instead being specified when the scripts are registered. The only script which gets low is comment-reply:

$scripts->add_data( 'comment-reply', 'fetchpriority', 'low' ); // In Chrome this is automatically low due to the async strategy, but in Firefox and Safari the priority is normal/medium.

There isn't any allowlist to default to low for non-module scripts since there is no generalizable pattern for being able to opt in like there is for script modules with the Interactivity API.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More than default I'm wondering about accepting arbitrary values. It's not likely harmful, but perhaps _doing_it_wrong could be shown and the value discarded if an unsupported fetchpriority is provided.

I understand it's a well defined set of values: high | low | auto.

For example:

wp_enqueue_script( 'example', '/ex.js', array(), '' );
wp_scripts()->add_data( 'example', 'fetchpriority', 'silly' );

produces

<script src="/ex.js?ver=…" id="example-js" fetchpriority="silly"></script>

which is… silly 🙂

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. For async/defer, this is handled in the add_data() function:

if ( 'strategy' === $key ) {
if ( ! empty( $value ) && ! $this->is_delayed_strategy( $value ) ) {
_doing_it_wrong(
__METHOD__,
sprintf(
/* translators: 1: $strategy, 2: $handle */
__( 'Invalid strategy `%1$s` defined for `%2$s` during script registration.' ),
$value,
$handle
),
'6.3.0'
);
return false;
} elseif ( ! $this->registered[ $handle ]->src && $this->is_delayed_strategy( $value ) ) {
_doing_it_wrong(
__METHOD__,
sprintf(
/* translators: 1: $strategy, 2: $handle */
__( 'Cannot supply a strategy `%1$s` for script `%2$s` because it is an alias (it lacks a `src` value).' ),
$value,
$handle
),
'6.3.0'
);
return false;
}
}

This would make sense to include there as well for fetchpriority.

}
return $registered;
}

Expand Down Expand Up @@ -352,8 +356,9 @@ function wp_deregister_script( $handle ) {
* Optional. An array of additional script loading strategies. Default empty array.
* Otherwise, it may be a boolean in which case it determines whether the script is printed in the footer. Default false.
*
* @type string $strategy Optional. If provided, may be either 'defer' or 'async'.
* @type bool $in_footer Optional. Whether to print the script in the footer. Default 'false'.
* @type string $strategy Optional. If provided, may be either 'defer' or 'async'.
* @type bool $in_footer Optional. Whether to print the script in the footer. Default 'false'.
* @type string $fetchpriority Optional. The fetch priority for the script. Default 'auto'.
* }
*/
function wp_enqueue_script( $handle, $src = '', $deps = array(), $ver = false, $args = array() ) {
Expand All @@ -378,6 +383,9 @@ function wp_enqueue_script( $handle, $src = '', $deps = array(), $ver = false, $
if ( ! empty( $args['strategy'] ) ) {
$wp_scripts->add_data( $_handle[0], 'strategy', $args['strategy'] );
}
if ( ! empty( $args['fetchpriority'] ) ) {
$wp_scripts->add_data( $_handle[0], 'fetchpriority', $args['fetchpriority'] );
}
}

$wp_scripts->enqueue( $handle );
Expand Down
43 changes: 37 additions & 6 deletions tests/phpunit/tests/dependencies/scripts.php
Original file line number Diff line number Diff line change
Expand Up @@ -1023,23 +1023,54 @@ public function test_loading_strategy_with_all_defer_dependencies() {
/**
* Tests that dependents that are async but attached to a deferred main script, print with defer as opposed to async.
*
* Also tests that fetchpriority attributes are added as expected.
*
* @ticket 12009
* @ticket 61734
*
* @covers WP_Scripts::do_item
* @covers WP_Scripts::get_eligible_loading_strategy
* @covers ::wp_register_script
* @covers ::wp_enqueue_script
*/
public function test_defer_with_async_dependent() {
// case with one async dependent.
wp_enqueue_script( 'main-script-d4', '/main-script-d4.js', array(), null, array( 'strategy' => 'defer' ) );
wp_enqueue_script( 'dependent-script-d4-1', '/dependent-script-d4-1.js', array( 'main-script-d4' ), null, array( 'strategy' => 'defer' ) );
wp_enqueue_script( 'dependent-script-d4-2', '/dependent-script-d4-2.js', array( 'dependent-script-d4-1' ), null, array( 'strategy' => 'async' ) );
wp_enqueue_script( 'dependent-script-d4-3', '/dependent-script-d4-3.js', array( 'dependent-script-d4-2' ), null, array( 'strategy' => 'defer' ) );
wp_register_script( 'main-script-d4', '/main-script-d4.js', array(), null, array( 'strategy' => 'defer' ) );
wp_enqueue_script(
'dependent-script-d4-1',
'/dependent-script-d4-1.js',
array( 'main-script-d4' ),
null,
array(
'strategy' => 'defer',
'fetchpriority' => 'auto',
)
);
wp_enqueue_script(
'dependent-script-d4-2',
'/dependent-script-d4-2.js',
array( 'dependent-script-d4-1' ),
null,
array(
'strategy' => 'async',
'fetchpriority' => 'low',
)
);
wp_enqueue_script(
'dependent-script-d4-3',
'/dependent-script-d4-3.js',
array( 'dependent-script-d4-2' ),
null,
array(
'strategy' => 'defer',
'fetchpriority' => 'high',
)
);
$output = get_echo( 'wp_print_scripts' );
$expected = "<script type='text/javascript' src='/main-script-d4.js' id='main-script-d4-js' defer data-wp-strategy='defer'></script>\n";
$expected .= "<script type='text/javascript' src='/dependent-script-d4-1.js' id='dependent-script-d4-1-js' defer data-wp-strategy='defer'></script>\n";
$expected .= "<script type='text/javascript' src='/dependent-script-d4-2.js' id='dependent-script-d4-2-js' defer data-wp-strategy='async'></script>\n";
$expected .= "<script type='text/javascript' src='/dependent-script-d4-3.js' id='dependent-script-d4-3-js' defer data-wp-strategy='defer'></script>\n";
$expected .= "<script type='text/javascript' src='/dependent-script-d4-2.js' id='dependent-script-d4-2-js' defer data-wp-strategy='async' fetchpriority='low'></script>\n";
$expected .= "<script type='text/javascript' src='/dependent-script-d4-3.js' id='dependent-script-d4-3-js' defer data-wp-strategy='defer' fetchpriority='high'></script>\n";

$this->assertEqualMarkup( $expected, $output, 'Scripts registered as defer but that have dependents that are async are expected to have said dependents deferred.' );
}
Expand Down