diff --git a/src/wp-includes/block-editor.php b/src/wp-includes/block-editor.php index e04b012e7dd08..c81ab65b9e593 100644 --- a/src/wp-includes/block-editor.php +++ b/src/wp-includes/block-editor.php @@ -766,6 +766,13 @@ function block_editor_rest_api_preload( array $preload_paths, $block_editor_cont ), 'after' ); + add_filter( + 'scriptmoduledata_@wordpress/api-fetch', + function ( $data ) use ( $preload_data ) { + $data['preloadData'] = $preload_data; + return $data; + } + ); } /** diff --git a/src/wp-includes/class-wp-script-modules.php b/src/wp-includes/class-wp-script-modules.php index d391878d7c4a0..8651779c7a8bd 100644 --- a/src/wp-includes/class-wp-script-modules.php +++ b/src/wp-includes/class-wp-script-modules.php @@ -178,6 +178,13 @@ 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_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_print_footer_scripts', array( $this, 'print_script_data' ) ); } /** @@ -359,4 +366,70 @@ 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 ) { + $data = apply_filters( 'scriptmoduledata_' . $module_id, array() ); + if ( ! empty( $data ) ) { + /* + * This data will be printed as JSON inside a script tag like this: + * + * + * A script tag must be closed by a sequence beginning with `` 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( $data, $json_encode_flags ), + array( + 'type' => 'application/json', + 'id' => 'wp-scriptmodule-data_' . $module_id, + ) + ); + } + } + } } diff --git a/src/wp-includes/script-modules.php b/src/wp-includes/script-modules.php index 7ff80df5bfdb1..3e5a9d13ac57e 100644 --- a/src/wp-includes/script-modules.php +++ b/src/wp-includes/script-modules.php @@ -123,3 +123,19 @@ 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( + 'scriptmoduledata_@wordpress/api-fetch', + function ( $data ) { + $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; + } + ); + wp_enqueue_script_module( '__DEV__/noop', includes_url( '/noop.js' ), array( '@wordpress/api-fetch' ) ); +} +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..b4495af8429c8 100644 --- a/src/wp-includes/theme-previews.php +++ b/src/wp-includes/theme-previews.php @@ -53,6 +53,14 @@ function wp_attach_theme_preview_middleware() { ), 'after' ); + + add_filter( + 'scriptmoduledata_@wordpress/api-fetch', + function ( $data ) { + $data['themePreviewPath'] = sanitize_text_field( wp_unslash( $_GET['wp_theme_preview'] ) ); + return $data; + } + ); } /**