From 83218e148035e40053587dc853efa4025898ef73 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 26 Apr 2024 13:46:57 +0200 Subject: [PATCH 1/9] Add Script Module hooks registration on admin_footer --- src/wp-includes/class-wp-script-modules.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wp-includes/class-wp-script-modules.php b/src/wp-includes/class-wp-script-modules.php index 89d12a6b3ff31..b87ccdbf1a294 100644 --- a/src/wp-includes/class-wp-script-modules.php +++ b/src/wp-includes/class-wp-script-modules.php @@ -178,6 +178,10 @@ public function add_hooks() { add_action( $position, array( $this, 'print_import_map' ) ); add_action( $position, array( $this, 'print_enqueued_script_modules' ) ); add_action( $position, array( $this, 'print_script_module_preloads' ) ); + + add_action( 'admin_footer', array( $this, 'print_import_map' ) ); + add_action( 'admin_footer', array( $this, 'print_enqueued_script_modules' ) ); + add_action( 'admin_footer', array( $this, 'print_script_module_preloads' ) ); } /** From c0d71acd9245cb7830288c61ad1e71e9e8ea60f5 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Mon, 22 Apr 2024 09:34:00 +0200 Subject: [PATCH 2/9] Remove empty line in PHPDoc --- src/wp-includes/script-loader.php | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index d98b0e4d2d1c2..b256ff72e50c0 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -508,6 +508,43 @@ function wp_default_packages_inline_scripts( $scripts ) { ); } +/** + * Adds script configuration data WordPress JavaScript packages may read. + * + * @since 6.6.0 + */ +function wp_default_packages_print_script_configuration_data( ) { + $scripts = wp_scripts(); + + // echo ''; + // var_dump( +// $scripts->query( 'wp-api-fetch', 'enqueued' ), +// $scripts->query( 'wp-api-fetch', 'done' ), + // ); + // die(); + + if ( + $scripts->query( 'wp-api-fetch', 'enqueued' ) || + $scripts->query( 'wp-api-fetch', 'done' ) + ) { + wp_print_inline_script_tag( + wp_json_encode( + array( + 'rootURL' => sanitize_url( get_rest_url() ), + 'nonce' => wp_installing() ? '' : wp_create_nonce( 'wp_rest' ), + 'shouldRegisterMediaUploadMiddleware' => true, + 'nonceEndpoint' => admin_url( 'admin-ajax.php?action=rest-nonce' ), + ), + JSON_HEX_TAG | JSON_HEX_AMP + ), + array( + 'type' => 'application/json', + 'id' => 'wp-apifetch-config-data', + ) + ); + } +} + /** * Adds inline scripts required for the TinyMCE in the block editor. * @@ -670,6 +707,8 @@ function wp_default_packages( $scripts ) { if ( did_action( 'init' ) ) { wp_default_packages_inline_scripts( $scripts ); } + + add_action( 'wp_footer', 'wp_default_packages_print_script_configuration_data' ); } /** From 70780de8e7d812851bd19b537a493c72a9baa41d Mon Sep 17 00:00:00 2001 From: Jon Surrell <sirreal@users.noreply.github.com> Date: Tue, 23 Apr 2024 13:30:15 +0200 Subject: [PATCH 3/9] add data passing for script modules --- src/wp-includes/block-editor.php | 9 +++++ src/wp-includes/class-wp-script-modules.php | 25 +++++++++++++ src/wp-includes/script-loader.php | 39 --------------------- src/wp-includes/script-modules.php | 19 ++++++++++ src/wp-includes/theme-previews.php | 12 +++++++ 5 files changed, 65 insertions(+), 39 deletions(-) diff --git a/src/wp-includes/block-editor.php b/src/wp-includes/block-editor.php index e04b012e7dd08..6441b59c91300 100644 --- a/src/wp-includes/block-editor.php +++ b/src/wp-includes/block-editor.php @@ -766,6 +766,15 @@ function block_editor_rest_api_preload( array $preload_paths, $block_editor_cont ), 'after' ); + add_filter( + 'scriptmoduleconfig_@wordpress/api-fetch', + function ( $data ) use ( $preload_data ) { + return array_merge( + $data, + array( 'preloadData' => $preload_data ) + ); + } + ); } /** diff --git a/src/wp-includes/class-wp-script-modules.php b/src/wp-includes/class-wp-script-modules.php index d391878d7c4a0..eed7f1757b314 100644 --- a/src/wp-includes/class-wp-script-modules.php +++ b/src/wp-includes/class-wp-script-modules.php @@ -178,6 +178,8 @@ public function add_hooks() { add_action( $position, array( $this, 'print_import_map' ) ); add_action( $position, array( $this, 'print_enqueued_script_modules' ) ); add_action( $position, array( $this, 'print_script_module_preloads' ) ); + + add_action( 'wp_footer', array( $this, 'print_script_data' ) ); } /** @@ -359,4 +361,27 @@ private function get_src( string $id ): string { return $src; } + + public function print_script_data(): void { + $modules = array(); + foreach ( array_keys( $this->get_marked_for_enqueue() ) as $id ) { + $modules[ $id ] = true; + } + foreach ( array_keys( $this->get_import_map()['imports'] ) as $id ) { + $modules[ $id ] = true; + } + + foreach ( array_keys( $modules ) as $module_id ) { + $config = apply_filters( 'scriptmoduledata_' . $module_id, array() ); + if ( ! empty( $config ) ) { + wp_print_inline_script_tag( + wp_json_encode( $config, JSON_HEX_TAG | JSON_HEX_AMP ), + array( + 'type' => 'application/json', + 'id' => 'wp-config-data_' . $module_id, + ) + ); + } + } + } } diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index b256ff72e50c0..d98b0e4d2d1c2 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -508,43 +508,6 @@ function wp_default_packages_inline_scripts( $scripts ) { ); } -/** - * Adds script configuration data WordPress JavaScript packages may read. - * - * @since 6.6.0 - */ -function wp_default_packages_print_script_configuration_data( ) { - $scripts = wp_scripts(); - - // echo '<plaintext>'; - // var_dump( -// $scripts->query( 'wp-api-fetch', 'enqueued' ), -// $scripts->query( 'wp-api-fetch', 'done' ), - // ); - // die(); - - if ( - $scripts->query( 'wp-api-fetch', 'enqueued' ) || - $scripts->query( 'wp-api-fetch', 'done' ) - ) { - wp_print_inline_script_tag( - wp_json_encode( - array( - 'rootURL' => sanitize_url( get_rest_url() ), - 'nonce' => wp_installing() ? '' : wp_create_nonce( 'wp_rest' ), - 'shouldRegisterMediaUploadMiddleware' => true, - 'nonceEndpoint' => admin_url( 'admin-ajax.php?action=rest-nonce' ), - ), - JSON_HEX_TAG | JSON_HEX_AMP - ), - array( - 'type' => 'application/json', - 'id' => 'wp-apifetch-config-data', - ) - ); - } -} - /** * Adds inline scripts required for the TinyMCE in the block editor. * @@ -707,8 +670,6 @@ function wp_default_packages( $scripts ) { if ( did_action( 'init' ) ) { wp_default_packages_inline_scripts( $scripts ); } - - add_action( 'wp_footer', 'wp_default_packages_print_script_configuration_data' ); } /** diff --git a/src/wp-includes/script-modules.php b/src/wp-includes/script-modules.php index 7ff80df5bfdb1..bde277bf0a11f 100644 --- a/src/wp-includes/script-modules.php +++ b/src/wp-includes/script-modules.php @@ -123,3 +123,22 @@ function wp_dequeue_script_module( string $id ) { function wp_deregister_script_module( string $id ) { wp_script_modules()->deregister( $id ); } + + +function wp_register_default_script_modules(): void { + add_filter( + 'scriptmoduleconfig_@wordpress/api-fetch', + function ( $data ) { + return array_merge( + $data, + array( + 'rootURL' => sanitize_url( get_rest_url() ), + 'nonce' => wp_installing() ? '' : wp_create_nonce( 'wp_rest' ), + 'shouldRegisterMediaUploadMiddleware' => true, + 'nonceEndpoint' => admin_url( 'admin-ajax.php?action=rest-nonce' ), + ) + ); + } + ); +} +add_action( 'init', 'wp_register_default_script_modules', 0 ); diff --git a/src/wp-includes/theme-previews.php b/src/wp-includes/theme-previews.php index 7e0c085b1c102..6a5a292dd0441 100644 --- a/src/wp-includes/theme-previews.php +++ b/src/wp-includes/theme-previews.php @@ -53,6 +53,18 @@ function wp_attach_theme_preview_middleware() { ), 'after' ); + + add_filter( + 'scriptmoduleconfig_@wordpress/api-fetch', + function ( $data ) { + return array_merge( + $data, + array( + 'themePreviewPath' => sanitize_text_field( wp_unslash( $_GET['wp_theme_preview'] ) ), + ) + ); + } + ); } /** From 2c052a1b5bcfaefef660f161064c5d2aecaaf339 Mon Sep 17 00:00:00 2001 From: Jon Surrell <sirreal@users.noreply.github.com> Date: Tue, 23 Apr 2024 17:57:46 +0200 Subject: [PATCH 4/9] Fix filters and ID --- src/wp-includes/block-editor.php | 2 +- src/wp-includes/class-wp-script-modules.php | 2 +- src/wp-includes/script-modules.php | 2 +- src/wp-includes/theme-previews.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/block-editor.php b/src/wp-includes/block-editor.php index 6441b59c91300..311deaf256590 100644 --- a/src/wp-includes/block-editor.php +++ b/src/wp-includes/block-editor.php @@ -767,7 +767,7 @@ function block_editor_rest_api_preload( array $preload_paths, $block_editor_cont 'after' ); add_filter( - 'scriptmoduleconfig_@wordpress/api-fetch', + 'scriptmoduledata_@wordpress/api-fetch', function ( $data ) use ( $preload_data ) { return array_merge( $data, diff --git a/src/wp-includes/class-wp-script-modules.php b/src/wp-includes/class-wp-script-modules.php index eed7f1757b314..ba2576435456e 100644 --- a/src/wp-includes/class-wp-script-modules.php +++ b/src/wp-includes/class-wp-script-modules.php @@ -378,7 +378,7 @@ public function print_script_data(): void { wp_json_encode( $config, JSON_HEX_TAG | JSON_HEX_AMP ), array( 'type' => 'application/json', - 'id' => 'wp-config-data_' . $module_id, + 'id' => 'wp-scriptmodule-data_' . $module_id, ) ); } diff --git a/src/wp-includes/script-modules.php b/src/wp-includes/script-modules.php index bde277bf0a11f..80ef13f0a2d8d 100644 --- a/src/wp-includes/script-modules.php +++ b/src/wp-includes/script-modules.php @@ -127,7 +127,7 @@ function wp_deregister_script_module( string $id ) { function wp_register_default_script_modules(): void { add_filter( - 'scriptmoduleconfig_@wordpress/api-fetch', + 'scriptmoduledata_@wordpress/api-fetch', function ( $data ) { return array_merge( $data, diff --git a/src/wp-includes/theme-previews.php b/src/wp-includes/theme-previews.php index 6a5a292dd0441..88e992a8cd4aa 100644 --- a/src/wp-includes/theme-previews.php +++ b/src/wp-includes/theme-previews.php @@ -55,7 +55,7 @@ function wp_attach_theme_preview_middleware() { ); add_filter( - 'scriptmoduleconfig_@wordpress/api-fetch', + 'scriptmoduledata_@wordpress/api-fetch', function ( $data ) { return array_merge( $data, From 3dc7e5f193b77cf982c787662c11421d6c81c708 Mon Sep 17 00:00:00 2001 From: Jon Surrell <sirreal@users.noreply.github.com> Date: Fri, 3 May 2024 09:41:53 +0200 Subject: [PATCH 5/9] Replace array_merge with assignment --- src/wp-includes/block-editor.php | 6 ++---- src/wp-includes/script-modules.php | 14 +++++--------- src/wp-includes/theme-previews.php | 8 ++------ 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/wp-includes/block-editor.php b/src/wp-includes/block-editor.php index 311deaf256590..c81ab65b9e593 100644 --- a/src/wp-includes/block-editor.php +++ b/src/wp-includes/block-editor.php @@ -769,10 +769,8 @@ function block_editor_rest_api_preload( array $preload_paths, $block_editor_cont add_filter( 'scriptmoduledata_@wordpress/api-fetch', function ( $data ) use ( $preload_data ) { - return array_merge( - $data, - array( 'preloadData' => $preload_data ) - ); + $data['preloadData'] = $preload_data; + return $data; } ); } diff --git a/src/wp-includes/script-modules.php b/src/wp-includes/script-modules.php index 80ef13f0a2d8d..3a3076f6c88bb 100644 --- a/src/wp-includes/script-modules.php +++ b/src/wp-includes/script-modules.php @@ -129,15 +129,11 @@ function wp_register_default_script_modules(): void { add_filter( 'scriptmoduledata_@wordpress/api-fetch', function ( $data ) { - return array_merge( - $data, - array( - 'rootURL' => sanitize_url( get_rest_url() ), - 'nonce' => wp_installing() ? '' : wp_create_nonce( 'wp_rest' ), - 'shouldRegisterMediaUploadMiddleware' => true, - 'nonceEndpoint' => admin_url( 'admin-ajax.php?action=rest-nonce' ), - ) - ); + $data['rootURL'] = sanitize_url( get_rest_url() ); + $data['nonce'] = wp_installing() ? '' : wp_create_nonce( 'wp_rest' ); + $data['shouldRegisterMediaUploadMiddleware'] = true; + $data['nonceEndpoint'] = admin_url( 'admin-ajax.php?action=rest-nonce' ); + return $data; } ); } diff --git a/src/wp-includes/theme-previews.php b/src/wp-includes/theme-previews.php index 88e992a8cd4aa..b4495af8429c8 100644 --- a/src/wp-includes/theme-previews.php +++ b/src/wp-includes/theme-previews.php @@ -57,12 +57,8 @@ function wp_attach_theme_preview_middleware() { add_filter( 'scriptmoduledata_@wordpress/api-fetch', function ( $data ) { - return array_merge( - $data, - array( - 'themePreviewPath' => sanitize_text_field( wp_unslash( $_GET['wp_theme_preview'] ) ), - ) - ); + $data['themePreviewPath'] = sanitize_text_field( wp_unslash( $_GET['wp_theme_preview'] ) ); + return $data; } ); } From e7c4afcb2c97e8e591c98048000af98996921ef6 Mon Sep 17 00:00:00 2001 From: Jon Surrell <sirreal@users.noreply.github.com> Date: Fri, 3 May 2024 11:33:12 +0200 Subject: [PATCH 6/9] Print script data in admin_footer --- src/wp-includes/class-wp-script-modules.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wp-includes/class-wp-script-modules.php b/src/wp-includes/class-wp-script-modules.php index b7117cc540f64..b5e88d12f86a6 100644 --- a/src/wp-includes/class-wp-script-modules.php +++ b/src/wp-includes/class-wp-script-modules.php @@ -184,6 +184,7 @@ public function add_hooks() { add_action( 'admin_footer', array( $this, 'print_script_module_preloads' ) ); add_action( 'wp_footer', array( $this, 'print_script_data' ) ); + add_action( 'admin_footer', array( $this, 'print_script_data' ) ); } /** From 21d006d0d2134332755c70c9a822bb58d2efb3b3 Mon Sep 17 00:00:00 2001 From: Jon Surrell <sirreal@users.noreply.github.com> Date: Fri, 3 May 2024 11:33:43 +0200 Subject: [PATCH 7/9] DROPME: Enqueue api-fetch module dependent This makes sure it's in the importmap so can always be used. --- src/wp-includes/script-modules.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wp-includes/script-modules.php b/src/wp-includes/script-modules.php index 3a3076f6c88bb..3e5a9d13ac57e 100644 --- a/src/wp-includes/script-modules.php +++ b/src/wp-includes/script-modules.php @@ -136,5 +136,6 @@ function ( $data ) { return $data; } ); + wp_enqueue_script_module( '__DEV__/noop', includes_url( '/noop.js' ), array( '@wordpress/api-fetch' ) ); } add_action( 'init', 'wp_register_default_script_modules', 0 ); From d36e0c279bb32cd6370e50b67cb5a143479669e3 Mon Sep 17 00:00:00 2001 From: Jon Surrell <sirreal@users.noreply.github.com> Date: Wed, 8 May 2024 17:37:36 +0200 Subject: [PATCH 8/9] Merge fix/script-modules-admin-hooks --- src/wp-includes/class-wp-script-modules.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/class-wp-script-modules.php b/src/wp-includes/class-wp-script-modules.php index b5e88d12f86a6..bd59efa4a9453 100644 --- a/src/wp-includes/class-wp-script-modules.php +++ b/src/wp-includes/class-wp-script-modules.php @@ -179,12 +179,12 @@ public function add_hooks() { add_action( $position, array( $this, 'print_enqueued_script_modules' ) ); add_action( $position, array( $this, 'print_script_module_preloads' ) ); - add_action( 'admin_footer', array( $this, 'print_import_map' ) ); - add_action( 'admin_footer', array( $this, 'print_enqueued_script_modules' ) ); - add_action( 'admin_footer', array( $this, 'print_script_module_preloads' ) ); + add_action( 'admin_print_footer_scripts', array( $this, 'print_import_map' ) ); + add_action( 'admin_print_footer_scripts', array( $this, 'print_enqueued_script_modules' ) ); + add_action( 'admin_print_footer_scripts', array( $this, 'print_script_module_preloads' ) ); add_action( 'wp_footer', array( $this, 'print_script_data' ) ); - add_action( 'admin_footer', array( $this, 'print_script_data' ) ); + add_action( 'admin_print_footer_scripts', array( $this, 'print_script_data' ) ); } /** From d185ac6f3c92fe7c6a174c2da23568a1555da842 Mon Sep 17 00:00:00 2001 From: Jon Surrell <sirreal@users.noreply.github.com> Date: Tue, 14 May 2024 16:58:59 +0200 Subject: [PATCH 9/9] Improve data encoding See https://github.com/WordPress/wordpress-develop/pull/6520 --- src/wp-includes/class-wp-script-modules.php | 49 +++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/class-wp-script-modules.php b/src/wp-includes/class-wp-script-modules.php index bd59efa4a9453..8651779c7a8bd 100644 --- a/src/wp-includes/class-wp-script-modules.php +++ b/src/wp-includes/class-wp-script-modules.php @@ -377,10 +377,53 @@ public function print_script_data(): void { } foreach ( array_keys( $modules ) as $module_id ) { - $config = apply_filters( 'scriptmoduledata_' . $module_id, array() ); - if ( ! empty( $config ) ) { + $data = apply_filters( 'scriptmoduledata_' . $module_id, array() ); + if ( ! empty( $data ) ) { + /* + * This data will be printed as JSON inside a script tag like this: + * <script type="application/json"></script> + * + * A script tag must be closed by a sequence beginning with `</`. It's impossible to + * close a script tag without using `<`. We ensure that `<` is escaped and `/` can + * remain unescaped, so `</script>` will be printed as `\u003C/script\u00E3`. + * + * - JSON_HEX_TAG: All < and > are converted to \u003C and \u003E. + * - JSON_UNESCAPED_SLASHES: Don't escape /. + * + * @see https://www.php.net/manual/en/json.constants.php for details on these constants. + * @see https://html.spec.whatwg.org/#script-data-state for details on script + * tag parsing. + */ + $json_encode_flags = JSON_HEX_TAG | JSON_UNESCAPED_SLASHES; + if ( 'UTF-8' === get_option( 'blog_charset' ) ) { + /* + * If the page will use UTF-8 encoding, it's safe to print unescaped unicode in + * JSON. Set the following flags: + * + * - JSON_UNESCAPED_UNICODE: Encode multibyte Unicode characters literally + * (default is to escape as \uXXXX). + * - JSON_UNESCAPED_LINE_TERMINATORS: The line terminators are kept unescaped when + * JSON_UNESCAPED_UNICODE is supplied. It uses the same behaviour as it was + * before PHP 7.1 without this constant. Available as of PHP 7.1.0. + * + * The JSON specification does not specify a character encoding, RFC-8259 + * suggests that UTF-8 be used everywhere. It's risky to print unicode if the page + * uses any other encoding. + * + * > JSON text exchanged between systems that are not part of a closed ecosystem + * > MUST be encoded using UTF-8. Previous specifications of JSON have not required + * > the use of UTF-8 when transmitting JSON text. However, the vast majority of + * > JSON- based software implementations have chosen to use the UTF-8 encoding, + * > to the extent that it is the only encoding that achieves interoperability. + * + * @see https://www.rfc-editor.org/rfc/rfc8259.html + * + */ + $json_encode_flags |= JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_LINE_TERMINATORS; + } + wp_print_inline_script_tag( - wp_json_encode( $config, JSON_HEX_TAG | JSON_HEX_AMP ), + wp_json_encode( $data, $json_encode_flags ), array( 'type' => 'application/json', 'id' => 'wp-scriptmodule-data_' . $module_id,