From 4a71c10d1044e583508b10664759d5883fc067c5 Mon Sep 17 00:00:00 2001 From: Sukhendu Sekhar Guria Date: Wed, 13 May 2026 12:57:11 +0530 Subject: [PATCH] Scripts: Add parameter to wp_add_inline_script() --- src/wp-includes/class-wp-scripts.php | 78 ++++++++++++++++++-- src/wp-includes/functions.wp-scripts.php | 9 ++- tests/phpunit/tests/dependencies/scripts.php | 68 +++++++++++++++++ 3 files changed, 148 insertions(+), 7 deletions(-) diff --git a/src/wp-includes/class-wp-scripts.php b/src/wp-includes/class-wp-scripts.php index cb37b2b653877..6870383cc63e3 100644 --- a/src/wp-includes/class-wp-scripts.php +++ b/src/wp-includes/class-wp-scripts.php @@ -508,15 +508,20 @@ public function do_item( $handle, $group = false ) { * Adds extra code to a registered script. * * @since 4.5.0 + * @since x.x.x Added the `$type` parameter. * * @param string $handle Name of the script to add the inline script to. * Must be lowercase. * @param string $data String containing the JavaScript to be added. * @param string $position Optional. Whether to add the inline script * before the handle or after. Default 'after'. + * @param string $type Optional. Value of the type attribute for the inline script tag. + * Use this to set a custom type such as 'application/json' or + * 'application/ld+json'. Scripts with a custom type are rendered + * in their own script tag separate from JavaScript. Default empty string. * @return bool True on success, false on failure. */ - public function add_inline_script( $handle, $data, $position = 'after' ) { + public function add_inline_script( $handle, $data, $position = 'after', $type = '' ) { if ( ! $data ) { return false; } @@ -528,6 +533,17 @@ public function add_inline_script( $handle, $data, $position = 'after' ) { $script = (array) $this->get_data( $handle, $position ); $script[] = $data; + // Store the type for this specific inline script if provided. + if ( '' !== $type ) { + $types_key = "{$position}_types"; + $types = $this->get_data( $handle, $types_key ); + if ( ! is_array( $types ) ) { + $types = array(); + } + $types[ count( $script ) - 1 ] = $type; + $this->add_data( $handle, $types_key, $types ); + } + return $this->add_data( $handle, $position, $script ); } @@ -562,7 +578,12 @@ public function print_inline_script( $handle, $position = 'after', $display = tr /** * Gets data for inline scripts registered for a specific handle. * + * Returns only the JavaScript inline scripts (those without a custom type). + * Scripts with a custom type are excluded as they are not JavaScript and + * should not be concatenated with JS code. + * * @since 6.3.0 + * @since x.x.x Scripts with a custom type are excluded from the returned data. * * @param string $handle Name of the script to get data for. * Must be lowercase. @@ -576,6 +597,25 @@ public function get_inline_script_data( $handle, $position = 'after' ) { return ''; } + // Exclude scripts that have a custom type (non-JavaScript). + $types = $this->get_data( $handle, "{$position}_types" ); + if ( is_array( $types ) && ! empty( $types ) ) { + $data = array_filter( + $data, + static function ( $index ) use ( $types ) { + return ! isset( $types[ $index ] ) || '' === $types[ $index ]; + }, + ARRAY_FILTER_USE_KEY + ); + } + + // Remove empty entries that may be artifacts of data initialization. + $data = array_filter( $data ); + + if ( empty( $data ) ) { + return ''; + } + /* * Print sourceURL comment regardless of concatenation. * @@ -593,7 +633,11 @@ public function get_inline_script_data( $handle, $position = 'after' ) { /** * Gets tags for inline scripts registered for a specific handle. * + * Scripts with a custom type are rendered in separate script tags + * from the default JavaScript inline scripts. + * * @since 6.3.0 + * @since x.x.x Scripts with a custom type are rendered in their own script tag. * * @param string $handle Name of the script to get associated inline script tag for. * Must be lowercase. @@ -602,14 +646,38 @@ public function get_inline_script_data( $handle, $position = 'after' ) { * @return string Inline script, which may be empty string. */ public function get_inline_script_tag( $handle, $position = 'after' ) { - $js = $this->get_inline_script_data( $handle, $position ); - if ( empty( $js ) ) { + $data = $this->get_data( $handle, $position ); + if ( empty( $data ) || ! is_array( $data ) ) { return ''; } - $id = "{$handle}-js-{$position}"; + $types = $this->get_data( $handle, "{$position}_types" ); + if ( ! is_array( $types ) ) { + $types = array(); + } + + $output = ''; - return wp_get_inline_script_tag( $js, compact( 'id' ) ); + // Render the default JavaScript inline scripts (concatenated). + $js = $this->get_inline_script_data( $handle, $position ); + if ( ! empty( $js ) ) { + $id = "{$handle}-js-{$position}"; + $output .= wp_get_inline_script_tag( $js, compact( 'id' ) ); + } + + // Render scripts with custom types in their own separate script tags. + if ( ! empty( $types ) ) { + foreach ( $types as $index => $type ) { + if ( '' === $type || ! isset( $data[ $index ] ) ) { + continue; + } + + $id = "{$handle}-js-{$position}-{$index}"; + $output .= wp_get_inline_script_tag( $data[ $index ], compact( 'id', 'type' ) ); + } + } + + return $output; } /** diff --git a/src/wp-includes/functions.wp-scripts.php b/src/wp-includes/functions.wp-scripts.php index 59e4e54a1a1ad..a41bf30f9824e 100644 --- a/src/wp-includes/functions.wp-scripts.php +++ b/src/wp-includes/functions.wp-scripts.php @@ -161,6 +161,7 @@ function wp_print_scripts( $handles = false ) { * they were added, i.e. the latter added code can redeclare the previous. * * @since 4.5.0 + * @since x.x.x Added the `$type` parameter. * * @see WP_Scripts::add_inline_script() * @@ -168,9 +169,13 @@ function wp_print_scripts( $handles = false ) { * @param string $data String containing the JavaScript to be added. * @param string $position Optional. Whether to add the inline script before the handle * or after. Default 'after'. + * @param string $type Optional. Value of the type attribute for the inline script tag. + * Use this to set a custom type such as 'application/json' or + * 'application/ld+json'. Scripts with a custom type are rendered + * in their own script tag separate from JavaScript. Default empty string. * @return bool True on success, false on failure. */ -function wp_add_inline_script( $handle, $data, $position = 'after' ) { +function wp_add_inline_script( $handle, $data, $position = 'after', $type = '' ) { _wp_scripts_maybe_doing_it_wrong( __FUNCTION__, $handle ); if ( false !== stripos( $data, '' ) ) { @@ -187,7 +192,7 @@ function wp_add_inline_script( $handle, $data, $position = 'after' ) { $data = trim( (string) preg_replace( '#]*>(.*)#is', '$1', $data ) ); } - return wp_scripts()->add_inline_script( $handle, $data, $position ); + return wp_scripts()->add_inline_script( $handle, $data, $position, $type ); } /** diff --git a/tests/phpunit/tests/dependencies/scripts.php b/tests/phpunit/tests/dependencies/scripts.php index 5f1c30fe4cf47..b182d185e3556 100644 --- a/tests/phpunit/tests/dependencies/scripts.php +++ b/tests/phpunit/tests/dependencies/scripts.php @@ -4307,6 +4307,74 @@ public function data_varying_versions_handle_args() { ); } + /** + * @ticket 51124 + * + * @covers ::wp_add_inline_script + * @covers WP_Scripts::add_inline_script + * @covers WP_Scripts::get_inline_script_tag + */ + public function test_wp_add_inline_script_with_custom_type() { + wp_enqueue_script( 'test-example', 'example.com', array(), null ); + wp_add_inline_script( 'test-example', '{"key":"value"}', 'after', 'application/json' ); + wp_add_inline_script( 'test-example', '{"config":true}', 'before', 'application/json' ); + + $expected = "\n"; + $expected .= "\n"; + $expected .= "\n"; + + $this->assertEqualHTML( $expected, get_echo( 'wp_print_scripts' ) ); + } + + /** + * @ticket 51124 + * + * @covers ::wp_add_inline_script + * @covers WP_Scripts::get_inline_script_data + * @covers WP_Scripts::get_inline_script_tag + */ + public function test_wp_add_inline_script_custom_type_separated_from_js() { + wp_enqueue_script( 'test-example', 'example.com', array(), null ); + wp_add_inline_script( 'test-example', 'console.log("js");', 'after' ); + wp_add_inline_script( 'test-example', '{"key":"value"}', 'after', 'application/json' ); + wp_add_inline_script( 'test-example', 'console.log("more js");', 'after' ); + + $expected = "\n"; + $expected .= "\n"; + $expected .= "\n"; + + $output = get_echo( 'wp_print_scripts' ); + $this->assertEqualHTML( $expected, $output ); + + // Typed scripts must not appear in get_inline_script_data() output. + wp_enqueue_script( 'test-verify', 'example2.com', array(), null ); + wp_add_inline_script( 'test-verify', 'var x = 1;', 'after' ); + wp_add_inline_script( 'test-verify', '{"excluded":true}', 'after', 'application/ld+json' ); + + global $wp_scripts; + $data = $wp_scripts->get_inline_script_data( 'test-verify', 'after' ); + $this->assertStringContainsString( 'var x = 1;', $data ); + $this->assertStringNotContainsString( '{"excluded":true}', $data ); + } + + /** + * @ticket 51124 + * + * @covers ::wp_add_inline_script + * @covers WP_Scripts::get_inline_script_tag + */ + public function test_wp_add_inline_script_multiple_custom_types() { + wp_enqueue_script( 'test-example', 'example.com', array(), null ); + wp_add_inline_script( 'test-example', '{"config":1}', 'after', 'application/json' ); + wp_add_inline_script( 'test-example', '{"@type":"Thing"}', 'after', 'application/ld+json' ); + + $expected = "\n"; + $expected .= "\n"; + $expected .= "\n"; + + $this->assertEqualHTML( $expected, get_echo( 'wp_print_scripts' ) ); + } + /** * Normalizes markup for snapshot. *