PluginProbe
Gutenberg / 24.0.0
Gutenberg v24.0.0
24.0.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 All 403 releases
← All changes | lib/client-assets.php +273 -461 12.6.024.0.0 View file →
@@ -1,8 +1,8 @@
1 1 <?php
2 2 /**
3 - * Functions to register client-side assets (scripts and stylesheets) for the
4 - * Gutenberg editor plugin.
3 + * Functions to register client-side assets (scripts and stylesheets) specific
4 + * for the Gutenberg editor plugin.
5 5 *
6 6 * @package gutenberg
7 7 */
8 8
@@ -12,11 +12,11 @@
12 12
13 13 /**
14 14 * Retrieves the root plugin path.
15 15 *
16 + * @since 0.1.0
17 + *
16 18 * @return string Root path to the gutenberg plugin.
17 - *
18 - * @since 0.1.0
19 19 */
20 20 function gutenberg_dir_path() {
21 21 return plugin_dir_path( __DIR__ );
22 22 }
@@ -23,13 +23,13 @@
23 23
24 24 /**
25 25 * Retrieves a URL to a file in the gutenberg plugin.
26 26 *
27 - * @param string $path Relative path of the desired file.
27 + * @since 0.1.0
28 28 *
29 - * @return string Fully qualified URL pointing to the desired file.
29 + * @param string $path Relative path of the desired file.
30 30 *
31 - * @since 0.1.0
31 + * @return string Fully qualified URL pointing to the desired file.
32 32 */
33 33 function gutenberg_url( $path ) {
34 34 return plugins_url( $path, __DIR__ );
35 35 }
@@ -34,85 +34,8 @@
34 34 return plugins_url( $path, __DIR__ );
35 35 }
36 36
37 37 /**
38 - * Registers a script according to `wp_register_script`. Honors this request by
39 - * reassigning internal dependency properties of any script handle already
40 - * registered by that name. It does not deregister the original script, to
41 - * avoid losing inline scripts which may have been attached.
42 - *
43 - * @since 4.1.0
44 - *
45 - * @param WP_Scripts $scripts WP_Scripts instance.
46 - * @param string $handle Name of the script. Should be unique.
47 - * @param string $src Full URL of the script, or path of the script relative to the WordPress root directory.
48 - * @param array $deps Optional. An array of registered script handles this script depends on. Default empty array.
49 - * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL
50 - * as a query string for cache busting purposes. If version is set to false, a version
51 - * number is automatically added equal to current installed WordPress version.
52 - * If set to null, no version is added.
53 - * @param bool $in_footer Optional. Whether to enqueue the script before </body> instead of in the <head>.
54 - * Default 'false'.
55 - */
56 -function gutenberg_override_script( $scripts, $handle, $src, $deps = array(), $ver = false, $in_footer = false ) {
57 - $script = $scripts->query( $handle, 'registered' );
58 - if ( $script ) {
59 - /*
60 - * In many ways, this is a reimplementation of `wp_register_script` but
61 - * bypassing consideration of whether a script by the given handle had
62 - * already been registered.
63 - */
64 -
65 - // See: `_WP_Dependency::__construct` .
66 - $script->src = $src;
67 - $script->deps = $deps;
68 - $script->ver = $ver;
69 - $script->args = $in_footer;
70 -
71 - /*
72 - * The script's `group` designation is an indication of whether it is
73 - * to be printed in the header or footer. The behavior here defers to
74 - * the arguments as passed. Specifically, group data is not assigned
75 - * for a script unless it is designated to be printed in the footer.
76 - */
77 -
78 - // See: `wp_register_script` .
79 - unset( $script->extra['group'] );
80 - if ( $in_footer ) {
81 - $script->add_data( 'group', 1 );
82 - }
83 - } else {
84 - $scripts->add( $handle, $src, $deps, $ver, $in_footer );
85 - }
86 -
87 - /*
88 - * `WP_Dependencies::set_translations` will fall over on itself if setting
89 - * translations on the `wp-i18n` handle, since it internally adds `wp-i18n`
90 - * as a dependency of itself, exhausting memory. The same applies for the
91 - * polyfill and hooks scripts, which are dependencies _of_ `wp-i18n`.
92 - *
93 - * See: https://core.trac.wordpress.org/ticket/46089
94 - */
95 - if ( ! in_array( $handle, array( 'wp-i18n', 'wp-polyfill', 'wp-hooks' ), true ) ) {
96 - $scripts->set_translations( $handle, 'default' );
97 - }
98 -
99 - /*
100 - * Wp-editor module is exposed as window.wp.editor.
101 - * Problem: there is quite some code expecting window.wp.oldEditor object available under window.wp.editor.
102 - * Solution: fuse the two objects together to maintain backward compatibility.
103 - * For more context, see https://github.com/WordPress/gutenberg/issues/33203
104 - */
105 - if ( 'wp-editor' === $handle ) {
106 - $scripts->add_inline_script(
107 - 'wp-editor',
108 - 'Object.assign( window.wp.editor, window.wp.oldEditor );',
109 - 'after'
110 - );
111 - }
112 -}
113 -
114 -/**
115 38 * Filters the default translation file load behavior to load the Gutenberg
116 39 * plugin translation file, if available.
117 40 *
118 41 * @param string|false $file Path to the translation file to load. False if
@@ -128,14 +51,14 @@
128 51 return $file;
129 52 }
130 53
131 54 // Ignore scripts whose handle does not have the "wp-" prefix.
132 - if ( 'wp-' !== substr( $handle, 0, 3 ) ) {
55 + if ( ! str_starts_with( $handle, 'wp-' ) ) {
133 56 return $file;
134 57 }
135 58
136 - // Ignore scripts that are not found in the expected `build/` location.
137 - $script_path = gutenberg_dir_path() . 'build/' . substr( $handle, 3 ) . '/index.min.js';
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';
138 61 if ( ! file_exists( $script_path ) ) {
139 62 return $file;
140 63 }
141 64
@@ -163,208 +86,137 @@
163 86 }
164 87 add_filter( 'load_script_translation_file', 'gutenberg_override_translation_file', 10, 2 );
165 88
166 89 /**
167 - * Registers a style according to `wp_register_style`. Honors this request by
168 - * deregistering any style by the same handler before registration.
90 + * Adds the 'editor' dependency to wp-block-library, required by the Classic block.
169 91 *
170 - * @since 4.1.0
171 - *
172 - * @param WP_Styles $styles WP_Styles instance.
173 - * @param string $handle Name of the stylesheet. Should be unique.
174 - * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory.
175 - * @param array $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array.
176 - * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL
177 - * as a query string for cache busting purposes. If version is set to false, a version
178 - * number is automatically added equal to current installed WordPress version.
179 - * If set to null, no version is added.
180 - * @param string $media Optional. The media for which this stylesheet has been defined.
181 - * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like
182 - * '(orientation: portrait)' and '(max-width: 640px)'.
92 + * @param WP_Scripts $scripts WP_Scripts instance.
183 93 */
184 -function gutenberg_override_style( $styles, $handle, $src, $deps = array(), $ver = false, $media = 'all' ) {
185 - $style = $styles->query( $handle, 'registered' );
186 - if ( $style ) {
187 - $styles->remove( $handle );
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';
188 99 }
189 - $styles->add( $handle, $src, $deps, $ver, $media );
190 100 }
101 +add_action( 'wp_default_scripts', 'gutenberg_register_block_library_script_special_case', 11 );
191 102
192 103 /**
193 - * Registers vendor JavaScript files to be used as dependencies of the editor
194 - * and plugins.
104 + * Registers WordPress package styles with complex requirements.
195 105 *
196 - * This function is called from a script during the plugin build process, so it
197 - * should not call any WordPress PHP functions.
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
198 113 *
199 - * @since 0.1.0
114 + * These override calls will replace the auto-registered versions as needed.
200 115 *
201 - * @param WP_Scripts $scripts WP_Scripts instance.
202 - */
203 -function gutenberg_register_vendor_scripts( $scripts ) {
204 - $suffix = SCRIPT_DEBUG ? '' : '.min';
205 -
206 - $react_suffix = ( SCRIPT_DEBUG ? '.development' : '.production' ) . $suffix;
207 - gutenberg_register_vendor_script(
208 - $scripts,
209 - 'react',
210 - 'https://unpkg.com/react@17.0.1/umd/react' . $react_suffix . '.js',
211 - // See https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/TROUBLESHOOTING.md#externalising-react.
212 - SCRIPT_DEBUG ? array( 'wp-react-refresh-entry', 'wp-polyfill' ) : array( 'wp-polyfill' )
213 - );
214 - gutenberg_register_vendor_script(
215 - $scripts,
216 - 'react-dom',
217 - 'https://unpkg.com/react-dom@17.0.1/umd/react-dom' . $react_suffix . '.js',
218 - array( 'react' )
219 - );
220 -}
221 -add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' );
222 -
223 -/**
224 - * Registers all the WordPress packages scripts that are in the standardized
225 - * `build/` location.
116 + * @since 6.7.0
226 117 *
227 - * @since 4.5.0
118 + * @global array $editor_styles
228 119 *
229 - * @param WP_Scripts $scripts WP_Scripts instance.
120 + * @param WP_Styles $styles WP_Styles instance.
230 121 */
231 -function gutenberg_register_packages_scripts( $scripts ) {
232 - // When in production, use the plugin's version as the default asset version;
122 +function gutenberg_register_packages_styles( $styles ) {
123 + // When in production, use the plugin's version as the asset version;
233 124 // else (for development or test) default to use the current time.
234 - $default_version = defined( 'GUTENBERG_VERSION' ) && ! ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? GUTENBERG_VERSION : time();
125 + $version = defined( 'GUTENBERG_VERSION' ) && ! SCRIPT_DEBUG ? GUTENBERG_VERSION : time();
126 + $suffix = SCRIPT_DEBUG ? '' : '.min';
235 127
236 - foreach ( glob( gutenberg_dir_path() . 'build/*/index.min.js' ) as $path ) {
237 - // Prefix `wp-` to package directory to get script handle.
238 - // For example, `…/build/a11y/index.min.js` becomes `wp-a11y`.
239 - $handle = 'wp-' . basename( dirname( $path ) );
128 + // wp-components: add dashicons (icon font dependency) and design tokens.
129 + $components_style = $styles->query( 'wp-components', 'registered' );
130 + $components_style->deps[] = 'dashicons';
131 + $components_style->deps[] = 'wp-theme';
240 132
241 - // Replace extension with `.asset.php` to find the generated dependencies file.
242 - $asset_file = substr( $path, 0, -( strlen( '.js' ) ) ) . '.asset.php';
243 - $asset = file_exists( $asset_file )
244 - ? require( $asset_file )
245 - : null;
246 - $dependencies = isset( $asset['dependencies'] ) ? $asset['dependencies'] : array();
247 - $version = isset( $asset['version'] ) ? $asset['version'] : $default_version;
133 + // wp-edit-post: add wp-edit-blocks (custom handle not auto-inferred)
134 + $styles->query( 'wp-edit-post', 'registered' )->deps[] = 'wp-edit-blocks';
248 135
249 - // Add dependencies that cannot be detected and generated by build tools.
250 - switch ( $handle ) {
251 - case 'wp-block-library':
252 - array_push( $dependencies, 'editor' );
253 - break;
136 + // wp-edit-site: add core WP styles and custom handles
137 + $edit_site_style = $styles->query( 'wp-edit-site', 'registered' );
138 + $edit_site_style->deps[] = 'common';
139 + $edit_site_style->deps[] = 'forms';
140 + $edit_site_style->deps[] = 'wp-block-library-editor';
254 141
255 - case 'wp-edit-post':
256 - array_push( $dependencies, 'media-models', 'media-views', 'postbox' );
257 - break;
142 + // wp-edit-widgets: add wp-edit-blocks (custom handle not auto-inferred)
143 + $styles->query( 'wp-edit-widgets', 'registered' )->deps[] = 'wp-edit-blocks';
258 144
259 - case 'wp-edit-site':
260 - array_push( $dependencies, 'wp-dom-ready' );
261 - break;
262 - }
145 + // wp-customize-widgets: add wp-edit-blocks (custom handle not auto-inferred)
146 + $styles->query( 'wp-customize-widgets', 'registered' )->deps[] = 'wp-edit-blocks';
263 147
264 - // Get the path from Gutenberg directory as expected by `gutenberg_url`.
265 - $gutenberg_path = substr( $path, strlen( gutenberg_dir_path() ) );
266 -
267 - gutenberg_override_script(
268 - $scripts,
269 - $handle,
270 - gutenberg_url( $gutenberg_path ),
271 - $dependencies,
272 - $version,
273 - true
274 - );
275 - }
276 -}
277 -add_action( 'wp_default_scripts', 'gutenberg_register_packages_scripts' );
278 -
279 -/**
280 - * Registers all the WordPress packages styles that are in the standardized
281 - * `build/` location.
282 - *
283 - * @since 6.7.0
284 -
285 - * @param WP_Styles $styles WP_Styles instance.
286 - */
287 -function gutenberg_register_packages_styles( $styles ) {
288 - // When in production, use the plugin's version as the asset version;
289 - // else (for development or test) default to use the current time.
290 - $version = defined( 'GUTENBERG_VERSION' ) && ! ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? GUTENBERG_VERSION : time();
291 -
292 - // Editor Styles.
293 148 gutenberg_override_style(
294 149 $styles,
295 - 'wp-block-editor',
296 - gutenberg_url( 'build/block-editor/style.css' ),
297 - array( 'wp-components' ),
150 + 'wp-theme',
151 + gutenberg_url( 'build/styles/theme/design-tokens' . $suffix . '.css' ),
152 + array(),
298 153 $version
299 154 );
300 - $styles->add_data( 'wp-block-editor', 'rtl', 'replace' );
155 + $styles->add_data( 'wp-theme', 'rtl', 'replace' );
156 + $styles->add_data( 'wp-theme', 'suffix', $suffix );
157 + $styles->add_data( 'wp-theme', 'path', gutenberg_dir_path() . 'build/styles/theme/design-tokens' . $suffix . '.css' );
301 158
159 + // Register wp-base-styles and add it to the already registered wp-admin stylesheet
302 160 gutenberg_override_style(
303 161 $styles,
304 - 'wp-editor',
305 - gutenberg_url( 'build/editor/style.css' ),
306 - array( 'wp-components', 'wp-block-editor', 'wp-nux', 'wp-reusable-blocks' ),
162 + 'wp-base-styles',
163 + gutenberg_url( 'build/styles/base-styles/admin-schemes' . $suffix . '.css' ),
164 + array(),
307 165 $version
308 166 );
309 - $styles->add_data( 'wp-editor', 'rtl', 'replace' );
167 + $styles->add_data( 'wp-base-styles', 'rtl', 'replace' );
168 + $styles->add_data( 'wp-base-styles', 'suffix', $suffix );
169 + $styles->add_data( 'wp-base-styles', 'path', gutenberg_dir_path() . 'build/styles/base-styles/admin-schemes' . $suffix . '.css' );
170 + $styles->query( 'wp-admin', 'registered' )->deps[] = 'wp-base-styles';
310 171
311 172 gutenberg_override_style(
312 173 $styles,
313 - 'wp-edit-post',
314 - gutenberg_url( 'build/edit-post/style.css' ),
315 - array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-block-library', 'wp-nux' ),
174 + 'wp-block-editor-content',
175 + gutenberg_url( 'build/styles/block-editor/content' . $suffix . '.css' ),
176 + array( 'wp-components' ),
316 177 $version
317 178 );
318 - $styles->add_data( 'wp-edit-post', 'rtl', 'replace' );
179 + $styles->add_data( 'wp-block-editor-content', 'rtl', 'replace' );
180 + $styles->add_data( 'wp-block-editor-content', 'suffix', $suffix );
319 181
320 - gutenberg_override_style(
321 - $styles,
322 - 'wp-components',
323 - gutenberg_url( 'build/components/style.css' ),
324 - array( 'dashicons' ),
325 - $version
326 - );
327 - $styles->add_data( 'wp-components', 'rtl', 'replace' );
328 -
329 182 $block_library_filename = wp_should_load_separate_core_block_assets() ? 'common' : 'style';
330 183 gutenberg_override_style(
331 184 $styles,
332 185 'wp-block-library',
333 - gutenberg_url( 'build/block-library/' . $block_library_filename . '.css' ),
186 + gutenberg_url( 'build/styles/block-library/' . $block_library_filename . $suffix . '.css' ),
334 187 array(),
335 188 $version
336 189 );
337 190 $styles->add_data( 'wp-block-library', 'rtl', 'replace' );
338 - $styles->add_data( 'wp-block-library', 'path', gutenberg_dir_path() . 'build/block-library/' . $block_library_filename . '.css' );
191 + $styles->add_data( 'wp-block-library', 'suffix', $suffix );
192 + $styles->add_data( 'wp-block-library', 'path', gutenberg_dir_path() . 'build/styles/block-library/' . $block_library_filename . $suffix . '.css' );
339 193
340 - gutenberg_override_style(
341 - $styles,
342 - 'wp-format-library',
343 - gutenberg_url( 'build/format-library/style.css' ),
344 - array( 'wp-block-editor', 'wp-components' ),
345 - $version
346 - );
347 - $styles->add_data( 'wp-format-library', 'rtl', 'replace' );
348 -
194 + // Only add CONTENT styles here that should be enqueued in the iframe!
349 195 $wp_edit_blocks_dependencies = array(
196 + // Design System tokens load first so the `:root` CSS custom
197 + // properties are defined before any consuming stylesheet reads
198 + // them inside the editor iframe.
199 + 'wp-theme',
350 200 'wp-components',
351 - 'wp-editor',
352 201 // This need to be added before the block library styles,
353 202 // The block library styles override the "reset" styles.
354 203 'wp-reset-editor-styles',
355 204 'wp-block-library',
356 - 'wp-reusable-blocks',
205 + // Until #37466, we can't specifically add them as editor styles yet,
206 + // so we must hard-code it here as a dependency.
207 + 'wp-block-editor-content',
208 + 'wp-base-styles',
357 209 );
358 210
359 211 // Only load the default layout and margin styles for themes without theme.json file.
360 - if ( ! WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) {
212 + if ( ! wp_theme_has_theme_json() ) {
361 213 $wp_edit_blocks_dependencies[] = 'wp-editor-classic-layout-styles';
362 214 }
363 215
364 216 global $editor_styles;
365 - if ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 ) {
366 - // Include opinionated block styles if no $editor_styles are declared, so the editor never appears broken.
217 + if ( current_theme_supports( 'wp-block-styles' ) && ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 ) ) {
218 + // Include opinionated block styles if the theme supports block styles and no $editor_styles are declared, so the editor never appears broken.
367 219 $wp_edit_blocks_dependencies[] = 'wp-block-library-theme';
368 220 }
369 221
370 222 gutenberg_override_style(
@@ -369,309 +221,269 @@
369 221
370 222 gutenberg_override_style(
371 223 $styles,
372 224 'wp-reset-editor-styles',
373 - gutenberg_url( 'build/block-library/reset.css' ),
225 + gutenberg_url( 'build/styles/block-library/reset' . $suffix . '.css' ),
374 226 array( 'common', 'forms' ), // Make sure the reset is loaded after the default WP Admin styles.
375 227 $version
376 228 );
377 229 $styles->add_data( 'wp-reset-editor-styles', 'rtl', 'replace' );
230 + $styles->add_data( 'wp-reset-editor-styles', 'suffix', $suffix );
378 231
379 232 gutenberg_override_style(
380 233 $styles,
381 234 'wp-editor-classic-layout-styles',
382 - gutenberg_url( 'build/edit-post/classic.css' ),
235 + gutenberg_url( 'build/styles/edit-post/classic' . $suffix . '.css' ),
383 236 array(),
384 237 $version
385 238 );
386 239 $styles->add_data( 'wp-editor-classic-layout-styles', 'rtl', 'replace' );
240 + $styles->add_data( 'wp-editor-classic-layout-styles', 'suffix', $suffix );
387 241
388 242 gutenberg_override_style(
389 243 $styles,
390 - 'wp-edit-blocks',
391 - gutenberg_url( 'build/block-library/editor.css' ),
392 - $wp_edit_blocks_dependencies,
244 + 'wp-block-library-editor',
245 + gutenberg_url( 'build/styles/block-library/editor' . $suffix . '.css' ),
246 + array(),
393 247 $version
394 248 );
395 - $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' );
249 + $styles->add_data( 'wp-block-library-editor', 'rtl', 'replace' );
250 + $styles->add_data( 'wp-block-library-editor', 'suffix', $suffix );
396 251
397 252 gutenberg_override_style(
398 253 $styles,
399 - 'wp-nux',
400 - gutenberg_url( 'build/nux/style.css' ),
401 - array( 'wp-components' ),
254 + 'wp-edit-blocks',
255 + gutenberg_url( 'build/styles/block-library/editor' . $suffix . '.css' ),
256 + $wp_edit_blocks_dependencies,
402 257 $version
403 258 );
404 - $styles->add_data( 'wp-nux', 'rtl', 'replace' );
259 + $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' );
260 + $styles->add_data( 'wp-edit-blocks', 'suffix', $suffix );
405 261
406 262 gutenberg_override_style(
407 263 $styles,
408 264 'wp-block-library-theme',
409 - gutenberg_url( 'build/block-library/theme.css' ),
265 + gutenberg_url( 'build/styles/block-library/theme' . $suffix . '.css' ),
410 266 array(),
411 267 $version
412 268 );
413 269 $styles->add_data( 'wp-block-library-theme', 'rtl', 'replace' );
270 + $styles->add_data( 'wp-block-library-theme', 'suffix', $suffix );
414 271
415 272 gutenberg_override_style(
416 273 $styles,
417 - 'wp-list-reusable-blocks',
418 - gutenberg_url( 'build/list-reusable-blocks/style.css' ),
419 - array( 'wp-components' ),
274 + 'classic-theme-styles',
275 + gutenberg_url( 'build/styles/block-library/classic' . $suffix . '.css' ),
276 + array(),
420 277 $version
421 278 );
422 - $styles->add_data( 'wp-list-reusable-block', 'rtl', 'replace' );
423 -
424 - gutenberg_override_style(
425 - $styles,
426 - 'wp-edit-navigation',
427 - gutenberg_url( 'build/edit-navigation/style.css' ),
428 - array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks' ),
429 - $version
430 - );
431 - $styles->add_data( 'wp-edit-navigation', 'rtl', 'replace' );
432 -
433 - gutenberg_override_style(
434 - $styles,
435 - 'wp-edit-site',
436 - gutenberg_url( 'build/edit-site/style.css' ),
437 - array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks' ),
438 - $version
439 - );
440 - $styles->add_data( 'wp-edit-site', 'rtl', 'replace' );
441 -
442 - gutenberg_override_style(
443 - $styles,
444 - 'wp-edit-widgets',
445 - gutenberg_url( 'build/edit-widgets/style.css' ),
446 - array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks', 'wp-reusable-blocks', 'wp-widgets' ),
447 - $version
448 - );
449 - $styles->add_data( 'wp-edit-widgets', 'rtl', 'replace' );
450 -
451 - gutenberg_override_style(
452 - $styles,
453 - 'wp-block-directory',
454 - gutenberg_url( 'build/block-directory/style.css' ),
455 - array( 'wp-block-editor', 'wp-components' ),
456 - $version
457 - );
458 - $styles->add_data( 'wp-block-directory', 'rtl', 'replace' );
459 -
460 - gutenberg_override_style(
461 - $styles,
462 - 'wp-customize-widgets',
463 - gutenberg_url( 'build/customize-widgets/style.css' ),
464 - array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks', 'wp-widgets' ),
465 - $version
466 - );
467 - $styles->add_data( 'wp-customize-widgets', 'rtl', 'replace' );
468 -
469 - gutenberg_override_style(
470 - $styles,
471 - 'wp-reusable-blocks',
472 - gutenberg_url( 'build/reusable-blocks/style.css' ),
473 - array( 'wp-components' ),
474 - $version
475 - );
476 - $styles->add_data( 'wp-reusable-block', 'rtl', 'replace' );
477 -
478 - gutenberg_override_style(
479 - $styles,
480 - 'wp-widgets',
481 - gutenberg_url( 'build/widgets/style.css' ),
482 - array( 'wp-components' )
483 - );
484 - $styles->add_data( 'wp-widgets', 'rtl', 'replace' );
279 + $styles->add_data( 'classic-theme-styles', 'rtl', 'replace' );
280 + $styles->add_data( 'classic-theme-styles', 'suffix', $suffix );
281 + $styles->add_data( 'classic-theme-styles', 'path', gutenberg_dir_path() . 'build/styles/block-library/classic' . $suffix . '.css' );
485 282 }
486 -add_action( 'wp_default_styles', 'gutenberg_register_packages_styles' );
283 +add_action( 'wp_default_styles', 'gutenberg_register_packages_styles', 15 );
487 284
488 285 /**
489 - * Retrieves a unique and reasonably short and human-friendly filename for a
490 - * vendor script based on a URL and the script handle.
286 + * Fetches, processes and compiles stored core styles, then combines and renders them to the page.
287 + * Styles are stored via the Style Engine API.
491 288 *
492 - * @param string $handle The name of the script.
493 - * @param string $src Full URL of the external script.
289 + * This hook also exists, and should be backported to Core in future versions.
290 + * However, it is envisaged that Gutenberg will continue to use the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes to aid continuous development.
494 291 *
495 - * @return string Script filename suitable for local caching.
292 + * @since 6.1
496 293 *
497 - * @since 0.1.0
498 - */
499 -function gutenberg_vendor_script_filename( $handle, $src ) {
500 - $filename = basename( $src );
501 - $match = preg_match(
502 - '/^'
503 - . '(?P<ignore>.*?)'
504 - . '(?P<suffix>\.min)?'
505 - . '(?P<extension>\.js)'
506 - . '(?P<extra>.*)'
507 - . '$/',
508 - $filename,
509 - $filename_pieces
510 - );
511 -
512 - $prefix = $handle;
513 - $suffix = $match ? $filename_pieces['suffix'] : '';
514 - $hash = substr( md5( $src ), 0, 8 );
515 -
516 - return "${prefix}${suffix}.${hash}.js";
517 -}
518 -
519 -/**
520 - * Registers a vendor script from a URL, preferring a locally cached version if
521 - * possible, or downloading it if the cached version is unavailable or
522 - * outdated.
294 + * @link https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/
523 295 *
524 - * @param WP_Scripts $scripts WP_Scripts instance.
525 - * @param string $handle Name of the script.
526 - * @param string $src Full URL of the external script.
527 - * @param array $deps Optional. An array of registered script handles this
528 - * script depends on.
529 - * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL
530 - * as a query string for cache busting purposes. If version is set to false, a version
531 - * number is automatically added equal to current installed WordPress version.
532 - * If set to null, no version is added.
533 - * @param bool $in_footer Optional. Whether to enqueue the script before </body> instead of in the <head>.
534 - * Default 'false'.
296 + * @param array $options {
297 + * Optional. An array of options to pass to gutenberg_style_engine_get_stylesheet_from_context(). Default empty array.
535 298 *
536 - * @since 0.1.0
299 + * @type bool $optimize Whether to optimize the CSS output, e.g., combine rules. Default is `false`.
300 + * @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.
301 + * }
302 + *
303 + * @return void
537 304 */
538 -function gutenberg_register_vendor_script( $scripts, $handle, $src, $deps = array(), $ver = null, $in_footer = false ) {
539 - if ( defined( 'GUTENBERG_LOAD_VENDOR_SCRIPTS' ) && ! GUTENBERG_LOAD_VENDOR_SCRIPTS ) {
305 +function gutenberg_enqueue_stored_styles( $options = array() ) {
306 + $is_block_theme = wp_is_block_theme();
307 + $is_classic_theme = ! $is_block_theme;
308 +
309 + /*
310 + * For block themes, print stored styles in the header.
311 + * For classic themes, in the footer.
312 + */
313 + if (
314 + ( $is_block_theme && doing_action( 'wp_footer' ) ) ||
315 + ( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) )
316 + ) {
540 317 return;
541 318 }
542 319
543 - $filename = gutenberg_vendor_script_filename( $handle, $src );
320 + $core_styles_keys = array( 'block-supports' );
321 + $compiled_core_stylesheet = '';
322 + $style_tag_id = 'core';
323 + foreach ( $core_styles_keys as $style_key ) {
324 + // Adds comment if code is prettified to identify core styles sections in debugging.
325 + $should_prettify = isset( $options['prettify'] ) ? true === $options['prettify'] : SCRIPT_DEBUG;
326 + if ( $should_prettify ) {
327 + $compiled_core_stylesheet .= "/**\n * Core styles: $style_key\n */\n";
328 + }
329 + // Chains core store ids to signify what the styles contain.
330 + $style_tag_id .= '-' . $style_key;
331 + $compiled_core_stylesheet .= gutenberg_style_engine_get_stylesheet_from_context( $style_key, $options );
332 + }
544 333
545 - if ( defined( 'GUTENBERG_LIST_VENDOR_ASSETS' ) && GUTENBERG_LIST_VENDOR_ASSETS ) {
546 - echo "$src|$filename\n";
547 - return;
334 + // Combines Core styles.
335 + if ( ! empty( $compiled_core_stylesheet ) ) {
336 + wp_register_style( $style_tag_id, false, array(), true );
337 + wp_add_inline_style( $style_tag_id, $compiled_core_stylesheet );
338 + wp_enqueue_style( $style_tag_id );
548 339 }
549 340
550 - $full_path = gutenberg_dir_path() . 'vendor/' . $filename;
341 + // If there are any other stores registered by themes etc., print them out.
342 + $additional_stores = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_stores();
551 343
552 - $needs_fetch = (
553 - defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE && (
554 - ! file_exists( $full_path ) ||
555 - time() - filemtime( $full_path ) >= DAY_IN_SECONDS
556 - )
557 - );
344 + /*
345 + * Since the corresponding action hook in Core is removed below,
346 + * this function should still honour any styles stored using the Core Style Engine store.
347 + */
348 + if ( class_exists( 'WP_Style_Engine_CSS_Rules_Store' ) ) {
349 + $additional_stores = array_merge( $additional_stores, WP_Style_Engine_CSS_Rules_Store::get_stores() );
350 + }
558 351
559 - if ( $needs_fetch ) {
560 - // Determine whether we can write to this file. If not, don't waste
561 - // time doing a network request.
562 - // @codingStandardsIgnoreStart
563 -
564 - $is_writable = is_writable( $full_path );
565 - if ( $is_writable ) {
566 - $f = @fopen( $full_path, 'a' );
567 - if ( ! $f ) {
568 - $is_writable = false;
569 - } else {
570 - fclose( $f );
571 - }
352 + foreach ( array_keys( $additional_stores ) as $store_name ) {
353 + if ( in_array( $store_name, $core_styles_keys, true ) ) {
354 + continue;
572 355 }
573 -
574 - // @codingStandardsIgnoreEnd
575 - if ( ! $is_writable ) {
576 - // Failed to open the file for writing, probably due to server
577 - // permissions. Enqueue the script directly from the URL instead.
578 - gutenberg_override_script( $scripts, $handle, $src, $deps, $ver, $in_footer );
579 - return;
356 + $styles = gutenberg_style_engine_get_stylesheet_from_context( $store_name, $options );
357 + if ( ! empty( $styles ) ) {
358 + $key = "wp-style-engine-$store_name";
359 + wp_register_style( $key, false, array(), true );
360 + wp_add_inline_style( $key, $styles );
361 + wp_enqueue_style( $key );
580 362 }
581 -
582 - $response = wp_remote_get( $src );
583 - if ( wp_remote_retrieve_response_code( $response ) === 200 ) {
584 - $f = fopen( $full_path, 'w' );
585 - fwrite( $f, wp_remote_retrieve_body( $response ) );
586 - fclose( $f );
587 - } elseif ( ! filesize( $full_path ) ) {
588 - // The request failed. If the file is already cached, continue to
589 - // use this file. If not, then unlink the 0 byte file, and enqueue
590 - // the script directly from the URL.
591 - gutenberg_override_script( $scripts, $handle, $src, $deps, $ver, $in_footer );
592 - unlink( $full_path );
593 - return;
594 - }
595 363 }
596 - gutenberg_override_script(
597 - $scripts,
598 - $handle,
599 - gutenberg_url( 'vendor/' . $filename ),
600 - $deps,
601 - $ver,
602 - $in_footer
603 - );
604 364 }
605 365
606 366 /**
607 - * Sets the editor styles to be consumed by JS.
367 + * Registers vendor JavaScript files to be used as dependencies of the editor
368 + * and plugins.
369 + *
370 + * This function is called from a script during the plugin build process, so it
371 + * should not call any WordPress PHP functions.
372 + *
373 + * @since 13.0
374 + *
375 + * @param WP_Scripts $scripts WP_Scripts instance.
608 376 */
609 -function gutenberg_resolve_assets() {
610 - global $pagenow;
377 +function gutenberg_register_vendor_scripts( $scripts ) {
378 + $extension = SCRIPT_DEBUG ? '.js' : '.min.js';
379 + $vendors_dir = gutenberg_dir_path() . 'build/scripts/vendors/';
611 380
612 - $script_handles = array();
613 - $style_handles = array(
614 - 'wp-block-editor',
615 - 'wp-block-library',
616 - 'wp-block-library-theme',
617 - 'wp-edit-blocks',
618 - );
381 + // When the React 19 experiment is enabled, register React 19 vendor
382 + // scripts under the `react`, `react-dom`, and `react-jsx-runtime` handles.
383 + $use_react_19 = gutenberg_is_experiment_enabled( 'gutenberg-react-19' );
619 384
620 - if ( 'widgets.php' === $pagenow || 'customize.php' === $pagenow ) {
621 - $style_handles[] = 'wp-widgets';
622 - $style_handles[] = 'wp-edit-widgets';
623 - }
385 + $vendor_handles = array( 'react', 'react-dom', 'react-jsx-runtime' );
624 386
625 - $block_registry = WP_Block_Type_Registry::get_instance();
387 + foreach ( $vendor_handles as $handle ) {
388 + $source = $use_react_19 ? $handle . '-19' : $handle;
389 + $asset_file = $vendors_dir . $source . '.min.asset.php';
390 + $asset = file_exists( $asset_file ) ? require $asset_file : array();
391 + $dependencies = $asset['dependencies'] ?? array();
392 + $version = $asset['version'] ?? '0';
626 393
627 - foreach ( $block_registry->get_all_registered() as $block_type ) {
628 - if ( ! empty( $block_type->style ) ) {
629 - $style_handles[] = $block_type->style;
630 - }
394 + gutenberg_override_script(
395 + $scripts,
396 + $handle,
397 + gutenberg_url( 'build/scripts/vendors/' . $source . $extension ),
398 + $dependencies,
399 + $version
400 + );
401 + }
631 402
632 - if ( ! empty( $block_type->editor_style ) ) {
633 - $style_handles[] = $block_type->editor_style;
403 + // WordPress Core in `wp_register_development_scripts` sets `wp-react-refresh-entry`
404 + // as a dependency to `react` when `SCRIPT_DEBUG` is true. Preserve that here.
405 + if ( SCRIPT_DEBUG ) {
406 + $react = $scripts->query( 'react', 'registered' );
407 + if ( $react && ! in_array( 'wp-react-refresh-entry', $react->deps, true ) ) {
408 + $react->deps[] = 'wp-react-refresh-entry';
634 409 }
410 + }
411 +}
412 +add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' );
635 413
636 - if ( ! empty( $block_type->script ) ) {
637 - $script_handles[] = $block_type->script;
638 - }
414 +/**
415 + * Registers or re-registers Gutenberg Script Modules.
416 + *
417 + * Script modules that are registered by Core will be re-registered by Gutenberg.
418 + *
419 + * @since 19.3.0
420 + */
421 +function gutenberg_define_interactivity_modules_support() {
422 + // Load the auto-generated module registry.
423 + $modules_registry_file = gutenberg_dir_path() . 'build/modules/index.php';
424 + if ( ! file_exists( $modules_registry_file ) ) {
425 + return;
639 426 }
640 427
641 - $style_handles = array_unique( $style_handles );
642 - $done = wp_styles()->done;
428 + $modules = require $modules_registry_file;
643 429
644 - ob_start();
430 + // Add client navigation support to block library modules.
431 + foreach ( $modules as $module ) {
432 + if ( str_starts_with( $module['id'], '@wordpress/block-library' ) && method_exists( 'WP_Interactivity_API', 'add_client_navigation_support_to_script_module' ) ) {
433 + wp_interactivity()->add_client_navigation_support_to_script_module( $module['id'] );
434 + }
435 + }
436 +}
437 +remove_action( 'wp_default_scripts', 'wp_define_interactivity_modules_support' );
438 +add_action( 'wp_default_scripts', 'gutenberg_define_interactivity_modules_support' );
645 439
646 - // We do not need reset styles for the iframed editor.
647 - wp_styles()->done = array( 'wp-reset-editor-styles' );
648 - wp_styles()->do_items( $style_handles );
649 - wp_styles()->done = $done;
440 +/**
441 + * Always remove the Core action hook while gutenberg_enqueue_stored_styles() exists to avoid styles being printed twice.
442 + * This is also because gutenberg_enqueue_stored_styles uses the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes,
443 + * which are in continuous development and generally ahead of Core.
444 + */
445 +remove_action( 'wp_enqueue_scripts', 'wp_enqueue_stored_styles' );
446 +remove_action( 'wp_footer', 'wp_enqueue_stored_styles', 1 );
650 447
651 - $styles = ob_get_clean();
448 +// Enqueue stored styles.
449 +add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_stored_styles' );
450 +add_action( 'wp_footer', 'gutenberg_enqueue_stored_styles', 1 );
652 451
653 - $script_handles = array_unique( $script_handles );
654 - $done = wp_scripts()->done;
452 +/**
453 + * Enqueues the LaTeX to MathML loader script module in the block editor.
454 + */
455 +add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_latex_to_mathml_loader' );
456 +function gutenberg_enqueue_latex_to_mathml_loader() {
457 + wp_enqueue_script_module( '@wordpress/latex-to-mathml/loader' );
458 +}
655 459
656 - ob_start();
460 +/**
461 + * Enqueue the vips loader script module in the block editor.
462 + *
463 + * This registers @wordpress/vips/worker as a dynamic dependency in the import map,
464 + * enabling on-demand loading of the ~3.8MB WASM-based image processing module
465 + * when client-side media processing is triggered via @wordpress/upload-media.
466 + *
467 + * @see packages/vips/src/loader.ts
468 + */
469 +add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_vips_loader' );
470 +function gutenberg_enqueue_vips_loader() {
471 + wp_enqueue_script_module( '@wordpress/vips/loader' );
472 +}
657 473
658 - wp_scripts()->done = array();
659 - wp_scripts()->do_items( $script_handles );
660 - wp_scripts()->done = $done;
661 -
662 - $scripts = ob_get_clean();
663 -
664 - return array(
665 - 'styles' => $styles,
666 - 'scripts' => $scripts,
667 - );
474 +/**
475 + * Enqueue the video-conversion loader script module in the block editor.
476 + *
477 + * This registers @wordpress/video-conversion/worker as a dynamic dependency
478 + * in the import map, enabling on-demand loading of the WebCodecs-based
479 + * GIF-to-video processing module when animated GIF conversion is triggered
480 + * via @wordpress/upload-media.
481 + *
482 + * @see packages/video-conversion/src/loader.ts
483 + */
484 +if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
485 + add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_video_conversion_loader' );
668 486 }
669 -
670 -add_filter(
671 - 'block_editor_settings_all',
672 - function( $settings ) {
673 - // In the future we can allow WP Dependency handles to be passed.
674 - $settings['__unstableResolvedAssets'] = gutenberg_resolve_assets();
675 - return $settings;
676 - }
677 -);
487 +function gutenberg_enqueue_video_conversion_loader() {
488 + wp_enqueue_script_module( '@wordpress/video-conversion/loader' );
489 +}