diff --git a/plugins/optimization-detective/detect-loader.js b/plugins/optimization-detective/detect-loader.js new file mode 100644 index 0000000000..47b714786d --- /dev/null +++ b/plugins/optimization-detective/detect-loader.js @@ -0,0 +1,41 @@ +/** + * Loads the detect logic after the page has loaded to prevent a high-priority script module network request from competing with other critical resources. + * + * This JavaScript file must be contain a single top-level function which is not exported. The file is inlined as part of another module which wraps the module in an IIFE. + * + * @param {string} detectSrc + * @param {Object} detectArgs + */ +// eslint-disable-next-line no-unused-vars +async function load( detectSrc, detectArgs ) { + const doc = document; + const win = window; + + // Ensure the DOM is loaded (although it surely already is since we're executing in a module). + await new Promise( ( resolve ) => { + if ( doc.readyState !== 'loading' ) { + resolve(); + } else { + doc.addEventListener( 'DOMContentLoaded', resolve, { once: true } ); + } + } ); + + // Wait until the resources on the page have fully loaded. + await new Promise( ( resolve ) => { + if ( doc.readyState === 'complete' ) { + resolve(); + } else { + win.addEventListener( 'load', resolve, { once: true } ); + } + } ); + + // Wait yet further until idle. + if ( typeof requestIdleCallback === 'function' ) { + await new Promise( ( resolve ) => { + requestIdleCallback( resolve ); + } ); + } + + const { default: detect } = await import( detectSrc ); + await detect( detectArgs ); +} diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index c38c9efacd..e3477c90ec 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -635,31 +635,6 @@ export default async function detect( { return; } - // Ensure the DOM is loaded (although it surely already is since we're executing in a module). - await new Promise( ( resolve ) => { - if ( doc.readyState !== 'loading' ) { - resolve(); - } else { - doc.addEventListener( 'DOMContentLoaded', resolve, { once: true } ); - } - } ); - - // Wait until the resources on the page have fully loaded. - await new Promise( ( resolve ) => { - if ( doc.readyState === 'complete' ) { - resolve(); - } else { - win.addEventListener( 'load', resolve, { once: true } ); - } - } ); - - // Wait yet further until idle. - if ( typeof requestIdleCallback === 'function' ) { - await new Promise( ( resolve ) => { - requestIdleCallback( resolve ); - } ); - } - // TODO: Does this make sense here? Should it be moved up above the isViewportNeeded condition? // As an alternative to this, the od_print_detection_script() function can short-circuit if the // od_is_url_metric_storage_locked() function returns true. However, the downside with that is page caching could diff --git a/plugins/optimization-detective/detection.php b/plugins/optimization-detective/detection.php index efad149448..8fb4e7d48f 100644 --- a/plugins/optimization-detective/detection.php +++ b/plugins/optimization-detective/detection.php @@ -151,7 +151,8 @@ static function ( OD_URL_Metric_Group $group ): array { return wp_get_inline_script_tag( sprintf( - 'import detect from %s; detect( %s );', + '( %s )( %s, %s );', + file_get_contents( __DIR__ . '/' . od_get_asset_path( 'detect-loader.js' ) ), // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- It's a local filesystem path not a remote request. wp_json_encode( plugins_url( add_query_arg( 'ver', OPTIMIZATION_DETECTIVE_VERSION, od_get_asset_path( 'detect.js' ) ), __FILE__ ) ), wp_json_encode( $detect_args ) ), diff --git a/webpack.config.js b/webpack.config.js index ddcf1f1a72..4b273521fa 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -214,6 +214,10 @@ const optimizationDetective = ( env ) => { cache: false, }, }, + { + from: `${ destination }/detect-loader.js`, + to: `${ destination }/detect-loader.min.js`, + }, { from: `${ destination }/detect.js`, to: `${ destination }/detect.min.js`,