Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b3809c8
Preload the admin's unconcatenated assets from the login screen
westonruter Aug 16, 2026
8789351
Prefetch rather than preload the admin's assets from the login screen
westonruter Aug 16, 2026
879ce15
Drop the redundant fetchpriority attribute from the admin prefetch links
westonruter Aug 16, 2026
98b2ee1
Only prefetch admin assets when the login actually leads to the admin
westonruter Aug 16, 2026
0db2f11
Pass the login redirect target to the admin asset prefetch filter
westonruter Aug 16, 2026
fa3677e
Prefetch the editor's stylesheets from the screens that lead to it
westonruter Aug 17, 2026
cefa78d
Type the handle expander against WP_Dependencies
westonruter Aug 18, 2026
814fcd7
Keep empty handles out of the expanded dependency set
westonruter Aug 18, 2026
6ad32b5
Simplify cap check
westonruter Aug 18, 2026
d80705e
Model the prefetch resource list on wp_preload_resources()
westonruter Aug 19, 2026
c6c18bb
Stop implying the prefetch filter only takes scripts and styles
westonruter Aug 19, 2026
a12640f
Carry each registry alongside the handles it registered
westonruter Aug 19, 2026
cec3373
Stop naming the login screen where any screen is meant
westonruter Aug 19, 2026
a6ab6ba
Inline the block editor check into its only caller
westonruter Aug 19, 2026
7a646ea
Remove default priority from add_action()
westonruter Aug 19, 2026
8fea842
Say why the URL resolver names two registries and not their parent
westonruter Aug 19, 2026
87e9460
Merge branch 'trunk' into add/admin-script-style-preloading
westonruter Sep 26, 2026
274eb55
Stop claiming prefetch reuse is independent of cache headers
westonruter Sep 26, 2026
9818520
Prefetch only what blocks the admin's first paint from the login screen
westonruter Sep 26, 2026
45e7e8a
Allow far-future caching of static assets in the local environment
westonruter Sep 26, 2026
90d764f
Merge branch 'trunk' of https://github.com/WordPress/wordpress-develo…
westonruter Sep 26, 2026
81f72fd
Derive the login prefetch set from root handles
westonruter Sep 27, 2026
8a36ca8
Share URL building between do_item() and the admin prefetch
westonruter Sep 27, 2026
176c7e4
Add tests for WP_Scripts::get_src() and WP_Styles::get_rtl_href()
westonruter Sep 27, 2026
a01bb6b
Note where the login prefetch roots are enqueued
westonruter Sep 27, 2026
9b63a14
Drop the redundant wp-pointer prefetch root
westonruter Sep 27, 2026
debcf11
Drop the redundant editor prefetch roots
westonruter Sep 27, 2026
68844a5
Note where the editor prefetch roots are enqueued
westonruter Sep 27, 2026
6893636
Add tests for wp_prefetch_admin_assets()
westonruter Sep 27, 2026
35b06f6
Enhance comment on resource attributes in script-loader
westonruter Sep 27, 2026
4524a2d
Fix Copilot's broken indent in 35b06f6479c42caa164f7c269457da5d6313b6a9
westonruter Sep 27, 2026
6368d7a
Clarify when the prefetch URLs may not match the admin's
westonruter Sep 27, 2026
2b11401
Use the login action wp-login.php resolved
westonruter Sep 27, 2026
4e14337
Skip the login prefetch when redirecting to another host
westonruter Sep 27, 2026
30703db
Derive the prefetch tests' URLs from the site's own
westonruter Sep 27, 2026
d4f6bf8
Test that network and user Dashboards prefetch nothing
westonruter Sep 27, 2026
d40ea0f
Pass stylesheet URLs to the prefetch filter unescaped
westonruter Sep 27, 2026
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
4 changes: 4 additions & 0 deletions src/wp-admin/admin-header.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@
<title><?php echo esc_html( $admin_title ); ?></title>
<?php

/*
* The dependencies of `colors` and the `utils` script are prefetched from the login screen by
* wp_prefetch_admin_assets(), which needs updating if these change.
*/
wp_enqueue_style( 'colors' );
wp_enqueue_script( 'utils' );
wp_enqueue_script( 'svg-painter' );
Expand Down
4 changes: 4 additions & 0 deletions src/wp-admin/admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@
$date_format = __( 'F j, Y' );
$time_format = __( 'g:i a' );

/*
* The `jquery` dependency of this script is prefetched from the login screen by
* wp_prefetch_admin_assets(), which needs updating if this changes.
*/
wp_enqueue_script( 'common' );

/**
Expand Down
1 change: 1 addition & 0 deletions src/wp-admin/edit-form-blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ static function ( $classes ) {
/**
* Styles
*/
// Prefetched for the block editor by wp_prefetch_admin_assets(), which needs updating if this changes.
wp_enqueue_style( 'wp-edit-post' );

