PluginProbe
Gutenberg / 22.1.1
Gutenberg v22.1.1
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / lib / client-assets.php

client-assets.php in Gutenberg 22.1.1, at lib/client-assets.php

407 lines 14.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Functions to register client-side assets (scripts and stylesheets) specific
4 * for the Gutenberg editor plugin.
5 *
6 * @package gutenberg
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 die( 'Silence is golden.' );
11 }
12
13 /**
14 * Retrieves the root plugin path.
15 *
16 * @since 0.1.0
17 *
18 * @return string Root path to the gutenberg plugin.
19 */
20 function gutenberg_dir_path() {
21 return plugin_dir_path( __DIR__ );
22 }
23
24 /**
25 * Retrieves a URL to a file in the gutenberg plugin.
26 *
27 * @since 0.1.0
28 *
29 * @param string $path Relative path of the desired file.
30 *
31 * @return string Fully qualified URL pointing to the desired file.
32 */
33 function gutenberg_url( $path ) {
34 return plugins_url( $path, __DIR__ );
35 }
36
37 /**
38 * Filters the default translation file load behavior to load the Gutenberg
39 * plugin translation file, if available.
40 *
41 * @param string|false $file Path to the translation file to load. False if
42 * there isn't one.
43 * @param string $handle Name of the script to register a translation
44 * domain to.
45 *
46 * @return string|false Filtered path to the Gutenberg translation file, if
47 * available.
48 */
49 function gutenberg_override_translation_file( $file, $handle ) {
50 if ( ! $file ) {
51 return $file;
52 }
53
54 // Ignore scripts whose handle does not have the "wp-" prefix.
55 if ( ! str_starts_with( $handle, 'wp-' ) ) {
56 return $file;
57 }
58
59 // Ignore scripts that are not found in the expected `build/scripts/` location.
60 $script_path = gutenberg_dir_path() . 'build/scripts/' . substr( $handle, 3 ) . '/index.min.js';
61 if ( ! file_exists( $script_path ) ) {
62 return $file;
63 }
64
65 /*
66 * The default file will be in the plugins language directory, omitting the
67 * domain since Gutenberg assigns the script translations as the default.
68 *
69 * Example: /www/wp-content/languages/plugins/de_DE-07d88e6a803e01276b9bfcc1203e862e.json
70 *
71 * The logic of `load_script_textdomain` is such that it will assume to
72 * search in the plugins language directory, since the assigned source of
73 * the overridden Gutenberg script originates in the plugins directory.
74 *
75 * The plugin translation files each begin with the slug of the plugin, so
76 * it's a simple matter of prepending the Gutenberg plugin slug.
77 */
78 $path_parts = pathinfo( $file );
79 $plugin_translation_file = (
80 $path_parts['dirname'] .
81 '/gutenberg-' .
82 $path_parts['basename']
83 );
84
85 return $plugin_translation_file;
86 }
87 add_filter( 'load_script_translation_file', 'gutenberg_override_translation_file', 10, 2 );
88
89 /**
90 * Handle special case dependencies for wp-block-library that depend on runtime conditions.
91 *
92 * This adds the 'editor' dependency conditionally based on experiments and classic block requirements.
93 * All other script registrations are handled by the auto-generated build/scripts.php file.
94 *
95 * @param WP_Scripts $scripts WP_Scripts instance.
96 */
97 function gutenberg_register_block_library_script_special_case( $scripts ) {
98 $handle = 'wp-block-library';
99 $script = $scripts->query( $handle, 'registered' );
100 if (
101 ! gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ||
102 ! empty( $_GET['requiresTinymce'] ) ||
103 gutenberg_post_being_edited_requires_classic_block()
104 ) {
105 if ( ! in_array( 'editor', $script->deps, true ) ) {
106 $script->deps[] = 'editor';
107 }
108 }
109 }
110 add_action( 'wp_default_scripts', 'gutenberg_register_block_library_script_special_case', 11 );
111
112 /**
113 * Registers WordPress package styles with complex requirements.
114 *
115 * Simple styles (main style.css with inferred dependencies) are auto-registered
116 * via build/styles.php at default priority (10). This function runs at priority 15 to handle:
117 * - Multiple style files per package (content.css, classic.css, etc.)
118 * - Non-WordPress dependencies (dashicons, common, forms)
119 * - Custom handles that don't match wp-{package} pattern
120 * - Conditional dependencies based on theme/settings
121 * - Dynamic filename logic
122 *
123 * These override calls will replace the auto-registered versions as needed.
124 *
125 * @since 6.7.0
126 *
127 * @global array $editor_styles
128 *
129 * @param WP_Styles $styles WP_Styles instance.
130 */
131 function gutenberg_register_packages_styles( $styles ) {
132 // When in production, use the plugin's version as the asset version;
133 // else (for development or test) default to use the current time.
134 $version = defined( 'GUTENBERG_VERSION' ) && ! SCRIPT_DEBUG ? GUTENBERG_VERSION : time();
135
136 // wp-components: add dashicons (icon font dependency)
137 $styles->query( 'wp-components', 'registered' )->deps[] = 'dashicons';
138
139 // wp-edit-post: add wp-edit-blocks (custom handle not auto-inferred)
140 $styles->query( 'wp-edit-post', 'registered' )->deps[] = 'wp-edit-blocks';
141
142 // wp-edit-site: add core WP styles and custom handles
143 $edit_site_style = $styles->query( 'wp-edit-site', 'registered' );
144 $edit_site_style->deps[] = 'common';
145 $edit_site_style->deps[] = 'forms';
146 $edit_site_style->deps[] = 'wp-block-library-editor';
147
148 // wp-edit-widgets: add wp-edit-blocks (custom handle not auto-inferred)
149 $styles->query( 'wp-edit-widgets', 'registered' )->deps[] = 'wp-edit-blocks';
150
151 // wp-customize-widgets: add wp-edit-blocks (custom handle not auto-inferred)
152 $styles->query( 'wp-customize-widgets', 'registered' )->deps[] = 'wp-edit-blocks';
153
154 gutenberg_override_style(
155 $styles,
156 'wp-block-editor-content',
157 gutenberg_url( 'build/styles/block-editor/content.css' ),
158 array( 'wp-components' ),
159 $version
160 );
161 $styles->add_data( 'wp-block-editor-content', 'rtl', 'replace' );
162
163 $block_library_filename = wp_should_load_separate_core_block_assets() ? 'common' : 'style';
164 gutenberg_override_style(
165 $styles,
166 'wp-block-library',
167 gutenberg_url( 'build/styles/block-library/' . $block_library_filename . '.css' ),
168 array(),
169 $version
170 );
171 $styles->add_data( 'wp-block-library', 'rtl', 'replace' );
172 $styles->add_data( 'wp-block-library', 'path', gutenberg_dir_path() . 'build/styles/block-library/' . $block_library_filename . '.css' );
173
174 // Only add CONTENT styles here that should be enqueued in the iframe!
175 $wp_edit_blocks_dependencies = array(
176 'wp-components',
177 // This need to be added before the block library styles,
178 // The block library styles override the "reset" styles.
179 'wp-reset-editor-styles',
180 'wp-block-library',
181 // Until #37466, we can't specifically add them as editor styles yet,
182 // so we must hard-code it here as a dependency.
183 'wp-block-editor-content',
184 );
185
186 // Only load the default layout and margin styles for themes without theme.json file.
187 if ( ! wp_theme_has_theme_json() ) {
188 $wp_edit_blocks_dependencies[] = 'wp-editor-classic-layout-styles';
189 }
190
191 global $editor_styles;
192 if ( current_theme_supports( 'wp-block-styles' ) && ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 ) ) {
193 // Include opinionated block styles if the theme supports block styles and no $editor_styles are declared, so the editor never appears broken.
194 $wp_edit_blocks_dependencies[] = 'wp-block-library-theme';
195 }
196
197 gutenberg_override_style(
198 $styles,
199 'wp-reset-editor-styles',
200 gutenberg_url( 'build/styles/block-library/reset.css' ),
201 array( 'common', 'forms' ), // Make sure the reset is loaded after the default WP Admin styles.
202 $version
203 );
204 $styles->add_data( 'wp-reset-editor-styles', 'rtl', 'replace' );
205
206 gutenberg_override_style(
207 $styles,
208 'wp-editor-classic-layout-styles',
209 gutenberg_url( 'build/styles/edit-post/classic.css' ),
210 array(),
211 $version
212 );
213 $styles->add_data( 'wp-editor-classic-layout-styles', 'rtl', 'replace' );
214
215 gutenberg_override_style(
216 $styles,
217 'wp-block-library-editor',
218 gutenberg_url( 'build/styles/block-library/editor.css' ),
219 array(),
220 $version
221 );
222 $styles->add_data( 'wp-block-library-editor', 'rtl', 'replace' );
223
224 gutenberg_override_style(
225 $styles,
226 'wp-edit-blocks',
227 gutenberg_url( 'build/styles/block-library/editor.css' ),
228 $wp_edit_blocks_dependencies,
229 $version
230 );
231 $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' );
232
233 gutenberg_override_style(
234 $styles,
235 'wp-block-library-theme',
236 gutenberg_url( 'build/styles/block-library/theme.css' ),
237 array(),
238 $version
239 );
240 $styles->add_data( 'wp-block-library-theme', 'rtl', 'replace' );
241 }
242 add_action( 'wp_default_styles', 'gutenberg_register_packages_styles', 15 );
243
244 /**
245 * Fetches, processes and compiles stored core styles, then combines and renders them to the page.
246 * Styles are stored via the Style Engine API.
247 *
248 * This hook also exists, and should be backported to Core in future versions.
249 * However, it is envisaged that Gutenberg will continue to use the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes to aid continuous development.
250 *
251 * @since 6.1
252 *
253 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/
254 *
255 * @param array $options {
256 * Optional. An array of options to pass to gutenberg_style_engine_get_stylesheet_from_context(). Default empty array.
257 *
258 * @type bool $optimize Whether to optimize the CSS output, e.g., combine rules. Default is `false`.
259 * @type bool $prettify Whether to add new lines and indents to output. Default is the test of whether the global constant `SCRIPT_DEBUG` is defined.
260 * }
261 *
262 * @return void
263 */
264 function gutenberg_enqueue_stored_styles( $options = array() ) {
265 $is_block_theme = wp_is_block_theme();
266 $is_classic_theme = ! $is_block_theme;
267
268 /*
269 * For block themes, print stored styles in the header.
270 * For classic themes, in the footer.
271 */
272 if (
273 ( $is_block_theme && doing_action( 'wp_footer' ) ) ||
274 ( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) )
275 ) {
276 return;
277 }
278
279 $core_styles_keys = array( 'block-supports' );
280 $compiled_core_stylesheet = '';
281 $style_tag_id = 'core';
282 foreach ( $core_styles_keys as $style_key ) {
283 // Adds comment if code is prettified to identify core styles sections in debugging.
284 $should_prettify = isset( $options['prettify'] ) ? true === $options['prettify'] : SCRIPT_DEBUG;
285 if ( $should_prettify ) {
286 $compiled_core_stylesheet .= "/**\n * Core styles: $style_key\n */\n";
287 }
288 // Chains core store ids to signify what the styles contain.
289 $style_tag_id .= '-' . $style_key;
290 $compiled_core_stylesheet .= gutenberg_style_engine_get_stylesheet_from_context( $style_key, $options );
291 }
292
293 // Combines Core styles.
294 if ( ! empty( $compiled_core_stylesheet ) ) {
295 wp_register_style( $style_tag_id, false, array(), true );
296 wp_add_inline_style( $style_tag_id, $compiled_core_stylesheet );
297 wp_enqueue_style( $style_tag_id );
298 }
299
300 // If there are any other stores registered by themes etc., print them out.
301 $additional_stores = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_stores();
302
303 /*
304 * Since the corresponding action hook in Core is removed below,
305 * this function should still honour any styles stored using the Core Style Engine store.
306 */
307 if ( class_exists( 'WP_Style_Engine_CSS_Rules_Store' ) ) {
308 $additional_stores = array_merge( $additional_stores, WP_Style_Engine_CSS_Rules_Store::get_stores() );
309 }
310
311 foreach ( array_keys( $additional_stores ) as $store_name ) {
312 if ( in_array( $store_name, $core_styles_keys, true ) ) {
313 continue;
314 }
315 $styles = gutenberg_style_engine_get_stylesheet_from_context( $store_name, $options );
316 if ( ! empty( $styles ) ) {
317 $key = "wp-style-engine-$store_name";
318 wp_register_style( $key, false, array(), true );
319 wp_add_inline_style( $key, $styles );
320 wp_enqueue_style( $key );
321 }
322 }
323 }
324
325 /**
326 * Registers vendor JavaScript files to be used as dependencies of the editor
327 * and plugins.
328 *
329 * This function is called from a script during the plugin build process, so it
330 * should not call any WordPress PHP functions.
331 *
332 * @since 13.0
333 *
334 * @param WP_Scripts $scripts WP_Scripts instance.
335 */
336 function gutenberg_register_vendor_scripts( $scripts ) {
337 $extension = SCRIPT_DEBUG ? '.js' : '.min.js';
338
339 gutenberg_override_script(
340 $scripts,
341 'react',
342 gutenberg_url( 'build/scripts/vendors/react' . $extension ),
343 // See https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/TROUBLESHOOTING.md#externalising-react.
344 SCRIPT_DEBUG ? array( 'wp-react-refresh-entry', 'wp-polyfill' ) : array( 'wp-polyfill' ),
345 '18'
346 );
347 gutenberg_override_script(
348 $scripts,
349 'react-dom',
350 gutenberg_url( 'build/scripts/vendors/react-dom' . $extension ),
351 array( 'react' ),
352 '18'
353 );
354
355 gutenberg_override_script(
356 $scripts,
357 'react-jsx-runtime',
358 gutenberg_url( 'build/scripts/vendors/react-jsx-runtime' . $extension ),
359 array( 'react' ),
360 '18'
361 );
362 }
363 add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' );
364
365 /**
366 * Registers or re-registers Gutenberg Script Modules.
367 *
368 * Script modules that are registered by Core will be re-registered by Gutenberg.
369 *
370 * @since 19.3.0
371 */
372 function gutenberg_define_interactivity_modules_support() {
373 // Load the auto-generated module registry.
374 $modules_registry_file = gutenberg_dir_path() . 'build/modules/index.php';
375 if ( ! file_exists( $modules_registry_file ) ) {
376 return;
377 }
378
379 $modules = require $modules_registry_file;
380
381 // Add client navigation support to block library modules.
382 foreach ( $modules as $module ) {
383 if ( str_starts_with( $module['id'], '@wordpress/block-library' ) && method_exists( 'WP_Interactivity_API', 'add_client_navigation_support_to_script_module' ) ) {
384 wp_interactivity()->add_client_navigation_support_to_script_module( $module['id'] );
385 }
386 }
387 }
388 remove_action( 'wp_default_scripts', 'wp_define_interactivity_modules_support' );
389 add_action( 'wp_default_scripts', 'gutenberg_define_interactivity_modules_support' );
390
391 /**
392 * Always remove the Core action hook while gutenberg_enqueue_stored_styles() exists to avoid styles being printed twice.
393 * This is also because gutenberg_enqueue_stored_styles uses the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes,
394 * which are in continuous development and generally ahead of Core.
395 */
396 remove_action( 'wp_enqueue_scripts', 'wp_enqueue_stored_styles' );
397 remove_action( 'wp_footer', 'wp_enqueue_stored_styles', 1 );
398
399 // Enqueue stored styles.
400 add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_stored_styles' );
401 add_action( 'wp_footer', 'gutenberg_enqueue_stored_styles', 1 );
402
403 add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_latex_to_mathml_loader' );
404 function gutenberg_enqueue_latex_to_mathml_loader() {
405 wp_enqueue_script_module( '@wordpress/latex-to-mathml/loader' );
406 }
407