Make WordPress Core

Opened 17 months ago

Last modified 8 weeks ago

#62709 new enhancement

Script modules integration with wp_resource_hints

Reported by: jonsurrell's profile jonsurrell Owned by:
Milestone: 7.1 Priority: normal
Severity: minor Version: 6.5
Component: Script Loader Keywords: has-patch has-unit-tests
Focuses: javascript, performance Cc:

Description

The wp_dependencies_unique_hosts() function returns a list of all the unique hosts that appear in script and style dependencies.

That data is used by the wp_resource_hints() function to print DNS <link rel="dns-prefetch" href="{{ HOST_NAME }}"> tags, a performance hint used by browsers.

wp_dependencies_unique_hosts does not inspect script modules at all. The result is that performance hints are not printed for script modules.

Script modules do handle some of their own performance hints, in particular rel=modulepreload link tags are printed via WP_Script_Modules::print_script_module_preloads(), but script modules miss out on this broader handling of dependencies via wp_resource_hints for dns-prefetch.

This is likely blocked on #60597, it requires exposing information about script modules for inspection.

Change History (8)

#1 @swissspidy
17 months ago

To clarify, you want to add dns-prefetch for script modules in addition to modulepreload? If you already preload there is no need to prefetch though.

#2 @jonsurrell
17 months ago

  • Severity changed from normal to minor

modulepreload is only added for static module dependencies.

Modules dependencies like the following will not have a modulepreload added (assuming it's not a static import anywhere else in the dependency graph):

$deps = array(
  array( 'id' => 'my-module', 'import' => 'dynamic' ),
);

Dynamic dependencies would still benefit from dns-prefetch.

These dns-prefetches are also added early to the page. On non-block themes, all of the script module tags are printed in the footer so there may also be an opportunity for dns-prefetch to happen before any of the module related tags are processed.

#3 @westonruter
5 months ago

  • Milestone changed from Awaiting Review to 7.0

This ticket was mentioned in Slack in #core-performance by westonruter. View the logs.


3 months ago

#5 @westonruter
3 months ago

  • Keywords needs-patch added
  • Milestone changed from 7.0 to Future Release

#6 @westonruter
3 months ago

  • Milestone changed from Future Release to 7.1

#7 @sanket.parmar
2 months ago

Thanks for filing this — the gap is clear. Here are some thoughts on a possible approach.

The Core Issue

wp_dependencies_unique_hosts() currently only loops over $wp_scripts and $wp_styles:

foreach ( array( $wp_scripts, $wp_styles ) as $dependencies ) {
    // ...
}

WP_Script_Modules is a completely separate system and is never consulted, so no dns-prefetch hints are emitted for external hosts used by script modules.

Possible Approaches

Option A: Hook into wp_resource_hints filter from within WP_Script_Modules

One approach could be to add a new public method to WP_Script_Modules — something like filter_resource_hints( array $urls, string $relation_type ): array — and register it from within add_hooks():

add_filter( 'wp_resource_hints', array( $this, 'filter_resource_hints' ), 10, 2 );

The method would:

  • Only act when $relation_type === 'dns-prefetch'
  • Walk the queue and all dependencies (both static and dynamic)
  • Parse the host from each src URL, filtering out same-site hosts
  • Merge the resulting hosts into $urls and return

This keeps all the logic inside WP_Script_Modules and avoids changing the signature or behavior of wp_dependencies_unique_hosts().

Option B: Extend wp_dependencies_unique_hosts() directly

Alternatively, wp_dependencies_unique_hosts() could be extended to also inspect $wp_script_modules. This would require exposing a getter on WP_Script_Modules (e.g. get_registered_srcs() or similar), which may partly overlap with the work in #60597.

A Note on Redundancy

As @swissspidy raised — print_script_module_preloads() already emits rel="modulepreload" for static dependencies, and modulepreload implies a full connection (DNS + TCP + TLS). Adding dns-prefetch on top of those may be redundant.

That said, two cases still seem clearly worth targeting:

  1. Dynamic dependencies — declared as 'import' => 'dynamic', these are never preloaded and currently receive no performance hint at all.
  2. Classic themeswp_resource_hints fires in wp_head at priority 2, while all module-related tags fire in wp_footer on classic themes. Even for static deps, an early dns-prefetch in the head could give the browser a head start before any module tag is encountered.

Option A could optionally be scoped to only emit dns-prefetch for dynamic-dependency hosts (skipping those already covered by modulepreload), though that could also be left as a follow-up to keep the initial patch simpler.

Tests

Any patch would likely need companion PHPUnit tests, probably alongside tests/phpunit/tests/general/wpResourceHints.php, covering:

  • Dynamic-dependency hosts appear in dns-prefetch
  • Static-dependency hosts (already covered by modulepreload) are either included or explicitly excluded, depending on the chosen design
  • Same-site hosts are not included

Happy to help put together a patch if this direction looks reasonable.

This ticket was mentioned in PR #11374 on WordPress/wordpress-develop by @sanket.parmar.


8 weeks ago
#8

  • Keywords has-patch has-unit-tests added; needs-patch removed

## Description

`wp_resource_hints()` prints <link rel="dns-prefetch"> tags for all external hosts used by enqueued scripts and styles, giving browsers an early signal to resolve DNS before requests are made. However, WP_Script_Modules was never wired into this system, so external hosts referenced by script modules received no DNS prefetch hints.

This PR addresses #62709 by adding a new public method WP_Script_Modules::filter_resource_hints(), registered on the wp_resource_hints filter inside add_hooks(). The method collects all enqueued module IDs and their full dependency tree — including dynamic dependencies, which never receive modulepreload hints and are therefore the clearest gap — parses each src for its host, filters out same-site hosts, and appends the external URLs to the dns-prefetch hints array. Deduplication by host is handled downstream by wp_resource_hints() itself, as it does for scripts and styles.

On classic themes, wp_resource_hints fires in wp_head at priority 2, while all module-related tags fire in wp_footer. This means even static dependency hosts will benefit from an early hint in the page <head> well before any module tag is processed.

## Changes

  • src/wp-includes/class-wp-script-modules.php
    • Added filter_resource_hints( array $urls, string $relation_type ): array public method.
    • Registered the filter in add_hooks() via wp_resource_hints.
  • tests/phpunit/tests/script-modules/wpScriptModulesResourceHints.php _(new file)_
    • 9 tests covering: enqueued modules, static dependencies, dynamic dependencies, same-site exclusion, relative-path exclusion, registered-only exclusion, non-dns-prefetch relation types left unmodified, host deduplication, and full wp_resource_hints() integration.

## How to test

  1. Enqueue a script module with an external src, e.g.: `php wp_enqueue_script_module( 'my-module', 'https://cdn.example.com/module.js' );

---

Trac ticket: https://core.trac.wordpress.org/ticket/62709

## Use of AI Tools

Note: See TracTickets for help on using tickets.