@wordpress/build 0.5.1-next.v.0 → 0.6.2-next.v.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.6.1-next.0 (2026-01-23)
6
+
7
+ - Update documentation to describe `wpPlugin.name`
8
+
9
+ ## 0.6.0 (2026-01-16)
10
+
11
+ ### Breaking Changes
12
+
13
+ - Renamed generated PHP files to avoid `index.php` naming conflicts:
14
+ - `build/index.php` → `build/build.php`
15
+ - `build/modules/index.php` → `build/modules/registry.php`
16
+ - `build/scripts/index.php` → `build/scripts/registry.php`
17
+ - `build/styles/index.php` → `build/styles/registry.php`
18
+ - `build/routes/index.php` → `build/routes/registry.php`
19
+ - All generated page functions now include the `{{PREFIX}}` (from `wpPlugin.name`) at the beginning:
20
+ - `register_my_page_route()` → `my_plugin_register_my_page_route()`
21
+ - `my_page_render_page()` → `my_plugin_my_page_render_page()`
22
+ - And similarly for all other page functions
23
+ - Route registration now uses named functions instead of anonymous closures, allowing third-party developers to unhook them
24
+
5
25
  ## 0.4.0 (2025-11-26)
6
26
 
7
27
  ## 0.3.0 (2025-11-12)
package/README.md CHANGED
@@ -136,6 +136,18 @@ Files to copy with optional PHP transformations:
136
136
 
137
137
  Configure your root `package.json` with a `wpPlugin` object to control global namespace and externalization behavior:
138
138
 
139
+ ### `wpPlugin.name`
140
+
141
+ Name used to prefix genereated PHP functions. Must follow function name rules in PHP, i.e. valid name starts with a letter or underscore, followed by any number of letters, numbers, or underscores.
142
+
143
+ ```json
144
+ {
145
+ "wpPlugin": {
146
+ "name": "myPlugin"
147
+ }
148
+ }
149
+ ```
150
+
139
151
  ### `wpPlugin.scriptGlobal`
140
152
 
141
153
  The global variable name for your packages (e.g., `"wp"`, `"myPlugin"`). Set to `false` to disable global exposure:
