PluginProbe
Gutenberg / 9.8.0
Gutenberg v9.8.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 +547 -289 24.0.09.8.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
27 + * @param string $path Relative path of the desired file.
28 28 *
29 - * @param string $path Relative path of the desired file.
29 + * @return string Fully qualified URL pointing to the desired file.
30 30 *
31 - * @return string Fully qualified URL pointing to the desired file.
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,76 @@
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 script, which is a dependency _of_ `wp-i18n`.
92 + *
93 + * See: https://core.trac.wordpress.org/ticket/46089
94 + */
95 + if ( 'wp-i18n' !== $handle && 'wp-polyfill' !== $handle ) {
96 + $scripts->set_translations( $handle, 'default' );
97 + }
98 + if ( 'wp-i18n' === $handle ) {
99 + $ltr = 'rtl' === _x( 'ltr', 'text direction', 'default' ) ? 'rtl' : 'ltr';
100 + $output = sprintf( "wp.i18n.setLocaleData( { 'text direction\u0004ltr': [ '%s' ] }, 'default' );", $ltr );
101 + $scripts->add_inline_script( 'wp-i18n', $output, 'after' );
102 + }
103 +}
104 +
105 +/**
38 106 * Filters the default translation file load behavior to load the Gutenberg
39 107 * plugin translation file, if available.
40 108 *
41 109 * @param string|false $file Path to the translation file to load. False if
@@ -51,14 +119,14 @@
51 119 return $file;
52 120 }
53 121
54 122 // Ignore scripts whose handle does not have the "wp-" prefix.
55 - if ( ! str_starts_with( $handle, 'wp-' ) ) {
123 + if ( 'wp-' !== substr( $handle, 0, 3 ) ) {
56 124 return $file;
57 125 }
58 126
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';
127 + // Ignore scripts that are not found in the expected `build/` location.
128 + $script_path = gutenberg_dir_path() . 'build/' . substr( $handle, 3 ) . '/index.js';
61 129 if ( ! file_exists( $script_path ) ) {
62 130 return $file;
63 131 }
64 132
@@ -86,404 +154,594 @@
86 154 }
87 155 add_filter( 'load_script_translation_file', 'gutenberg_override_translation_file', 10, 2 );
88 156
89 157 /**
90 - * Adds the 'editor' dependency to wp-block-library, required by the Classic block.
158 + * Filters the default labels for common post types to change the case style
159 + * from capitalized (e.g. "Featured Image") to sentence-style (e.g. "Featured
160 + * image").
91 161 *
92 - * @param WP_Scripts $scripts WP_Scripts instance.
162 + * See: https://github.com/WordPress/gutenberg/pull/18758
163 + *
164 + * @param object $labels Object with all the labels as member variables.
165 + *
166 + * @return object Object with all the labels, including overridden ones.
93 167 */
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';
168 +function gutenberg_override_posttype_labels( $labels ) {
169 + $labels->featured_image = __( 'Featured image', 'gutenberg' );
170 + return $labels;
171 +}
172 +foreach ( array( 'post', 'page' ) as $post_type ) {
173 + add_filter( "post_type_labels_{$post_type}", 'gutenberg_override_posttype_labels' );
174 +}
175 +
176 +/**
177 + * Registers a style according to `wp_register_style`. Honors this request by
178 + * deregistering any style by the same handler before registration.
179 + *
180 + * @since 4.1.0
181 + *
182 + * @param WP_Styles $styles WP_Styles instance.
183 + * @param string $handle Name of the stylesheet. Should be unique.
184 + * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory.
185 + * @param array $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array.
186 + * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL
187 + * as a query string for cache busting purposes. If version is set to false, a version
188 + * number is automatically added equal to current installed WordPress version.
189 + * If set to null, no version is added.
190 + * @param string $media Optional. The media for which this stylesheet has been defined.
191 + * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like
192 + * '(orientation: portrait)' and '(max-width: 640px)'.
193 + */
194 +function gutenberg_override_style( $styles, $handle, $src, $deps = array(), $ver = false, $media = 'all' ) {
195 + $style = $styles->query( $handle, 'registered' );
196 + if ( $style ) {
197 + $styles->remove( $handle );
99 198 }
199 + $styles->add( $handle, $src, $deps, $ver, $media );
100 200 }
101 -add_action( 'wp_default_scripts', 'gutenberg_register_block_library_script_special_case', 11 );
102 201
103 202 /**
104 - * Registers WordPress package styles with complex requirements.
203 + * Registers vendor JavaScript files to be used as dependencies of the editor
204 + * and plugins.
105 205 *
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
206 + * This function is called from a script during the plugin build process, so it
207 + * should not call any WordPress PHP functions.
113 208 *
114 - * These override calls will replace the auto-registered versions as needed.
209 + * @since 0.1.0
115 210 *
116 - * @since 6.7.0
211 + * @param WP_Scripts $scripts WP_Scripts instance.
212 + */
213 +function gutenberg_register_vendor_scripts( $scripts ) {
214 + $suffix = SCRIPT_DEBUG ? '' : '.min';
215 +
216 + $react_suffix = ( SCRIPT_DEBUG ? '.development' : '.production' ) . $suffix;
217 + gutenberg_register_vendor_script(
218 + $scripts,
219 + 'react',
220 + 'https://unpkg.com/react@16.13.1/umd/react' . $react_suffix . '.js',
221 + array( 'wp-polyfill' )
222 + );
223 + gutenberg_register_vendor_script(
224 + $scripts,
225 + 'react-dom',
226 + 'https://unpkg.com/react-dom@16.13.1/umd/react-dom' . $react_suffix . '.js',
227 + array( 'react' )
228 + );
229 +
230 + /*
231 + * This script registration and the corresponding function should be removed
232 + * removed once the plugin is updated to support WordPress 5.6.0 and newer.
233 + */
234 + gutenberg_register_vendor_script(
235 + $scripts,
236 + 'lodash',
237 + 'https://unpkg.com/lodash@4.17.19/lodash.js',
238 + array(),
239 + '4.17.19',
240 + true
241 + );
242 +
243 + gutenberg_register_vendor_script(
244 + $scripts,
245 + 'object-fit-polyfill',
246 + 'https://unpkg.com/objectFitPolyfill@2.3.0/dist/objectFitPolyfill.min.js',
247 + array(),
248 + '2.3.0'
249 + );
250 +}
251 +add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' );
252 +
253 +/**
254 + * Registers all the WordPress packages scripts that are in the standardized
255 + * `build/` location.
117 256 *
118 - * @global array $editor_styles
257 + * @since 4.5.0
119 258 *
120 - * @param WP_Styles $styles WP_Styles instance.
259 + * @param WP_Scripts $scripts WP_Scripts instance.
121 260 */
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';
261 +function gutenberg_register_packages_scripts( $scripts ) {
262 + foreach ( glob( gutenberg_dir_path() . 'build/*/index.js' ) as $path ) {
263 + // Prefix `wp-` to package directory to get script handle.
264 + // For example, `…/build/a11y/index.js` becomes `wp-a11y`.
265 + $handle = 'wp-' . basename( dirname( $path ) );
127 266
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';
267 + // Replace `.js` extension with `.asset.php` to find the generated dependencies file.
268 + $asset_file = substr( $path, 0, -3 ) . '.asset.php';
269 + $asset = file_exists( $asset_file )
270 + ? require( $asset_file )
271 + : null;
272 + $dependencies = isset( $asset['dependencies'] ) ? $asset['dependencies'] : array();
273 + $version = isset( $asset['version'] ) ? $asset['version'] : filemtime( $path );
132 274
133 - // wp-edit-post: add wp-edit-blocks (custom handle not auto-inferred)
134 - $styles->query( 'wp-edit-post', 'registered' )->deps[] = 'wp-edit-blocks';
275 + // Add dependencies that cannot be detected and generated by build tools.
276 + switch ( $handle ) {
277 + case 'wp-block-library':
278 + array_push( $dependencies, 'editor' );
279 + break;
135 280
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';
281 + case 'wp-edit-post':
282 + array_push( $dependencies, 'media-models', 'media-views', 'postbox' );
283 + break;
141 284
142 - // wp-edit-widgets: add wp-edit-blocks (custom handle not auto-inferred)
143 - $styles->query( 'wp-edit-widgets', 'registered' )->deps[] = 'wp-edit-blocks';
285 + case 'wp-edit-site':
286 + array_push( $dependencies, 'wp-dom-ready' );
287 + break;
288 + }
144 289
145 - // wp-customize-widgets: add wp-edit-blocks (custom handle not auto-inferred)
146 - $styles->query( 'wp-customize-widgets', 'registered' )->deps[] = 'wp-edit-blocks';
290 + // Get the path from Gutenberg directory as expected by `gutenberg_url`.
291 + $gutenberg_path = substr( $path, strlen( gutenberg_dir_path() ) );
147 292
293 + gutenberg_override_script(
294 + $scripts,
295 + $handle,
296 + gutenberg_url( $gutenberg_path ),
297 + $dependencies,
298 + $version,
299 + true
300 + );
301 + }
302 +}
303 +add_action( 'wp_default_scripts', 'gutenberg_register_packages_scripts' );
304 +
305 +/**
306 + * Registers all the WordPress packages styles that are in the standardized
307 + * `build/` location.
308 + *
309 + * @since 6.7.0
310 +
311 + * @param WP_Styles $styles WP_Styles instance.
312 + */
313 +function gutenberg_register_packages_styles( $styles ) {
314 + // Editor Styles.
148 315 gutenberg_override_style(
149 316 $styles,
150 - 'wp-theme',
151 - gutenberg_url( 'build/styles/theme/design-tokens' . $suffix . '.css' ),
152 - array(),
153 - $version
317 + 'wp-block-editor',
318 + gutenberg_url( 'build/block-editor/style.css' ),
319 + array( 'wp-components', 'wp-editor-font' ),
320 + filemtime( gutenberg_dir_path() . 'build/editor/style.css' )
154 321 );
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' );
322 + $styles->add_data( 'wp-block-editor', 'rtl', 'replace' );
158 323
159 - // Register wp-base-styles and add it to the already registered wp-admin stylesheet
160 324 gutenberg_override_style(
161 325 $styles,
162 - 'wp-base-styles',
163 - gutenberg_url( 'build/styles/base-styles/admin-schemes' . $suffix . '.css' ),
164 - array(),
165 - $version
326 + 'wp-editor',
327 + gutenberg_url( 'build/editor/style.css' ),
328 + array( 'wp-components', 'wp-block-editor', 'wp-nux' ),
329 + filemtime( gutenberg_dir_path() . 'build/editor/style.css' )
166 330 );
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';
331 + $styles->add_data( 'wp-editor', 'rtl', 'replace' );
171 332
172 333 gutenberg_override_style(
173 334 $styles,
174 - 'wp-block-editor-content',
175 - gutenberg_url( 'build/styles/block-editor/content' . $suffix . '.css' ),
176 - array( 'wp-components' ),
177 - $version
335 + 'wp-edit-post',
336 + gutenberg_url( 'build/edit-post/style.css' ),
337 + array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-block-library', 'wp-nux' ),
338 + filemtime( gutenberg_dir_path() . 'build/edit-post/style.css' )
178 339 );
179 - $styles->add_data( 'wp-block-editor-content', 'rtl', 'replace' );
180 - $styles->add_data( 'wp-block-editor-content', 'suffix', $suffix );
340 + $styles->add_data( 'wp-edit-post', 'rtl', 'replace' );
181 341
182 - $block_library_filename = wp_should_load_separate_core_block_assets() ? 'common' : 'style';
183 342 gutenberg_override_style(
184 343 $styles,
344 + 'wp-components',
345 + gutenberg_url( 'build/components/style.css' ),
346 + array( 'dashicons' ),
347 + filemtime( gutenberg_dir_path() . 'build/components/style.css' )
348 + );
349 + $styles->add_data( 'wp-components', 'rtl', 'replace' );
350 +
351 + $block_library_filename = gutenberg_should_load_separate_block_styles() ? 'common' : 'style';
352 + gutenberg_override_style(
353 + $styles,
185 354 'wp-block-library',
186 - gutenberg_url( 'build/styles/block-library/' . $block_library_filename . $suffix . '.css' ),
355 + gutenberg_url( 'build/block-library/' . $block_library_filename . '.css' ),
187 356 array(),
188 - $version
357 + filemtime( gutenberg_dir_path() . 'build/block-library/' . $block_library_filename . '.css' )
189 358 );
190 359 $styles->add_data( 'wp-block-library', 'rtl', 'replace' );
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' );
193 360
194 - // Only add CONTENT styles here that should be enqueued in the iframe!
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',
200 - 'wp-components',
201 - // This need to be added before the block library styles,
202 - // The block library styles override the "reset" styles.
203 - 'wp-reset-editor-styles',
204 - 'wp-block-library',
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',
361 + gutenberg_override_style(
362 + $styles,
363 + 'wp-format-library',
364 + gutenberg_url( 'build/format-library/style.css' ),
365 + array( 'wp-block-editor', 'wp-components' ),
366 + filemtime( gutenberg_dir_path() . 'build/format-library/style.css' )
209 367 );
368 + $styles->add_data( 'wp-format-library', 'rtl', 'replace' );
210 369
211 - // Only load the default layout and margin styles for themes without theme.json file.
212 - if ( ! wp_theme_has_theme_json() ) {
213 - $wp_edit_blocks_dependencies[] = 'wp-editor-classic-layout-styles';
214 - }
370 + gutenberg_override_style(
371 + $styles,
372 + 'wp-edit-blocks',
373 + gutenberg_url( 'build/block-library/editor.css' ),
374 + array(
375 + 'wp-components',
376 + 'wp-editor',
377 + 'wp-block-library',
378 + // Always include visual styles so the editor never appears broken.
379 + 'wp-block-library-theme',
380 + ),
381 + filemtime( gutenberg_dir_path() . 'build/block-library/editor.css' )
382 + );
383 + $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' );
215 384
216 - global $editor_styles;
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.
219 - $wp_edit_blocks_dependencies[] = 'wp-block-library-theme';
220 - }
385 + gutenberg_override_style(
386 + $styles,
387 + 'wp-nux',
388 + gutenberg_url( 'build/nux/style.css' ),
389 + array( 'wp-components' ),
390 + filemtime( gutenberg_dir_path() . 'build/nux/style.css' )
391 + );
392 + $styles->add_data( 'wp-nux', 'rtl', 'replace' );
221 393
222 394 gutenberg_override_style(
223 395 $styles,
224 - 'wp-reset-editor-styles',
225 - gutenberg_url( 'build/styles/block-library/reset' . $suffix . '.css' ),
226 - array( 'common', 'forms' ), // Make sure the reset is loaded after the default WP Admin styles.
227 - $version
396 + 'wp-block-library-theme',
397 + gutenberg_url( 'build/block-library/theme.css' ),
398 + array(),
399 + filemtime( gutenberg_dir_path() . 'build/block-library/theme.css' )
228 400 );
229 - $styles->add_data( 'wp-reset-editor-styles', 'rtl', 'replace' );
230 - $styles->add_data( 'wp-reset-editor-styles', 'suffix', $suffix );
401 + $styles->add_data( 'wp-block-library-theme', 'rtl', 'replace' );
231 402
232 403 gutenberg_override_style(
233 404 $styles,
234 - 'wp-editor-classic-layout-styles',
235 - gutenberg_url( 'build/styles/edit-post/classic' . $suffix . '.css' ),
236 - array(),
237 - $version
405 + 'wp-list-reusable-blocks',
406 + gutenberg_url( 'build/list-reusable-blocks/style.css' ),
407 + array( 'wp-components' ),
408 + filemtime( gutenberg_dir_path() . 'build/list-reusable-blocks/style.css' )
238 409 );
239 - $styles->add_data( 'wp-editor-classic-layout-styles', 'rtl', 'replace' );
240 - $styles->add_data( 'wp-editor-classic-layout-styles', 'suffix', $suffix );
410 + $styles->add_data( 'wp-list-reusable-block', 'rtl', 'replace' );
241 411
242 412 gutenberg_override_style(
243 413 $styles,
244 - 'wp-block-library-editor',
245 - gutenberg_url( 'build/styles/block-library/editor' . $suffix . '.css' ),
246 - array(),
247 - $version
414 + 'wp-edit-navigation',
415 + gutenberg_url( 'build/edit-navigation/style.css' ),
416 + array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks' ),
417 + filemtime( gutenberg_dir_path() . 'build/edit-navigation/style.css' )
248 418 );
249 - $styles->add_data( 'wp-block-library-editor', 'rtl', 'replace' );
250 - $styles->add_data( 'wp-block-library-editor', 'suffix', $suffix );
419 + $styles->add_data( 'wp-edit-navigation', 'rtl', 'replace' );
251 420
252 421 gutenberg_override_style(
253 422 $styles,
254 - 'wp-edit-blocks',
255 - gutenberg_url( 'build/styles/block-library/editor' . $suffix . '.css' ),
256 - $wp_edit_blocks_dependencies,
257 - $version
423 + 'wp-edit-site',
424 + gutenberg_url( 'build/edit-site/style.css' ),
425 + array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks' ),
426 + filemtime( gutenberg_dir_path() . 'build/edit-site/style.css' )
258 427 );
259 - $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' );
260 - $styles->add_data( 'wp-edit-blocks', 'suffix', $suffix );
428 + $styles->add_data( 'wp-edit-site', 'rtl', 'replace' );
261 429
262 430 gutenberg_override_style(
263 431 $styles,
264 - 'wp-block-library-theme',
265 - gutenberg_url( 'build/styles/block-library/theme' . $suffix . '.css' ),
266 - array(),
267 - $version
432 + 'wp-edit-widgets',
433 + gutenberg_url( 'build/edit-widgets/style.css' ),
434 + array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks' ),
435 + filemtime( gutenberg_dir_path() . 'build/edit-widgets/style.css' )
268 436 );
269 - $styles->add_data( 'wp-block-library-theme', 'rtl', 'replace' );
270 - $styles->add_data( 'wp-block-library-theme', 'suffix', $suffix );
437 + $styles->add_data( 'wp-edit-widgets', 'rtl', 'replace' );
271 438
272 439 gutenberg_override_style(
273 440 $styles,
274 - 'classic-theme-styles',
275 - gutenberg_url( 'build/styles/block-library/classic' . $suffix . '.css' ),
276 - array(),
277 - $version
441 + 'wp-block-directory',
442 + gutenberg_url( 'build/block-directory/style.css' ),
443 + array( 'wp-block-editor', 'wp-components' ),
444 + filemtime( gutenberg_dir_path() . 'build/block-directory/style.css' )
278 445 );
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' );
446 + $styles->add_data( 'wp-block-directory', 'rtl', 'replace' );
282 447 }
283 -add_action( 'wp_default_styles', 'gutenberg_register_packages_styles', 15 );
448 +add_action( 'wp_default_styles', 'gutenberg_register_packages_styles' );
284 449
285 450 /**
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.
451 + * Registers common scripts and styles to be used as dependencies of the editor
452 + * and plugins.
288 453 *
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.
454 + * @since 0.1.0
455 + */
456 +function gutenberg_enqueue_block_editor_assets() {
457 + if ( defined( 'GUTENBERG_LIVE_RELOAD' ) && GUTENBERG_LIVE_RELOAD ) {
458 + $live_reload_url = ( GUTENBERG_LIVE_RELOAD === true ) ? 'http://localhost:35729/livereload.js' : GUTENBERG_LIVE_RELOAD;
459 +
460 + wp_enqueue_script(
461 + 'gutenberg-live-reload',
462 + $live_reload_url
463 + );
464 + }
465 +}
466 +add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_block_editor_assets' );
467 +
468 +/**
469 + * Retrieves a unique and reasonably short and human-friendly filename for a
470 + * vendor script based on a URL and the script handle.
291 471 *
292 - * @since 6.1
472 + * @param string $handle The name of the script.
473 + * @param string $src Full URL of the external script.
293 474 *
294 - * @link https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/
475 + * @return string Script filename suitable for local caching.
295 476 *
296 - * @param array $options {
297 - * Optional. An array of options to pass to gutenberg_style_engine_get_stylesheet_from_context(). Default empty array.
477 + * @since 0.1.0
478 + */
479 +function gutenberg_vendor_script_filename( $handle, $src ) {
480 + $filename = basename( $src );
481 + $match = preg_match(
482 + '/^'
483 + . '(?P<ignore>.*?)'
484 + . '(?P<suffix>\.min)?'
485 + . '(?P<extension>\.js)'
486 + . '(?P<extra>.*)'
487 + . '$/',
488 + $filename,
489 + $filename_pieces
490 + );
491 +
492 + $prefix = $handle;
493 + $suffix = $match ? $filename_pieces['suffix'] : '';
494 + $hash = substr( md5( $src ), 0, 8 );
495 +
496 + return "${prefix}${suffix}.${hash}.js";
497 +}
498 +
499 +/**
500 + * Registers a vendor script from a URL, preferring a locally cached version if
501 + * possible, or downloading it if the cached version is unavailable or
502 + * outdated.
298 503 *
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 - * }
504 + * @param WP_Scripts $scripts WP_Scripts instance.
505 + * @param string $handle Name of the script.
506 + * @param string $src Full URL of the external script.
507 + * @param array $deps Optional. An array of registered script handles this
508 + * script depends on.
509 + * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL
510 + * as a query string for cache busting purposes. If version is set to false, a version
511 + * number is automatically added equal to current installed WordPress version.
512 + * If set to null, no version is added.
513 + * @param bool $in_footer Optional. Whether to enqueue the script before </body> instead of in the <head>.
514 + * Default 'false'.
302 515 *
303 - * @return void
516 + * @since 0.1.0
304 517 */
305 -function gutenberg_enqueue_stored_styles( $options = array() ) {
306 - $is_block_theme = wp_is_block_theme();
307 - $is_classic_theme = ! $is_block_theme;
518 +function gutenberg_register_vendor_script( $scripts, $handle, $src, $deps = array(), $ver = null, $in_footer = false ) {
519 + if ( defined( 'GUTENBERG_LOAD_VENDOR_SCRIPTS' ) && ! GUTENBERG_LOAD_VENDOR_SCRIPTS ) {
520 + return;
521 + }
308 522
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 - ) {
523 + $filename = gutenberg_vendor_script_filename( $handle, $src );
524 +
525 + if ( defined( 'GUTENBERG_LIST_VENDOR_ASSETS' ) && GUTENBERG_LIST_VENDOR_ASSETS ) {
526 + echo "$src|$filename\n";
317 527 return;
318 528 }
319 529
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 - }
530 + $full_path = gutenberg_dir_path() . 'vendor/' . $filename;
333 531
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 );
339 - }
532 + $needs_fetch = (
533 + defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE && (
534 + ! file_exists( $full_path ) ||
535 + time() - filemtime( $full_path ) >= DAY_IN_SECONDS
536 + )
537 + );
340 538
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();
539 + if ( $needs_fetch ) {
540 + // Determine whether we can write to this file. If not, don't waste
541 + // time doing a network request.
542 + // @codingStandardsIgnoreStart
343 543
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 - }
544 + $is_writable = is_writable( $full_path );
545 + if ( $is_writable ) {
546 + $f = @fopen( $full_path, 'a' );
547 + if ( ! $f ) {
548 + $is_writable = false;
549 + } else {
550 + fclose( $f );
551 + }
552 + }
351 553
352 - foreach ( array_keys( $additional_stores ) as $store_name ) {
353 - if ( in_array( $store_name, $core_styles_keys, true ) ) {
354 - continue;
554 + // @codingStandardsIgnoreEnd
555 + if ( ! $is_writable ) {
556 + // Failed to open the file for writing, probably due to server
557 + // permissions. Enqueue the script directly from the URL instead.
558 + gutenberg_override_script( $scripts, $handle, $src, $deps, $ver, $in_footer );
559 + return;
355 560 }
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 );
561 +
562 + $response = wp_remote_get( $src );
563 + if ( wp_remote_retrieve_response_code( $response ) === 200 ) {
564 + $f = fopen( $full_path, 'w' );
565 + fwrite( $f, wp_remote_retrieve_body( $response ) );
566 + fclose( $f );
567 + } elseif ( ! filesize( $full_path ) ) {
568 + // The request failed. If the file is already cached, continue to
569 + // use this file. If not, then unlink the 0 byte file, and enqueue
570 + // the script directly from the URL.
571 + gutenberg_override_script( $scripts, $handle, $src, $deps, $ver, $in_footer );
572 + unlink( $full_path );
573 + return;
362 574 }
363 575 }
576 + gutenberg_override_script(
577 + $scripts,
578 + $handle,
579 + gutenberg_url( 'vendor/' . $filename ),
580 + $deps,
581 + $ver,
582 + $in_footer
583 + );
364 584 }
365 585
366 586 /**
367 - * Registers vendor JavaScript files to be used as dependencies of the editor
368 - * and plugins.
587 + * Extends block editor settings to include Gutenberg's `editor-styles.css` as
588 + * taking precedent those styles shipped with core.
369 589 *
370 - * This function is called from a script during the plugin build process, so it
371 - * should not call any WordPress PHP functions.
590 + * @param array $settings Default editor settings.
372 591 *
373 - * @since 13.0
374 - *
375 - * @param WP_Scripts $scripts WP_Scripts instance.
592 + * @return array Filtered editor settings.
376 593 */
377 -function gutenberg_register_vendor_scripts( $scripts ) {
378 - $extension = SCRIPT_DEBUG ? '.js' : '.min.js';
379 - $vendors_dir = gutenberg_dir_path() . 'build/scripts/vendors/';
594 +function gutenberg_extend_block_editor_styles( $settings ) {
595 + $editor_styles_file = is_rtl() ?
596 + gutenberg_dir_path() . 'build/editor/editor-styles-rtl.css' :
597 + gutenberg_dir_path() . 'build/editor/editor-styles.css';
380 598
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' );
599 + /*
600 + * If, for whatever reason, the built editor styles do not exist, avoid
601 + * override and fall back to the default.
602 + */
603 + if ( ! file_exists( $editor_styles_file ) ) {
604 + return $settings;
605 + }
384 606
385 - $vendor_handles = array( 'react', 'react-dom', 'react-jsx-runtime' );
607 + if ( empty( $settings['styles'] ) ) {
608 + $settings['styles'] = array();
609 + } else {
610 + /*
611 + * The styles setting is an array of CSS strings, so there is no direct
612 + * way to find the default styles. To maximize stability, load (again)
613 + * the default styles from disk and find its place in the array.
614 + *
615 + * See: https://github.com/WordPress/wordpress-develop/blob/5.0.3/src/wp-admin/edit-form-blocks.php#L168-L175
616 + */
386 617
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';
618 + $default_styles = file_get_contents(
619 + is_rtl() ?
620 + ABSPATH . WPINC . '/css/dist/editor/editor-styles-rtl.css' :
621 + ABSPATH . WPINC . '/css/dist/editor/editor-styles.css'
622 + );
393 623
394 - gutenberg_override_script(
395 - $scripts,
396 - $handle,
397 - gutenberg_url( 'build/scripts/vendors/' . $source . $extension ),
398 - $dependencies,
399 - $version
400 - );
624 + /*
625 + * Iterate backwards from the end of the array since the preferred
626 + * insertion point in case not found is prepended as first entry.
627 + */
628 + for ( $i = count( $settings['styles'] ) - 1; $i >= 0; $i-- ) {
629 + if ( isset( $settings['styles'][ $i ]['css'] ) &&
630 + $default_styles === $settings['styles'][ $i ]['css'] ) {
631 + break;
632 + }
633 + }
401 634 }
402 635
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';
409 - }
636 + $editor_styles = array(
637 + 'css' => file_get_contents( $editor_styles_file ),
638 + );
639 +
640 + // Substitute default styles if found. Otherwise, prepend to setting array.
641 + if ( isset( $i ) && $i >= 0 ) {
642 + $settings['styles'][ $i ] = $editor_styles;
643 + } else {
644 + array_unshift( $settings['styles'], $editor_styles );
410 645 }
646 +
647 + return $settings;
411 648 }
412 -add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' );
649 +add_filter( 'block_editor_settings', 'gutenberg_extend_block_editor_styles' );
413 650
414 651 /**
415 - * Registers or re-registers Gutenberg Script Modules.
652 + * Load the default editor styles.
653 + * These styles are used if the "no theme styles" options is triggered.
416 654 *
417 - * Script modules that are registered by Core will be re-registered by Gutenberg.
655 + * @param array $settings Default editor settings.
418 656 *
419 - * @since 19.3.0
657 + * @return array Filtered editor settings.
420 658 */
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;
426 - }
659 +function gutenberg_extend_block_editor_settings_with_default_editor_styles( $settings ) {
660 + $editor_styles_file = is_rtl() ?
661 + gutenberg_dir_path() . 'build/editor/editor-styles-rtl.css' :
662 + gutenberg_dir_path() . 'build/editor/editor-styles.css';
663 + $settings['defaultEditorStyles'] = array(
664 + array(
665 + 'css' => file_get_contents( $editor_styles_file ),
666 + ),
667 + );
427 668
428 - $modules = require $modules_registry_file;
669 + return $settings;
670 +}
671 +add_filter( 'block_editor_settings', 'gutenberg_extend_block_editor_settings_with_default_editor_styles' );
429 672
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 - }
673 +/**
674 + * Adds a flag to the editor settings to know whether we're in FSE theme or not.
675 + *
676 + * @param array $settings Default editor settings.
677 + *
678 + * @return array Filtered editor settings.
679 + */
680 +function gutenberg_extend_block_editor_settings_with_fse_theme_flag( $settings ) {
681 + $settings['isFSETheme'] = gutenberg_is_fse_theme();
682 + return $settings;
436 683 }
437 -remove_action( 'wp_default_scripts', 'wp_define_interactivity_modules_support' );
438 -add_action( 'wp_default_scripts', 'gutenberg_define_interactivity_modules_support' );
684 +add_filter( 'block_editor_settings', 'gutenberg_extend_block_editor_settings_with_fse_theme_flag' );
439 685
440 686 /**
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.
687 + * Sets the editor styles to be consumed by JS.
444 688 */
445 -remove_action( 'wp_enqueue_scripts', 'wp_enqueue_stored_styles' );
446 -remove_action( 'wp_footer', 'wp_enqueue_stored_styles', 1 );
689 +function gutenberg_extend_block_editor_styles_html() {
690 + $handles = array(
691 + 'wp-block-editor',
692 + 'wp-block-library',
693 + 'wp-edit-blocks',
694 + );
447 695
448 -// Enqueue stored styles.
449 -add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_stored_styles' );
450 -add_action( 'wp_footer', 'gutenberg_enqueue_stored_styles', 1 );
696 + $block_registry = WP_Block_Type_Registry::get_instance();
451 697
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' );
698 + foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
699 + if ( ! empty( $block_type->style ) ) {
700 + $handles[] = $block_type->style;
701 + }
702 +
703 + if ( ! empty( $block_type->editor_style ) ) {
704 + $handles[] = $block_type->editor_style;
705 + }
706 + }
707 +
708 + $handles = array_unique( $handles );
709 + $done = wp_styles()->done;
710 +
711 + ob_start();
712 +
713 + wp_styles()->done = array();
714 + wp_styles()->do_items( $handles );
715 + wp_styles()->done = $done;
716 +
717 + $editor_styles = wp_json_encode( array( 'html' => ob_get_clean() ) );
718 +
719 + echo "<script>window.__editorStyles = $editor_styles</script>";
458 720 }
721 +add_action( 'admin_footer-toplevel_page_gutenberg-edit-site', 'gutenberg_extend_block_editor_styles_html' );
459 722
460 723 /**
461 - * Enqueue the vips loader script module in the block editor.
724 + * Adds a polyfill for object-fit in environments which do not support it.
462 725 *
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.
726 + * The script registration occurs in `gutenberg_register_vendor_scripts`, which
727 + * should be removed in coordination with this function.
466 728 *
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 -}
473 -
474 -/**
475 - * Enqueue the video-conversion loader script module in the block editor.
729 + * @see gutenberg_register_vendor_scripts
730 + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
476 731 *
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.
732 + * @since 9.1.0
481 733 *
482 - * @see packages/video-conversion/src/loader.ts
734 + * @param WP_Scripts $scripts WP_Scripts object.
483 735 */
484 -if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
485 - add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_video_conversion_loader' );
736 +function gutenberg_add_object_fit_polyfill( $scripts ) {
737 + did_action( 'init' ) && $scripts->add_inline_script(
738 + 'wp-polyfill',
739 + wp_get_script_polyfill(
740 + $scripts,
741 + array(
742 + '"objectFit" in document.documentElement.style' => 'object-fit-polyfill',
743 + )
744 + )
745 + );
486 746 }
487 -function gutenberg_enqueue_video_conversion_loader() {
488 - wp_enqueue_script_module( '@wordpress/video-conversion/loader' );
489 -}
747 +add_action( 'wp_default_scripts', 'gutenberg_add_object_fit_polyfill', 20 );