| @@ -82,16 +82,17 @@ | ||
| 82 | 82 | // Leave scripts the delay-JS module has already rewritten alone. |
| 83 | 83 | if ( false !== strpos( $tag, 'ablocks/delayed' ) ) { |
| 84 | 84 | return $tag; |
| 85 | 85 | } |
| 86 | - // Never defer a script that carries inline before/after data. WordPress | |
| 87 | - // prints that inline as a plain (non-deferred) <script> right beside the | |
| 88 | - // tag, so it executes during parse — before the deferred external file | |
| 89 | - // runs. The canonical break is `wp-i18n`: its `wp-i18n-js-after` inline | |
| 90 | - // calls `wp.i18n.setLocaleData()` before the deferred i18n.js defines | |
| 91 | - // `wp.i18n`, throwing and taking every downstream `__()` call (checkout, | |
| 92 | - // storefront bundles) down with it. | |
| 93 | - if ( $this->has_inline_data( $handle ) ) { | |
| 86 | + // Never defer a script that has a blocking inline `after` script. That | |
| 87 | + // inline runs synchronously while the document parses, so deferring the | |
| 88 | + // external src makes the inline execute BEFORE the library it depends on. | |
| 89 | + // wp-i18n is the canonical case: core prints | |
| 90 | + // `wp.i18n.setLocaleData( … )` as wp-i18n's inline `after`, and deferring | |
| 91 | + // wp-i18n leaves `wp.i18n` undefined for the whole page (breaking every | |
| 92 | + // script that calls `wp.i18n.__`). This mirrors WordPress core, whose own | |
| 93 | + // strategy API declares such scripts ineligible for defer/async. | |
| 94 | + if ( $this->has_blocking_inline( $handle ) ) { | |
| 94 | 95 | return $tag; |
| 95 | 96 | } |
| 96 | 97 | return preg_replace( '/^<script\s/', '<script defer ', $tag, 1 ); |
| 97 | 98 | } |
| @@ -96,18 +97,23 @@ | ||
| 96 | 97 | return preg_replace( '/^<script\s/', '<script defer ', $tag, 1 ); |
| 97 | 98 | } |
| 98 | 99 | |
| 99 | 100 | /** |
| 100 | - * Whether the handle has inline `before`/`after` script data queued, which | |
| 101 | - * WordPress emits as non-deferrable inline <script> tags. | |
| 101 | + * Whether a registered script carries an inline `after` script — which must | |
| 102 | + * run synchronously right after the external file and therefore blocks safe | |
| 103 | + * deferral of that file. | |
| 104 | + * | |
| 105 | + * @param string $handle | |
| 106 | + * | |
| 107 | + * @return bool | |
| 102 | 108 | */ |
| 103 | - private function has_inline_data( $handle ) { | |
| 104 | - $wp_scripts = wp_scripts(); | |
| 105 | - if ( ! $wp_scripts ) { | |
| 109 | + private function has_blocking_inline( $handle ) { | |
| 110 | + $scripts = wp_scripts(); | |
| 111 | + if ( ! $scripts ) { | |
| 106 | 112 | return false; |
| 107 | 113 | } |
| 108 | - return (bool) $wp_scripts->get_data( $handle, 'before' ) | |
| 109 | - || (bool) $wp_scripts->get_data( $handle, 'after' ); | |
| 114 | + | |
| 115 | + return ! empty( $scripts->get_data( $handle, 'after' ) ); | |
| 110 | 116 | } |
| 111 | 117 | |
| 112 | 118 | private function should_defer( $handle ) { |
| 113 | 119 | if ( in_array( $handle, $this->handles(), true ) ) { |