@@ -248,15 +260,17 @@ WP-Admin mode integrates within the standard WordPress admin interface (keeping
248
260
 
249
261
  ```php
250
262
  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
263
+ 'themes.php', // Parent menu
264
+ __( 'My Page', 'my-plugin' ), // Page title
265
+ __( 'My Page', 'my-plugin' ), // Menu title
266
+ 'edit_theme_options', // Capability
267
+ 'my-admin-page-wp-admin', // Menu slug (simple)
268
+ 'my_plugin_my_admin_page_wp_admin_render_page' // Callback from generated PHP (prefixed)
257
269
  );
258
270
  ```
259
271
 
272
+ 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`.
273
+
260
274
  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.
261
275
 
262
276
  **Deep linking with the `p` query parameter:**
@@ -274,7 +288,7 @@ When the page loads, the JavaScript boot system reads the `p` parameter and navi
274
288
  Full-page mode takes over the entire admin screen with a custom sidebar:
275
289
 
276
290
  ```php
277
- add_menu_page( 'Title', 'Menu', 'capability', 'my-admin-page', 'my_admin_page_render_page', 'icon', 20 );
291
+ add_menu_page( 'Title', 'Menu', 'capability', 'my-admin-page', 'my_plugin_my_admin_page_render_page', 'icon', 20 );
278
292
  ```
279
293
 
280
294
  **Init Modules:**
@@ -337,6 +351,7 @@ This configuration:
337
351
  ```json
338
352
  {
339
353
  "wpPlugin": {
354
+ "name": "acme",
340
355
  "scriptGlobal": "acme",
341
356
  "packageNamespace": "acme"
342
357
  }
@@ -364,7 +379,7 @@ The built tool generates several files in the `build/` directory, but the primar
364
379
  Make sure to include the generated PHP file in your plugin file.
365
380
 
366
381
  ```php
367
- require_once plugin_dir_path( __FILE__ ) . 'build/index.php';
382
+ require_once plugin_dir_path( __FILE__ ) . 'build/build.php';
368
383
  ```
369
384
 
370
385
  ## Routes (Experimental)
@@ -470,7 +485,7 @@ The `canvas()` function controls which canvas is rendered:
470
485
  The build system generates:
471
486
  - `build/routes/{route-name}/content.js` - Bundled stage/inspector/canvas components
472
487
  - `build/routes/{route-name}/route.js` - Bundled lifecycle hooks (if present)
473
- - `build/routes/index.php` - Route registry data
488
+ - `build/routes/registry.php` - Route registry data
474
489
  - `build/routes.php` - Route registration logic
475
490
 
476
491
  The boot package in Gutenberg will automatically use these routes and make them available.
package/lib/build.mjs CHANGED
@@ -30,6 +30,7 @@ import {
30
30
  import {
31
31
  generatePhpFromTemplate,
32
32
  getPhpReplacements,
33
+ renderTemplateToString,
33
34
  } from './php-generator.mjs';
34
35
  import { getPackageInfo, getPackageInfoFromFile } from './package-utils.mjs';
35
36
  import { createWordpressExternalsPlugin } from './wordpress-externals-plugin.mjs';
@@ -743,7 +744,7 @@ async function generateModuleRegistrationPhp( modules, replacements ) {
743
744
  await Promise.all( [
744
745
  generatePhpFromTemplate(
745
746
  'module-registry.php.template',
746
- path.join( BUILD_DIR, 'modules', 'index.php' ),
747
+ path.join( BUILD_DIR, 'modules', 'registry.php' ),
747
748
  { ...replacements, '{{MODULES}}': modulesArray }
748
749
  ),
749
750
  generatePhpFromTemplate(
@@ -776,7 +777,7 @@ async function generateScriptRegistrationPhp( scripts, replacements ) {
776
777
  await Promise.all( [
777
778
  generatePhpFromTemplate(
778
779
  'script-registry.php.template',
779
- path.join( BUILD_DIR, 'scripts', 'index.php' ),
780
+ path.join( BUILD_DIR, 'scripts', 'registry.php' ),
780
781
  { ...replacements, '{{SCRIPTS}}': scriptsArray }
781
782
  ),
782
783
  generatePhpFromTemplate(
@@ -824,7 +825,7 @@ async function generateStyleRegistrationPhp( styles, replacements ) {
824
825
  await Promise.all( [
825
826
  generatePhpFromTemplate(
826
827
  'style-registry.php.template',
827
- path.join( BUILD_DIR, 'styles', 'index.php' ),
828
+ path.join( BUILD_DIR, 'styles', 'registry.php' ),
828
829
  { ...replacements, '{{STYLES}}': stylesArray }
829
830
  ),
830
831
  generatePhpFromTemplate(
@@ -836,14 +837,14 @@ async function generateStyleRegistrationPhp( styles, replacements ) {
836
837
  }
837
838
 
838
839
  /**
839
- * Generate main index.php that loads both modules and scripts.
840
+ * Generate main build.php that loads both modules and scripts.
840
841
  *
841
842
  * @param {Record<string, string>} replacements PHP template replacements.
842
843
  */
843
- async function generateMainIndexPhp( replacements ) {
844
+ async function generateMainBuildPhp( replacements ) {
844
845
  await generatePhpFromTemplate(
845
- 'index.php.template',
846
- path.join( BUILD_DIR, 'index.php' ),
846
+ 'build.php.template',
847
+ path.join( BUILD_DIR, 'build.php' ),
847
848
  replacements
848
849
  );
849
850
  }
@@ -876,10 +877,10 @@ async function generateRoutesRegistry( routes, replacements ) {
876
877
  } )
877
878
  .join( ',\n' );
878
879
 
879
- // Generate single global registry at build/routes/index.php
880
+ // Generate single global registry at build/routes/registry.php
880
881
  await generatePhpFromTemplate(
881
882
  'route-registry.php.template',
882
- path.join( BUILD_DIR, 'routes', 'index.php' ),
883
+ path.join( BUILD_DIR, 'routes', 'registry.php' ),
883
884
  { ...replacements, '{{ROUTES}}': routeEntries }
884
885
  );
885
886
  }
@@ -897,11 +898,35 @@ async function generateRoutesPhp( routes, replacements ) {
897
898
  return;
898
899
  }
899
900
 
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
901
922
  await generatePhpFromTemplate(
902
923
  'routes-registration.php.template',
903
924
  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
+ }
905
930
  );
906
931
  }
907
932
 
@@ -1343,12 +1368,6 @@ async function buildRoute( routeName ) {
1343
1368
  // Ensure output directory exists
1344
1369
  await mkdir( outputDir, { recursive: true } );
1345
1370
 
1346
- // Copy package.json
1347
- await copyFile(
1348
- path.join( routeDir, 'package.json' ),
1349
- path.join( outputDir, 'package.json' )
1350
- );
1351
-
1352
1371
  const files = getRouteFiles( routeDir );
1353
1372
 
1354
1373
  // Build route.js if it exists
@@ -1646,7 +1665,7 @@ async function buildAll( baseUrlExpression ) {
1646
1665
  baseUrlExpression
1647
1666
  );
1648
1667
  await Promise.all( [
1649
- generateMainIndexPhp( phpReplacements ),
1668
+ generateMainBuildPhp( phpReplacements ),
1650
1669
  generateModuleRegistrationPhp( modules, phpReplacements ),
1651
1670
  generateScriptRegistrationPhp( scripts, phpReplacements ),
1652
1671
  generateStyleRegistrationPhp( styles, phpReplacements ),
@@ -1655,13 +1674,14 @@ async function buildAll( baseUrlExpression ) {
1655
1674
  generateRoutesPhp( routes, phpReplacements ),
1656
1675
  generatePagesPhp( pageData, phpReplacements ),
1657
1676
  ] );
1677
+ console.log( ' ✔ Generated build/build.php' );
1658
1678
  console.log( ' ✔ Generated build/modules.php' );
1659
- console.log( ' ✔ Generated build/modules/index.php' );
1679
+ console.log( ' ✔ Generated build/modules/registry.php' );
1660
1680
  console.log( ' ✔ Generated build/scripts.php' );
1661
- console.log( ' ✔ Generated build/scripts/index.php' );
1681
+ console.log( ' ✔ Generated build/scripts/registry.php' );
1662
1682
  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' );
1665
1685
  console.log( ' ✔ Generated build/routes.php' );
1666
1686
  if ( pageData.length > 0 ) {
1667
1687
  console.log( ' ✔ Generated build/pages.php' );
@@ -1674,7 +1694,6 @@ async function buildAll( baseUrlExpression ) {
1674
1694
  );
1675
1695
  }
1676
1696
  }
1677
- console.log( ' ✔ Generated build/index.php' );
1678
1697
 
1679
1698
  const totalTime = Date.now() - startTime;
1680
1699
  console.log(
@@ -1944,6 +1963,7 @@ async function main() {
1944
1963
  default: 'plugin_dir_url( __FILE__ )',
1945
1964
  },
1946
1965
  },
1966
+ strict: false,
1947
1967
  } );
1948
1968
 
1949
1969
  const baseUrlExpression = values[ 'base-url' ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/build",
3
- "version": "0.5.1-next.v.0+500f87dd8",
3
+ "version": "0.6.2-next.v.0+b8934fcf9",
4
4
  "description": "Build tool for WordPress plugins.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -77,5 +77,5 @@
77
77
  "publishConfig": {
78
78
  "access": "public"
79
79
  },
80
- "gitHead": "ca0db0ee8ac2116cd307650136027d26d0cdd9bd"
80
+ "gitHead": "17529010285784fd2e735348a28391c79de87c88"
81
81
  }
@@ -13,8 +13,8 @@ if ( ! function_exists( '{{PREFIX}}_register_script_modules' ) ) {
13
13
  function {{PREFIX}}_register_script_modules() {
14
14
  // Load build constants
15
15
  $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';
18
18
 
19
19
  if ( ! file_exists( $modules_file ) ) {
20
20
  return;
@@ -12,10 +12,10 @@
12
12
 
13
13
  // Global storage for {{PAGE_SLUG}} routes and menu items
14
14
  global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes, ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items;
15
- ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes = array();
15
+ ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes = array();
16
16
  ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items = array();
17
17
 
18
- if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_route' ) ) {
18
+ if ( ! function_exists( '{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_route' ) ) {
19
19
  /**
20
20
  * Register a route for the {{PAGE_SLUG}}-wp-admin page.
21
21
  *
@@ -23,7 +23,7 @@ if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_route' ) ) {
23
23
  * @param string|null $content_module Script module ID for content (stage/inspector).
24
24
  * @param string|null $route_module Script module ID for route lifecycle hooks.
25
25
  */
26
- function register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_route( $path, $content_module = null, $route_module = null ) {
26
+ function {{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_route( $path, $content_module = null, $route_module = null ) {
27
27
  global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes;
28
28
 
29
29
  $route = array( 'path' => $path );
@@ -38,7 +38,7 @@ if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_route' ) ) {
38
38
  }
39
39
  }
40
40
 
41
- if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_item' ) ) {
41
+ if ( ! function_exists( '{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_item' ) ) {
42
42
  /**
43
43
  * Register a menu item for the {{PAGE_SLUG}}-wp-admin page.
44
44
  * Note: Menu items are registered but not displayed in single-page mode.
@@ -48,7 +48,7 @@ if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_item' )
48
48
  * @param string $to Route path to navigate to.
49
49
  * @param string $parent_id Optional. Parent menu item ID.
50
50
  */
51
- function register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_item( $id, $label, $to, $parent_id = '' ) {
51
+ function {{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_item( $id, $label, $to, $parent_id = '' ) {
52
52
  global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items;
53
53
 
54
54
  $menu_item = array(
@@ -65,36 +65,36 @@ if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_item' )
65
65
  }
66
66
  }
67
67
 
68
- if ( ! function_exists( 'get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes' ) ) {
68
+ if ( ! function_exists( '{{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes' ) ) {
69
69
  /**
70
70
  * Get all registered routes for the {{PAGE_SLUG}}-wp-admin page.
71
71
  *
72
72
  * @return array Array of route objects.
73
73
  */
74
- function get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes() {
74
+ function {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes() {
75
75
  global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes;
76
76
  return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes ?? array();
77
77
  }
78
78
  }
79
79
 
80
- if ( ! function_exists( 'get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items' ) ) {
80
+ if ( ! function_exists( '{{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items' ) ) {
81
81
  /**
82
82
  * Get all registered menu items for the {{PAGE_SLUG}}-wp-admin page.
83
83
  *
84
84
  * @return array Array of menu item objects.
85
85
  */
86
- function get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items() {
86
+ function {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items() {
87
87
  global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items;
88
88
  return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items ?? array();
89
89
  }
90
90
  }
91
91
 
92
- if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data' ) ) {
92
+ if ( ! function_exists( '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data' ) ) {
93
93
  /**
94
94
  * Preload REST API data for the {{PAGE_SLUG}}-wp-admin page.
95
95
  * Automatically called during page rendering.
96
96
  */
97
- function {{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data() {
97
+ function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data() {
98
98
  // Define paths to preload - same for all pages
99
99
  $preload_paths = array(
100
100
  '/?_fields=description,gmt_offset,home,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front',
@@ -120,14 +120,14 @@ if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data' ) ) {
120
120
  }
121
121
  }
122
122
 
123
- if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts' ) ) {
123
+ if ( ! function_exists( '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts' ) ) {
124
124
  /**
125
125
  * Enqueue scripts and styles for the {{PAGE_SLUG}}-wp-admin page.
126
126
  * Hooked to admin_enqueue_scripts.
127
127
  *
128
128
  * @param string $hook_suffix The current admin page.
129
129
  */
130
- function {{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts( $hook_suffix ) {
130
+ function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts( $hook_suffix ) {
131
131
  // Check all possible ways this page can be accessed:
132
132
  // 1. Menu page via admin.php?page={{PAGE_SLUG}}-wp-admin (plugin)
133
133
  // 2. Direct file via {{PAGE_SLUG}}.php (Core) - screen ID will be '{{PAGE_SLUG}}'
@@ -148,10 +148,10 @@ if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts' ) )
148
148
  do_action( '{{PAGE_SLUG}}-wp-admin_init' );
149
149
 
150
150
  // Preload REST API data
151
- {{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data();
151
+ {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data();
152
152
 
153
153
  // Get all registered routes
154
- $routes = get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes();
154
+ $routes = {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes();
155
155
 
156
156
  // Get boot module asset file for dependencies
157
157
  $asset_file = __DIR__ . '/../../modules/boot/index.min.asset.php';
@@ -221,13 +221,13 @@ if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts' ) )
221
221
  }
222
222
  }
223
223
 
224
- if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_render_page' ) ) {
224
+ if ( ! function_exists( '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_render_page' ) ) {
225
225
  /**
226
226
  * Render the {{PAGE_SLUG}}-wp-admin page.
227
227
  * Call this function from add_menu_page or add_submenu_page.
228
228
  * This renders within the normal WordPress admin interface.
229
229
  */
230
- function {{PAGE_SLUG_UNDERSCORE}}_wp_admin_render_page() {
230
+ function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_render_page() {
231
231
  ?>
232
232
  <style>
233
233
  /* Critical styles to prevent layout shifts - inlined for immediate application */
@@ -288,5 +288,5 @@ if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_render_page' ) ) {
288
288
  }
289
289
 
290
290
  // Hook the enqueue function to admin_enqueue_scripts
291
- add_action( 'admin_enqueue_scripts', '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts' );
291
+ add_action( 'admin_enqueue_scripts', '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts' );
292
292
 
@@ -9,10 +9,10 @@
9
9
 
10
10
  // Global storage for {{PAGE_SLUG}} routes and menu items
11
11
  global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes, ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items;
12
- ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes = array();
12
+ ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes = array();
13
13
  ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items = array();
14
14
 
15
- if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_route' ) ) {
15
+ if ( ! function_exists( '{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_route' ) ) {
16
16
  /**
17
17
  * Register a route for the {{PAGE_SLUG}} page.
18
18
  *
@@ -20,7 +20,7 @@ if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_route' ) ) {
20
20
  * @param string|null $content_module Script module ID for content (stage/inspector).
21
21
  * @param string|null $route_module Script module ID for route lifecycle hooks.
22
22
  */
23
- function register_{{PAGE_SLUG_UNDERSCORE}}_route( $path, $content_module = null, $route_module = null ) {
23
+ function {{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_route( $path, $content_module = null, $route_module = null ) {
24
24
  global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes;
25
25
 
26
26
  $route = array( 'path' => $path );
@@ -35,7 +35,7 @@ if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_route' ) ) {
35
35
  }
36
36
  }
37
37
 
38
- if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_menu_item' ) ) {
38
+ if ( ! function_exists( '{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_menu_item' ) ) {
39
39
  /**
40
40
  * Register a menu item for the {{PAGE_SLUG}} page.
41
41
  *
@@ -45,7 +45,7 @@ if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_menu_item' ) ) {
45
45
  * @param string $parent_id Optional. Parent menu item ID.
46
46
  * @param string $parent_type Optional. Parent type: 'drilldown' or 'dropdown'.
47
47
  */
48
- function register_{{PAGE_SLUG_UNDERSCORE}}_menu_item( $id, $label, $to, $parent_id = '', $parent_type = '' ) {
48
+ function {{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_menu_item( $id, $label, $to, $parent_id = '', $parent_type = '' ) {
49
49
  global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items;
50
50
 
51
51
  $menu_item = array(
@@ -66,36 +66,36 @@ if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_menu_item' ) ) {
66
66
  }
67
67
  }
68
68
 
69
- if ( ! function_exists( 'get_{{PAGE_SLUG_UNDERSCORE}}_routes' ) ) {
69
+ if ( ! function_exists( '{{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_routes' ) ) {
70
70
  /**
71
71
  * Get all registered routes for the {{PAGE_SLUG}} page.
72
72
  *
73
73
  * @return array Array of route objects.
74
74
  */
75
- function get_{{PAGE_SLUG_UNDERSCORE}}_routes() {
75
+ function {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_routes() {
76
76
  global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes;
77
77
  return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes ?? array();
78
78
  }
79
79
  }
80
80
 
81
- if ( ! function_exists( 'get_{{PAGE_SLUG_UNDERSCORE}}_menu_items' ) ) {
81
+ if ( ! function_exists( '{{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_menu_items' ) ) {
82
82
  /**
83
83
  * Get all registered menu items for the {{PAGE_SLUG}} page.
84
84
  *
85
85
  * @return array Array of menu item objects.
86
86
  */
87
- function get_{{PAGE_SLUG_UNDERSCORE}}_menu_items() {
87
+ function {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_menu_items() {
88
88
  global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items;
89
89
  return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items ?? array();
90
90
  }
91
91
  }
92
92
 
93
- if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_preload_data' ) ) {
93
+ if ( ! function_exists( '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_preload_data' ) ) {
94
94
  /**
95
95
  * Preload REST API data for the {{PAGE_SLUG}} page.
96
96
  * Automatically called during page rendering.
97
97
  */
98
- function {{PAGE_SLUG_UNDERSCORE}}_preload_data() {
98
+ function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_preload_data() {
99
99
  // Define paths to preload - same for all pages
100
100
  $preload_paths = array(
101
101
  '/?_fields=description,gmt_offset,home,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front',
@@ -121,12 +121,12 @@ if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_preload_data' ) ) {
121
121
  }
122
122
  }
123
123
 
124
- if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_render_page' ) ) {
124
+ if ( ! function_exists( '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_render_page' ) ) {
125
125
  /**
126
126
  * Render the {{PAGE_SLUG}} page.
127
127
  * Call this function from add_menu_page or add_submenu_page.
128
128
  */
129
- function {{PAGE_SLUG_UNDERSCORE}}_render_page() {
129
+ function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_render_page() {
130
130
  // Load build constants
131
131
  $build_constants = require __DIR__ . '/../../constants.php';
132
132
 
@@ -153,11 +153,11 @@ if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_render_page' ) ) {
153
153
  }
154
154
 
155
155
  // Preload REST API data
156
- {{PAGE_SLUG_UNDERSCORE}}_preload_data();
156
+ {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_preload_data();
157
157
 
158
158
  // Get all registered routes and menu items
159
- $menu_items = get_{{PAGE_SLUG_UNDERSCORE}}_menu_items();
160
- $routes = get_{{PAGE_SLUG_UNDERSCORE}}_routes();
159
+ $menu_items = {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_menu_items();
160
+ $routes = {{PREFIX}}_get_{{PAGE_SLUG_UNDERSCORE}}_routes();
161
161
 
162
162
  // Get boot module asset file for dependencies
163
163
  $asset_file = __DIR__ . '/../../modules/boot/index.min.asset.php';
@@ -312,19 +312,19 @@ if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_render_page' ) ) {
312
312
  }
313
313
  }
314
314
 
315
- if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_intercept_render' ) ) {
315
+ if ( ! function_exists( '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_intercept_render' ) ) {
316
316
  /**
317
317
  * Intercept admin_init to render the page early.
318
318
  * This bypasses the default WordPress admin template.
319
319
  */
320
- function {{PAGE_SLUG_UNDERSCORE}}_intercept_render() {
320
+ function {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_intercept_render() {
321
321
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
322
322
  if ( isset( $_GET['page'] ) && '{{PAGE_SLUG}}' === $_GET['page'] ) {
323
- {{PAGE_SLUG_UNDERSCORE}}_render_page();
323
+ {{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_render_page();
324
324
  exit;
325
325
  }
326
326
  }
327
327
  }
328
328
 
329
329
  // Hook the interceptor to admin_init
330
- add_action( 'admin_init', '{{PAGE_SLUG_UNDERSCORE}}_intercept_render' );
330
+ add_action( 'admin_init', '{{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_intercept_render' );
@@ -0,0 +1,22 @@
1
+ // Page-specific route registration functions for {{PAGE_SLUG}}
2
+ if ( ! function_exists( '{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_page_routes' ) ) {
3
+ /**
4
+ * Register routes for {{PAGE_SLUG}} page (full-page mode).
5
+ */
6
+ function {{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_page_routes() {
7
+ global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes_data;
8
+ {{PREFIX}}_register_page_routes( ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes_data, '{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_route' );
9
+ }
10
+ }
11
+ add_action( '{{PAGE_SLUG}}_init', '{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_page_routes' );
12
+
13
+ if ( ! function_exists( '{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_page_routes' ) ) {
14
+ /**
15
+ * Register routes for {{PAGE_SLUG}} page (wp-admin mode).
16
+ */
17
+ function {{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_page_routes() {
18
+ global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes_data;
19
+ {{PREFIX}}_register_page_routes( ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes_data, '{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_route' );
20
+ }
21
+ }
22
+ add_action( '{{PAGE_SLUG}}-wp-admin_init', '{{PREFIX}}_register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_page_routes' );
@@ -8,14 +8,14 @@
8
8
  */
9
9
 
10
10
  // Load routes registry
11
- $routes_file = __DIR__ . '/routes/index.php';
11
+ $routes_file = __DIR__ . '/routes/registry.php';
12
12
  if ( ! file_exists( $routes_file ) ) {
13
13
  return;
14
14
  }
15
15
 
16
16
  $routes = require $routes_file;
17
17
 
18
- // Group routes by page
18
+ // Group routes by page and store in globals for page-specific functions
19
19
  $routes_by_page = array();
20
20
  foreach ( $routes as $route ) {
21
21
  $page_slug = $route['page'];
@@ -25,11 +25,24 @@ foreach ( $routes as $route ) {
25
25
  $routes_by_page[ $page_slug ][] = $route;
26
26
  }
27
27
 
28
- // Helper function to register routes for a page
29
- $register_routes_callback = function ( $page_routes, $page_slug_underscore, $register_function_name ) {
30
- return function () use ( $page_routes, $page_slug_underscore, $register_function_name ) {
28
+ // Store routes data in globals for each page
29
+ foreach ( $routes_by_page as $page_slug => $page_routes ) {
30
+ $page_slug_underscore = str_replace( '-', '_', $page_slug );
31
+ $global_name = '{{PREFIX}}_' . $page_slug_underscore . '_routes_data';
32
+ $GLOBALS[ $global_name ] = $page_routes;
33
+ }
34
+
35
+ if ( ! function_exists( '{{PREFIX}}_register_page_routes' ) ) {
36
+ /**
37
+ * Generic helper function to register routes for a page.
38
+ *
39
+ * @param array $page_routes Array of route data for the page.
40
+ * @param string $register_function_name Name of the function to call for registering each route.
41
+ */
42
+ function {{PREFIX}}_register_page_routes( $page_routes, $register_function_name ) {
31
43
  // Load build constants
32
44
  $build_constants = require __DIR__ . '/constants.php';
45
+
33
46
  foreach ( $page_routes as $route ) {
34
47
  $content_handle = null;
35
48
  $route_handle = null;
@@ -71,22 +84,8 @@ $register_routes_callback = function ( $page_routes, $page_slug_underscore, $reg
71
84
  call_user_func( $register_function_name, $route['path'], $content_handle, $route_handle );
72
85
  }
73
86
  }
74
- };
75
- };
76
-
77
- // Register hooks for both full-page and wp-admin modes
78
- foreach ( $routes_by_page as $page_slug => $page_routes ) {
79
- $page_slug_underscore = str_replace( '-', '_', $page_slug );
80
-
81
- // Register all routes for full-page mode (page.php)
82
- add_action(
83
- "{$page_slug}_init",
84
- $register_routes_callback( $page_routes, $page_slug_underscore, "register_{$page_slug_underscore}_route" )
85
- );
86
-
87
- // Register all routes for wp-admin mode (page-wp-admin.php)
88
- add_action(
89
- "{$page_slug}-wp-admin_init",
90
- $register_routes_callback( $page_routes, $page_slug_underscore, "register_{$page_slug_underscore}_wp_admin_route" )
91
- );
87
+ }
92
88
  }
89
+
90
+ // Page-specific route registration functions
91
+ {{PAGE_ROUTE_FUNCTIONS}}
@@ -59,7 +59,7 @@ if ( ! function_exists( '{{PREFIX}}_register_package_scripts' ) ) {
59
59
  $extension = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.js' : '.min.js';
60
60
 
61
61
  $scripts_dir = __DIR__ . '/scripts';
62
- $scripts_file = $scripts_dir . '/index.php';
62
+ $scripts_file = $scripts_dir . '/registry.php';
63
63
 
64
64
  if ( ! file_exists( $scripts_file ) ) {
65
65
  return;
@@ -49,7 +49,7 @@ if ( ! function_exists( '{{PREFIX}}_register_package_styles' ) ) {
49
49
  $suffix = SCRIPT_DEBUG ? '' : '.min';
50
50
 
51
51
  $styles_dir = __DIR__ . '/styles';
52
- $styles_file = $styles_dir . '/index.php';
52
+ $styles_file = $styles_dir . '/registry.php';
53
53
 
54
54
  if ( ! file_exists( $styles_file ) ) {
55
55
  return;