/**
Expand Down
1 change: 1 addition & 0 deletions src/wp-includes/class-wp-admin-bar.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public function initialize() {
add_action( 'wp_head', $header_callback );

wp_enqueue_script( 'admin-bar' );
// Prefetched from the login screen by wp_prefetch_admin_assets(), which needs updating if this changes.
wp_enqueue_style( 'admin-bar' );

/**
Expand Down
5 changes: 4 additions & 1 deletion src/wp-includes/class-wp-editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,10 @@ public static function enqueue_default_editor() {

self::enqueue_scripts( true );

// Also add wp-includes/css/editor.css.
/*
* Also add wp-includes/css/editor.css. It is prefetched for the block editor by
* wp_prefetch_admin_assets(), which needs updating if this changes.
*/
wp_enqueue_style( 'editor-buttons' );

if ( is_admin() ) {
Expand Down
95 changes: 62 additions & 33 deletions src/wp-includes/class-wp-scripts.php
Original file line number Diff line number Diff line change
Expand Up @@ -415,39 +415,7 @@ public function do_item( $handle, $group = false ) {
return true;
}

if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $this->content_url && str_starts_with( $src, $this->content_url ) ) ) {
$src = $this->base_url . $src;
}

$ver_to_add = '';
if ( empty( $obj->ver ) && null !== $obj->ver && is_string( $this->default_version ) ) {
$ver_to_add = $this->default_version;
} elseif ( is_scalar( $obj->ver ) ) {
$ver_to_add = (string) $obj->ver;
}

$added_args = (string) ( $this->args[ $handle ] ?? '' );

if ( '' !== $ver_to_add || '' !== $added_args ) {
$fragment = strstr( $src, '#' );
if ( false !== $fragment ) {
$src = substr( $src, 0, -strlen( $fragment ) );
}

if ( '' !== $ver_to_add ) {
$src .= ( str_contains( $src, '?' ) ? '&' : '?' ) . 'ver=' . rawurlencode( $ver_to_add );
}
if ( '' !== $added_args ) {
$src .= ( str_contains( $src, '?' ) ? '&' : '?' ) . $added_args;
}

if ( false !== $fragment ) {
$src .= $fragment;
}
}

/** This filter is documented in wp-includes/class-wp-scripts.php */
$src = esc_url_raw( apply_filters( 'script_loader_src', $src, $handle ) );
$src = $this->get_src( $handle );

if ( ! $src ) {
return true;
Expand Down Expand Up @@ -506,6 +474,67 @@ public function do_item( $handle, $group = false ) {
return true;
}

/**
* Gets the URL a registered script is loaded from.
*
* This is the URL printed in the script's `src` attribute, including the version query
* argument and any arguments added to the handle, after the {@see 'script_loader_src'} filter.
*
* @since 7.2.0
*
* @param string $handle Script handle.
* @return string Script URL, or an empty string when the script is not registered, has no
* source of its own because it only aliases other scripts, or was filtered away.
*/
public function get_src( string $handle ): string {
if ( ! isset( $this->registered[ $handle ] ) ) {
return '';
}

$obj = $this->registered[ $handle ];
$src = $obj->src;

if ( ! $src || ! is_string( $src ) ) {
return '';
}

if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $this->content_url && str_starts_with( $src, $this->content_url ) ) ) {
$src = $this->base_url . $src;
}

$ver_to_add = '';
if ( empty( $obj->ver ) && null !== $obj->ver && is_string( $this->default_version ) ) {
$ver_to_add = $this->default_version;
} elseif ( is_scalar( $obj->ver ) ) {
$ver_to_add = (string) $obj->ver;
}

$added_args = (string) ( $this->args[ $handle ] ?? '' );

if ( '' !== $ver_to_add || '' !== $added_args ) {
$fragment = strstr( $src, '#' );
if ( false !== $fragment ) {
$src = substr( $src, 0, -strlen( $fragment ) );
}

if ( '' !== $ver_to_add ) {
$src .= ( str_contains( $src, '?' ) ? '&' : '?' ) . 'ver=' . rawurlencode( $ver_to_add );
}
if ( '' !== $added_args ) {
$src .= ( str_contains( $src, '?' ) ? '&' : '?' ) . $added_args;
}

if ( false !== $fragment ) {
$src .= $fragment;
}
}

/** This filter is documented in wp-includes/class-wp-scripts.php */
$src = apply_filters( 'script_loader_src', $src, $handle );

return is_string( $src ) ? esc_url_raw( $src ) : '';
}

/**
* Adds extra code to a registered script.
*
Expand Down
57 changes: 50 additions & 7 deletions src/wp-includes/class-wp-styles.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,9 @@ public function do_item( $handle, $group = false ) {
*/
$tag = apply_filters( 'style_loader_tag', $tag, $handle, $href, $media );

