@wordpress/build 0.2.0 → 0.3.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/.cache/tsconfig.tsbuildinfo +1 -1
- package/CHANGELOG.md +2 -0
- package/README.md +95 -0
- package/package.json +2 -2
- package/src/build.mjs +501 -107
- package/src/dependency-graph.mjs +110 -12
- package/src/package-utils.mjs +5 -0
- package/src/php-generator.mjs +36 -12
- package/src/route-utils.mjs +132 -0
- package/src/wordpress-externals-plugin.mjs +27 -10
- package/templates/index.php.template +12 -0
- package/templates/page-wp-admin.php.template +252 -0
- package/templates/page.php.template +311 -0
- package/templates/pages.php.template +9 -0
- package/templates/route-registry.php.template +11 -0
- package/templates/routes-registration.php.template +90 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* Page: {{PAGE_SLUG}} (wp-admin integrated)
|
|
4
|
+
* Auto-generated by build process.
|
|
5
|
+
* Do not edit this file manually.
|
|
6
|
+
*
|
|
7
|
+
* This version integrates with the standard WordPress admin interface,
|
|
8
|
+
* keeping the wp-admin sidebar and scripts/styles intact.
|
|
9
|
+
*
|
|
10
|
+
* @package {{PREFIX}}
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Global storage for {{PAGE_SLUG}} routes and menu items
|
|
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();
|
|
16
|
+
${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items = array();
|
|
17
|
+
|
|
18
|
+
if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_route' ) ) {
|
|
19
|
+
/**
|
|
20
|
+
* Register a route for the {{PAGE_SLUG}}-wp-admin page.
|
|
21
|
+
*
|
|
22
|
+
* @param string $path Route path (e.g., '/types/$type/edit/$id').
|
|
23
|
+
* @param string|null $content_module Script module ID for content (stage/inspector).
|
|
24
|
+
* @param string|null $route_module Script module ID for route lifecycle hooks.
|
|
25
|
+
*/
|
|
26
|
+
function register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_route( $path, $content_module = null, $route_module = null ) {
|
|
27
|
+
global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes;
|
|
28
|
+
|
|
29
|
+
$route = array( 'path' => $path );
|
|
30
|
+
if ( ! empty( $content_module ) ) {
|
|
31
|
+
$route['content_module'] = $content_module;
|
|
32
|
+
}
|
|
33
|
+
if ( ! empty( $route_module ) ) {
|
|
34
|
+
$route['route_module'] = $route_module;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes[] = $route;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_item' ) ) {
|
|
42
|
+
/**
|
|
43
|
+
* Register a menu item for the {{PAGE_SLUG}}-wp-admin page.
|
|
44
|
+
* Note: Menu items are registered but not displayed in single-page mode.
|
|
45
|
+
*
|
|
46
|
+
* @param string $id Menu item ID.
|
|
47
|
+
* @param string $label Display label.
|
|
48
|
+
* @param string $to Route path to navigate to.
|
|
49
|
+
* @param string $parent_id Optional. Parent menu item ID.
|
|
50
|
+
*/
|
|
51
|
+
function register_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_item( $id, $label, $to, $parent_id = '' ) {
|
|
52
|
+
global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items;
|
|
53
|
+
|
|
54
|
+
$menu_item = array(
|
|
55
|
+
'id' => $id,
|
|
56
|
+
'label' => $label,
|
|
57
|
+
'to' => $to,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
if ( ! empty( $parent_id ) ) {
|
|
61
|
+
$menu_item['parent'] = $parent_id;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items[] = $menu_item;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if ( ! function_exists( 'get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes' ) ) {
|
|
69
|
+
/**
|
|
70
|
+
* Get all registered routes for the {{PAGE_SLUG}}-wp-admin page.
|
|
71
|
+
*
|
|
72
|
+
* @return array Array of route objects.
|
|
73
|
+
*/
|
|
74
|
+
function get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes() {
|
|
75
|
+
global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes;
|
|
76
|
+
return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes ?? array();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if ( ! function_exists( 'get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items' ) ) {
|
|
81
|
+
/**
|
|
82
|
+
* Get all registered menu items for the {{PAGE_SLUG}}-wp-admin page.
|
|
83
|
+
*
|
|
84
|
+
* @return array Array of menu item objects.
|
|
85
|
+
*/
|
|
86
|
+
function get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items() {
|
|
87
|
+
global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items;
|
|
88
|
+
return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_menu_items ?? array();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data' ) ) {
|
|
93
|
+
/**
|
|
94
|
+
* Preload REST API data for the {{PAGE_SLUG}}-wp-admin page.
|
|
95
|
+
* Automatically called during page rendering.
|
|
96
|
+
*/
|
|
97
|
+
function {{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data() {
|
|
98
|
+
// Define paths to preload - same for all pages
|
|
99
|
+
$preload_paths = array(
|
|
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',
|
|
101
|
+
array( '/wp/v2/settings', 'OPTIONS' ),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Use rest_preload_api_request to gather the preloaded data
|
|
105
|
+
$preload_data = array_reduce(
|
|
106
|
+
$preload_paths,
|
|
107
|
+
'rest_preload_api_request',
|
|
108
|
+
array()
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Register the preloading middleware with wp-api-fetch
|
|
112
|
+
wp_add_inline_script(
|
|
113
|
+
'wp-api-fetch',
|
|
114
|
+
sprintf(
|
|
115
|
+
'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );',
|
|
116
|
+
wp_json_encode( $preload_data )
|
|
117
|
+
),
|
|
118
|
+
'after'
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts' ) ) {
|
|
124
|
+
/**
|
|
125
|
+
* Enqueue scripts and styles for the {{PAGE_SLUG}}-wp-admin page.
|
|
126
|
+
* Hooked to admin_enqueue_scripts.
|
|
127
|
+
*
|
|
128
|
+
* @param string $hook_suffix The current admin page.
|
|
129
|
+
*/
|
|
130
|
+
function {{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts( $hook_suffix ) {
|
|
131
|
+
// Only enqueue on our page
|
|
132
|
+
if ( ! isset( $_GET['page'] ) || '{{PAGE_SLUG}}-wp-admin' !== $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Fire init action for extensions to register routes and menu items
|
|
137
|
+
do_action( '{{PAGE_SLUG}}-wp-admin_init' );
|
|
138
|
+
|
|
139
|
+
// Preload REST API data
|
|
140
|
+
{{PAGE_SLUG_UNDERSCORE}}_wp_admin_preload_data();
|
|
141
|
+
|
|
142
|
+
// Get all registered routes
|
|
143
|
+
$routes = get_{{PAGE_SLUG_UNDERSCORE}}_wp_admin_routes();
|
|
144
|
+
|
|
145
|
+
// Get boot module asset file for dependencies
|
|
146
|
+
$asset_file = plugin_dir_path( __FILE__ ) . '../../modules/boot/index.min.asset.php';
|
|
147
|
+
if ( file_exists( $asset_file ) ) {
|
|
148
|
+
$asset = require $asset_file;
|
|
149
|
+
|
|
150
|
+
// This script serves two purposes:
|
|
151
|
+
// 1. It ensures all the globals that are made available to the modules are loaded.
|
|
152
|
+
// 2. It initializes the boot module as an inline script.
|
|
153
|
+
wp_register_script( '{{PAGE_SLUG}}-wp-admin-prerequisites', '', $asset['dependencies'], $asset['version'], true );
|
|
154
|
+
|
|
155
|
+
// Add inline script to initialize the app using initSinglePage (no menuItems)
|
|
156
|
+
wp_add_inline_script(
|
|
157
|
+
'{{PAGE_SLUG}}-wp-admin-prerequisites',
|
|
158
|
+
sprintf(
|
|
159
|
+
'import("@wordpress/boot").then(mod => mod.initSinglePage({mountId: "%s", routes: %s}));',
|
|
160
|
+
'{{PAGE_SLUG}}-wp-admin-app',
|
|
161
|
+
wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES )
|
|
162
|
+
)
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// Register prerequisites style by filtering script dependencies to find registered styles
|
|
166
|
+
$style_dependencies = array_filter(
|
|
167
|
+
$asset['dependencies'],
|
|
168
|
+
function ( $handle ) {
|
|
169
|
+
return wp_style_is( $handle, 'registered' );
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
wp_register_style( '{{PAGE_SLUG}}-wp-admin-prerequisites', false, $style_dependencies, $asset['version'] );
|
|
173
|
+
|
|
174
|
+
// Build dependencies for {{PAGE_SLUG}}-wp-admin module
|
|
175
|
+
$boot_dependencies = array(
|
|
176
|
+
array(
|
|
177
|
+
'import' => 'static',
|
|
178
|
+
'id' => '@wordpress/boot',
|
|
179
|
+
),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// Add all registered routes as dependencies
|
|
183
|
+
foreach ( $routes as $route ) {
|
|
184
|
+
if ( isset( $route['route_module'] ) ) {
|
|
185
|
+
$boot_dependencies[] = array(
|
|
186
|
+
'import' => 'static',
|
|
187
|
+
'id' => $route['route_module'],
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
if ( isset( $route['content_module'] ) ) {
|
|
191
|
+
$boot_dependencies[] = array(
|
|
192
|
+
'import' => 'dynamic',
|
|
193
|
+
'id' => $route['content_module'],
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Dummy script module to ensure dependencies are loaded
|
|
199
|
+
wp_register_script_module(
|
|
200
|
+
'{{PAGE_SLUG}}-wp-admin',
|
|
201
|
+
plugin_dir_url( __FILE__ ) . 'loader.js',
|
|
202
|
+
$boot_dependencies
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// Enqueue the boot scripts and styles
|
|
206
|
+
wp_enqueue_script( '{{PAGE_SLUG}}-wp-admin-prerequisites' );
|
|
207
|
+
wp_enqueue_script_module( '{{PAGE_SLUG}}-wp-admin' );
|
|
208
|
+
wp_enqueue_style( '{{PAGE_SLUG}}-wp-admin-prerequisites' );
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_render_page' ) ) {
|
|
214
|
+
/**
|
|
215
|
+
* Render the {{PAGE_SLUG}}-wp-admin page.
|
|
216
|
+
* Call this function from add_menu_page or add_submenu_page.
|
|
217
|
+
* This renders within the normal WordPress admin interface.
|
|
218
|
+
*/
|
|
219
|
+
function {{PAGE_SLUG_UNDERSCORE}}_wp_admin_render_page() {
|
|
220
|
+
?>
|
|
221
|
+
<div id="{{PAGE_SLUG}}-wp-admin-app" class="boot-layout-container"></div>
|
|
222
|
+
<?php
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Hook the enqueue function to admin_enqueue_scripts
|
|
227
|
+
add_action( 'admin_enqueue_scripts', '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_enqueue_scripts' );
|
|
228
|
+
|
|
229
|
+
if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_register_page' ) ) {
|
|
230
|
+
/**
|
|
231
|
+
* Register the {{PAGE_SLUG}}-wp-admin base page handler.
|
|
232
|
+
* This is hidden from the menu but provides the page callback for URL routing.
|
|
233
|
+
* Visible menu items should use the full URL pattern with query parameters.
|
|
234
|
+
*
|
|
235
|
+
* Example:
|
|
236
|
+
* $url = admin_url( 'admin.php?page={{PAGE_SLUG}}-wp-admin&p=' . urlencode( '/your/route' ) );
|
|
237
|
+
* add_menu_page( 'Title', 'Menu', 'capability', $url, '', 'icon', 10 );
|
|
238
|
+
*/
|
|
239
|
+
function {{PAGE_SLUG_UNDERSCORE}}_wp_admin_register_page() {
|
|
240
|
+
add_submenu_page(
|
|
241
|
+
'nothing-{{PAGE_SLUG}}', // Hidden page (no parent)
|
|
242
|
+
__( '{{PAGE_SLUG}} (WP Admin)', '{{PREFIX}}' ), // Page title
|
|
243
|
+
__( '{{PAGE_SLUG}} (WP Admin)', '{{PREFIX}}' ), // Menu title (not visible)
|
|
244
|
+
'read', // Minimum capability
|
|
245
|
+
'{{PAGE_SLUG}}-wp-admin', // Menu slug for URL routing
|
|
246
|
+
'{{PAGE_SLUG_UNDERSCORE}}_wp_admin_render_page' // Callback function
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Hook the registration to admin_menu
|
|
252
|
+
add_action( 'admin_menu', '{{PAGE_SLUG_UNDERSCORE}}_wp_admin_register_page' );
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* Page: {{PAGE_SLUG}}
|
|
4
|
+
* Auto-generated by build process.
|
|
5
|
+
* Do not edit this file manually.
|
|
6
|
+
*
|
|
7
|
+
* @package {{PREFIX}}
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Global storage for {{PAGE_SLUG}} routes and menu items
|
|
11
|
+
global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes, ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items;
|
|
12
|
+
${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes = array();
|
|
13
|
+
${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items = array();
|
|
14
|
+
|
|
15
|
+
if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_route' ) ) {
|
|
16
|
+
/**
|
|
17
|
+
* Register a route for the {{PAGE_SLUG}} page.
|
|
18
|
+
*
|
|
19
|
+
* @param string $path Route path (e.g., '/types/$type/edit/$id').
|
|
20
|
+
* @param string|null $content_module Script module ID for content (stage/inspector).
|
|
21
|
+
* @param string|null $route_module Script module ID for route lifecycle hooks.
|
|
22
|
+
*/
|
|
23
|
+
function register_{{PAGE_SLUG_UNDERSCORE}}_route( $path, $content_module = null, $route_module = null ) {
|
|
24
|
+
global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes;
|
|
25
|
+
|
|
26
|
+
$route = array( 'path' => $path );
|
|
27
|
+
if ( ! empty( $content_module ) ) {
|
|
28
|
+
$route['content_module'] = $content_module;
|
|
29
|
+
}
|
|
30
|
+
if ( ! empty( $route_module ) ) {
|
|
31
|
+
$route['route_module'] = $route_module;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes[] = $route;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if ( ! function_exists( 'register_{{PAGE_SLUG_UNDERSCORE}}_menu_item' ) ) {
|
|
39
|
+
/**
|
|
40
|
+
* Register a menu item for the {{PAGE_SLUG}} page.
|
|
41
|
+
*
|
|
42
|
+
* @param string $id Menu item ID.
|
|
43
|
+
* @param string $label Display label.
|
|
44
|
+
* @param string $to Route path to navigate to.
|
|
45
|
+
* @param string $parent_id Optional. Parent menu item ID.
|
|
46
|
+
*/
|
|
47
|
+
function register_{{PAGE_SLUG_UNDERSCORE}}_menu_item( $id, $label, $to, $parent_id = '' ) {
|
|
48
|
+
global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items;
|
|
49
|
+
|
|
50
|
+
$menu_item = array(
|
|
51
|
+
'id' => $id,
|
|
52
|
+
'label' => $label,
|
|
53
|
+
'to' => $to,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
if ( ! empty( $parent_id ) ) {
|
|
57
|
+
$menu_item['parent'] = $parent_id;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items[] = $menu_item;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if ( ! function_exists( 'get_{{PAGE_SLUG_UNDERSCORE}}_routes' ) ) {
|
|
65
|
+
/**
|
|
66
|
+
* Get all registered routes for the {{PAGE_SLUG}} page.
|
|
67
|
+
*
|
|
68
|
+
* @return array Array of route objects.
|
|
69
|
+
*/
|
|
70
|
+
function get_{{PAGE_SLUG_UNDERSCORE}}_routes() {
|
|
71
|
+
global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes;
|
|
72
|
+
return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_routes ?? array();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if ( ! function_exists( 'get_{{PAGE_SLUG_UNDERSCORE}}_menu_items' ) ) {
|
|
77
|
+
/**
|
|
78
|
+
* Get all registered menu items for the {{PAGE_SLUG}} page.
|
|
79
|
+
*
|
|
80
|
+
* @return array Array of menu item objects.
|
|
81
|
+
*/
|
|
82
|
+
function get_{{PAGE_SLUG_UNDERSCORE}}_menu_items() {
|
|
83
|
+
global ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items;
|
|
84
|
+
return ${{PREFIX}}_{{PAGE_SLUG_UNDERSCORE}}_menu_items ?? array();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_preload_data' ) ) {
|
|
89
|
+
/**
|
|
90
|
+
* Preload REST API data for the {{PAGE_SLUG}} page.
|
|
91
|
+
* Automatically called during page rendering.
|
|
92
|
+
*/
|
|
93
|
+
function {{PAGE_SLUG_UNDERSCORE}}_preload_data() {
|
|
94
|
+
// Define paths to preload - same for all pages
|
|
95
|
+
$preload_paths = array(
|
|
96
|
+
'/?_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',
|
|
97
|
+
array( '/wp/v2/settings', 'OPTIONS' ),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Use rest_preload_api_request to gather the preloaded data
|
|
101
|
+
$preload_data = array_reduce(
|
|
102
|
+
$preload_paths,
|
|
103
|
+
'rest_preload_api_request',
|
|
104
|
+
array()
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// Register the preloading middleware with wp-api-fetch
|
|
108
|
+
wp_add_inline_script(
|
|
109
|
+
'wp-api-fetch',
|
|
110
|
+
sprintf(
|
|
111
|
+
'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );',
|
|
112
|
+
wp_json_encode( $preload_data )
|
|
113
|
+
),
|
|
114
|
+
'after'
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_render_page' ) ) {
|
|
120
|
+
/**
|
|
121
|
+
* Render the {{PAGE_SLUG}} page.
|
|
122
|
+
* Call this function from add_menu_page or add_submenu_page.
|
|
123
|
+
*/
|
|
124
|
+
function {{PAGE_SLUG_UNDERSCORE}}_render_page() {
|
|
125
|
+
// Set current screen
|
|
126
|
+
set_current_screen();
|
|
127
|
+
|
|
128
|
+
// Remove unwanted deprecated handler
|
|
129
|
+
remove_action( 'admin_head', 'wp_admin_bar_header' );
|
|
130
|
+
|
|
131
|
+
// Remove unwanted scripts and styles that were enqueued during `admin_init`
|
|
132
|
+
foreach ( wp_scripts()->queue as $script ) {
|
|
133
|
+
wp_dequeue_script( $script );
|
|
134
|
+
}
|
|
135
|
+
foreach ( wp_styles()->queue as $style ) {
|
|
136
|
+
wp_dequeue_style( $style );
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Fire init action for extensions to register routes and menu items
|
|
140
|
+
do_action( '{{PAGE_SLUG}}_init' );
|
|
141
|
+
|
|
142
|
+
// Preload REST API data
|
|
143
|
+
{{PAGE_SLUG_UNDERSCORE}}_preload_data();
|
|
144
|
+
|
|
145
|
+
// Get all registered routes and menu items
|
|
146
|
+
$menu_items = get_{{PAGE_SLUG_UNDERSCORE}}_menu_items();
|
|
147
|
+
$routes = get_{{PAGE_SLUG_UNDERSCORE}}_routes();
|
|
148
|
+
|
|
149
|
+
// Get boot module asset file for dependencies
|
|
150
|
+
$asset_file = plugin_dir_path( __FILE__ ) . '../../modules/boot/index.min.asset.php';
|
|
151
|
+
if ( file_exists( $asset_file ) ) {
|
|
152
|
+
$asset = require $asset_file;
|
|
153
|
+
|
|
154
|
+
// This script serves two purposes:
|
|
155
|
+
// 1. It ensures all the globals that are made available to the modules are loaded.
|
|
156
|
+
// 2. It initializes the boot module as an inline script.
|
|
157
|
+
wp_register_script( '{{PAGE_SLUG}}-prerequisites', '', $asset['dependencies'], $asset['version'], true );
|
|
158
|
+
|
|
159
|
+
// Add inline script to initialize the app
|
|
160
|
+
wp_add_inline_script(
|
|
161
|
+
'{{PAGE_SLUG}}-prerequisites',
|
|
162
|
+
sprintf(
|
|
163
|
+
'import("@wordpress/boot").then(mod => mod.init({mountId: "%s", menuItems: %s, routes: %s}));',
|
|
164
|
+
'{{PAGE_SLUG}}-app',
|
|
165
|
+
wp_json_encode( $menu_items, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
|
|
166
|
+
wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES )
|
|
167
|
+
)
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// Register prerequisites style by filtering script dependencies to find registered styles
|
|
171
|
+
$style_dependencies = array_filter(
|
|
172
|
+
$asset['dependencies'],
|
|
173
|
+
function ( $handle ) {
|
|
174
|
+
return wp_style_is( $handle, 'registered' );
|
|
175
|
+
}
|
|
176
|
+
);
|
|
177
|
+
wp_register_style( '{{PAGE_SLUG}}-prerequisites', false, $style_dependencies, $asset['version'] );
|
|
178
|
+
|
|
179
|
+
// Build dependencies for {{PAGE_SLUG}} module
|
|
180
|
+
$boot_dependencies = array(
|
|
181
|
+
array(
|
|
182
|
+
'import' => 'static',
|
|
183
|
+
'id' => '@wordpress/boot',
|
|
184
|
+
),
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// Add all registered routes as dependencies
|
|
188
|
+
foreach ( $routes as $route ) {
|
|
189
|
+
if ( isset( $route['route_module'] ) ) {
|
|
190
|
+
$boot_dependencies[] = array(
|
|
191
|
+
'import' => 'static',
|
|
192
|
+
'id' => $route['route_module'],
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
if ( isset( $route['content_module'] ) ) {
|
|
196
|
+
$boot_dependencies[] = array(
|
|
197
|
+
'import' => 'dynamic',
|
|
198
|
+
'id' => $route['content_module'],
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Dummy script module to ensure dependencies are loaded
|
|
204
|
+
wp_register_script_module(
|
|
205
|
+
'{{PAGE_SLUG}}',
|
|
206
|
+
plugin_dir_url( __FILE__ ) . 'loader.js',
|
|
207
|
+
$boot_dependencies
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// Enqueue the boot scripts and styles
|
|
211
|
+
wp_enqueue_script( '{{PAGE_SLUG}}-prerequisites' );
|
|
212
|
+
wp_enqueue_script_module( '{{PAGE_SLUG}}' );
|
|
213
|
+
wp_enqueue_style( '{{PAGE_SLUG}}-prerequisites' );
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Output the HTML
|
|
217
|
+
?>
|
|
218
|
+
<!DOCTYPE html>
|
|
219
|
+
<html <?php language_attributes(); ?>>
|
|
220
|
+
<head>
|
|
221
|
+
<meta charset="<?php bloginfo( 'charset' ); ?>">
|
|
222
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
223
|
+
<title><?php echo esc_html( get_admin_page_title() ); ?></title>
|
|
224
|
+
<style>
|
|
225
|
+
html {
|
|
226
|
+
background: #f1f1f1;
|
|
227
|
+
color: #444;
|
|
228
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
|
229
|
+
font-size: 13px;
|
|
230
|
+
line-height: 1.4em;
|
|
231
|
+
}
|
|
232
|
+
body {
|
|
233
|
+
margin: 0;
|
|
234
|
+
}
|
|
235
|
+
#wpadminbar { display: none; }
|
|
236
|
+
</style>
|
|
237
|
+
<?php
|
|
238
|
+
global $hook_suffix;
|
|
239
|
+
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
|
240
|
+
$hook_suffix = '{{PAGE_SLUG}}';
|
|
241
|
+
|
|
242
|
+
// BEGIN see wp-admin/admin-header.php
|
|
243
|
+
print_admin_styles();
|
|
244
|
+
print_head_scripts();
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Fires in head section for a specific admin page.
|
|
248
|
+
*
|
|
249
|
+
* @since 2.1.0
|
|
250
|
+
*/
|
|
251
|
+
do_action( "admin_head-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Fires in head section for all admin pages.
|
|
255
|
+
*
|
|
256
|
+
* @since 2.1.0
|
|
257
|
+
*/
|
|
258
|
+
do_action( 'admin_head' );
|
|
259
|
+
// END see wp-admin/admin-header.php
|
|
260
|
+
?>
|
|
261
|
+
</head>
|
|
262
|
+
<body class="{{PAGE_SLUG}}">
|
|
263
|
+
<div id="{{PAGE_SLUG}}-app" style="height: 100vh; box-sizing: border-box;"></div>
|
|
264
|
+
<?php
|
|
265
|
+
// BEGIN see wp-admin/admin-footer.php
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Prints scripts or data before the default footer scripts.
|
|
269
|
+
*
|
|
270
|
+
* @since 1.2.0
|
|
271
|
+
*/
|
|
272
|
+
do_action( 'admin_footer', '' );
|
|
273
|
+
|
|
274
|
+
// Print import map first so it's available for inline scripts
|
|
275
|
+
wp_script_modules()->print_import_map();
|
|
276
|
+
print_footer_scripts();
|
|
277
|
+
wp_script_modules()->print_enqueued_script_modules();
|
|
278
|
+
wp_script_modules()->print_script_module_preloads();
|
|
279
|
+
wp_script_modules()->print_script_module_data();
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Prints scripts or data after the default footer scripts.
|
|
283
|
+
*
|
|
284
|
+
* @since 2.8.0
|
|
285
|
+
*/
|
|
286
|
+
do_action( "admin_footer-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
|
287
|
+
// END see wp-admin/admin-footer.php
|
|
288
|
+
?>
|
|
289
|
+
</body>
|
|
290
|
+
</html>
|
|
291
|
+
<?php
|
|
292
|
+
exit;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if ( ! function_exists( '{{PAGE_SLUG_UNDERSCORE}}_intercept_render' ) ) {
|
|
297
|
+
/**
|
|
298
|
+
* Intercept admin_init to render the page early.
|
|
299
|
+
* This bypasses the default WordPress admin template.
|
|
300
|
+
*/
|
|
301
|
+
function {{PAGE_SLUG_UNDERSCORE}}_intercept_render() {
|
|
302
|
+
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
303
|
+
if ( isset( $_GET['page'] ) && '{{PAGE_SLUG}}' === $_GET['page'] ) {
|
|
304
|
+
{{PAGE_SLUG_UNDERSCORE}}_render_page();
|
|
305
|
+
exit;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Hook the interceptor to admin_init
|
|
311
|
+
add_action( 'admin_init', '{{PAGE_SLUG_UNDERSCORE}}_intercept_render' );
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* Routes registration - Auto-generated by build process.
|
|
4
|
+
* Registers all routes on their respective page init hooks.
|
|
5
|
+
* Do not edit this file manually.
|
|
6
|
+
*
|
|
7
|
+
* @package {{PREFIX}}
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Load routes registry
|
|
11
|
+
$routes_file = __DIR__ . '/routes/index.php';
|
|
12
|
+
if ( ! file_exists( $routes_file ) ) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
$routes = require $routes_file;
|
|
17
|
+
|
|
18
|
+
// Group routes by page
|
|
19
|
+
$routes_by_page = array();
|
|
20
|
+
foreach ( $routes as $route ) {
|
|
21
|
+
$page_slug = $route['page'];
|
|
22
|
+
if ( ! isset( $routes_by_page[ $page_slug ] ) ) {
|
|
23
|
+
$routes_by_page[ $page_slug ] = array();
|
|
24
|
+
}
|
|
25
|
+
$routes_by_page[ $page_slug ][] = $route;
|
|
26
|
+
}
|
|
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 ) {
|
|
31
|
+
foreach ( $page_routes as $route ) {
|
|
32
|
+
$content_handle = null;
|
|
33
|
+
$route_handle = null;
|
|
34
|
+
|
|
35
|
+
// Register content module if exists
|
|
36
|
+
if ( $route['has_content'] ) {
|
|
37
|
+
$content_asset_path = __DIR__ . "/routes/{$route['name']}/content.min.asset.php";
|
|
38
|
+
if ( file_exists( $content_asset_path ) ) {
|
|
39
|
+
$content_asset = require $content_asset_path;
|
|
40
|
+
$content_handle = '{{HANDLE_PREFIX}}/routes/' . $route['name'] . '/content';
|
|
41
|
+
$extension = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.js' : '.min.js';
|
|
42
|
+
wp_register_script_module(
|
|
43
|
+
$content_handle,
|
|
44
|
+
plugins_url( 'build/routes/' . $route['name'] . '/content' . $extension, dirname( __FILE__ ) ),
|
|
45
|
+
$content_asset['module_dependencies'] ?? array(),
|
|
46
|
+
$content_asset['version'] ?? false
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Register route module if exists
|
|
52
|
+
if ( $route['has_route'] ) {
|
|
53
|
+
$route_asset_path = __DIR__ . "/routes/{$route['name']}/route.min.asset.php";
|
|
54
|
+
if ( file_exists( $route_asset_path ) ) {
|
|
55
|
+
$route_asset = require $route_asset_path;
|
|
56
|
+
$route_handle = '{{HANDLE_PREFIX}}/routes/' . $route['name'] . '/route';
|
|
57
|
+
$extension = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.js' : '.min.js';
|
|
58
|
+
wp_register_script_module(
|
|
59
|
+
$route_handle,
|
|
60
|
+
plugins_url( 'build/routes/' . $route['name'] . '/route' . $extension, dirname( __FILE__ ) ),
|
|
61
|
+
$route_asset['module_dependencies'] ?? array(),
|
|
62
|
+
$route_asset['version'] ?? false
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Register route with page
|
|
68
|
+
if ( function_exists( $register_function_name ) ) {
|
|
69
|
+
call_user_func( $register_function_name, $route['path'], $content_handle, $route_handle );
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Register hooks for both full-page and wp-admin modes
|
|
76
|
+
foreach ( $routes_by_page as $page_slug => $page_routes ) {
|
|
77
|
+
$page_slug_underscore = str_replace( '-', '_', $page_slug );
|
|
78
|
+
|
|
79
|
+
// Register all routes for full-page mode (page.php)
|
|
80
|
+
add_action(
|
|
81
|
+
"{$page_slug}_init",
|
|
82
|
+
$register_routes_callback( $page_routes, $page_slug_underscore, "register_{$page_slug_underscore}_route" )
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Register all routes for wp-admin mode (page-wp-admin.php)
|
|
86
|
+
add_action(
|
|
87
|
+
"{$page_slug}-wp-admin_init",
|
|
88
|
+
$register_routes_callback( $page_routes, $page_slug_underscore, "register_{$page_slug_underscore}_wp_admin_route" )
|
|
89
|
+
);
|
|
90
|
+
}
|