PluginProbe
Gutenberg / 22.9.0
Gutenberg v22.9.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.9.0, at lib/client-assets.php

464 lines 16.8 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 $suffix = SCRIPT_DEBUG ? '' : '.min';
136
137 // wp-components: add dashicons (icon font dependency)
138 $styles->query( 'wp-components', 'registered' )->deps[] = 'dashicons';
139
140 // wp-edit-post: add wp-edit-blocks (custom handle not auto-inferred)
141 $styles->query( 'wp-edit-post', 'registered' )->deps[] = 'wp-edit-blocks';
142
143 // wp-edit-site: add core WP styles and custom handles
144 $edit_site_style = $styles->query( 'wp-edit-site', 'registered' );
145 $edit_site_style->deps[] = 'common';
146 $edit_site_style->deps[] = 'forms';
147 $edit_site_style->deps[] = 'wp-block-library-editor';
148
149 // wp-edit-widgets: add wp-edit-blocks (custom handle not auto-inferred)
150 $styles->query( 'wp-edit-widgets', 'registered' )->deps[] = 'wp-edit-blocks';
151
152 // wp-customize-widgets: add wp-edit-blocks (custom handle not auto-inferred)
153 $styles->query( 'wp-customize-widgets', 'registered' )->deps[] = 'wp-edit-blocks';
154
155 // Register wp-base-styles and add it to the already registered wp-admin stylesheet
156 gutenberg_override_style(
157 $styles,
158 'wp-base-styles',
159 gutenberg_url( 'build/styles/base-styles/admin-schemes' . $suffix . '.css' ),
160 array(),
161 $version
162 );
163 $styles->add_data( 'wp-base-styles', 'rtl', 'replace' );
164 $styles->add_data( 'wp-base-styles', 'suffix', $suffix );
165 $styles->add_data( 'wp-base-styles', 'path', gutenberg_dir_path() . 'build/styles/base-styles/admin-schemes' . $suffix . '.css' );
166 $styles->query( 'wp-admin', 'registered' )->deps[] = 'wp-base-styles';
167
168 gutenberg_override_style(
169 $styles,
170 'wp-block-editor-content',
171 gutenberg_url( 'build/styles/block-editor/content' . $suffix . '.css' ),
172 array( 'wp-components' ),
173 $version
174 );
175 $styles->add_data( 'wp-block-editor-content', 'rtl', 'replace' );
176 $styles->add_data( 'wp-block-editor-content', 'suffix', $suffix );
177
178 $block_library_filename = wp_should_load_separate_core_block_assets() ? 'common' : 'style';
179 gutenberg_override_style(
180 $styles,
181 'wp-block-library',
182 gutenberg_url( 'build/styles/block-library/' . $block_library_filename . $suffix . '.css' ),
183 array(),
184 $version
185 );
186 $styles->add_data( 'wp-block-library', 'rtl', 'replace' );
187 $styles->add_data( 'wp-block-library', 'suffix', $suffix );
188 $styles->add_data( 'wp-block-library', 'path', gutenberg_dir_path() . 'build/styles/block-library/' . $block_library_filename . $suffix . '.css' );
189
190 // Only add CONTENT styles here that should be enqueued in the iframe!
191 $wp_edit_blocks_dependencies = array(
192 'wp-components',
193 // This need to be added before the block library styles,
194 // The block library styles override the "reset" styles.
195 'wp-reset-editor-styles',
196 'wp-block-library',
197 // Until #37466, we can't specifically add them as editor styles yet,
198 // so we must hard-code it here as a dependency.
199 'wp-block-editor-content',
200 'wp-base-styles',
201 );
202
203 // Only load the default layout and margin styles for themes without theme.json file.
204 if ( ! wp_theme_has_theme_json() ) {
205 $wp_edit_blocks_dependencies[] = 'wp-editor-classic-layout-styles';
206 }
207
208 global $editor_styles;
209 if ( current_theme_supports( 'wp-block-styles' ) && ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 ) ) {
210 // Include opinionated block styles if the theme supports block styles and no $editor_styles are declared, so the editor never appears broken.
211 $wp_edit_blocks_dependencies[] = 'wp-block-library-theme';
212 }
213
214 gutenberg_override_style(
215 $styles,
216 'wp-reset-editor-styles',
217 gutenberg_url( 'build/styles/block-library/reset' . $suffix . '.css' ),
218 array( 'common', 'forms' ), // Make sure the reset is loaded after the default WP Admin styles.
219 $version
220 );
221 $styles->add_data( 'wp-reset-editor-styles', 'rtl', 'replace' );
222 $styles->add_data( 'wp-reset-editor-styles', 'suffix', $suffix );
223
224 gutenberg_override_style(
225 $styles,
226 'wp-editor-classic-layout-styles',
227 gutenberg_url( 'build/styles/edit-post/classic' . $suffix . '.css' ),
228 array(),
229 $version
230 );
231 $styles->add_data( 'wp-editor-classic-layout-styles', 'rtl', 'replace' );
232 $styles->add_data( 'wp-editor-classic-layout-styles', 'suffix', $suffix );
233
234 gutenberg_override_style(
235 $styles,
236 'wp-block-library-editor',
237 gutenberg_url( 'build/styles/block-library/editor' . $suffix . '.css' ),
238 array(),
239 $version
240 );
241 $styles->add_data( 'wp-block-library-editor', 'rtl', 'replace' );
242 $styles->add_data( 'wp-block-library-editor', 'suffix', $suffix );
243
244 gutenberg_override_style(
245 $styles,
246 'wp-edit-blocks',
247 gutenberg_url( 'build/styles/block-library/editor' . $suffix . '.css' ),
248 $wp_edit_blocks_dependencies,
249 $version
250 );
251 $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' );
252 $styles->add_data( 'wp-edit-blocks', 'suffix', $suffix );
253
254 gutenberg_override_style(
255 $styles,
256 'wp-block-library-theme',
257 gutenberg_url( 'build/styles/block-library/theme' . $suffix . '.css' ),
258 array(),
259 $version
260 );
261 $styles->add_data( 'wp-block-library-theme', 'rtl', 'replace' );
262 $styles->add_data( 'wp-block-library-theme', 'suffix', $suffix );
263
264 gutenberg_override_style(
265 $styles,
266 'classic-theme-styles',
267 gutenberg_url( 'build/styles/block-library/classic' . $suffix . '.css' ),
268 array(),
269 $version
270 );
271 $styles->add_data( 'classic-theme-styles', 'rtl', 'replace' );
272 $styles->add_data( 'classic-theme-styles', 'suffix', $suffix );
273 $styles->add_data( 'classic-theme-styles', 'path', gutenberg_dir_path() . 'build/styles/block-library/classic' . $suffix . '.css' );
274 }
275 add_action( 'wp_default_styles', 'gutenberg_register_packages_styles', 15 );
276
277 /**
278 * Fetches, processes and compiles stored core styles, then combines and renders them to the page.
279 * Styles are stored via the Style Engine API.
280 *
281 * This hook also exists, and should be backported to Core in future versions.
282 * However, it is envisaged that Gutenberg will continue to use the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes to aid continuous development.
283 *
284 * @since 6.1
285 *
286 * @link https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/
287 *
288 * @param array $options {
289 * Optional. An array of options to pass to gutenberg_style_engine_get_stylesheet_from_context(). Default empty array.
290 *
291 * @type bool $optimize Whether to optimize the CSS output, e.g., combine rules. Default is `false`.
292 * @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.
293 * }
294 *
295 * @return void
296 */
297 function gutenberg_enqueue_stored_styles( $options = array() ) {
298 $is_block_theme = wp_is_block_theme();
299 $is_classic_theme = ! $is_block_theme;
300
301 /*
302 * For block themes, print stored styles in the header.
303 * For classic themes, in the footer.
304 */
305 if (
306 ( $is_block_theme && doing_action( 'wp_footer' ) ) ||
307 ( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) )
308 ) {
309 return;
310 }
311
312 $core_styles_keys = array( 'block-supports' );
313 $compiled_core_stylesheet = '';
314 $style_tag_id = 'core';
315 foreach ( $core_styles_keys as $style_key ) {
316 // Adds comment if code is prettified to identify core styles sections in debugging.
317 $should_prettify = isset( $options['prettify'] ) ? true === $options['prettify'] : SCRIPT_DEBUG;
318 if ( $should_prettify ) {
319 $compiled_core_stylesheet .= "/**\n * Core styles: $style_key\n */\n";
320 }
321 // Chains core store ids to signify what the styles contain.
322 $style_tag_id .= '-' . $style_key;
323 $compiled_core_stylesheet .= gutenberg_style_engine_get_stylesheet_from_context( $style_key, $options );
324 }
325
326 // Combines Core styles.
327 if ( ! empty( $compiled_core_stylesheet ) ) {
328 wp_register_style( $style_tag_id, false, array(), true );
329 wp_add_inline_style( $style_tag_id, $compiled_core_stylesheet );
330 wp_enqueue_style( $style_tag_id );
331 }
332
333 // If there are any other stores registered by themes etc., print them out.
334 $additional_stores = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_stores();
335
336 /*
337 * Since the corresponding action hook in Core is removed below,
338 * this function should still honour any styles stored using the Core Style Engine store.
339 */
340 if ( class_exists( 'WP_Style_Engine_CSS_Rules_Store' ) ) {
341 $additional_stores = array_merge( $additional_stores, WP_Style_Engine_CSS_Rules_Store::get_stores() );
342 }
343
344 foreach ( array_keys( $additional_stores ) as $store_name ) {
345 if ( in_array( $store_name, $core_styles_keys, true ) ) {
346 continue;
347 }
348 $styles = gutenberg_style_engine_get_stylesheet_from_context( $store_name, $options );
349 if ( ! empty( $styles ) ) {
350 $key = "wp-style-engine-$store_name";
351 wp_register_style( $key, false, array(), true );
352 wp_add_inline_style( $key, $styles );
353 wp_enqueue_style( $key );
354 }
355 }
356 }
357
358 /**
359 * Registers vendor JavaScript files to be used as dependencies of the editor
360 * and plugins.
361 *
362 * This function is called from a script during the plugin build process, so it
363 * should not call any WordPress PHP functions.
364 *
365 * @since 13.0
366 *
367 * @param WP_Scripts $scripts WP_Scripts instance.
368 */
369 function gutenberg_register_vendor_scripts( $scripts ) {
370 $extension = SCRIPT_DEBUG ? '.js' : '.min.js';
371 $vendors_dir = gutenberg_dir_path() . 'build/scripts/vendors/';
372
373 $vendor_handles = array( 'react', 'react-dom', 'react-jsx-runtime' );
374
375 foreach ( $vendor_handles as $handle ) {
376 $asset_file = $vendors_dir . $handle . '.min.asset.php';
377 $asset = file_exists( $asset_file ) ? require $asset_file : array();
378 $dependencies = $asset['dependencies'] ?? array();
379 $version = $asset['version'] ?? '0';
380
381 gutenberg_override_script(
382 $scripts,
383 $handle,
384 gutenberg_url( 'build/scripts/vendors/' . $handle . $extension ),
385 $dependencies,
386 $version
387 );
388 }
389
390 // WordPress Core in `wp_register_development_scripts` sets `wp-react-refresh-entry`
391 // as a dependency to `react` when `SCRIPT_DEBUG` is true. Preserve that here.
392 if ( SCRIPT_DEBUG ) {
393 $react = $scripts->query( 'react', 'registered' );
394 if ( $react && ! in_array( 'wp-react-refresh-entry', $react->deps, true ) ) {
395 $react->deps[] = 'wp-react-refresh-entry';
396 }
397 }
398 }
399 add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' );
400
401 /**
402 * Registers or re-registers Gutenberg Script Modules.
403 *
404 * Script modules that are registered by Core will be re-registered by Gutenberg.
405 *
406 * @since 19.3.0
407 */
408 function gutenberg_define_interactivity_modules_support() {
409 // Load the auto-generated module registry.
410 $modules_registry_file = gutenberg_dir_path() . 'build/modules/index.php';
411 if ( ! file_exists( $modules_registry_file ) ) {
412 return;
413 }
414
415 $modules = require $modules_registry_file;
416
417 // Add client navigation support to block library modules.
418 foreach ( $modules as $module ) {
419 if ( str_starts_with( $module['id'], '@wordpress/block-library' ) && method_exists( 'WP_Interactivity_API', 'add_client_navigation_support_to_script_module' ) ) {
420 wp_interactivity()->add_client_navigation_support_to_script_module( $module['id'] );
421 }
422 }
423 }
424 remove_action( 'wp_default_scripts', 'wp_define_interactivity_modules_support' );
425 add_action( 'wp_default_scripts', 'gutenberg_define_interactivity_modules_support' );
426
427 /**
428 * Always remove the Core action hook while gutenberg_enqueue_stored_styles() exists to avoid styles being printed twice.
429 * This is also because gutenberg_enqueue_stored_styles uses the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes,
430 * which are in continuous development and generally ahead of Core.
431 */
432 remove_action( 'wp_enqueue_scripts', 'wp_enqueue_stored_styles' );
433 remove_action( 'wp_footer', 'wp_enqueue_stored_styles', 1 );
434
435 // Enqueue stored styles.
436 add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_stored_styles' );
437 add_action( 'wp_footer', 'gutenberg_enqueue_stored_styles', 1 );
438
439 add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_latex_to_mathml_loader' );
440 function gutenberg_enqueue_latex_to_mathml_loader() {
441 wp_enqueue_script_module( '@wordpress/latex-to-mathml/loader' );
442 }
443
444 /**
445 * Enqueue the vips loader script module in the block editor.
446 *
447 * This registers @wordpress/vips/worker as a dynamic dependency in the import map,
448 * enabling on-demand loading of the ~3.8MB WASM-based image processing module
449 * when client-side media processing is triggered via @wordpress/upload-media.
450 *
451 * @see packages/vips/src/loader.ts
452 */
453 if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
454 add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_vips_loader' );
455 }
456 function gutenberg_enqueue_vips_loader() {
457 wp_enqueue_script_module( '@wordpress/vips/loader' );
458 }
459
460 add_action( 'admin_enqueue_scripts', 'gutenberg_enqueue_core_abilities' );
461 function gutenberg_enqueue_core_abilities() {
462 wp_enqueue_script_module( '@wordpress/core-abilities' );
463 }
464