PluginProbe
Gutenberg / 22.3.0
Gutenberg v22.3.0
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.3.0, at lib/client-assets.php

422 lines 14.6 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 gutenberg_override_style(
243 $styles,
244 'classic-theme-styles',
245 gutenberg_url( 'build/styles/block-library/classic.css' ),
246 array(),
247 $version
248 );
249 $styles->add_data( 'classic-theme-styles', 'rtl', 'replace' );
250 $styles->add_data( 'classic-theme-styles', 'path', gutenberg_dir_path() . 'build/styles/block-library/classic.css' );
251 }
252 add_action( 'wp_default_styles', 'gutenberg_register_packages_styles', 15 );
253
254 /**
255 * Fetches, processes and compiles stored core styles, then combines and renders them to the page.
256 * Styles are stored via the Style Engine API.
257 *
258 * This hook also exists, and should be backported to Core in future versions.
259 * However, it is envisaged that Gutenberg will continue to use the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes to aid continuous development.
260 *
261 * @since 6.1
262 *
263 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/
264 *
265 * @param array $options {
266 * Optional. An array of options to pass to gutenberg_style_engine_get_stylesheet_from_context(). Default empty array.
267 *
268 * @type bool $optimize Whether to optimize the CSS output, e.g., combine rules. Default is `false`.
269 * @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.
270 * }
271 *
272 * @return void
273 */
274 function gutenberg_enqueue_stored_styles( $options = array() ) {
275 $is_block_theme = wp_is_block_theme();
276 $is_classic_theme = ! $is_block_theme;
277
278 /*
279 * For block themes, print stored styles in the header.
280 * For classic themes, in the footer.
281 */
282 if (
283 ( $is_block_theme && doing_action( 'wp_footer' ) ) ||
284 ( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) )
285 ) {
286 return;
287 }
288
289 $core_styles_keys = array( 'block-supports' );
290 $compiled_core_stylesheet = '';
291 $style_tag_id = 'core';
292 foreach ( $core_styles_keys as $style_key ) {
293 // Adds comment if code is prettified to identify core styles sections in debugging.
294 $should_prettify = isset( $options['prettify'] ) ? true === $options['prettify'] : SCRIPT_DEBUG;
295 if ( $should_prettify ) {
296 $compiled_core_stylesheet .= "/**\n * Core styles: $style_key\n */\n";
297 }
298 // Chains core store ids to signify what the styles contain.
299 $style_tag_id .= '-' . $style_key;
300 $compiled_core_stylesheet .= gutenberg_style_engine_get_stylesheet_from_context( $style_key, $options );
301 }
302
303 // Combines Core styles.
304 if ( ! empty( $compiled_core_stylesheet ) ) {
305 wp_register_style( $style_tag_id, false, array(), true );
306 wp_add_inline_style( $style_tag_id, $compiled_core_stylesheet );
307 wp_enqueue_style( $style_tag_id );
308 }
309
310 // If there are any other stores registered by themes etc., print them out.
311 $additional_stores = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_stores();
312
313 /*
314 * Since the corresponding action hook in Core is removed below,
315 * this function should still honour any styles stored using the Core Style Engine store.
316 */
317 if ( class_exists( 'WP_Style_Engine_CSS_Rules_Store' ) ) {
318 $additional_stores = array_merge( $additional_stores, WP_Style_Engine_CSS_Rules_Store::get_stores() );
319 }
320
321 foreach ( array_keys( $additional_stores ) as $store_name ) {
322 if ( in_array( $store_name, $core_styles_keys, true ) ) {
323 continue;
324 }
325 $styles = gutenberg_style_engine_get_stylesheet_from_context( $store_name, $options );
326 if ( ! empty( $styles ) ) {
327 $key = "wp-style-engine-$store_name";
328 wp_register_style( $key, false, array(), true );
329 wp_add_inline_style( $key, $styles );
330 wp_enqueue_style( $key );
331 }
332 }
333 }
334
335 /**
336 * Registers vendor JavaScript files to be used as dependencies of the editor
337 * and plugins.
338 *
339 * This function is called from a script during the plugin build process, so it
340 * should not call any WordPress PHP functions.
341 *
342 * @since 13.0
343 *
344 * @param WP_Scripts $scripts WP_Scripts instance.
345 */
346 function gutenberg_register_vendor_scripts( $scripts ) {
347 $extension = SCRIPT_DEBUG ? '.js' : '.min.js';
348
349 gutenberg_override_script(
350 $scripts,
351 'react',
352 gutenberg_url( 'build/scripts/vendors/react' . $extension ),
353 // See https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/TROUBLESHOOTING.md#externalising-react.
354 SCRIPT_DEBUG ? array( 'wp-react-refresh-entry', 'wp-polyfill' ) : array( 'wp-polyfill' ),
355 '18'
356 );
357 gutenberg_override_script(
358 $scripts,
359 'react-dom',
360 gutenberg_url( 'build/scripts/vendors/react-dom' . $extension ),
361 array( 'react' ),
362 '18'
363 );
364
365 gutenberg_override_script(
366 $scripts,
367 'react-jsx-runtime',
368 gutenberg_url( 'build/scripts/vendors/react-jsx-runtime' . $extension ),
369 array( 'react' ),
370 '18'
371 );
372 }
373 add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' );
374
375 /**
376 * Registers or re-registers Gutenberg Script Modules.
377 *
378 * Script modules that are registered by Core will be re-registered by Gutenberg.
379 *
380 * @since 19.3.0
381 */
382 function gutenberg_define_interactivity_modules_support() {
383 // Load the auto-generated module registry.
384 $modules_registry_file = gutenberg_dir_path() . 'build/modules/index.php';
385 if ( ! file_exists( $modules_registry_file ) ) {
386 return;
387 }
388
389 $modules = require $modules_registry_file;
390
391 // Add client navigation support to block library modules.
392 foreach ( $modules as $module ) {
393 if ( str_starts_with( $module['id'], '@wordpress/block-library' ) && method_exists( 'WP_Interactivity_API', 'add_client_navigation_support_to_script_module' ) ) {
394 wp_interactivity()->add_client_navigation_support_to_script_module( $module['id'] );
395 }
396 }
397 }
398 remove_action( 'wp_default_scripts', 'wp_define_interactivity_modules_support' );
399 add_action( 'wp_default_scripts', 'gutenberg_define_interactivity_modules_support' );
400
401 /**
402 * Always remove the Core action hook while gutenberg_enqueue_stored_styles() exists to avoid styles being printed twice.
403 * This is also because gutenberg_enqueue_stored_styles uses the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes,
404 * which are in continuous development and generally ahead of Core.
405 */
406 remove_action( 'wp_enqueue_scripts', 'wp_enqueue_stored_styles' );
407 remove_action( 'wp_footer', 'wp_enqueue_stored_styles', 1 );
408
409 // Enqueue stored styles.
410 add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_stored_styles' );
411 add_action( 'wp_footer', 'gutenberg_enqueue_stored_styles', 1 );
412
413 add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_latex_to_mathml_loader' );
414 function gutenberg_enqueue_latex_to_mathml_loader() {
415 wp_enqueue_script_module( '@wordpress/latex-to-mathml/loader' );
416 }
417
418 add_action( 'admin_enqueue_scripts', 'gutenberg_enqueue_core_abilities' );
419 function gutenberg_enqueue_core_abilities() {
420 wp_enqueue_script_module( '@wordpress/core-abilities' );
421 }
422