diff --git a/packages/wp-build/lib/php-generator.mjs b/packages/wp-build/lib/php-generator.mjs index 0a781cdffa9259..182825a9a10c6c 100644 --- a/packages/wp-build/lib/php-generator.mjs +++ b/packages/wp-build/lib/php-generator.mjs @@ -38,6 +38,19 @@ export async function getPhpReplacements( rootDir, baseUrlExpression ) { }; } +/** + * Checks if the build script is being run for WordPress Core. + * + * @param {Record} replacements Replacements object from getPhpReplacements(). + * @return {boolean} Whether this is a WordPress Core build. + */ +function isWordPressCoreBuild( replacements ) { + return ( + Boolean( process.env.npm_package_config_IS_WORDPRESS_CORE ) && + replacements[ '{{PREFIX}}' ] === 'wp' + ); +} + /** * Apply template replacements to a template string. * @@ -56,6 +69,11 @@ export function applyTemplateReplacements( template, replacements ) { /** * Render a template to a string with replacements. * + * Performs two passes: + * 1. Static placeholder replacements ({{PREFIX}}, {{VERSION}}, {{BASE_URL}}, etc.) + * 2. Pluggable guard resolution — {{IF_PLUGGABLE:fname}} / {{END_IF_PLUGGABLE}} markers + * are expanded to `! function_exists( 'fname' )` checks or removed entirely. + * * @param {string} templateName Template file name. * @param {Record} replacements Replacements object (e.g. {'{{PREFIX}}': 'gutenberg'}). * @return {Promise} Rendered template string. @@ -70,8 +88,37 @@ export async function renderTemplateToString( templateName, replacements ) { 'utf8' ); - // Apply replacements - return applyTemplateReplacements( template, replacements ); + // Apply static replacements + let content = applyTemplateReplacements( template, replacements ); + + // Resolve pluggable guard markers. + const pluggableBlockRegex = + /\{\{IF_PLUGGABLE:([^}]+)\}\}\n([\s\S]*?)\n\{\{END_IF_PLUGGABLE\}\}/g; + + if ( isWordPressCoreBuild( replacements ) ) { + // Remove the guard wrappers entirely. + content = content.replace( + pluggableBlockRegex, + ( _match, _fnName, body ) => body + ); + } else { + // Wrap block body in `if ( ! function_exists() )` and indent correctly. + content = content.replace( + pluggableBlockRegex, + ( _match, fnName, body ) => { + const indented = body + .split( '\n' ) + .map( + /** @param {string} line */ + ( line ) => ( line.length ? `\t${ line }` : line ) + ) + .join( '\n' ); + return `if ( ! function_exists( '${ fnName }' ) ) {\n${ indented }\n}`; + } + ); + } + + return content; } /** diff --git a/packages/wp-build/templates/module-registration.php.template b/packages/wp-build/templates/module-registration.php.template index c9cc9ac3bad559..f89b429a3e7a72 100644 --- a/packages/wp-build/templates/module-registration.php.template +++ b/packages/wp-build/templates/module-registration.php.template @@ -6,44 +6,44 @@ * @package {{PREFIX}} */ -if ( ! function_exists( '{{PREFIX}}_register_script_modules' ) ) { - /** - * Register all script modules. - */ - function {{PREFIX}}_register_script_modules() { - // Load build constants - $build_constants = require __DIR__ . '/constants.php'; - $modules_dir = __DIR__ . '/modules'; - $modules_file = $modules_dir . '/registry.php'; +{{IF_PLUGGABLE:{{PREFIX}}_register_script_modules}} +/** + * Register all script modules. + */ +function {{PREFIX}}_register_script_modules() { + // Load build constants + $build_constants = require __DIR__ . '/constants.php'; + $modules_dir = __DIR__ . '/modules'; + $modules_file = $modules_dir . '/registry.php'; - if ( ! file_exists( $modules_file ) ) { - return; - } + if ( ! file_exists( $modules_file ) ) { + return; + } - $modules = require $modules_file; - $base_url = $build_constants['build_url'] . 'modules/'; - $extension = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.js' : '.min.js'; + $modules = require $modules_file; + $base_url = $build_constants['build_url'] . 'modules/'; + $extension = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.js' : '.min.js'; - foreach ( $modules as $module ) { - $asset_path = $modules_dir . '/' . $module['asset']; - $asset = file_exists( $asset_path ) ? require $asset_path : array(); + foreach ( $modules as $module ) { + $asset_path = $modules_dir . '/' . $module['asset']; + $asset = file_exists( $asset_path ) ? require $asset_path : array(); - // Deregister first to override any previously registered version - // (e.g., Core's default modules when running as a plugin). - wp_deregister_script_module( $module['id'] ); + // Deregister first to override any previously registered version + // (e.g., Core's default modules when running as a plugin). + wp_deregister_script_module( $module['id'] ); - wp_register_script_module( - $module['id'], - $base_url . $module['path'] . $extension, - $asset['module_dependencies'] ?? array(), - $asset['version'] ?? false, - array( - 'fetchpriority' => 'low', - 'in_footer' => true, - ) - ); - } + wp_register_script_module( + $module['id'], + $base_url . $module['path'] . $extension, + $asset['module_dependencies'] ?? array(), + $asset['version'] ?? false, + array( + 'fetchpriority' => 'low', + 'in_footer' => true, + ) + ); } - - add_action( 'wp_default_scripts', '{{PREFIX}}_register_script_modules' ); } + +add_action( 'wp_default_scripts', '{{PREFIX}}_register_script_modules' ); +{{END_IF_PLUGGABLE}} diff --git a/packages/wp-build/templates/page-wp-admin.php.template b/packages/wp-build/templates/page-wp-admin.php.template index 970954d34330af..2931864b85f258 100644 --- a/packages/wp-build/templates/page-wp-admin.php.template +++ b/packages/wp-build/templates/page-wp-admin.php.template @@ -15,120 +15,120 @@ global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes, ${{PREFIX}}_{{PAGE_ ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes = array(); ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items = array(); -if ( ! function_exists( '{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_route' ) ) { - /** - * Register a route for the {{PAGE_SLUG}}-wp-admin page. - * - * @param string $path Route path (e.g., '/types/$type/edit/$id'). - * @param string|null $content_module Script module ID for content (stage/inspector). - * @param string|null $route_module Script module ID for route lifecycle hooks. - */ - function {{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_route( $path, $content_module = null, $route_module = null ) { - global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes; - - $route = array( 'path' => $path ); - if ( ! empty( $content_module ) ) { - $route['content_module'] = $content_module; - } - if ( ! empty( $route_module ) ) { - $route['route_module'] = $route_module; - } +{{IF_PLUGGABLE:{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_route}} +/** + * Register a route for the {{PAGE_SLUG}}-wp-admin page. + * + * @param string $path Route path (e.g., '/types/$type/edit/$id'). + * @param string|null $content_module Script module ID for content (stage/inspector). + * @param string|null $route_module Script module ID for route lifecycle hooks. + */ +function {{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_route( $path, $content_module = null, $route_module = null ) { + global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes; - ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes[] = $route; + $route = array( 'path' => $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes[] = $route; } +{{END_IF_PLUGGABLE}} -if ( ! function_exists( '{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_item' ) ) { - /** - * Register a menu item for the {{PAGE_SLUG}}-wp-admin page. - * Note: Menu items are registered but not displayed in single-page mode. - * - * @param string $id Menu item ID. - * @param string $label Display label. - * @param string $to Route path to navigate to. - * @param string $parent_id Optional. Parent menu item ID. - */ - function {{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_item( $id, $label, $to, $parent_id = '' ) { - global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items; - - $menu_item = array( - 'id' => $id, - 'label' => $label, - 'to' => $to, - ); +{{IF_PLUGGABLE:{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_item}} +/** + * Register a menu item for the {{PAGE_SLUG}}-wp-admin page. + * Note: Menu items are registered but not displayed in single-page mode. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + */ +function {{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_item( $id, $label, $to, $parent_id = '' ) { + global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items; - if ( ! empty( $parent_id ) ) { - $menu_item['parent'] = $parent_id; - } + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); - ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items[] = $menu_item; + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; } -} -if ( ! function_exists( '{{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes' ) ) { - /** - * Get all registered routes for the {{PAGE_SLUG}}-wp-admin page. - * - * @return array Array of route objects. - */ - function {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes() { - global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes; - return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes ?? array(); - } + ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items[] = $menu_item; } +{{END_IF_PLUGGABLE}} -if ( ! function_exists( '{{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items' ) ) { - /** - * Get all registered menu items for the {{PAGE_SLUG}}-wp-admin page. - * - * @return array Array of menu item objects. - */ - function {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items() { - global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items; - return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items ?? array(); - } +{{IF_PLUGGABLE:{{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes}} +/** + * Get all registered routes for the {{PAGE_SLUG}}-wp-admin page. + * + * @return array Array of route objects. + */ +function {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes() { + global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes; + return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes ?? array(); } +{{END_IF_PLUGGABLE}} -if ( ! function_exists( '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data' ) ) { - /** - * Preload REST API data for the {{PAGE_SLUG}}-wp-admin page. - * Automatically called during page rendering. - */ - function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data() { - // Define paths to preload - same for all pages - // Please also change packages/core-data/src/entities.js when changing this. - $preload_paths = array( - '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,image_output_formats,jpeg_interlaced,png_interlaced,gif_interlaced,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', - array( '/wp/v2/settings', 'OPTIONS' ), - ); - - // Use rest_preload_api_request to gather the preloaded data - $preload_data = array_reduce( - $preload_paths, - 'rest_preload_api_request', - array() - ); +{{IF_PLUGGABLE:{{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items}} +/** + * Get all registered menu items for the {{PAGE_SLUG}}-wp-admin page. + * + * @return array Array of menu item objects. + */ +function {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items() { + global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items; + return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items ?? array(); +} +{{END_IF_PLUGGABLE}} - // Register the preloading middleware with wp-api-fetch - wp_add_inline_script( - 'wp-api-fetch', - sprintf( - 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', - wp_json_encode( $preload_data ) - ), - 'after' - ); - } +{{IF_PLUGGABLE:{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data}} +/** + * Preload REST API data for the {{PAGE_SLUG}}-wp-admin page. + * Automatically called during page rendering. + */ +function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,image_output_formats,jpeg_interlaced,png_interlaced,gif_interlaced,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); } +{{END_IF_PLUGGABLE}} -if ( ! function_exists( '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts' ) ) { - /** - * Enqueue scripts and styles for the {{PAGE_SLUG}}-wp-admin page. - * Hooked to admin_enqueue_scripts. - * - * @param string $hook_suffix The current admin page. - */ - function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts( $hook_suffix ) { +{{IF_PLUGGABLE:{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts}} +/** + * Enqueue scripts and styles for the {{PAGE_SLUG}}-wp-admin page. + * Hooked to admin_enqueue_scripts. + * + * @param string $hook_suffix The current admin page. + */ +function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts( $hook_suffix ) { // Check all possible ways this page can be accessed: // 1. Menu page via admin.php?page={{PAGE_SLUG}}-wp-admin (plugin) // 2. Direct file via {{PAGE_SLUG}}.php (Core) - screen ID will be '{{PAGE_SLUG}}' @@ -219,16 +219,16 @@ if ( ! function_exists( '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_sc wp_enqueue_script_module( '{{PAGE_SLUG}}-wp-admin' ); wp_enqueue_style( '{{PAGE_SLUG}}-wp-admin-prerequisites' ); } - } } +{{END_IF_PLUGGABLE}} -if ( ! function_exists( '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_render_page' ) ) { - /** - * Render the {{PAGE_SLUG}}-wp-admin page. - * Call this function from add_menu_page or add_submenu_page. - * This renders within the normal WordPress admin interface. - */ - function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_render_page() { +{{IF_PLUGGABLE:{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_render_page}} +/** + * Render the {{PAGE_SLUG}}-wp-admin page. + * Call this function from add_menu_page or add_submenu_page. + * This renders within the normal WordPress admin interface. + */ +function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_render_page() { ?>
$path ); - if ( ! empty( $content_module ) ) { - $route['content_module'] = $content_module; - } - if ( ! empty( $route_module ) ) { - $route['route_module'] = $route_module; - } +{{IF_PLUGGABLE:{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_route}} +/** + * Register a route for the {{PAGE_SLUG}} page. + * + * @param string $path Route path (e.g., '/types/$type/edit/$id'). + * @param string|null $content_module Script module ID for content (stage/inspector). + * @param string|null $route_module Script module ID for route lifecycle hooks. + */ +function {{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_route( $path, $content_module = null, $route_module = null ) { + global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes; - ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes[] = $route; + $route = array( 'path' => $path ); + if ( ! empty( $content_module ) ) { + $route['content_module'] = $content_module; } + if ( ! empty( $route_module ) ) { + $route['route_module'] = $route_module; + } + + ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes[] = $route; } +{{END_IF_PLUGGABLE}} -if ( ! function_exists( '{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_menu_item' ) ) { - /** - * Register a menu item for the {{PAGE_SLUG}} page. - * - * @param string $id Menu item ID. - * @param string $label Display label. - * @param string $to Route path to navigate to. - * @param string $parent_id Optional. Parent menu item ID. - * @param string $parent_type Optional. Parent type: 'drilldown' or 'dropdown'. - */ - function {{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_menu_item( $id, $label, $to, $parent_id = '', $parent_type = '' ) { - global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items; - - $menu_item = array( - 'id' => $id, - 'label' => $label, - 'to' => $to, - ); - - if ( ! empty( $parent_id ) ) { - $menu_item['parent'] = $parent_id; - } +{{IF_PLUGGABLE:{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_menu_item}} +/** + * Register a menu item for the {{PAGE_SLUG}} page. + * + * @param string $id Menu item ID. + * @param string $label Display label. + * @param string $to Route path to navigate to. + * @param string $parent_id Optional. Parent menu item ID. + * @param string $parent_type Optional. Parent type: 'drilldown' or 'dropdown'. + */ +function {{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_menu_item( $id, $label, $to, $parent_id = '', $parent_type = '' ) { + global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items; - if ( ! empty( $parent_type ) && in_array( $parent_type, array( 'drilldown', 'dropdown' ), true ) ) { - $menu_item['parent_type'] = $parent_type; - } + $menu_item = array( + 'id' => $id, + 'label' => $label, + 'to' => $to, + ); - ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items[] = $menu_item; + if ( ! empty( $parent_id ) ) { + $menu_item['parent'] = $parent_id; } -} -if ( ! function_exists( '{{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_routes' ) ) { - /** - * Get all registered routes for the {{PAGE_SLUG}} page. - * - * @return array Array of route objects. - */ - function {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_routes() { - global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes; - return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes ?? array(); + if ( ! empty( $parent_type ) && in_array( $parent_type, array( 'drilldown', 'dropdown' ), true ) ) { + $menu_item['parent_type'] = $parent_type; } + + ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items[] = $menu_item; } +{{END_IF_PLUGGABLE}} -if ( ! function_exists( '{{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_menu_items' ) ) { - /** - * Get all registered menu items for the {{PAGE_SLUG}} page. - * - * @return array Array of menu item objects. - */ - function {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_menu_items() { - global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items; - return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items ?? array(); - } +{{IF_PLUGGABLE:{{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_routes}} +/** + * Get all registered routes for the {{PAGE_SLUG}} page. + * + * @return array Array of route objects. + */ +function {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_routes() { + global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes; + return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes ?? array(); } +{{END_IF_PLUGGABLE}} -if ( ! function_exists( '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_preload_data' ) ) { - /** - * Preload REST API data for the {{PAGE_SLUG}} page. - * Automatically called during page rendering. - */ - function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_preload_data() { - // Define paths to preload - same for all pages - // Please also change packages/core-data/src/entities.js when changing this. - $preload_paths = array( - '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,image_output_formats,jpeg_interlaced,png_interlaced,gif_interlaced,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', - array( '/wp/v2/settings', 'OPTIONS' ), - ); - - // Use rest_preload_api_request to gather the preloaded data - $preload_data = array_reduce( - $preload_paths, - 'rest_preload_api_request', - array() - ); - - // Register the preloading middleware with wp-api-fetch - wp_add_inline_script( - 'wp-api-fetch', - sprintf( - 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', - wp_json_encode( $preload_data ) - ), - 'after' - ); - } +{{IF_PLUGGABLE:{{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_menu_items}} +/** + * Get all registered menu items for the {{PAGE_SLUG}} page. + * + * @return array Array of menu item objects. + */ +function {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_menu_items() { + global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items; + return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items ?? array(); +} +{{END_IF_PLUGGABLE}} + +{{IF_PLUGGABLE:{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_preload_data}} +/** + * Preload REST API data for the {{PAGE_SLUG}} page. + * Automatically called during page rendering. + */ +function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_preload_data() { + // Define paths to preload - same for all pages + // Please also change packages/core-data/src/entities.js when changing this. + $preload_paths = array( + '/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,image_output_formats,jpeg_interlaced,png_interlaced,gif_interlaced,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front', + array( '/wp/v2/settings', 'OPTIONS' ), + ); + + // Use rest_preload_api_request to gather the preloaded data + $preload_data = array_reduce( + $preload_paths, + 'rest_preload_api_request', + array() + ); + + // Register the preloading middleware with wp-api-fetch + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( $preload_data ) + ), + 'after' + ); } +{{END_IF_PLUGGABLE}} -if ( ! function_exists( '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_render_page' ) ) { - /** - * Render the {{PAGE_SLUG}} page. - * Call this function from add_menu_page or add_submenu_page. - */ - function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_render_page() { +{{IF_PLUGGABLE:{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_render_page}} +/** + * Render the {{PAGE_SLUG}} page. + * Call this function from add_menu_page or add_submenu_page. + */ +function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_render_page() { // Load build constants $build_constants = require __DIR__ . '/../../constants.php'; @@ -310,22 +310,22 @@ if ( ! function_exists( '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_render_page' ) ) { $page_routes ) { $GLOBALS[ $global_name ] = $page_routes; } -if ( ! function_exists( '{{PREFIX}}_register_page_routes' ) ) { - /** - * Generic helper function to register routes for a page. - * - * @param array $page_routes Array of route data for the page. - * @param string $register_function_name Name of the function to call for registering each route. - */ - function {{PREFIX}}_register_page_routes( $page_routes, $register_function_name ) { - // Load build constants - $build_constants = require __DIR__ . '/constants.php'; +{{IF_PLUGGABLE:{{PREFIX}}_register_page_routes}} +/** + * Generic helper function to register routes for a page. + * + * @param array $page_routes Array of route data for the page. + * @param string $register_function_name Name of the function to call for registering each route. + */ +function {{PREFIX}}_register_page_routes( $page_routes, $register_function_name ) { + // Load build constants + $build_constants = require __DIR__ . '/constants.php'; - foreach ( $page_routes as $route ) { - $content_handle = null; - $route_handle = null; + foreach ( $page_routes as $route ) { + $content_handle = null; + $route_handle = null; - // Register content module if exists - if ( $route['has_content'] ) { - $content_asset_path = __DIR__ . "/routes/{$route['name']}/content.min.asset.php"; - if ( file_exists( $content_asset_path ) ) { - $content_asset = require $content_asset_path; - $content_handle = '{{HANDLE_PREFIX}}/routes/' . $route['name'] . '/content'; - $extension = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.js' : '.min.js'; - wp_register_script_module( - $content_handle, - $build_constants['build_url'] . 'routes/' . $route['name'] . '/content' . $extension, - $content_asset['module_dependencies'] ?? array(), - $content_asset['version'] ?? false - ); - } + // Register content module if exists + if ( $route['has_content'] ) { + $content_asset_path = __DIR__ . "/routes/{$route['name']}/content.min.asset.php"; + if ( file_exists( $content_asset_path ) ) { + $content_asset = require $content_asset_path; + $content_handle = '{{HANDLE_PREFIX}}/routes/' . $route['name'] . '/content'; + $extension = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.js' : '.min.js'; + wp_register_script_module( + $content_handle, + $build_constants['build_url'] . 'routes/' . $route['name'] . '/content' . $extension, + $content_asset['module_dependencies'] ?? array(), + $content_asset['version'] ?? false + ); } + } - // Register route module if exists - if ( $route['has_route'] ) { - $route_asset_path = __DIR__ . "/routes/{$route['name']}/route.min.asset.php"; - if ( file_exists( $route_asset_path ) ) { - $route_asset = require $route_asset_path; - $route_handle = '{{HANDLE_PREFIX}}/routes/' . $route['name'] . '/route'; - $extension = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.js' : '.min.js'; - wp_register_script_module( - $route_handle, - $build_constants['build_url'] . 'routes/' . $route['name'] . '/route' . $extension, - $route_asset['module_dependencies'] ?? array(), - $route_asset['version'] ?? false - ); - } + // Register route module if exists + if ( $route['has_route'] ) { + $route_asset_path = __DIR__ . "/routes/{$route['name']}/route.min.asset.php"; + if ( file_exists( $route_asset_path ) ) { + $route_asset = require $route_asset_path; + $route_handle = '{{HANDLE_PREFIX}}/routes/' . $route['name'] . '/route'; + $extension = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.js' : '.min.js'; + wp_register_script_module( + $route_handle, + $build_constants['build_url'] . 'routes/' . $route['name'] . '/route' . $extension, + $route_asset['module_dependencies'] ?? array(), + $route_asset['version'] ?? false + ); } + } - // Register route with page - if ( function_exists( $register_function_name ) ) { - call_user_func( $register_function_name, $route['path'], $content_handle, $route_handle ); - } + // Register route with page + if ( function_exists( $register_function_name ) ) { + call_user_func( $register_function_name, $route['path'], $content_handle, $route_handle ); } } } +{{END_IF_PLUGGABLE}} // Page-specific route registration functions {{PAGE_ROUTE_FUNCTIONS}} diff --git a/packages/wp-build/templates/script-registration.php.template b/packages/wp-build/templates/script-registration.php.template index 401bfb5d1eea46..b9eda20c91ab3b 100644 --- a/packages/wp-build/templates/script-registration.php.template +++ b/packages/wp-build/templates/script-registration.php.template @@ -6,84 +6,84 @@ * @package {{PREFIX}} */ -if ( ! function_exists( '{{PREFIX}}_override_script' ) ) { - /** - * Registers a script according to `wp_register_script`. Honors this request by - * reassigning internal dependency properties of any script handle already - * registered by that name. It does not deregister the original script, to - * avoid losing inline scripts which may have been attached. - * - * @param WP_Scripts $scripts WP_Scripts instance. - * @param string $handle Name of the script. Should be unique. - * @param string $src Full URL of the script, or path of the script relative to the WordPress root directory. - * @param array $deps Optional. An array of registered script handles this script depends on. Default empty array. - * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL - * as a query string for cache busting purposes. If version is set to false, a version - * number is automatically added equal to current installed WordPress version. - * If set to null, no version is added. - * @param bool $in_footer Optional. Whether to enqueue the script before instead of in the . - * Default 'false'. - */ - function {{PREFIX}}_override_script( $scripts, $handle, $src, $deps = array(), $ver = false, $in_footer = false ) { - $script = $scripts->query( $handle, 'registered' ); - if ( $script ) { - /* - * In many ways, this is a reimplementation of `wp_register_script` but - * bypassing consideration of whether a script by the given handle had - * already been registered. - */ +{{IF_PLUGGABLE:{{PREFIX}}_override_script}} +/** + * Registers a script according to `wp_register_script`. Honors this request by + * reassigning internal dependency properties of any script handle already + * registered by that name. It does not deregister the original script, to + * avoid losing inline scripts which may have been attached. + * + * @param WP_Scripts $scripts WP_Scripts instance. + * @param string $handle Name of the script. Should be unique. + * @param string $src Full URL of the script, or path of the script relative to the WordPress root directory. + * @param array $deps Optional. An array of registered script handles this script depends on. Default empty array. + * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL + * as a query string for cache busting purposes. If version is set to false, a version + * number is automatically added equal to current installed WordPress version. + * If set to null, no version is added. + * @param bool $in_footer Optional. Whether to enqueue the script before instead of in the . + * Default 'false'. + */ +function {{PREFIX}}_override_script( $scripts, $handle, $src, $deps = array(), $ver = false, $in_footer = false ) { + $script = $scripts->query( $handle, 'registered' ); + if ( $script ) { + /* + * In many ways, this is a reimplementation of `wp_register_script` but + * bypassing consideration of whether a script by the given handle had + * already been registered. + */ - // See: `_WP_Dependency::__construct` . - $script->src = $src; - $script->deps = $deps; - $script->ver = $ver; - $script->args = $in_footer ? 1 : null; - } else { - $scripts->add( $handle, $src, $deps, $ver, ( $in_footer ? 1 : null ) ); - } + // See: `_WP_Dependency::__construct` . + $script->src = $src; + $script->deps = $deps; + $script->ver = $ver; + $script->args = $in_footer ? 1 : null; + } else { + $scripts->add( $handle, $src, $deps, $ver, ( $in_footer ? 1 : null ) ); + } - if ( in_array( 'wp-i18n', $deps, true ) ) { - $scripts->set_translations( $handle ); - } + if ( in_array( 'wp-i18n', $deps, true ) ) { + $scripts->set_translations( $handle ); } } +{{END_IF_PLUGGABLE}} -if ( ! function_exists( '{{PREFIX}}_register_package_scripts' ) ) { - /** - * Register all package scripts. - */ - function {{PREFIX}}_register_package_scripts( $scripts ) { - // Load build constants - $build_constants = require __DIR__ . '/constants.php'; - $default_version = ! SCRIPT_DEBUG ? $build_constants['version'] : time(); - $extension = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.js' : '.min.js'; +{{IF_PLUGGABLE:{{PREFIX}}_register_package_scripts}} +/** + * Register all package scripts. + */ +function {{PREFIX}}_register_package_scripts( $scripts ) { + // Load build constants + $build_constants = require __DIR__ . '/constants.php'; + $default_version = ! SCRIPT_DEBUG ? $build_constants['version'] : time(); + $extension = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.js' : '.min.js'; - $scripts_dir = __DIR__ . '/scripts'; - $scripts_file = $scripts_dir . '/registry.php'; + $scripts_dir = __DIR__ . '/scripts'; + $scripts_file = $scripts_dir . '/registry.php'; - if ( ! file_exists( $scripts_file ) ) { - return; - } + if ( ! file_exists( $scripts_file ) ) { + return; + } - $scripts_data = require $scripts_file; - $plugin_dir = dirname( __FILE__ ); + $scripts_data = require $scripts_file; + $plugin_dir = dirname( __FILE__ ); - foreach ( $scripts_data as $script_data ) { - $asset_file = $scripts_dir . '/' . $script_data['asset']; - $asset = file_exists( $asset_file ) ? require $asset_file : array(); - $dependencies = $asset['dependencies'] ?? array(); - $version = $asset['version'] ?? $default_version; + foreach ( $scripts_data as $script_data ) { + $asset_file = $scripts_dir . '/' . $script_data['asset']; + $asset = file_exists( $asset_file ) ? require $asset_file : array(); + $dependencies = $asset['dependencies'] ?? array(); + $version = $asset['version'] ?? $default_version; - {{PREFIX}}_override_script( - $scripts, - $script_data['handle'], - $build_constants['build_url'] . 'scripts/' . $script_data['path'] . $extension, - $dependencies, - $version, - true - ); - } + {{PREFIX}}_override_script( + $scripts, + $script_data['handle'], + $build_constants['build_url'] . 'scripts/' . $script_data['path'] . $extension, + $dependencies, + $version, + true + ); } - - add_action( 'wp_default_scripts', '{{PREFIX}}_register_package_scripts' ); } + +add_action( 'wp_default_scripts', '{{PREFIX}}_register_package_scripts' ); +{{END_IF_PLUGGABLE}} diff --git a/packages/wp-build/templates/style-registration.php.template b/packages/wp-build/templates/style-registration.php.template index 061fd0aa7fcf7c..8436c664f7f334 100644 --- a/packages/wp-build/templates/style-registration.php.template +++ b/packages/wp-build/templates/style-registration.php.template @@ -9,69 +9,69 @@ * @package {{PREFIX}} */ -if ( ! function_exists( '{{PREFIX}}_override_style' ) ) { - /** - * Registers a style according to `wp_register_style`. Honors this request by - * deregistering any style by the same handler before registration. - * - * @param WP_Styles $styles WP_Styles instance. - * @param string $handle Name of the stylesheet. Should be unique. - * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory. - * @param array $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array. - * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL - * as a query string for cache busting purposes. If version is set to false, a version - * number is automatically added equal to current installed WordPress version. - * If set to null, no version is added. - * @param string $media Optional. The media for which this stylesheet has been defined. - * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like - * '(orientation: portrait)' and '(max-width: 640px)'. - */ - function {{PREFIX}}_override_style( $styles, $handle, $src, $deps = array(), $ver = false, $media = 'all' ) { - $style = $styles->query( $handle, 'registered' ); - if ( $style ) { - $styles->remove( $handle ); - } - $styles->add( $handle, $src, $deps, $ver, $media ); +{{IF_PLUGGABLE:{{PREFIX}}_override_style}} +/** + * Registers a style according to `wp_register_style`. Honors this request by + * deregistering any style by the same handler before registration. + * + * @param WP_Styles $styles WP_Styles instance. + * @param string $handle Name of the stylesheet. Should be unique. + * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory. + * @param array $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array. + * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL + * as a query string for cache busting purposes. If version is set to false, a version + * number is automatically added equal to current installed WordPress version. + * If set to null, no version is added. + * @param string $media Optional. The media for which this stylesheet has been defined. + * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like + * '(orientation: portrait)' and '(max-width: 640px)'. + */ +function {{PREFIX}}_override_style( $styles, $handle, $src, $deps = array(), $ver = false, $media = 'all' ) { + $style = $styles->query( $handle, 'registered' ); + if ( $style ) { + $styles->remove( $handle ); } + $styles->add( $handle, $src, $deps, $ver, $media ); } +{{END_IF_PLUGGABLE}} -if ( ! function_exists( '{{PREFIX}}_register_package_styles' ) ) { - /** - * Register all package styles (simple cases only). - * Complex cases should be manually registered in lib/client-assets.php. - * - * @param WP_Styles $styles WP_Styles instance. - */ - function {{PREFIX}}_register_package_styles( $styles ) { - // Load build constants - $build_constants = require __DIR__ . '/constants.php'; - $default_version = ! SCRIPT_DEBUG ? $build_constants['version'] : time(); - $suffix = SCRIPT_DEBUG ? '' : '.min'; +{{IF_PLUGGABLE:{{PREFIX}}_register_package_styles}} +/** + * Register all package styles (simple cases only). + * Complex cases should be manually registered in lib/client-assets.php. + * + * @param WP_Styles $styles WP_Styles instance. + */ +function {{PREFIX}}_register_package_styles( $styles ) { + // Load build constants + $build_constants = require __DIR__ . '/constants.php'; + $default_version = ! SCRIPT_DEBUG ? $build_constants['version'] : time(); + $suffix = SCRIPT_DEBUG ? '' : '.min'; - $styles_dir = __DIR__ . '/styles'; - $styles_file = $styles_dir . '/registry.php'; + $styles_dir = __DIR__ . '/styles'; + $styles_file = $styles_dir . '/registry.php'; - if ( ! file_exists( $styles_file ) ) { - return; - } + if ( ! file_exists( $styles_file ) ) { + return; + } - $styles_data = require $styles_file; - $plugin_dir = dirname( __FILE__ ); + $styles_data = require $styles_file; + $plugin_dir = dirname( __FILE__ ); - foreach ( $styles_data as $style_data ) { - {{PREFIX}}_override_style( - $styles, - $style_data['handle'], - $build_constants['build_url'] . 'styles/' . $style_data['path'] . $suffix . '.css', - $style_data['dependencies'], - $default_version - ); + foreach ( $styles_data as $style_data ) { + {{PREFIX}}_override_style( + $styles, + $style_data['handle'], + $build_constants['build_url'] . 'styles/' . $style_data['path'] . $suffix . '.css', + $style_data['dependencies'], + $default_version + ); - // Enable RTL support (WordPress automatically loads -rtl.css variant) - $styles->add_data( $style_data['handle'], 'rtl', 'replace' ); - $styles->add_data( $style_data['handle'], 'suffix', $suffix ); - } + // Enable RTL support (WordPress automatically loads -rtl.css variant) + $styles->add_data( $style_data['handle'], 'rtl', 'replace' ); + $styles->add_data( $style_data['handle'], 'suffix', $suffix ); } - - add_action( 'wp_default_styles', '{{PREFIX}}_register_package_styles' ); } + +add_action( 'wp_default_styles', '{{PREFIX}}_register_package_styles' ); +{{END_IF_PLUGGABLE}}