From dc15d66ee6203bf7b480de3c120d0d64f14009e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Zi=C3=83=C2=B3=C3=85=E2=80=9Akowski?= Date: Wed, 31 Jan 2024 08:29:18 +0000 Subject: [PATCH] Script Modules API: Add import map polyfill for older browsers Syncs the changes from https://github.com/WordPress/gutenberg/pull/58263. Adds a polyfill to make import maps compatible with unsported browsers (https://caniuse.com/import-maps). Fixes #60348. Props cbravobernal, jorbin, luisherranz, jonsurrell. git-svn-id: https://develop.svn.wordpress.org/trunk@57492 602fd350-edb4-49c9-b593-d223f7449a82 --- Gruntfile.js | 12 +++--- package-lock.json | 11 ++++++ package.json | 1 + src/wp-includes/class-wp-script-modules.php | 16 ++++++++ src/wp-includes/script-loader.php | 2 + tests/phpunit/tests/dependencies/scripts.php | 30 +++++++++++++++ .../tests/script-modules/wpScriptModules.php | 37 ++++++++++++++++++- tools/webpack/packages.js | 2 + 8 files changed, 105 insertions(+), 6 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 43e7bd02ef..38904c6dac 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1559,16 +1559,18 @@ module.exports = function(grunt) { } ); /** - * Build assertions for the lack of source maps in JavaScript files. + * Compiled JavaScript files may link to sourcemaps. In some cases, + * the source map may not be available, which can cause 404 errors when + * browsers try to download the sourcemap from the referenced URLs. + * Ensure that sourcemap links are not included in JavaScript files. * * @ticket 24994 * @ticket 46218 + * @ticket 60348 */ grunt.registerTask( 'verify:source-maps', function() { const ignoredFiles = [ 'build/wp-includes/js/dist/components.js', - 'build/wp-includes/js/dist/block-editor.js', - 'build/wp-includes/js/dist/block-editor.min.js' ]; const files = buildFiles.reduce( ( acc, path ) => { // Skip excluded paths and any path that isn't a file. @@ -1591,10 +1593,10 @@ module.exports = function(grunt) { encoding: 'utf8', } ); // `data:` URLs are allowed: - const match = contents.match( /sourceMappingURL=((?!data:).)/ ); + const doesNotHaveSourceMap = ! /^\/\/# sourceMappingURL=((?!data:).)/m.test(contents); assert( - match === null, + doesNotHaveSourceMap, `The ${ file } file must not contain a sourceMappingURL.` ); } ); diff --git a/package-lock.json b/package-lock.json index 8d53eab8e8..91b8368007 100644 --- a/package-lock.json +++ b/package-lock.json @@ -81,6 +81,7 @@ "clipboard": "2.0.11", "core-js-url-browser": "3.6.4", "element-closest": "^3.0.2", + "es-module-shims": "1.8.2", "formdata-polyfill": "4.0.10", "framer-motion": "10.16.4", "hoverintent": "2.2.1", @@ -13349,6 +13350,11 @@ "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", "dev": true }, + "node_modules/es-module-shims": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/es-module-shims/-/es-module-shims-1.8.2.tgz", + "integrity": "sha512-7vIYVzpOhXtpc3Yn03itB+GSgVZFW7oL4kdydA+iL+IEi7HiSLBUxM05QFw4SxTl6e++pMpGqZPo2+vdNs3TbA==" + }, "node_modules/es-set-tostringtag": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", @@ -43316,6 +43322,11 @@ "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", "dev": true }, + "es-module-shims": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/es-module-shims/-/es-module-shims-1.8.2.tgz", + "integrity": "sha512-7vIYVzpOhXtpc3Yn03itB+GSgVZFW7oL4kdydA+iL+IEi7HiSLBUxM05QFw4SxTl6e++pMpGqZPo2+vdNs3TbA==" + }, "es-set-tostringtag": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", diff --git a/package.json b/package.json index 192386d8b9..9e5e13653e 100644 --- a/package.json +++ b/package.json @@ -150,6 +150,7 @@ "clipboard": "2.0.11", "core-js-url-browser": "3.6.4", "element-closest": "^3.0.2", + "es-module-shims": "1.8.2", "formdata-polyfill": "4.0.10", "framer-motion": "10.16.4", "hoverintent": "2.2.1", diff --git a/src/wp-includes/class-wp-script-modules.php b/src/wp-includes/class-wp-script-modules.php index 7eebb79da1..b2413d0f24 100644 --- a/src/wp-includes/class-wp-script-modules.php +++ b/src/wp-includes/class-wp-script-modules.php @@ -211,10 +211,26 @@ class WP_Script_Modules { * Prints the import map using a script tag with a type="importmap" attribute. * * @since 6.5.0 + * + * @global WP_Scripts $wp_scripts The WP_Scripts object for printing the polyfill. */ public function print_import_map() { $import_map = $this->get_import_map(); if ( ! empty( $import_map['imports'] ) ) { + global $wp_scripts; + if ( isset( $wp_scripts ) ) { + wp_print_inline_script_tag( + wp_get_script_polyfill( + $wp_scripts, + array( + 'HTMLScriptElement.supports && HTMLScriptElement.supports("importmap")' => 'wp-polyfill-importmap', + ) + ), + array( + 'id' => 'wp-load-polyfill-importmap', + ) + ); + } wp_print_inline_script_tag( wp_json_encode( $import_map, JSON_HEX_TAG | JSON_HEX_AMP ), array( diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 50864cfd7b..25977a9bd7 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -96,6 +96,7 @@ function wp_default_packages_vendor( $scripts ) { 'lodash', 'wp-polyfill-fetch', 'wp-polyfill-formdata', + 'wp-polyfill-importmap', 'wp-polyfill-node-contains', 'wp-polyfill-url', 'wp-polyfill-dom-rect', @@ -120,6 +121,7 @@ function wp_default_packages_vendor( $scripts ) { 'wp-polyfill-object-fit' => '2.3.5', 'wp-polyfill-inert' => '3.1.2', 'wp-polyfill' => '3.15.0', + 'wp-polyfill-importmap' => '1.8.2', ); foreach ( $vendor_scripts as $handle => $dependencies ) { diff --git a/tests/phpunit/tests/dependencies/scripts.php b/tests/phpunit/tests/dependencies/scripts.php index 2280771880..5e9cec7039 100644 --- a/tests/phpunit/tests/dependencies/scripts.php +++ b/tests/phpunit/tests/dependencies/scripts.php @@ -3104,6 +3104,36 @@ HTML $this->assertEquals( $expected_groups, wp_scripts()->groups, 'Expected groups to match.' ); } + /** + * Test that get_script_polyfill() returns the correct polyfill. + * + * @ticket 60348 + * + * @covers ::wp_get_script_polyfill + * + * @global WP_Scripts $wp_scripts WP_Scripts instance. + */ + public function test_wp_get_script_polyfill() { + global $wp_scripts; + $script_name = 'wp-polyfill-importmap'; + $test_script = 'HTMLScriptElement.supports && HTMLScriptElement.supports("importmap")'; + $script_url = 'https://example.com/wp-polyfill-importmap.js'; + wp_register_script( $script_name, $script_url ); + + $polyfill = wp_get_script_polyfill( + $wp_scripts, + array( + $test_script => $script_name, + ) + ); + + wp_deregister_script( $script_name ); + + $expected = '( ' . $test_script . ' ) || document.write( \'