PluginProbe
Gutenberg / 12.1.0
Gutenberg v12.1.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
← All changes | lib/client-assets.php +552 -263 23.6.012.1.0 View file →
@@ -1,8 +1,8 @@
1 1 <?php
2 2 /**
3 - * Functions to register client-side assets (scripts and stylesheets) specific
4 - * for the Gutenberg editor plugin.
3 + * Functions to register client-side assets (scripts and stylesheets) for the
4 + * 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 + * @return string Root path to the gutenberg plugin.
17 + *
16 18 * @since 0.1.0
17 - *
18 - * @return string Root path to the gutenberg plugin.
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 - * @since 0.1.0
28 - *
29 27 * @param string $path Relative path of the desired file.
30 28 *
31 29 * @return string Fully qualified URL pointing to the desired file.
30 + *
31 + * @since 0.1.0
32 32 */
33 33 function gutenberg_url( $path ) {
34 34 return plugins_url( $path, __DIR__ );
35 35 }
@@ -34,8 +34,85 @@
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 +/**
38 115 * Filters the default translation file load behavior to load the Gutenberg
39 116 * plugin translation file, if available.
40 117 *
41 118 * @param string|false $file Path to the translation file to load. False if
@@ -51,14 +128,14 @@
51 128 return $file;
52 129 }
53 130
54 131 // Ignore scripts whose handle does not have the "wp-" prefix.
55 - if ( ! str_starts_with( $handle, 'wp-' ) ) {
132 + if ( 'wp-' !== substr( $handle, 0, 3 ) ) {
56 133 return $file;
57 134 }
58 135
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';
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';
61 138 if ( ! file_exists( $script_path ) ) {
62 139 return $file;
63 140 }
64 141
@@ -86,139 +163,207 @@
86 163 }
87 164 add_filter( 'load_script_translation_file', 'gutenberg_override_translation_file', 10, 2 );
88 165
89 166 /**
90 - * Adds the 'editor' dependency to wp-block-library, required by the Classic block.
167 + * Registers a style according to `wp_register_style`. Honors this request by
168 + * deregistering any style by the same handler before registration.
91 169 *
92 - * @param WP_Scripts $scripts WP_Scripts instance.
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)'.
93 183 */
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';
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 );
99 188 }
189 + $styles->add( $handle, $src, $deps, $ver, $media );
100 190 }
101 -add_action( 'wp_default_scripts', 'gutenberg_register_block_library_script_special_case', 11 );
102 191
103 192 /**
104 - * Registers WordPress package styles with complex requirements.
193 + * Registers vendor JavaScript files to be used as dependencies of the editor
194 + * and plugins.
105 195 *
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
196 + * This function is called from a script during the plugin build process, so it
197 + * should not call any WordPress PHP functions.
113 198 *
114 - * These override calls will replace the auto-registered versions as needed.
199 + * @since 0.1.0
115 200 *
116 - * @since 6.7.0
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 + array( 'wp-polyfill' )
212 + );
213 + gutenberg_register_vendor_script(
214 + $scripts,
215 + 'react-dom',
216 + 'https://unpkg.com/react-dom@17.0.1/umd/react-dom' . $react_suffix . '.js',
217 + array( 'react' )
218 + );
219 +}
220 +add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' );
221 +
222 +/**
223 + * Registers all the WordPress packages scripts that are in the standardized
224 + * `build/` location.
117 225 *
118 - * @global array $editor_styles
226 + * @since 4.5.0
119 227 *
120 - * @param WP_Styles $styles WP_Styles instance.
228 + * @param WP_Scripts $scripts WP_Scripts instance.
121 229 */
122 -function gutenberg_register_packages_styles( $styles ) {
123 - // When in production, use the plugin's version as the asset version;
230 +function gutenberg_register_packages_scripts( $scripts ) {
231 + // When in production, use the plugin's version as the default asset version;
124 232 // 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';
233 + $default_version = defined( 'GUTENBERG_VERSION' ) && ! ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? GUTENBERG_VERSION : time();
127 234
128 - // wp-components: add dashicons (icon font dependency)
129 - $styles->query( 'wp-components', 'registered' )->deps[] = 'dashicons';
235 + foreach ( glob( gutenberg_dir_path() . 'build/*/index.min.js' ) as $path ) {
236 + // Prefix `wp-` to package directory to get script handle.
237 + // For example, `…/build/a11y/index.min.js` becomes `wp-a11y`.
238 + $handle = 'wp-' . basename( dirname( $path ) );
130 239
131 - // wp-edit-post: add wp-edit-blocks (custom handle not auto-inferred)
132 - $styles->query( 'wp-edit-post', 'registered' )->deps[] = 'wp-edit-blocks';
240 + // Replace extension with `.asset.php` to find the generated dependencies file.
241 + $asset_file = substr( $path, 0, -( strlen( '.js' ) ) ) . '.asset.php';
242 + $asset = file_exists( $asset_file )
243 + ? require( $asset_file )
244 + : null;
245 + $dependencies = isset( $asset['dependencies'] ) ? $asset['dependencies'] : array();
246 + $version = isset( $asset['version'] ) ? $asset['version'] : $default_version;
133 247
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';
248 + // Add dependencies that cannot be detected and generated by build tools.
249 + switch ( $handle ) {
250 + case 'wp-block-library':
251 + array_push( $dependencies, 'editor' );
252 + break;
139 253
140 - // wp-edit-widgets: add wp-edit-blocks (custom handle not auto-inferred)
141 - $styles->query( 'wp-edit-widgets', 'registered' )->deps[] = 'wp-edit-blocks';
254 + case 'wp-edit-post':
255 + array_push( $dependencies, 'media-models', 'media-views', 'postbox' );
256 + break;
142 257
143 - // wp-customize-widgets: add wp-edit-blocks (custom handle not auto-inferred)
144 - $styles->query( 'wp-customize-widgets', 'registered' )->deps[] = 'wp-edit-blocks';
258 + case 'wp-edit-site':
259 + array_push( $dependencies, 'wp-dom-ready' );
260 + break;
261 + }
145 262
146 - // Register wp-theme (Design System tokens from @wordpress/theme) as a
147 - // dependency of wp-base-styles so its `:root` token block loads
148 - // everywhere wp-base-styles does, including the editor iframe via
149 - // $wp_edit_blocks_dependencies below.
263 + // Get the path from Gutenberg directory as expected by `gutenberg_url`.
264 + $gutenberg_path = substr( $path, strlen( gutenberg_dir_path() ) );
265 +
266 + gutenberg_override_script(
267 + $scripts,
268 + $handle,
269 + gutenberg_url( $gutenberg_path ),
270 + $dependencies,
271 + $version,
272 + true
273 + );
274 + }
275 +}
276 +add_action( 'wp_default_scripts', 'gutenberg_register_packages_scripts' );
277 +
278 +/**
279 + * Registers all the WordPress packages styles that are in the standardized
280 + * `build/` location.
281 + *
282 + * @since 6.7.0
283 +
284 + * @param WP_Styles $styles WP_Styles instance.
285 + */
286 +function gutenberg_register_packages_styles( $styles ) {
287 + // When in production, use the plugin's version as the asset version;
288 + // else (for development or test) default to use the current time.
289 + $version = defined( 'GUTENBERG_VERSION' ) && ! ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? GUTENBERG_VERSION : time();
290 +
291 + // Editor Styles.
150 292 gutenberg_override_style(
151 293 $styles,
152 - 'wp-theme',
153 - gutenberg_url( 'build/styles/theme/design-tokens' . $suffix . '.css' ),
154 - array(),
294 + 'wp-block-editor',
295 + gutenberg_url( 'build/block-editor/style.css' ),
296 + array( 'wp-components' ),
155 297 $version
156 298 );
157 - $styles->add_data( 'wp-theme', 'rtl', 'replace' );
158 - $styles->add_data( 'wp-theme', 'suffix', $suffix );
159 - $styles->add_data( 'wp-theme', 'path', gutenberg_dir_path() . 'build/styles/theme/design-tokens' . $suffix . '.css' );
299 + $styles->add_data( 'wp-block-editor', 'rtl', 'replace' );
160 300
161 - // Register wp-base-styles and add it to the already registered wp-admin stylesheet
162 301 gutenberg_override_style(
163 302 $styles,
164 - 'wp-base-styles',
165 - gutenberg_url( 'build/styles/base-styles/admin-schemes' . $suffix . '.css' ),
166 - array( 'wp-theme' ),
303 + 'wp-editor',
304 + gutenberg_url( 'build/editor/style.css' ),
305 + array( 'wp-components', 'wp-block-editor', 'wp-nux', 'wp-reusable-blocks' ),
167 306 $version
168 307 );
169 - $styles->add_data( 'wp-base-styles', 'rtl', 'replace' );
170 - $styles->add_data( 'wp-base-styles', 'suffix', $suffix );
171 - $styles->add_data( 'wp-base-styles', 'path', gutenberg_dir_path() . 'build/styles/base-styles/admin-schemes' . $suffix . '.css' );
172 - $styles->query( 'wp-admin', 'registered' )->deps[] = 'wp-base-styles';
308 + $styles->add_data( 'wp-editor', 'rtl', 'replace' );
173 309
174 310 gutenberg_override_style(
175 311 $styles,
176 - 'wp-block-editor-content',
177 - gutenberg_url( 'build/styles/block-editor/content' . $suffix . '.css' ),
178 - array( 'wp-components' ),
312 + 'wp-edit-post',
313 + gutenberg_url( 'build/edit-post/style.css' ),
314 + array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-block-library', 'wp-nux' ),
179 315 $version
180 316 );
181 - $styles->add_data( 'wp-block-editor-content', 'rtl', 'replace' );
182 - $styles->add_data( 'wp-block-editor-content', 'suffix', $suffix );
317 + $styles->add_data( 'wp-edit-post', 'rtl', 'replace' );
183 318
319 + gutenberg_override_style(
320 + $styles,
321 + 'wp-components',
322 + gutenberg_url( 'build/components/style.css' ),
323 + array( 'dashicons' ),
324 + $version
325 + );
326 + $styles->add_data( 'wp-components', 'rtl', 'replace' );
327 +
184 328 $block_library_filename = wp_should_load_separate_core_block_assets() ? 'common' : 'style';
185 329 gutenberg_override_style(
186 330 $styles,
187 331 'wp-block-library',
188 - gutenberg_url( 'build/styles/block-library/' . $block_library_filename . $suffix . '.css' ),
332 + gutenberg_url( 'build/block-library/' . $block_library_filename . '.css' ),
189 333 array(),
190 334 $version
191 335 );
192 336 $styles->add_data( 'wp-block-library', 'rtl', 'replace' );
193 - $styles->add_data( 'wp-block-library', 'suffix', $suffix );
194 - $styles->add_data( 'wp-block-library', 'path', gutenberg_dir_path() . 'build/styles/block-library/' . $block_library_filename . $suffix . '.css' );
337 + $styles->add_data( 'wp-block-library', 'path', gutenberg_dir_path() . 'build/block-library/' . $block_library_filename . '.css' );
195 338
196 - // Only add CONTENT styles here that should be enqueued in the iframe!
339 + gutenberg_override_style(
340 + $styles,
341 + 'wp-format-library',
342 + gutenberg_url( 'build/format-library/style.css' ),
343 + array( 'wp-block-editor', 'wp-components' ),
344 + $version
345 + );
346 + $styles->add_data( 'wp-format-library', 'rtl', 'replace' );
347 +
197 348 $wp_edit_blocks_dependencies = array(
198 - // Design System tokens load first so the `:root` CSS custom
199 - // properties are defined before any consuming stylesheet reads
200 - // them inside the editor iframe.
201 - 'wp-theme',
202 349 'wp-components',
350 + 'wp-editor',
203 351 // This need to be added before the block library styles,
204 352 // The block library styles override the "reset" styles.
205 353 'wp-reset-editor-styles',
206 354 'wp-block-library',
207 - // Until #37466, we can't specifically add them as editor styles yet,
208 - // so we must hard-code it here as a dependency.
209 - 'wp-block-editor-content',
210 - 'wp-base-styles',
355 + 'wp-reusable-blocks',
211 356 );
212 357
213 358 // Only load the default layout and margin styles for themes without theme.json file.
214 - if ( ! wp_theme_has_theme_json() ) {
359 + if ( ! WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) {
215 360 $wp_edit_blocks_dependencies[] = 'wp-editor-classic-layout-styles';
216 361 }
217 362
218 363 global $editor_styles;
219 - if ( current_theme_supports( 'wp-block-styles' ) && ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 ) ) {
220 - // Include opinionated block styles if the theme supports block styles and no $editor_styles are declared, so the editor never appears broken.
364 + if ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 ) {
365 + // Include opinionated block styles if no $editor_styles are declared, so the editor never appears broken.
221 366 $wp_edit_blocks_dependencies[] = 'wp-block-library-theme';
222 367 }
223 368
224 369 gutenberg_override_style(
@@ -223,269 +368,413 @@
223 368
224 369 gutenberg_override_style(
225 370 $styles,
226 371 'wp-reset-editor-styles',
227 - gutenberg_url( 'build/styles/block-library/reset' . $suffix . '.css' ),
228 - array( 'common', 'forms' ), // Make sure the reset is loaded after the default WP Admin styles.
372 + gutenberg_url( 'build/block-library/reset.css' ),
373 + array( 'common', 'forms' ), // Make sure the reset is loaded after the default WP Adminn styles.
229 374 $version
230 375 );
231 376 $styles->add_data( 'wp-reset-editor-styles', 'rtl', 'replace' );
232 - $styles->add_data( 'wp-reset-editor-styles', 'suffix', $suffix );
233 377
234 378 gutenberg_override_style(
235 379 $styles,
236 380 'wp-editor-classic-layout-styles',
237 - gutenberg_url( 'build/styles/edit-post/classic' . $suffix . '.css' ),
381 + gutenberg_url( 'build/edit-post/classic.css' ),
238 382 array(),
239 383 $version
240 384 );
241 385 $styles->add_data( 'wp-editor-classic-layout-styles', 'rtl', 'replace' );
242 - $styles->add_data( 'wp-editor-classic-layout-styles', 'suffix', $suffix );
243 386
244 387 gutenberg_override_style(
245 388 $styles,
246 - 'wp-block-library-editor',
247 - gutenberg_url( 'build/styles/block-library/editor' . $suffix . '.css' ),
248 - array(),
389 + 'wp-edit-blocks',
390 + gutenberg_url( 'build/block-library/editor.css' ),
391 + $wp_edit_blocks_dependencies,
249 392 $version
250 393 );
251 - $styles->add_data( 'wp-block-library-editor', 'rtl', 'replace' );
252 - $styles->add_data( 'wp-block-library-editor', 'suffix', $suffix );
394 + $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' );
253 395
254 396 gutenberg_override_style(
255 397 $styles,
256 - 'wp-edit-blocks',
257 - gutenberg_url( 'build/styles/block-library/editor' . $suffix . '.css' ),
258 - $wp_edit_blocks_dependencies,
398 + 'wp-nux',
399 + gutenberg_url( 'build/nux/style.css' ),
400 + array( 'wp-components' ),
259 401 $version
260 402 );
261 - $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' );
262 - $styles->add_data( 'wp-edit-blocks', 'suffix', $suffix );
403 + $styles->add_data( 'wp-nux', 'rtl', 'replace' );
263 404
264 405 gutenberg_override_style(
265 406 $styles,
266 407 'wp-block-library-theme',
267 - gutenberg_url( 'build/styles/block-library/theme' . $suffix . '.css' ),
408 + gutenberg_url( 'build/block-library/theme.css' ),
268 409 array(),
269 410 $version
270 411 );
271 412 $styles->add_data( 'wp-block-library-theme', 'rtl', 'replace' );
272 - $styles->add_data( 'wp-block-library-theme', 'suffix', $suffix );
273 413
274 414 gutenberg_override_style(
275 415 $styles,
276 - 'classic-theme-styles',
277 - gutenberg_url( 'build/styles/block-library/classic' . $suffix . '.css' ),
278 - array(),
416 + 'wp-list-reusable-blocks',
417 + gutenberg_url( 'build/list-reusable-blocks/style.css' ),
418 + array( 'wp-components' ),
279 419 $version
280 420 );
281 - $styles->add_data( 'classic-theme-styles', 'rtl', 'replace' );
282 - $styles->add_data( 'classic-theme-styles', 'suffix', $suffix );
283 - $styles->add_data( 'classic-theme-styles', 'path', gutenberg_dir_path() . 'build/styles/block-library/classic' . $suffix . '.css' );
421 + $styles->add_data( 'wp-list-reusable-block', 'rtl', 'replace' );
422 +
423 + gutenberg_override_style(
424 + $styles,
425 + 'wp-edit-navigation',
426 + gutenberg_url( 'build/edit-navigation/style.css' ),
427 + array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks' ),
428 + $version
429 + );
430 + $styles->add_data( 'wp-edit-navigation', 'rtl', 'replace' );
431 +
432 + gutenberg_override_style(
433 + $styles,
434 + 'wp-edit-site',
435 + gutenberg_url( 'build/edit-site/style.css' ),
436 + array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks' ),
437 + $version
438 + );
439 + $styles->add_data( 'wp-edit-site', 'rtl', 'replace' );
440 +
441 + gutenberg_override_style(
442 + $styles,
443 + 'wp-edit-widgets',
444 + gutenberg_url( 'build/edit-widgets/style.css' ),
445 + array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks', 'wp-reusable-blocks', 'wp-widgets' ),
446 + $version
447 + );
448 + $styles->add_data( 'wp-edit-widgets', 'rtl', 'replace' );
449 +
450 + gutenberg_override_style(
451 + $styles,
452 + 'wp-block-directory',
453 + gutenberg_url( 'build/block-directory/style.css' ),
454 + array( 'wp-block-editor', 'wp-components' ),
455 + $version
456 + );
457 + $styles->add_data( 'wp-block-directory', 'rtl', 'replace' );
458 +
459 + gutenberg_override_style(
460 + $styles,
461 + 'wp-customize-widgets',
462 + gutenberg_url( 'build/customize-widgets/style.css' ),
463 + array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks', 'wp-widgets' ),
464 + $version
465 + );
466 + $styles->add_data( 'wp-customize-widgets', 'rtl', 'replace' );
467 +
468 + gutenberg_override_style(
469 + $styles,
470 + 'wp-reusable-blocks',
471 + gutenberg_url( 'build/reusable-blocks/style.css' ),
472 + array( 'wp-components' ),
473 + $version
474 + );
475 + $styles->add_data( 'wp-reusable-block', 'rtl', 'replace' );
476 +
477 + gutenberg_override_style(
478 + $styles,
479 + 'wp-widgets',
480 + gutenberg_url( 'build/widgets/style.css' ),
481 + array( 'wp-components' )
482 + );
483 + $styles->add_data( 'wp-widgets', 'rtl', 'replace' );
284 484 }
285 -add_action( 'wp_default_styles', 'gutenberg_register_packages_styles', 15 );
485 +add_action( 'wp_default_styles', 'gutenberg_register_packages_styles' );
286 486
287 487 /**
288 - * Fetches, processes and compiles stored core styles, then combines and renders them to the page.
289 - * Styles are stored via the Style Engine API.
488 + * Registers common scripts and styles to be used as dependencies of the editor
489 + * and plugins.
290 490 *
291 - * This hook also exists, and should be backported to Core in future versions.
292 - * However, it is envisaged that Gutenberg will continue to use the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes to aid continuous development.
491 + * @since 0.1.0
492 + */
493 +function gutenberg_enqueue_block_editor_assets() {
494 + if ( defined( 'GUTENBERG_LIVE_RELOAD' ) && GUTENBERG_LIVE_RELOAD ) {
495 + $live_reload_url = ( GUTENBERG_LIVE_RELOAD === true ) ? 'http://localhost:35729/livereload.js' : GUTENBERG_LIVE_RELOAD;
496 +
497 + wp_enqueue_script(
498 + 'gutenberg-live-reload',
499 + $live_reload_url
500 + );
501 + }
502 +}
503 +add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_block_editor_assets' );
504 +
505 +/**
506 + * Retrieves a unique and reasonably short and human-friendly filename for a
507 + * vendor script based on a URL and the script handle.
293 508 *
294 - * @since 6.1
509 + * @param string $handle The name of the script.
510 + * @param string $src Full URL of the external script.
295 511 *
296 - * @link https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/
512 + * @return string Script filename suitable for local caching.
297 513 *
298 - * @param array $options {
299 - * Optional. An array of options to pass to gutenberg_style_engine_get_stylesheet_from_context(). Default empty array.
514 + * @since 0.1.0
515 + */
516 +function gutenberg_vendor_script_filename( $handle, $src ) {
517 + $filename = basename( $src );
518 + $match = preg_match(
519 + '/^'
520 + . '(?P<ignore>.*?)'
521 + . '(?P<suffix>\.min)?'
522 + . '(?P<extension>\.js)'
523 + . '(?P<extra>.*)'
524 + . '$/',
525 + $filename,
526 + $filename_pieces
527 + );
528 +
529 + $prefix = $handle;
530 + $suffix = $match ? $filename_pieces['suffix'] : '';
531 + $hash = substr( md5( $src ), 0, 8 );
532 +
533 + return "${prefix}${suffix}.${hash}.js";
534 +}
535 +
536 +/**
537 + * Registers a vendor script from a URL, preferring a locally cached version if
538 + * possible, or downloading it if the cached version is unavailable or
539 + * outdated.
300 540 *
301 - * @type bool $optimize Whether to optimize the CSS output, e.g., combine rules. Default is `false`.
302 - * @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.
303 - * }
541 + * @param WP_Scripts $scripts WP_Scripts instance.
542 + * @param string $handle Name of the script.
543 + * @param string $src Full URL of the external script.
544 + * @param array $deps Optional. An array of registered script handles this
545 + * script depends on.
546 + * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL
547 + * as a query string for cache busting purposes. If version is set to false, a version
548 + * number is automatically added equal to current installed WordPress version.
549 + * If set to null, no version is added.
550 + * @param bool $in_footer Optional. Whether to enqueue the script before </body> instead of in the <head>.
551 + * Default 'false'.
304 552 *
305 - * @return void
553 + * @since 0.1.0
306 554 */
307 -function gutenberg_enqueue_stored_styles( $options = array() ) {
308 - $is_block_theme = wp_is_block_theme();
309 - $is_classic_theme = ! $is_block_theme;
555 +function gutenberg_register_vendor_script( $scripts, $handle, $src, $deps = array(), $ver = null, $in_footer = false ) {
556 + if ( defined( 'GUTENBERG_LOAD_VENDOR_SCRIPTS' ) && ! GUTENBERG_LOAD_VENDOR_SCRIPTS ) {
557 + return;
558 + }
310 559
311 - /*
312 - * For block themes, print stored styles in the header.
313 - * For classic themes, in the footer.
314 - */
315 - if (
316 - ( $is_block_theme && doing_action( 'wp_footer' ) ) ||
317 - ( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) )
318 - ) {
560 + $filename = gutenberg_vendor_script_filename( $handle, $src );
561 +
562 + if ( defined( 'GUTENBERG_LIST_VENDOR_ASSETS' ) && GUTENBERG_LIST_VENDOR_ASSETS ) {
563 + echo "$src|$filename\n";
319 564 return;
320 565 }
321 566
322 - $core_styles_keys = array( 'block-supports' );
323 - $compiled_core_stylesheet = '';
324 - $style_tag_id = 'core';
325 - foreach ( $core_styles_keys as $style_key ) {
326 - // Adds comment if code is prettified to identify core styles sections in debugging.
327 - $should_prettify = isset( $options['prettify'] ) ? true === $options['prettify'] : SCRIPT_DEBUG;
328 - if ( $should_prettify ) {
329 - $compiled_core_stylesheet .= "/**\n * Core styles: $style_key\n */\n";
330 - }
331 - // Chains core store ids to signify what the styles contain.
332 - $style_tag_id .= '-' . $style_key;
333 - $compiled_core_stylesheet .= gutenberg_style_engine_get_stylesheet_from_context( $style_key, $options );
334 - }
567 + $full_path = gutenberg_dir_path() . 'vendor/' . $filename;
335 568
336 - // Combines Core styles.
337 - if ( ! empty( $compiled_core_stylesheet ) ) {
338 - wp_register_style( $style_tag_id, false, array(), true );
339 - wp_add_inline_style( $style_tag_id, $compiled_core_stylesheet );
340 - wp_enqueue_style( $style_tag_id );
341 - }
569 + $needs_fetch = (
570 + defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE && (
571 + ! file_exists( $full_path ) ||
572 + time() - filemtime( $full_path ) >= DAY_IN_SECONDS
573 + )
574 + );
342 575
343 - // If there are any other stores registered by themes etc., print them out.
344 - $additional_stores = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_stores();
576 + if ( $needs_fetch ) {
577 + // Determine whether we can write to this file. If not, don't waste
578 + // time doing a network request.
579 + // @codingStandardsIgnoreStart
345 580
346 - /*
347 - * Since the corresponding action hook in Core is removed below,
348 - * this function should still honour any styles stored using the Core Style Engine store.
349 - */
350 - if ( class_exists( 'WP_Style_Engine_CSS_Rules_Store' ) ) {
351 - $additional_stores = array_merge( $additional_stores, WP_Style_Engine_CSS_Rules_Store::get_stores() );
352 - }
581 + $is_writable = is_writable( $full_path );
582 + if ( $is_writable ) {
583 + $f = @fopen( $full_path, 'a' );
584 + if ( ! $f ) {
585 + $is_writable = false;
586 + } else {
587 + fclose( $f );
588 + }
589 + }
353 590
354 - foreach ( array_keys( $additional_stores ) as $store_name ) {
355 - if ( in_array( $store_name, $core_styles_keys, true ) ) {
356 - continue;
591 + // @codingStandardsIgnoreEnd
592 + if ( ! $is_writable ) {
593 + // Failed to open the file for writing, probably due to server
594 + // permissions. Enqueue the script directly from the URL instead.
595 + gutenberg_override_script( $scripts, $handle, $src, $deps, $ver, $in_footer );
596 + return;
357 597 }
358 - $styles = gutenberg_style_engine_get_stylesheet_from_context( $store_name, $options );
359 - if ( ! empty( $styles ) ) {
360 - $key = "wp-style-engine-$store_name";
361 - wp_register_style( $key, false, array(), true );
362 - wp_add_inline_style( $key, $styles );
363 - wp_enqueue_style( $key );
598 +
599 + $response = wp_remote_get( $src );
600 + if ( wp_remote_retrieve_response_code( $response ) === 200 ) {
601 + $f = fopen( $full_path, 'w' );
602 + fwrite( $f, wp_remote_retrieve_body( $response ) );
603 + fclose( $f );
604 + } elseif ( ! filesize( $full_path ) ) {
605 + // The request failed. If the file is already cached, continue to
606 + // use this file. If not, then unlink the 0 byte file, and enqueue
607 + // the script directly from the URL.
608 + gutenberg_override_script( $scripts, $handle, $src, $deps, $ver, $in_footer );
609 + unlink( $full_path );
610 + return;
364 611 }
365 612 }
613 + gutenberg_override_script(
614 + $scripts,
615 + $handle,
616 + gutenberg_url( 'vendor/' . $filename ),
617 + $deps,
618 + $ver,
619 + $in_footer
620 + );
366 621 }
367 622
368 623 /**
369 - * Registers vendor JavaScript files to be used as dependencies of the editor
370 - * and plugins.
624 + * Extends block editor settings to remove the Gutenberg's `editor-styles.css`;
371 625 *
372 - * This function is called from a script during the plugin build process, so it
373 - * should not call any WordPress PHP functions.
626 + * This can be removed when plugin support requires WordPress 5.8.0+.
374 627 *
375 - * @since 13.0
628 + * @param array $settings Default editor settings.
376 629 *
377 - * @param WP_Scripts $scripts WP_Scripts instance.
630 + * @return array Filtered editor settings.
378 631 */
379 -function gutenberg_register_vendor_scripts( $scripts ) {
380 - $extension = SCRIPT_DEBUG ? '.js' : '.min.js';
381 - $vendors_dir = gutenberg_dir_path() . 'build/scripts/vendors/';
632 +function gutenberg_extend_block_editor_styles( $settings ) {
633 + if ( empty( $settings['styles'] ) ) {
634 + $settings['styles'] = array();
635 + } else {
636 + /*
637 + * WordPress versions prior to 5.8 include a legacy editor styles file
638 + * that need to be removed.
639 + * This code can be removed from the Gutenberg plugin when the supported WP
640 + * version is 5.8
641 + */
642 + $default_styles_file = is_rtl() ?
643 + ABSPATH . WPINC . '/css/dist/editor/editor-styles-rtl.css' :
644 + ABSPATH . WPINC . '/css/dist/editor/editor-styles.css';
382 645
383 - // When the React 19 experiment is enabled, register React 19 vendor
384 - // scripts under the `react`, `react-dom`, and `react-jsx-runtime` handles.
385 - $use_react_19 = gutenberg_is_experiment_enabled( 'gutenberg-react-19' );
646 + if ( file_exists( $default_styles_file ) ) {
647 + $default_styles = file_get_contents(
648 + $default_styles_file
649 + );
386 650
387 - $vendor_handles = array( 'react', 'react-dom', 'react-jsx-runtime' );
651 + /*
652 + * Iterate backwards from the end of the array since the preferred
653 + * insertion point in case not found is prepended as first entry.
654 + */
655 + for ( $i = count( $settings['styles'] ) - 1; $i >= 0; $i-- ) {
656 + if ( isset( $settings['styles'][ $i ]['css'] ) &&
657 + $default_styles === $settings['styles'][ $i ]['css'] ) {
658 + break;
659 + }
660 + }
388 661
389 - foreach ( $vendor_handles as $handle ) {
390 - $source = $use_react_19 ? $handle . '-19' : $handle;
391 - $asset_file = $vendors_dir . $source . '.min.asset.php';
392 - $asset = file_exists( $asset_file ) ? require $asset_file : array();
393 - $dependencies = $asset['dependencies'] ?? array();
394 - $version = $asset['version'] ?? '0';
395 -
396 - gutenberg_override_script(
397 - $scripts,
398 - $handle,
399 - gutenberg_url( 'build/scripts/vendors/' . $source . $extension ),
400 - $dependencies,
401 - $version
402 - );
662 + if ( isset( $i ) && $i >= 0 ) {
663 + unset( $settings['styles'][ $i ] );
664 + }
665 + }
403 666 }
404 667
405 - // WordPress Core in `wp_register_development_scripts` sets `wp-react-refresh-entry`
406 - // as a dependency to `react` when `SCRIPT_DEBUG` is true. Preserve that here.
407 - if ( SCRIPT_DEBUG ) {
408 - $react = $scripts->query( 'react', 'registered' );
409 - if ( $react && ! in_array( 'wp-react-refresh-entry', $react->deps, true ) ) {
410 - $react->deps[] = 'wp-react-refresh-entry';
668 + // Remove the default font editor styles for FSE themes.
669 + if ( WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) {
670 + foreach ( $settings['styles'] as $j => $style ) {
671 + if ( 0 === strpos( $style['css'], 'body { font-family:' ) ) {
672 + unset( $settings['styles'][ $j ] );
673 + }
411 674 }
412 675 }
676 +
677 + return $settings;
413 678 }
414 -add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' );
679 +// This can be removed when plugin support requires WordPress 5.8.0+.
680 +if ( function_exists( 'get_block_editor_settings' ) ) {
681 + add_filter( 'block_editor_settings_all', 'gutenberg_extend_block_editor_styles' );
682 +} else {
683 + add_filter( 'block_editor_settings', 'gutenberg_extend_block_editor_styles' );
684 +}
415 685
416 686 /**
417 - * Registers or re-registers Gutenberg Script Modules.
687 + * Adds a flag to the editor settings to know whether we're in FSE theme or not.
418 688 *
419 - * Script modules that are registered by Core will be re-registered by Gutenberg.
689 + * This can be removed when plugin support requires WordPress 5.8.0+.
420 690 *
421 - * @since 19.3.0
691 + * @param array $settings Default editor settings.
692 + *
693 + * @return array Filtered editor settings.
422 694 */
423 -function gutenberg_define_interactivity_modules_support() {
424 - // Load the auto-generated module registry.
425 - $modules_registry_file = gutenberg_dir_path() . 'build/modules/index.php';
426 - if ( ! file_exists( $modules_registry_file ) ) {
427 - return;
695 +function gutenberg_extend_block_editor_settings_with_fse_theme_flag( $settings ) {
696 + $settings['supportsTemplateMode'] = gutenberg_supports_block_templates();
697 +
698 + // Enable the new layout options for themes with a theme.json file.
699 + $settings['supportsLayout'] = WP_Theme_JSON_Resolver_Gutenberg::theme_has_support();
700 +
701 + return $settings;
702 +}
703 +// This can be removed when plugin support requires WordPress 5.8.0+.
704 +if ( function_exists( 'get_block_editor_settings' ) ) {
705 + add_filter( 'block_editor_settings_all', 'gutenberg_extend_block_editor_settings_with_fse_theme_flag' );
706 +} else {
707 + add_filter( 'block_editor_settings', 'gutenberg_extend_block_editor_settings_with_fse_theme_flag' );
708 +}
709 +
710 +/**
711 + * Sets the editor styles to be consumed by JS.
712 + */
713 +function gutenberg_extend_block_editor_styles_html() {
714 + global $pagenow;
715 +
716 + $script_handles = array();
717 + $style_handles = array(
718 + 'wp-block-editor',
719 + 'wp-block-library',
720 + 'wp-block-library-theme',
721 + 'wp-edit-blocks',
722 + );
723 +
724 + if ( 'widgets.php' === $pagenow || 'customize.php' === $pagenow ) {
725 + $style_handles[] = 'wp-widgets';
726 + $style_handles[] = 'wp-edit-widgets';
428 727 }
429 728
430 - $modules = require $modules_registry_file;
729 + $block_registry = WP_Block_Type_Registry::get_instance();
431 730
432 - // Add client navigation support to block library modules.
433 - foreach ( $modules as $module ) {
434 - if ( str_starts_with( $module['id'], '@wordpress/block-library' ) && method_exists( 'WP_Interactivity_API', 'add_client_navigation_support_to_script_module' ) ) {
435 - wp_interactivity()->add_client_navigation_support_to_script_module( $module['id'] );
731 + foreach ( $block_registry->get_all_registered() as $block_type ) {
732 + if ( ! empty( $block_type->style ) ) {
733 + $style_handles[] = $block_type->style;
436 734 }
735 +
736 + if ( ! empty( $block_type->editor_style ) ) {
737 + $style_handles[] = $block_type->editor_style;
738 + }
739 +
740 + if ( ! empty( $block_type->script ) ) {
741 + $script_handles[] = $block_type->script;
742 + }
437 743 }
438 -}
439 -remove_action( 'wp_default_scripts', 'wp_define_interactivity_modules_support' );
440 -add_action( 'wp_default_scripts', 'gutenberg_define_interactivity_modules_support' );
441 744
442 -/**
443 - * Always remove the Core action hook while gutenberg_enqueue_stored_styles() exists to avoid styles being printed twice.
444 - * This is also because gutenberg_enqueue_stored_styles uses the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes,
445 - * which are in continuous development and generally ahead of Core.
446 - */
447 -remove_action( 'wp_enqueue_scripts', 'wp_enqueue_stored_styles' );
448 -remove_action( 'wp_footer', 'wp_enqueue_stored_styles', 1 );
745 + $style_handles = array_unique( $style_handles );
746 + $done = wp_styles()->done;
449 747
450 -// Enqueue stored styles.
451 -add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_stored_styles' );
452 -add_action( 'wp_footer', 'gutenberg_enqueue_stored_styles', 1 );
748 + ob_start();
453 749
454 -/**
455 - * Enqueues the LaTeX to MathML loader script module in the block editor.
456 - */
457 -add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_latex_to_mathml_loader' );
458 -function gutenberg_enqueue_latex_to_mathml_loader() {
459 - wp_enqueue_script_module( '@wordpress/latex-to-mathml/loader' );
460 -}
750 + // We do not need reset styles for the iframed editor.
751 + wp_styles()->done = array( 'wp-reset-editor-styles' );
752 + wp_styles()->do_items( $style_handles );
753 + wp_styles()->done = $done;
461 754
462 -/**
463 - * Enqueue the vips loader script module in the block editor.
464 - *
465 - * This registers @wordpress/vips/worker as a dynamic dependency in the import map,
466 - * enabling on-demand loading of the ~3.8MB WASM-based image processing module
467 - * when client-side media processing is triggered via @wordpress/upload-media.
468 - *
469 - * @see packages/vips/src/loader.ts
470 - */
471 -add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_vips_loader' );
472 -function gutenberg_enqueue_vips_loader() {
473 - wp_enqueue_script_module( '@wordpress/vips/loader' );
474 -}
755 + $styles = ob_get_clean();
475 756
476 -/**
477 - * Enqueue the video-conversion loader script module in the block editor.
478 - *
479 - * This registers @wordpress/video-conversion/worker as a dynamic dependency
480 - * in the import map, enabling on-demand loading of the WebCodecs-based
481 - * GIF-to-video processing module when animated GIF conversion is triggered
482 - * via @wordpress/upload-media.
483 - *
484 - * @see packages/video-conversion/src/loader.ts
485 - */
486 -if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
487 - add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_video_conversion_loader' );
757 + $script_handles = array_unique( $script_handles );
758 + $done = wp_scripts()->done;
759 +
760 + ob_start();
761 +
762 + wp_scripts()->done = array();
763 + wp_scripts()->do_items( $script_handles );
764 + wp_scripts()->done = $done;
765 +
766 + $scripts = ob_get_clean();
767 +
768 + $editor_assets = wp_json_encode(
769 + array(
770 + 'styles' => $styles,
771 + 'scripts' => $scripts,
772 + )
773 + );
774 +
775 + echo "<script>window.__editorAssets = $editor_assets</script>";
488 776 }
489 -function gutenberg_enqueue_video_conversion_loader() {
490 - wp_enqueue_script_module( '@wordpress/video-conversion/loader' );
491 -}
777 +add_action( 'admin_footer-appearance_page_gutenberg-edit-site', 'gutenberg_extend_block_editor_styles_html' );
778 +add_action( 'admin_footer-post.php', 'gutenberg_extend_block_editor_styles_html' );
779 +add_action( 'admin_footer-post-new.php', 'gutenberg_extend_block_editor_styles_html' );
780 +add_action( 'admin_footer-widgets.php', 'gutenberg_extend_block_editor_styles_html' );