if ( 'rtl' === $this->text_direction && isset( $obj->extra['rtl'] ) && $obj->extra['rtl'] ) {
if ( is_bool( $obj->extra['rtl'] ) || 'replace' === $obj->extra['rtl'] ) {
$suffix = $obj->extra['suffix'] ?? '';
$rtl_href = str_replace( "{$suffix}.css", "-rtl{$suffix}.css", $this->_css_href( $src, $ver, "$handle-rtl" ) );
} else {
$rtl_href = $this->_css_href( $obj->extra['rtl'], $ver, "$handle-rtl" );
}
$rtl_href = $this->get_rtl_href( $handle );

if ( null !== $rtl_href ) {
$rtl_tag = sprintf(
"<link rel='%s' id='%s-rtl-css'%s href='%s' media='%s' />\n",
$rel,
Expand Down Expand Up @@ -264,6 +259,54 @@ public function do_item( $handle, $group = false ) {
return true;
}

/**
* Gets the URL of the right-to-left stylesheet for a registered style.
*
* On a right-to-left locale, a style registered with `rtl` data is served by a separate
* stylesheet, which either replaces its left-to-right one (when the data is `'replace'`)
* or loads alongside it.
*
* @since 7.2.0
*
* @param string $handle The style's registered handle.
* @return string|null URL of the right-to-left stylesheet, after the {@see 'style_loader_src'}
* filter. Null when the text direction is not right-to-left, or the style is
* not registered, has no source of its own, or has no right-to-left variant.
*/
public function get_rtl_href( string $handle ): ?string {
if ( 'rtl' !== $this->text_direction || ! isset( $this->registered[ $handle ] ) ) {
return null;
}

$obj = $this->registered[ $handle ];

if ( ! $obj->src || ! isset( $obj->extra['rtl'] ) || ! $obj->extra['rtl'] ) {
return null;
}

if ( null === $obj->ver ) {
$ver = '';
} else {
$ver = $obj->ver ? $obj->ver : $this->default_version;
}

if ( isset( $this->args[ $handle ] ) ) {
$ver = $ver ? $ver . '&amp;' . $this->args[ $handle ] : $this->args[ $handle ];
}

if ( is_bool( $obj->extra['rtl'] ) || 'replace' === $obj->extra['rtl'] ) {
$suffix = isset( $obj->extra['suffix'] ) && is_string( $obj->extra['suffix'] ) ? $obj->extra['suffix'] : '';
return str_replace( "{$suffix}.css", "-rtl{$suffix}.css", $this->_css_href( $obj->src, $ver, "$handle-rtl" ) );
}

// Any other value is the URL of the right-to-left stylesheet itself.
if ( ! is_string( $obj->extra['rtl'] ) ) {
return null;
}

return $this->_css_href( $obj->extra['rtl'], $ver, "$handle-rtl" );
}

/**
* Adds extra CSS styles to a registered stylesheet.
*
Expand Down
2 changes: 2 additions & 0 deletions src/wp-includes/default-filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,8 @@
add_action( 'login_head', 'wp_resource_hints', 8 );
add_action( 'login_head', 'wp_print_head_scripts', 9 );
add_action( 'login_head', 'print_admin_styles', 9 );
add_action( 'login_head', 'wp_prefetch_admin_assets' );
add_action( 'admin_head', 'wp_prefetch_admin_assets' );
add_action( 'login_head', 'wp_site_icon', 99 );
add_action( 'login_footer', 'wp_print_footer_scripts', 20 );
add_action( 'login_init', 'send_frame_options_header', 10, 0 );
Expand Down
1 change: 1 addition & 0 deletions src/wp-includes/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -7664,6 +7664,7 @@ function wp_auth_check_load() {
* @param WP_Screen $screen The current screen object.
*/
if ( apply_filters( 'wp_auth_check_load', $show, $screen ) ) {
// Prefetched from the login screen by wp_prefetch_admin_assets(), which needs updating if this changes.
wp_enqueue_style( 'wp-auth-check' );
wp_enqueue_script( 'wp-auth-check' );

Expand Down
2 changes: 2 additions & 0 deletions src/wp-includes/media.php
Original file line number Diff line number Diff line change
Expand Up @@ -5305,11 +5305,13 @@ function wp_enqueue_media( $args = array() ) {
wp_localize_script( 'media-views', '_wpMediaViewsL10n', $strings );

wp_enqueue_script( 'media-audiovideo' );
// Prefetched for the block editor by wp_prefetch_admin_assets(), which needs updating if this changes.
wp_enqueue_style( 'media-views' );
if ( is_admin() ) {
wp_enqueue_script( 'mce-view' );
wp_enqueue_script( 'image-edit' );
}
// Prefetched for the block editor by wp_prefetch_admin_assets(), which needs updating if this changes.
wp_enqueue_style( 'imgareaselect' );
wp_plupload_default_settings();

Expand Down
Loading
Loading