| @@ -86,11 +86,12 @@ | ||
| 86 | 86 | * Automatically called during page rendering. |
| 87 | 87 | */ |
| 88 | 88 | function gutenberg_experiments_wp_admin_preload_data() { |
| 89 | 89 | // Define paths to preload - same for all pages |
| 90 | - // Please also change packages/core-data/src/entities.js when changing this. | |
| 90 | + // This must exactly match the _fields list in packages/core-data/src/entities.js, | |
| 91 | + // same fields in the same order, or the preload is never consumed. | |
| 91 | 92 | $preload_paths = array( |
| 92 | - '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', | |
| 93 | + '/?_fields=description,gmt_offset,home,image_max_bit_depth,image_sizes,image_size_threshold,image_strip_meta,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', | |
| 93 | 94 | array( '/wp/v2/settings', 'OPTIONS' ), |
| 94 | 95 | ); |
| 95 | 96 | |
| 96 | 97 | // Use rest_preload_api_request to gather the preloaded data |
| @@ -133,9 +134,11 @@ | ||
| 133 | 134 | |
| 134 | 135 | // Load build constants |
| 135 | 136 | $build_constants = require __DIR__ . '/../../constants.php'; |
| 136 | 137 | |
| 137 | - // Fire init action for extensions to register routes and menu items | |
| 138 | + /** | |
| 139 | + * Fires when the experiments admin page is initialized so extensions can register routes and menu items. | |
| 140 | + */ | |
| 138 | 141 | do_action( 'experiments-wp-admin_init' ); |
| 139 | 142 | |
| 140 | 143 | // Preload REST API data |
| 141 | 144 | gutenberg_experiments_wp_admin_preload_data(); |
| @@ -142,10 +145,14 @@ | ||
| 142 | 145 | |
| 143 | 146 | // Get all registered routes |
| 144 | 147 | $routes = gutenberg_get_experiments_wp_admin_routes(); |
| 145 | 148 | |
| 146 | - // Get boot module asset file for dependencies | |
| 149 | + // Get boot module asset file for dependencies. Plugins that build their own | |
| 150 | + // boot module use it; everyone else falls back to the copy bundled with Core. | |
| 147 | 151 | $asset_file = __DIR__ . '/../../modules/boot/index.min.asset.php'; |
| 152 | + if ( ! file_exists( $asset_file ) ) { | |
| 153 | + $asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php'; | |
| 154 | + } | |
| 148 | 155 | if ( file_exists( $asset_file ) ) { |
| 149 | 156 | $asset = require $asset_file; |
| 150 | 157 | |
| 151 | 158 | // This script serves two purposes: |
| @@ -152,15 +159,41 @@ | ||
| 152 | 159 | // 1. It ensures all the globals that are made available to the modules are loaded. |
| 153 | 160 | // 2. It initializes the boot module as an inline script. |
| 154 | 161 | wp_register_script( 'experiments-wp-admin-prerequisites', '', $asset['dependencies'], $asset['version'], true ); |
| 155 | 162 | |
| 156 | - // Add inline script to initialize the app using initSinglePage (no menuItems) | |
| 163 | + $init_modules = []; | |
| 164 | + | |
| 165 | + /* | |
| 166 | + * Add inline script to initialize the app using initSinglePage (no menuItems). | |
| 167 | + * The dynamic import is deferred until DOMContentLoaded so that all classic | |
| 168 | + * script dependencies of @wordpress/boot (wp-private-apis, wp-components, | |
| 169 | + * wp-theme, etc.) have finished parsing and executing before the boot module | |
| 170 | + * evaluates. Otherwise, a modulepreloaded @wordpress/boot can win the race | |
| 171 | + * against the classic-script-printing pass on fast CDN-fronted hosts in | |
| 172 | + * Chrome, evaluating before wp.theme.privateApis is defined and throwing | |
| 173 | + * "Cannot unlock an undefined object". See <https://core.trac.wordpress.org/ticket/65103>. | |
| 174 | + */ | |
| 175 | + $init_js_function = <<<'JS' | |
| 176 | + ( mountId, routes, initModules ) => { | |
| 177 | + const run = async () => { | |
| 178 | + const mod = await import( "@wordpress/boot" ); | |
| 179 | + mod.initSinglePage( { mountId, routes, initModules } ); | |
| 180 | + }; | |
| 181 | + if ( document.readyState === "loading" ) { | |
| 182 | + document.addEventListener( "DOMContentLoaded", run ); | |
| 183 | + } else { | |
| 184 | + run(); | |
| 185 | + } | |
| 186 | + } | |
| 187 | + JS; | |
| 157 | 188 | wp_add_inline_script( |
| 158 | 189 | 'experiments-wp-admin-prerequisites', |
| 159 | 190 | sprintf( |
| 160 | - 'import("@wordpress/boot").then(mod => mod.initSinglePage({mountId: "%s", routes: %s}));', | |
| 161 | - 'experiments-wp-admin-app', | |
| 162 | - wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) | |
| 191 | + '( %s )( %s, %s, %s );', | |
| 192 | + $init_js_function, | |
| 193 | + wp_json_encode( 'experiments-wp-admin-app', JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), | |
| 194 | + wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), | |
| 195 | + wp_json_encode( $init_modules, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) | |
| 163 | 196 | ) |
| 164 | 197 | ); |
| 165 | 198 | |
| 166 | 199 | // Register prerequisites style by filtering script dependencies to find registered styles |
| @@ -179,8 +212,11 @@ | ||
| 179 | 212 | 'id' => '@wordpress/boot', |
| 180 | 213 | ), |
| 181 | 214 | ); |
| 182 | 215 | |
| 216 | + // Add init modules as static dependencies | |
| 217 | + // No init modules configured | |
| 218 | + | |
| 183 | 219 | // Add all registered routes as dependencies |
| 184 | 220 | foreach ( $routes as $route ) { |
| 185 | 221 | if ( isset( $route['route_module'] ) ) { |
| 186 | 222 | $boot_dependencies[] = array( |
| @@ -234,30 +270,28 @@ | ||
| 234 | 270 | ?> |
| 235 | 271 | <style> |
| 236 | 272 | /* Critical styles to prevent layout shifts - inlined for immediate application */ |
| 237 | 273 | |
| 238 | - /* Background colors */ | |
| 239 | 274 | #wpwrap { |
| 240 | - background: var(--wpds-color-fg-content-neutral, #1e1e1e); | |
| 241 | 275 | overflow-y: auto; |
| 242 | 276 | } |
| 243 | - body { | |
| 277 | + body.js { | |
| 244 | 278 | background: #fff; |
| 245 | 279 | } |
| 246 | 280 | |
| 247 | 281 | /* Reset wp-admin padding */ |
| 248 | - #wpcontent { | |
| 282 | + body.js #wpcontent { | |
| 249 | 283 | padding-inline-start: 0; |
| 250 | 284 | } |
| 251 | - #wpbody-content { | |
| 285 | + body.js #wpbody-content { | |
| 252 | 286 | padding-bottom: 0; |
| 253 | 287 | } |
| 254 | 288 | |
| 255 | 289 | /* Hide legacy admin elements */ |
| 256 | - #wpbody-content > div:not(.boot-layout-container):not(#screen-meta) { | |
| 290 | + body.js #wpbody-content > div:not(#experiments-wp-admin-app):not(#screen-meta) { | |
| 257 | 291 | display: none; |
| 258 | 292 | } |
| 259 | - #wpfooter { | |
| 293 | + body.js #wpfooter { | |
| 260 | 294 | display: none; |
| 261 | 295 | } |
| 262 | 296 | |
| 263 | 297 | /* Accessibility regions */ |
| @@ -284,8 +318,22 @@ | ||
| 284 | 318 | overflow-y: initial; |
| 285 | 319 | } |
| 286 | 320 | } |
| 287 | 321 | </style> |
| 322 | + <div class="wrap hide-if-js"> | |
| 323 | + <h1 class="wp-heading-inline"><?php echo esc_html( get_admin_page_title() ); ?></h1> | |
| 324 | + <?php | |
| 325 | + wp_admin_notice( | |
| 326 | + __( 'This screen requires JavaScript. Enable JavaScript in your browser settings and reload the page.' ), | |
| 327 | + array( 'type' => 'error' ) | |
| 328 | + ); | |
| 329 | + ?> | |
| 330 | + </div> | |
| 331 | + <?php | |
| 332 | + // Core's pre-CSS Modules Boot layout uses this class for viewport sizing. | |
| 333 | + // Remove it when the minimum supported WordPress version includes the Boot | |
| 334 | + // changes from Gutenberg #81756. | |
| 335 | + ?> | |
| 288 | 336 | <div id="experiments-wp-admin-app" class="boot-layout-container"></div> |
| 289 | 337 | <?php |
| 290 | 338 | } |
| 291 | 339 | |
| @@ -290,5 +338,4 @@ | ||
| 290 | 338 | } |
| 291 | 339 | |
| 292 | 340 | // Hook the enqueue function to admin_enqueue_scripts |
| 293 | 341 | add_action( 'admin_enqueue_scripts', 'gutenberg_experiments_wp_admin_enqueue_scripts' ); |
| 294 | - | |