PluginProbe
Gutenberg / 23.4.0
Gutenberg v23.4.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 23.4.0, at lib/client-assets.php

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