@@ -197,3 +197,94 @@ function gutenberg_dequeue_module( $module_identifier ) {
197197 _deprecated_function ( __FUNCTION__ , 'Gutenberg 17.6.0 ' , 'wp_dequeue_script_module ' );
198198 wp_script_modules ()->dequeue ( $ module_identifier );
199199}
200+
201+
202+ /**
203+ * Print data associated with Script Modules in Script tags.
204+ *
205+ * This embeds data in the page HTML so that it is available on page load.
206+ *
207+ * Data can be associated with a given Script Module by using the
208+ * `scriptmoduledata_{$module_id}` filter.
209+ *
210+ * The data for a given Script Module will be JSON serialized in a script tag with an ID
211+ * like `wp-scriptmodule-data_{$module_id}`.
212+ */
213+ function gutenberg_print_script_module_data (): void {
214+ $ get_marked_for_enqueue = new ReflectionMethod ( 'WP_Script_Modules ' , 'get_marked_for_enqueue ' );
215+ $ get_import_map = new ReflectionMethod ( 'WP_Script_Modules ' , 'get_import_map ' );
216+
217+ $ modules = array ();
218+ foreach ( array_keys ( $ get_marked_for_enqueue ->invoke ( wp_script_modules () ) ) as $ id ) {
219+ $ modules [ $ id ] = true ;
220+ }
221+ foreach ( array_keys ( $ get_import_map ->invoke ( wp_script_modules () )['imports ' ] ) as $ id ) {
222+ $ modules [ $ id ] = true ;
223+ }
224+
225+ foreach ( array_keys ( $ modules ) as $ module_id ) {
226+ /**
227+ * Filters data associated with a given Script Module.
228+ *
229+ * Script Modules may require data that is required for initialization or is essential to
230+ * have immediately available on page load. These are suitable use cases for this data.
231+ *
232+ * This is best suited to a minimal set of data and is not intended to replace the REST API.
233+ *
234+ * If the filter returns no data (an empty array), nothing will be embedded in the page.
235+ *
236+ * The data for a given Script Module, if provided, will be JSON serialized in a script tag
237+ * with an ID like `wp-scriptmodule-data_{$module_id}`.
238+ *
239+ * The dynamic portion of the hook name, `$module_id`, refers to the Script Module ID that
240+ * the data is associated with.
241+ *
242+ * @param array $data The data that should be associated with the array.
243+ */
244+ $ data = apply_filters ( "scriptmoduledata_ {$ module_id }" , array () );
245+
246+ if ( is_array ( $ data ) && ! empty ( $ data ) ) {
247+ /*
248+ * This data will be printed as JSON inside a script tag like this:
249+ * <script type="application/json"></script>
250+ *
251+ * A script tag must be closed by a sequence beginning with `</`. It's impossible to
252+ * close a script tag without using `<`. We ensure that `<` is escaped and `/` can
253+ * remain unescaped, so `</script>` will be printed as `\u003C/script\u00E3`.
254+ *
255+ * - JSON_HEX_TAG: All < and > are converted to \u003C and \u003E.
256+ * - JSON_UNESCAPED_SLASHES: Don't escape /.
257+ *
258+ * If the page will use UTF-8 encoding, it's safe to print unescaped unicode:
259+ *
260+ * - JSON_UNESCAPED_UNICODE: Encode multibyte Unicode characters literally (instead of as `\uXXXX`).
261+ * - JSON_UNESCAPED_LINE_TERMINATORS: The line terminators are kept unescaped when
262+ * JSON_UNESCAPED_UNICODE is supplied. It uses the same behaviour as it was
263+ * before PHP 7.1 without this constant. Available as of PHP 7.1.0.
264+ *
265+ * The JSON specification requires encoding in UTF-8, so if the generated HTML page
266+ * is not encoded in UTF-8 then it's not safe to include those literals. They must
267+ * be escaped to avoid encoding issues.
268+ *
269+ * @see https://www.rfc-editor.org/rfc/rfc8259.html for details on encoding requirements.
270+ * @see https://www.php.net/manual/en/json.constants.php for details on these constants.
271+ * @see https://html.spec.whatwg.org/#script-data-state for details on script tag parsing.
272+ */
273+ $ json_encode_flags = JSON_HEX_TAG | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_LINE_TERMINATORS ;
274+ if ( 'UTF-8 ' !== get_option ( 'blog_charset ' ) ) {
275+ $ json_encode_flags = JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ;
276+ }
277+
278+ wp_print_inline_script_tag (
279+ wp_json_encode ( $ data , $ json_encode_flags ),
280+ array (
281+ 'type ' => 'application/json ' ,
282+ 'id ' => "wp-scriptmodule-data_ {$ module_id }" ,
283+ )
284+ );
285+ }
286+ }
287+ }
288+
289+ add_action ( 'wp_footer ' , 'gutenberg_print_script_module_data ' );
290+ add_action ( 'admin_print_footer_scripts ' , 'gutenberg_print_script_module_data ' );
0 commit comments