Skip to content

Commit acf01a2

Browse files
youknowriadpeterwilsonccjorgefilipecosta
authored
Improve wp-build generated PHP files with proper prefixing and naming (#74490)
Co-authored-by: youknowriad <youknowriad@git.wordpress.org> Co-authored-by: peterwilsoncc <peterwilsoncc@git.wordpress.org> Co-authored-by: jorgefilipecosta <jorgefilipecosta@git.wordpress.org>
1 parent 313a8c3 commit acf01a2

16 files changed

Lines changed: 177 additions & 160 deletions

lib/clear-core-routes.php

Lines changed: 0 additions & 48 deletions
This file was deleted.

lib/experimental/fonts/load.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ function gutenberg_register_fonts_menu_item() {
2222
__( 'Fonts', 'gutenberg' ),
2323
'edit_theme_options',
2424
'font-library-wp-admin',
25-
'font_library_wp_admin_render_page'
25+
'gutenberg_font_library_wp_admin_render_page'
2626
);
2727
}

lib/experimental/pages/site-editor.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function gutenberg_register_site_editor_admin_page() {
1515
__( 'Site Editor', 'gutenberg' ),
1616
'manage_options',
1717
'site-editor-v2',
18-
'site_editor_v2_render_page'
18+
'gutenberg_site_editor_v2_render_page'
1919
);
2020
}
2121
add_action( 'admin_menu', 'gutenberg_register_site_editor_admin_page' );
@@ -24,13 +24,13 @@ function gutenberg_register_site_editor_admin_page() {
2424
* Register default menu items for the site editor page.
2525
*/
2626
function gutenberg_site_editor_register_default_menu_items() {
27-
register_site_editor_v2_menu_item( 'home', __( 'Home', 'gutenberg' ), '/', '' );
28-
register_site_editor_v2_menu_item( 'styles', __( 'Styles', 'gutenberg' ), '/styles', '' );
29-
register_site_editor_v2_menu_item( 'navigation', __( 'Navigation', 'gutenberg' ), '/navigation', '' );
30-
register_site_editor_v2_menu_item( 'pages', __( 'Pages', 'gutenberg' ), '/types/page', '' );
31-
register_site_editor_v2_menu_item( 'templates', __( 'Templates', 'gutenberg' ), '/templates', '' );
32-
register_site_editor_v2_menu_item( 'templateParts', __( 'Template Parts', 'gutenberg' ), '/template-parts', '' );
33-
register_site_editor_v2_menu_item( 'patterns', __( 'Patterns', 'gutenberg' ), '/patterns', '' );
34-
register_site_editor_v2_menu_item( 'fontList', __( 'Fonts', 'gutenberg' ), '/font-list', '' );
27+
gutenberg_register_site_editor_v2_menu_item( 'home', __( 'Home', 'gutenberg' ), '/', '' );
28+
gutenberg_register_site_editor_v2_menu_item( 'styles', __( 'Styles', 'gutenberg' ), '/styles', '' );
29+
gutenberg_register_site_editor_v2_menu_item( 'navigation', __( 'Navigation', 'gutenberg' ), '/navigation', '' );
30+
gutenberg_register_site_editor_v2_menu_item( 'pages', __( 'Pages', 'gutenberg' ), '/types/page', '' );
31+
gutenberg_register_site_editor_v2_menu_item( 'templates', __( 'Templates', 'gutenberg' ), '/templates', '' );
32+
gutenberg_register_site_editor_v2_menu_item( 'templateParts', __( 'Template Parts', 'gutenberg' ), '/template-parts', '' );
33+
gutenberg_register_site_editor_v2_menu_item( 'patterns', __( 'Patterns', 'gutenberg' ), '/patterns', '' );
34+
gutenberg_register_site_editor_v2_menu_item( 'fontList', __( 'Fonts', 'gutenberg' ), '/font-list', '' );
3535
}
3636
add_action( 'site-editor-v2_init', 'gutenberg_site_editor_register_default_menu_items', 5 );

lib/load.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,8 @@
1414
require_once __DIR__ . '/init.php';
1515
require_once __DIR__ . '/upgrade.php';
1616

17-
// Clear Core's routes before Gutenberg's build/index.php registers its own.
18-
// This must be loaded before build/index.php to ensure correct action ordering.
19-
require_once __DIR__ . '/clear-core-routes.php';
20-
2117
// Load auto-generated build registration.
22-
$build_registration = plugin_dir_path( __DIR__ ) . 'build/index.php';
18+
$build_registration = plugin_dir_path( __DIR__ ) . 'build/build.php';
2319
if ( file_exists( $build_registration ) ) {
2420
require_once $build_registration;
2521
}
@@ -86,6 +82,7 @@ function gutenberg_is_experiment_enabled( $name ) {
8682
require_once __DIR__ . '/experimental/kses-allowed-html.php';
8783
}
8884

85+
require_once __DIR__ . '/remove-core-enqueue-scripts.php';
8986
require_once __DIR__ . '/experimental/editor-settings.php';
9087
require_once __DIR__ . '/experimental/rest-api-overrides.php';
9188

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
/**
3+
* Removes Core's admin enqueue scripts hooks to prevent duplicate route registration.
4+
*
5+
* Core's *_enqueue_scripts functions fire the page init actions, but Gutenberg's
6+
* render functions also fire them. We remove Core's to avoid duplicate route registration.
7+
*
8+
* @package gutenberg
9+
*/
10+
11+
remove_action( 'admin_enqueue_scripts', 'font_library_wp_admin_enqueue_scripts' );
12+
remove_action( 'admin_enqueue_scripts', 'site_editor_v2_wp_admin_enqueue_scripts' );

packages/wp-build/CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@
22

33
## Unreleased
44

5+
### Breaking Changes
6+
7+
- Renamed generated PHP files to avoid `index.php` naming conflicts:
8+
- `build/index.php``build/build.php`
9+
- `build/modules/index.php``build/modules/registry.php`
10+
- `build/scripts/index.php``build/scripts/registry.php`
11+
- `build/styles/index.php``build/styles/registry.php`
12+
- `build/routes/index.php``build/routes/registry.php`
13+
- All generated page functions now include the `{{PREFIX}}` (from `wpPlugin.name`) at the beginning:
14+
- `register_my_page_route()``my_plugin_register_my_page_route()`
15+
- `my_page_render_page()``my_plugin_my_page_render_page()`
16+
- And similarly for all other page functions
17+
- Route registration now uses named functions instead of anonymous closures, allowing third-party developers to unhook them
18+
519
## 0.4.0 (2025-11-26)
620

721
## 0.3.0 (2025-11-12)

packages/wp-build/README.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -248,15 +248,17 @@ WP-Admin mode integrates within the standard WordPress admin interface (keeping
248248

249249
```php
250250
add_submenu_page(
251-
'themes.php', // Parent menu
252-
__( 'My Page', 'my-plugin' ), // Page title
253-
__( 'My Page', 'my-plugin' ), // Menu title
254-
'edit_theme_options', // Capability
255-
'my-admin-page-wp-admin', // Menu slug (simple)
256-
'my_admin_page_wp_admin_render_page' // Callback from generated PHP
251+
'themes.php', // Parent menu
252+
__( 'My Page', 'my-plugin' ), // Page title
253+
__( 'My Page', 'my-plugin' ), // Menu title
254+
'edit_theme_options', // Capability
255+
'my-admin-page-wp-admin', // Menu slug (simple)
256+
'my_plugin_my_admin_page_wp_admin_render_page' // Callback from generated PHP (prefixed)
257257
);
258258
```
259259

260+
Note: The callback function name is prefixed with your plugin name (from `wpPlugin.name` in root `package.json`). For example, if your plugin name is `my-plugin`, the function will be `my_plugin_my_admin_page_wp_admin_render_page`.
261+
260262
The page slug is `my-admin-page-wp-admin` (your page ID + `-wp-admin`). WordPress routes all requests to this callback, and the JavaScript router handles internal navigation.
261263

262264
**Deep linking with the `p` query parameter:**
@@ -274,7 +276,7 @@ When the page loads, the JavaScript boot system reads the `p` parameter and navi
274276
Full-page mode takes over the entire admin screen with a custom sidebar:
275277

276278
```php
277-
add_menu_page( 'Title', 'Menu', 'capability', 'my-admin-page', 'my_admin_page_render_page', 'icon', 20 );
279+
add_menu_page( 'Title', 'Menu', 'capability', 'my-admin-page', 'my_plugin_my_admin_page_render_page', 'icon', 20 );
278280
```
279281

280282
**Init Modules:**
@@ -364,7 +366,7 @@ The built tool generates several files in the `build/` directory, but the primar
364366
Make sure to include the generated PHP file in your plugin file.
365367

366368
```php
367-
require_once plugin_dir_path( __FILE__ ) . 'build/index.php';
369+
require_once plugin_dir_path( __FILE__ ) . 'build/build.php';
368370
```
369371

370372
## Routes (Experimental)
@@ -470,7 +472,7 @@ The `canvas()` function controls which canvas is rendered:
470472
The build system generates:
471473
- `build/routes/{route-name}/content.js` - Bundled stage/inspector/canvas components
472474
- `build/routes/{route-name}/route.js` - Bundled lifecycle hooks (if present)
473-
- `build/routes/index.php` - Route registry data
475+
- `build/routes/registry.php` - Route registry data
474476
- `build/routes.php` - Route registration logic
475477

476478
The boot package in Gutenberg will automatically use these routes and make them available.

packages/wp-build/lib/build.mjs

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
import {
3131
generatePhpFromTemplate,
3232
getPhpReplacements,
33+
renderTemplateToString,
3334
} from './php-generator.mjs';
3435
import { getPackageInfo, getPackageInfoFromFile } from './package-utils.mjs';
3536
import { createWordpressExternalsPlugin } from './wordpress-externals-plugin.mjs';
@@ -743,7 +744,7 @@ async function generateModuleRegistrationPhp( modules, replacements ) {
743744
await Promise.all( [
744745
generatePhpFromTemplate(
745746
'module-registry.php.template',
746-
path.join( BUILD_DIR, 'modules', 'index.php' ),
747+
path.join( BUILD_DIR, 'modules', 'registry.php' ),
747748
{ ...replacements, '{{MODULES}}': modulesArray }
748749
),
749750
generatePhpFromTemplate(
@@ -776,7 +777,7 @@ async function generateScriptRegistrationPhp( scripts, replacements ) {
776777
await Promise.all( [
777778
generatePhpFromTemplate(
778779
'script-registry.php.template',
779-
path.join( BUILD_DIR, 'scripts', 'index.php' ),
780+
path.join( BUILD_DIR, 'scripts', 'registry.php' ),
780781
{ ...replacements, '{{SCRIPTS}}': scriptsArray }
781782
),
782783
generatePhpFromTemplate(
@@ -824,7 +825,7 @@ async function generateStyleRegistrationPhp( styles, replacements ) {
824825
await Promise.all( [
825826
generatePhpFromTemplate(
826827
'style-registry.php.template',
827-
path.join( BUILD_DIR, 'styles', 'index.php' ),
828+
path.join( BUILD_DIR, 'styles', 'registry.php' ),
828829
{ ...replacements, '{{STYLES}}': stylesArray }
829830
),
830831
generatePhpFromTemplate(
@@ -836,14 +837,14 @@ async function generateStyleRegistrationPhp( styles, replacements ) {
836837
}
837838

838839
/**
839-
* Generate main index.php that loads both modules and scripts.
840+
* Generate main build.php that loads both modules and scripts.
840841
*
841842
* @param {Record<string, string>} replacements PHP template replacements.
842843
*/
843-
async function generateMainIndexPhp( replacements ) {
844+
async function generateMainBuildPhp( replacements ) {
844845
await generatePhpFromTemplate(
845-
'index.php.template',
846-
path.join( BUILD_DIR, 'index.php' ),
846+
'build.php.template',
847+
path.join( BUILD_DIR, 'build.php' ),
847848
replacements
848849
);
849850
}
@@ -876,10 +877,10 @@ async function generateRoutesRegistry( routes, replacements ) {
876877
} )
877878
.join( ',\n' );
878879

879-
// Generate single global registry at build/routes/index.php
880+
// Generate single global registry at build/routes/registry.php
880881
await generatePhpFromTemplate(
881882
'route-registry.php.template',
882-
path.join( BUILD_DIR, 'routes', 'index.php' ),
883+
path.join( BUILD_DIR, 'routes', 'registry.php' ),
883884
{ ...replacements, '{{ROUTES}}': routeEntries }
884885
);
885886
}
@@ -897,11 +898,35 @@ async function generateRoutesPhp( routes, replacements ) {
897898
return;
898899
}
899900

900-
// Generate routes.php from template
901+
// Get unique pages from routes
902+
const pages = [ ...new Set( routes.map( ( route ) => route.page ) ) ];
903+
904+
// Generate page-specific functions for each page
905+
const pageFunctionsPromises = pages.map( async ( pageSlug ) => {
906+
const pageSlugUnderscore = pageSlug.replace( /-/g, '_' );
907+
const pageReplacements = {
908+
...replacements,
909+
'{{PAGE_SLUG}}': pageSlug,
910+
'{{PAGE_SLUG_UNDERSCORE}}': pageSlugUnderscore,
911+
};
912+
return renderTemplateToString(
913+
'routes-page-functions.php.template',
914+
pageReplacements
915+
);
916+
} );
917+
918+
const pageFunctions = await Promise.all( pageFunctionsPromises );
919+
const pageRouteFunctionsCode = pageFunctions.join( '\n' );
920+
921+
// Generate routes.php from template with injected page functions
901922
await generatePhpFromTemplate(
902923
'routes-registration.php.template',
903924
path.join( BUILD_DIR, 'routes.php' ),
904-
{ ...replacements, '{{HANDLE_PREFIX}}': HANDLE_PREFIX }
925+
{
926+
...replacements,
927+
'{{HANDLE_PREFIX}}': HANDLE_PREFIX,
928+
'{{PAGE_ROUTE_FUNCTIONS}}': pageRouteFunctionsCode,
929+
}
905930
);
906931
}
907932

@@ -1343,12 +1368,6 @@ async function buildRoute( routeName ) {
13431368
// Ensure output directory exists
13441369
await mkdir( outputDir, { recursive: true } );
13451370

1346-
// Copy package.json
1347-
await copyFile(
1348-
path.join( routeDir, 'package.json' ),
1349-
path.join( outputDir, 'package.json' )
1350-
);
1351-
13521371
const files = getRouteFiles( routeDir );
13531372

13541373
// Build route.js if it exists
@@ -1646,7 +1665,7 @@ async function buildAll( baseUrlExpression ) {
16461665
baseUrlExpression
16471666
);
16481667
await Promise.all( [
1649-
generateMainIndexPhp( phpReplacements ),
1668+
generateMainBuildPhp( phpReplacements ),
16501669
generateModuleRegistrationPhp( modules, phpReplacements ),
16511670
generateScriptRegistrationPhp( scripts, phpReplacements ),
16521671
generateStyleRegistrationPhp( styles, phpReplacements ),
@@ -1655,13 +1674,14 @@ async function buildAll( baseUrlExpression ) {
16551674
generateRoutesPhp( routes, phpReplacements ),
16561675
generatePagesPhp( pageData, phpReplacements ),
16571676
] );
1677+
console.log( ' ✔ Generated build/build.php' );
16581678
console.log( ' ✔ Generated build/modules.php' );
1659-
console.log( ' ✔ Generated build/modules/index.php' );
1679+
console.log( ' ✔ Generated build/modules/registry.php' );
16601680
console.log( ' ✔ Generated build/scripts.php' );
1661-
console.log( ' ✔ Generated build/scripts/index.php' );
1681+
console.log( ' ✔ Generated build/scripts/registry.php' );
16621682
console.log( ' ✔ Generated build/styles.php' );
1663-
console.log( ' ✔ Generated build/styles/index.php' );
1664-
console.log( ' ✔ Generated build/version.php' );
1683+
console.log( ' ✔ Generated build/styles/registry.php' );
1684+
console.log( ' ✔ Generated build/constants.php' );
16651685
console.log( ' ✔ Generated build/routes.php' );
16661686
if ( pageData.length > 0 ) {
16671687
console.log( ' ✔ Generated build/pages.php' );
@@ -1674,7 +1694,6 @@ async function buildAll( baseUrlExpression ) {
16741694
);
16751695
}
16761696
}
1677-
console.log( ' ✔ Generated build/index.php' );
16781697

16791698
const totalTime = Date.now() - startTime;
16801699
console.log(
File renamed without changes.

packages/wp-build/templates/module-registration.php.template

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ if ( ! function_exists( '{{PREFIX}}_register_script_modules' ) ) {
1313
function {{PREFIX}}_register_script_modules() {
1414
// Load build constants
1515
$build_constants = require __DIR__ . '/constants.php';
16-
$modules_dir = __DIR__ . '/modules';
17-
$modules_file = $modules_dir . '/index.php';
16+
$modules_dir = __DIR__ . '/modules';
17+
$modules_file = $modules_dir . '/registry.php';
1818

1919
if ( ! file_exists( $modules_file ) ) {
2020
return;

0 commit comments

Comments
 (0)