@10up/wp-vite-plugins 1.0.0-alpha.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/dist/index.js ADDED
@@ -0,0 +1,2194 @@
1
+ import { resolve, join, relative, dirname, extname } from 'path';
2
+ import { readdirSync, existsSync, readFileSync } from 'fs';
3
+ import crypto, { createHash } from 'crypto';
4
+ import fg from 'fast-glob';
5
+ import { init, parse } from 'es-module-lexer';
6
+ import MagicString from 'magic-string';
7
+ import { createRequire } from 'module';
8
+
9
+ // src/wp.ts
10
+
11
+ // src/php-templates.ts
12
+ var DEFAULT_REST_PRELOAD = [
13
+ "/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front",
14
+ ["/wp/v2/settings", "OPTIONS"]
15
+ ];
16
+ var ALL_PAGE_MODES = ["full-page", "wp-admin"];
17
+ function phpString(value) {
18
+ return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
19
+ }
20
+ function phpStringArray(values) {
21
+ if (values.length === 0) return "array()";
22
+ return `array( ${values.map(phpString).join(", ")} )`;
23
+ }
24
+ function toUnderscore(value) {
25
+ return value.replace(/-/g, "_");
26
+ }
27
+ function isValidPhpIdentifier(value) {
28
+ return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(toUnderscore(value));
29
+ }
30
+ function isValidSlug(value) {
31
+ return /^[a-z0-9-]+$/.test(value);
32
+ }
33
+ function pageNames(page, mode) {
34
+ const slugUnderscore = toUnderscore(page.slug);
35
+ const isWpAdmin = mode === "wp-admin";
36
+ return {
37
+ /** Script handle / module id / hook base: `settings-wp-admin`. */
38
+ handle: isWpAdmin ? `${page.slug}-wp-admin` : page.slug,
39
+ /** Function-name infix: `settings_wp_admin`. */
40
+ fn: isWpAdmin ? `${slugUnderscore}_wp_admin` : slugUnderscore,
41
+ /** Mount element id. */
42
+ mountId: isWpAdmin ? `${page.slug}-wp-admin-app` : `${page.slug}-app`
43
+ };
44
+ }
45
+ function assetFileList(page) {
46
+ const files = [];
47
+ for (const route of page.routes) {
48
+ if (route.hasContent) files.push(`${route.dir}/content.asset.php`);
49
+ if (route.hasRoute) files.push(`${route.dir}/route.asset.php`);
50
+ }
51
+ return files;
52
+ }
53
+ function preloadPathsPhp(entries, indent) {
54
+ if (entries.length === 0) return `${indent}// No REST paths configured.`;
55
+ return entries.map((entry) => {
56
+ const value = Array.isArray(entry) ? `array( ${phpString(entry[0])}, ${phpString(entry[1])} )` : phpString(entry);
57
+ return `${indent}${value},`;
58
+ }).join("\n");
59
+ }
60
+ function initModulesPhp(initModules, indent) {
61
+ if (initModules.length === 0) return `${indent}// No init modules configured.`;
62
+ return initModules.map(
63
+ (moduleId) => `${indent}$boot_dependencies[] = array( 'import' => 'static', 'id' => ${phpString(
64
+ moduleId
65
+ )} );`
66
+ ).join("\n");
67
+ }
68
+ function renderPageStateBlock(ctx, page, mode) {
69
+ const prefix = toUnderscore(ctx.prefix);
70
+ const { handle, fn } = pageNames(page, mode);
71
+ const routesGlobal = `$${prefix}_${fn}_routes`;
72
+ const menuItemsGlobal = `$${prefix}_${fn}_menu_items`;
73
+ const parentTypeParam = mode === "full-page" ? ", $parent_type = ''" : "";
74
+ const parentTypeDoc = mode === "full-page" ? " * @param string $parent_type Optional. Parent type: 'drilldown' or 'dropdown'.\n" : "";
75
+ const parentTypeBody = mode === "full-page" ? `
76
+ if ( ! empty( $parent_type ) && in_array( $parent_type, array( 'drilldown', 'dropdown' ), true ) ) {
77
+ $menu_item['parent_type'] = $parent_type;
78
+ }
79
+ ` : "";
80
+ const routesPad = " ".repeat(Math.max(0, menuItemsGlobal.length - routesGlobal.length));
81
+ return `// Global storage for ${handle} routes and menu items.
82
+ global ${routesGlobal}, ${menuItemsGlobal};
83
+ ${routesGlobal}${routesPad} = array();
84
+ ${menuItemsGlobal} = array();
85
+
86
+ /**
87
+ * Register a route for the ${handle} page.
88
+ *
89
+ * @param string $path Route path (e.g., '/types/$type/edit/$id').
90
+ * @param string|null $content_module Script module ID for content (stage/inspector).
91
+ * @param string|null $route_module Script module ID for route lifecycle hooks.
92
+ */
93
+ function ${prefix}_register_${fn}_route( $path, $content_module = null, $route_module = null ) {
94
+ global ${routesGlobal};
95
+
96
+ $route = array( 'path' => $path );
97
+ if ( ! empty( $content_module ) ) {
98
+ $route['content_module'] = $content_module;
99
+ }
100
+ if ( ! empty( $route_module ) ) {
101
+ $route['route_module'] = $route_module;
102
+ }
103
+
104
+ ${routesGlobal}[] = $route;
105
+ }
106
+
107
+ /**
108
+ * Register a menu item for the ${handle} page.
109
+ *
110
+ * @param string $id Menu item ID.
111
+ * @param string $label Display label.
112
+ * @param string $to Route path to navigate to.
113
+ * @param string $parent_id Optional. Parent menu item ID.
114
+ ${parentTypeDoc} */
115
+ function ${prefix}_register_${fn}_menu_item( $id, $label, $to, $parent_id = ''${parentTypeParam} ) {
116
+ global ${menuItemsGlobal};
117
+
118
+ $menu_item = array(
119
+ 'id' => $id,
120
+ 'label' => $label,
121
+ 'to' => $to,
122
+ );
123
+
124
+ if ( ! empty( $parent_id ) ) {
125
+ $menu_item['parent'] = $parent_id;
126
+ }
127
+ ${parentTypeBody}
128
+ ${menuItemsGlobal}[] = $menu_item;
129
+ }
130
+
131
+ /**
132
+ * Get all registered routes for the ${handle} page.
133
+ *
134
+ * @return array Array of route objects.
135
+ */
136
+ function ${prefix}_get_${fn}_routes() {
137
+ global ${routesGlobal};
138
+ return ${routesGlobal} ?? array();
139
+ }
140
+
141
+ /**
142
+ * Get all registered menu items for the ${handle} page.
143
+ *
144
+ * @return array Array of menu item objects.
145
+ */
146
+ function ${prefix}_get_${fn}_menu_items() {
147
+ global ${menuItemsGlobal};
148
+ return ${menuItemsGlobal} ?? array();
149
+ }
150
+
151
+ /**
152
+ * Preload REST API data for the ${handle} page.
153
+ * Automatically called during page rendering.
154
+ */
155
+ function ${prefix}_${fn}_preload_data() {
156
+ $preload_paths = array(
157
+ ${preloadPathsPhp(page.restPreload, " ")}
158
+ );
159
+
160
+ // Use rest_preload_api_request to gather the preloaded data.
161
+ $preload_data = array_reduce(
162
+ $preload_paths,
163
+ 'rest_preload_api_request',
164
+ array()
165
+ );
166
+
167
+ // Register the preloading middleware with wp-api-fetch.
168
+ wp_add_inline_script(
169
+ 'wp-api-fetch',
170
+ sprintf(
171
+ 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );',
172
+ wp_json_encode( $preload_data )
173
+ ),
174
+ 'after'
175
+ );
176
+ }
177
+ `;
178
+ }
179
+ function renderCarrierBlock(ctx, page, mode) {
180
+ const { handle } = pageNames(page, mode);
181
+ const assetFiles = assetFileList(page).map((file) => ` __DIR__ . '/../../${file}',`).join("\n");
182
+ return ` // Every classic script handle the page's modules read off window.* lives
183
+ // in their .asset.php sidecars; the carrier depends on the union of them.
184
+ $asset_files = array(
185
+ ${assetFiles}
186
+ );
187
+
188
+ $script_dependencies = array();
189
+ $version = false;
190
+ $has_assets = false;
191
+
192
+ foreach ( $asset_files as $asset_file ) {
193
+ if ( ! file_exists( $asset_file ) ) {
194
+ continue;
195
+ }
196
+ $has_assets = true;
197
+ $asset = require $asset_file;
198
+ if ( ! empty( $asset['dependencies'] ) ) {
199
+ $script_dependencies = array_merge( $script_dependencies, $asset['dependencies'] );
200
+ }
201
+ if ( false === $version && ! empty( $asset['version'] ) ) {
202
+ $version = $asset['version'];
203
+ }
204
+ }
205
+
206
+ // Core's boot module carries classic dependencies of its own.
207
+ $boot_asset_file = ABSPATH . WPINC . '/js/dist/script-modules/boot/index.min.asset.php';
208
+ if ( file_exists( $boot_asset_file ) ) {
209
+ $boot_asset = require $boot_asset_file;
210
+ if ( ! empty( $boot_asset['dependencies'] ) ) {
211
+ $script_dependencies = array_merge( $script_dependencies, $boot_asset['dependencies'] );
212
+ }
213
+ }
214
+
215
+ $script_dependencies = array_values( array_unique( $script_dependencies ) );
216
+
217
+ if ( $has_assets ) {
218
+ // This script serves two purposes:
219
+ // 1. It ensures all the globals that are made available to the modules are loaded.
220
+ // 2. It initializes the boot module as an inline script.
221
+ // The src is \`false\`, not \`''\`: an empty-src carrier script.
222
+ wp_register_script( '${handle}-prerequisites', false, $script_dependencies, $version, true );
223
+ `;
224
+ }
225
+ function renderBootGraphBlock(ctx, page, mode) {
226
+ const { handle } = pageNames(page, mode);
227
+ return ` // Register prerequisites style by filtering script dependencies to find registered styles.
228
+ $style_dependencies = array_filter(
229
+ $script_dependencies,
230
+ function ( $handle ) {
231
+ return wp_style_is( $handle, 'registered' );
232
+ }
233
+ );
234
+ wp_register_style( '${handle}-prerequisites', false, $style_dependencies, $version );
235
+
236
+ // Build dependencies for the ${handle} module.
237
+ $boot_dependencies = array(
238
+ array(
239
+ 'import' => 'static',
240
+ 'id' => '@wordpress/boot',
241
+ ),
242
+ );
243
+
244
+ // Add init modules as static dependencies.
245
+ ${initModulesPhp(page.initModules, " ")}
246
+
247
+ // Add all registered routes as dependencies.
248
+ foreach ( $routes as $route ) {
249
+ if ( isset( $route['route_module'] ) ) {
250
+ $boot_dependencies[] = array(
251
+ 'import' => 'static',
252
+ 'id' => $route['route_module'],
253
+ );
254
+ }
255
+ if ( isset( $route['content_module'] ) ) {
256
+ $boot_dependencies[] = array(
257
+ 'import' => 'dynamic',
258
+ 'id' => $route['content_module'],
259
+ );
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Filters the boot script-module dependencies for the
265
+ * ${handle} page.
266
+ *
267
+ * Surfaces extending this page can append entries to the boot
268
+ * dependency list. Each entry is an array with 'import' (string
269
+ * 'static' or 'dynamic') and 'id' (script-module handle) keys.
270
+ *
271
+ * @param array $boot_dependencies Boot dependencies for the page.
272
+ */
273
+ $boot_dependencies = apply_filters(
274
+ '${handle}_boot_dependencies',
275
+ $boot_dependencies
276
+ );
277
+
278
+ // Dummy script module to ensure dependencies are loaded.
279
+ wp_register_script_module(
280
+ '${handle}',
281
+ $build_constants['build_url'] . 'modules/${page.slug}/loader.js',
282
+ $boot_dependencies
283
+ );
284
+
285
+ // Enqueue the boot scripts and styles.
286
+ wp_enqueue_script( '${handle}-prerequisites' );
287
+ wp_enqueue_script_module( '${handle}' );
288
+ wp_enqueue_style( '${handle}-prerequisites' );
289
+ }
290
+ `;
291
+ }
292
+ function renderMenuBlock(ctx, page, mode) {
293
+ if (!page.menu) return "";
294
+ const prefix = toUnderscore(ctx.prefix);
295
+ const slugUnderscore = toUnderscore(page.slug);
296
+ const { handle, fn } = pageNames(page, mode);
297
+ const menu = page.menu;
298
+ const capability = phpString(menu.capability ?? "manage_options");
299
+ const title = `__( ${phpString(page.title)}, ${phpString(ctx.textdomain)} )`;
300
+ const renderCallback = phpString(`${prefix}_${fn}_render_page`);
301
+ const menuSlug = phpString(handle);
302
+ let call;
303
+ if (menu.parent === false || menu.parent === "toplevel") {
304
+ const icon = phpString(menu.icon ?? "dashicons-admin-generic");
305
+ const position = typeof menu.position === "number" ? String(menu.position) : "null";
306
+ call = ` add_menu_page(
307
+ ${title},
308
+ ${title},
309
+ ${capability},
310
+ ${menuSlug},
311
+ ${renderCallback},
312
+ ${icon},
313
+ ${position}
314
+ );`;
315
+ } else if (menu.parent === void 0 || menu.parent === "options-general.php") {
316
+ call = ` add_options_page(
317
+ ${title},
318
+ ${title},
319
+ ${capability},
320
+ ${menuSlug},
321
+ ${renderCallback}
322
+ );`;
323
+ } else {
324
+ call = ` add_submenu_page(
325
+ ${phpString(menu.parent)},
326
+ ${title},
327
+ ${title},
328
+ ${capability},
329
+ ${menuSlug},
330
+ ${renderCallback}
331
+ );`;
332
+ }
333
+ return `
334
+ /**
335
+ * Register the admin menu entry for the ${handle} page.
336
+ */
337
+ function ${prefix}_register_${slugUnderscore}_menu() {
338
+ ${call}
339
+ }
340
+ add_action( 'admin_menu', '${prefix}_register_${slugUnderscore}_menu' );
341
+ `;
342
+ }
343
+ function renderPagePhp(ctx, page) {
344
+ const prefix = toUnderscore(ctx.prefix);
345
+ const mode = "full-page";
346
+ const { handle, fn, mountId } = pageNames(page, mode);
347
+ return `<?php
348
+ /**
349
+ * Page: ${handle}
350
+ * Auto-generated by build process. Do not edit this file manually.
351
+ *
352
+ * Full-page takeover mode: renders its own HTML document and boots the app
353
+ * with \`init()\`, so the wp-admin chrome is replaced entirely.
354
+ *
355
+ * @package ${ctx.prefix}
356
+ */
357
+
358
+ ${renderPageStateBlock(ctx, page, mode)}
359
+ /**
360
+ * Render the ${handle} page.
361
+ * Call this function from add_menu_page or add_submenu_page.
362
+ */
363
+ function ${prefix}_${fn}_render_page() {
364
+ // Load build constants.
365
+ $build_constants = require __DIR__ . '/../../constants.php';
366
+
367
+ // Set current screen.
368
+ set_current_screen();
369
+
370
+ // Remove unwanted deprecated handler.
371
+ remove_action( 'admin_head', 'wp_admin_bar_header' );
372
+
373
+ // Remove unwanted scripts and styles that were enqueued during \`admin_init\`.
374
+ foreach ( wp_scripts()->queue as $script ) {
375
+ wp_dequeue_script( $script );
376
+ }
377
+ foreach ( wp_styles()->queue as $style ) {
378
+ wp_dequeue_style( $style );
379
+ }
380
+
381
+ /**
382
+ * Fires when the ${handle} page is initialized so extensions can register routes and menu items.
383
+ */
384
+ do_action( '${handle}_init' );
385
+
386
+ // Enqueue command palette assets for boot-based pages.
387
+ if ( function_exists( 'wp_enqueue_command_palette_assets' ) ) {
388
+ wp_enqueue_command_palette_assets();
389
+ }
390
+
391
+ // Preload REST API data.
392
+ ${prefix}_${fn}_preload_data();
393
+
394
+ // Get all registered routes and menu items.
395
+ $menu_items = ${prefix}_get_${fn}_menu_items();
396
+ $routes = ${prefix}_get_${fn}_routes();
397
+
398
+ ${renderCarrierBlock(ctx, page, mode)}
399
+ // Add inline script to initialize the app.
400
+ $init_modules = ${phpStringArray(page.initModules)};
401
+ wp_add_inline_script(
402
+ '${handle}-prerequisites',
403
+ sprintf(
404
+ 'import("@wordpress/boot").then(mod => mod.init({mountId: "%s", menuItems: %s, routes: %s, initModules: %s, dashboardLink: "%s"}));',
405
+ '${mountId}',
406
+ wp_json_encode( $menu_items, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
407
+ wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
408
+ wp_json_encode( $init_modules, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
409
+ esc_url( admin_url( '/' ) )
410
+ )
411
+ );
412
+
413
+ ${renderBootGraphBlock(ctx, page, mode)}
414
+ // Output the HTML.
415
+ ?>
416
+ <!DOCTYPE html>
417
+ <html <?php language_attributes(); ?>>
418
+ <head>
419
+ <meta charset="<?php bloginfo( 'charset' ); ?>">
420
+ <meta name="viewport" content="width=device-width, initial-scale=1">
421
+ <title><?php echo esc_html( get_admin_page_title() ); ?></title>
422
+ <style>
423
+ html {
424
+ background: #f1f1f1;
425
+ color: #444;
426
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
427
+ font-size: 13px;
428
+ line-height: 1.4em;
429
+ }
430
+ body {
431
+ margin: 0;
432
+ }
433
+ #wpadminbar { display: none; }
434
+ </style>
435
+ <?php
436
+ global $hook_suffix;
437
+ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
438
+ $hook_suffix = '${handle}';
439
+
440
+ // BEGIN see wp-admin/admin-header.php
441
+ print_admin_styles();
442
+ print_head_scripts();
443
+
444
+ /** This action is documented in wp-admin/admin-header.php */
445
+ do_action( "admin_head-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
446
+
447
+ /** This action is documented in wp-admin/admin-header.php */
448
+ do_action( 'admin_head' );
449
+ // END see wp-admin/admin-header.php
450
+ ?>
451
+ </head>
452
+ <body class="${handle}">
453
+ <div id="${mountId}" style="height: 100vh; box-sizing: border-box;"></div>
454
+ <?php
455
+ // BEGIN see wp-admin/admin-footer.php
456
+
457
+ /** This action is documented in wp-admin/admin-footer.php */
458
+ do_action( 'admin_footer', '' );
459
+
460
+ // Print import map first so it's available for inline scripts.
461
+ wp_script_modules()->print_import_map();
462
+ print_footer_scripts();
463
+ wp_script_modules()->print_enqueued_script_modules();
464
+ wp_script_modules()->print_script_module_preloads();
465
+ wp_script_modules()->print_script_module_data();
466
+
467
+ /** This action is documented in wp-admin/admin-footer.php */
468
+ do_action( "admin_footer-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
469
+ // END see wp-admin/admin-footer.php
470
+ ?>
471
+ </body>
472
+ </html>
473
+ <?php
474
+ exit;
475
+ }
476
+
477
+ /**
478
+ * Intercept admin_init to render the page early.
479
+ * This bypasses the default WordPress admin template.
480
+ */
481
+ function ${prefix}_${fn}_intercept_render() {
482
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
483
+ if ( isset( $_GET['page'] ) && '${handle}' === $_GET['page'] ) {
484
+ ${prefix}_${fn}_render_page();
485
+ exit;
486
+ }
487
+ }
488
+
489
+ // Hook the interceptor to admin_init.
490
+ add_action( 'admin_init', '${prefix}_${fn}_intercept_render' );
491
+ ${renderMenuBlock(ctx, page, mode)}`;
492
+ }
493
+ function renderPageWpAdminPhp(ctx, page) {
494
+ const prefix = toUnderscore(ctx.prefix);
495
+ const mode = "wp-admin";
496
+ const { handle, fn, mountId } = pageNames(page, mode);
497
+ return `<?php
498
+ /**
499
+ * Page: ${page.slug} (wp-admin integrated)
500
+ * Auto-generated by build process. Do not edit this file manually.
501
+ *
502
+ * This version integrates with the standard WordPress admin interface,
503
+ * keeping the wp-admin sidebar and scripts/styles intact.
504
+ *
505
+ * @package ${ctx.prefix}
506
+ */
507
+
508
+ ${renderPageStateBlock(ctx, page, mode)}
509
+ /**
510
+ * Enqueue scripts and styles for the ${handle} page.
511
+ * Hooked to admin_enqueue_scripts.
512
+ *
513
+ * @param string $hook_suffix The current admin page.
514
+ */
515
+ function ${prefix}_${fn}_enqueue_scripts( $hook_suffix ) {
516
+ // Check all possible ways this page can be accessed:
517
+ // 1. Menu page via admin.php?page=${handle}
518
+ // 2. Direct file whose screen ID is '${page.slug}'
519
+ $current_screen = get_current_screen();
520
+ $is_our_page = (
521
+ ( isset( $_GET['page'] ) && '${handle}' === $_GET['page'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended
522
+ ( $current_screen && '${page.slug}' === $current_screen->id )
523
+ );
524
+
525
+ if ( ! $is_our_page ) {
526
+ return;
527
+ }
528
+
529
+ // Load build constants.
530
+ $build_constants = require __DIR__ . '/../../constants.php';
531
+
532
+ /**
533
+ * Fires when the ${handle} page is initialized so extensions can register routes and menu items.
534
+ */
535
+ do_action( '${handle}_init' );
536
+
537
+ // Preload REST API data.
538
+ ${prefix}_${fn}_preload_data();
539
+
540
+ // Get all registered routes.
541
+ $routes = ${prefix}_get_${fn}_routes();
542
+
543
+ ${renderCarrierBlock(ctx, page, mode)}
544
+ $init_modules = ${phpStringArray(page.initModules)};
545
+
546
+ /*
547
+ * Add inline script to initialize the app using initSinglePage (no menuItems).
548
+ * The dynamic import is deferred until DOMContentLoaded so that all classic
549
+ * script dependencies of @wordpress/boot (wp-private-apis, wp-components,
550
+ * wp-theme, etc.) have finished parsing and executing before the boot module
551
+ * evaluates. Otherwise, a modulepreloaded @wordpress/boot can win the race
552
+ * against the classic-script-printing pass on fast CDN-fronted hosts in
553
+ * Chrome, evaluating before wp.theme.privateApis is defined and throwing
554
+ * "Cannot unlock an undefined object". See <https://core.trac.wordpress.org/ticket/65103>.
555
+ */
556
+ $init_js_function = <<<'JS'
557
+ ( mountId, routes, initModules ) => {
558
+ const run = async () => {
559
+ const mod = await import( "@wordpress/boot" );
560
+ mod.initSinglePage( { mountId, routes, initModules } );
561
+ };
562
+ if ( document.readyState === "loading" ) {
563
+ document.addEventListener( "DOMContentLoaded", run );
564
+ } else {
565
+ run();
566
+ }
567
+ }
568
+ JS;
569
+ wp_add_inline_script(
570
+ '${handle}-prerequisites',
571
+ sprintf(
572
+ '( %s )( %s, %s, %s );',
573
+ $init_js_function,
574
+ wp_json_encode( '${mountId}', JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
575
+ wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
576
+ wp_json_encode( $init_modules, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES )
577
+ )
578
+ );
579
+
580
+ ${renderBootGraphBlock(ctx, page, mode)}}
581
+
582
+ /**
583
+ * Render the ${handle} page.
584
+ * Call this function from add_menu_page or add_submenu_page.
585
+ * This renders within the normal WordPress admin interface.
586
+ */
587
+ function ${prefix}_${fn}_render_page() {
588
+ ?>
589
+ <style>
590
+ /* Critical styles to prevent layout shifts - inlined for immediate application */
591
+
592
+ #wpwrap {
593
+ overflow-y: auto;
594
+ }
595
+ body {
596
+ background: #fff;
597
+ }
598
+
599
+ /* Reset wp-admin padding */
600
+ #wpcontent {
601
+ padding-inline-start: 0;
602
+ }
603
+ #wpbody-content {
604
+ padding-bottom: 0;
605
+ }
606
+
607
+ /* Hide legacy admin elements */
608
+ #wpbody-content > div:not(.boot-layout-container):not(#screen-meta) {
609
+ display: none;
610
+ }
611
+ #wpfooter {
612
+ display: none;
613
+ }
614
+
615
+ /* Accessibility regions */
616
+ .a11y-speak-region {
617
+ inset-inline-start: -1px;
618
+ top: -1px;
619
+ }
620
+
621
+ /* Admin menu indicators */
622
+ ul#adminmenu a.wp-has-current-submenu::after,
623
+ ul#adminmenu > li.current > a.current::after {
624
+ border-inline-end-color: #fff;
625
+ }
626
+
627
+ /* Media frame fix */
628
+ .media-frame select.attachment-filters:last-of-type {
629
+ width: auto;
630
+ max-width: 100%;
631
+ }
632
+
633
+ /* Responsive overflow fix for #wpwrap */
634
+ @media (min-width: 782px) {
635
+ #wpwrap {
636
+ overflow-y: initial;
637
+ }
638
+ }
639
+ </style>
640
+ <div id="${mountId}" class="boot-layout-container"></div>
641
+ <?php
642
+ }
643
+
644
+ // Hook the enqueue function to admin_enqueue_scripts.
645
+ add_action( 'admin_enqueue_scripts', '${prefix}_${fn}_enqueue_scripts' );
646
+ ${renderMenuBlock(ctx, page, mode)}`;
647
+ }
648
+ function renderPagesPhp(ctx, pages) {
649
+ const files = [];
650
+ for (const page of pages) {
651
+ if (page.modes.includes("full-page")) {
652
+ files.push(` __DIR__ . '/pages/${page.slug}/page.php',`);
653
+ }
654
+ if (page.modes.includes("wp-admin")) {
655
+ files.push(` __DIR__ . '/pages/${page.slug}/page-wp-admin.php',`);
656
+ }
657
+ }
658
+ return `<?php
659
+ /**
660
+ * Pages loader - Auto-generated by build process.
661
+ * Do not edit this file manually.
662
+ *
663
+ * @package ${ctx.prefix}
664
+ */
665
+
666
+ $${toUnderscore(ctx.prefix)}_page_files = array(
667
+ ${files.join("\n")}
668
+ );
669
+
670
+ foreach ( $${toUnderscore(ctx.prefix)}_page_files as $page_file ) {
671
+ if ( file_exists( $page_file ) ) {
672
+ require_once $page_file;
673
+ }
674
+ }
675
+ `;
676
+ }
677
+ function renderBuildPhp(ctx) {
678
+ return `<?php
679
+ /**
680
+ * Main entry point for auto-generated asset registration.
681
+ * Auto-generated by build process. Do not edit this file manually.
682
+ *
683
+ * Require this file from your plugin or theme:
684
+ *
685
+ * require_once __DIR__ . '/dist/build.php';
686
+ *
687
+ * @package ${ctx.prefix}
688
+ */
689
+
690
+ // Load routes registration.
691
+ $routes_file = __DIR__ . '/routes.php';
692
+ if ( file_exists( $routes_file ) ) {
693
+ require_once $routes_file;
694
+ }
695
+
696
+ // Load pages registration.
697
+ $pages_file = __DIR__ . '/pages.php';
698
+ if ( file_exists( $pages_file ) ) {
699
+ require_once $pages_file;
700
+ }
701
+ `;
702
+ }
703
+ function renderConstantsPhp(ctx) {
704
+ return `<?php
705
+ /**
706
+ * Plugin constants - Auto-generated by build process.
707
+ * Do not edit this file manually.
708
+ *
709
+ * Returns an array of constants for use in other generated files.
710
+ *
711
+ * @package ${ctx.prefix}
712
+ */
713
+
714
+ return array(
715
+ 'version' => ${phpString(ctx.version)},
716
+ 'build_url' => ${ctx.baseUrl},
717
+ );
718
+ `;
719
+ }
720
+ function renderRouteRegistryPhp(ctx, pages) {
721
+ const rows = [];
722
+ for (const page of pages) {
723
+ for (const route of page.routes) {
724
+ rows.push(` array(
725
+ 'name' => ${phpString(route.name)},
726
+ 'path' => ${phpString(route.path)},
727
+ 'page' => ${phpString(page.slug)},
728
+ 'dir' => ${phpString(route.dir)},
729
+ 'has_route' => ${route.hasRoute ? "true" : "false"},
730
+ 'has_content' => ${route.hasContent ? "true" : "false"},
731
+ ),`);
732
+ }
733
+ }
734
+ return `<?php
735
+ /**
736
+ * Route registry - Auto-generated by build process.
737
+ * Do not edit this file manually.
738
+ *
739
+ * @package ${ctx.prefix}
740
+ */
741
+
742
+ return array(
743
+ ${rows.join("\n")}
744
+ );
745
+ `;
746
+ }
747
+ function renderRoutesPageFunctions(ctx, page) {
748
+ const prefix = toUnderscore(ctx.prefix);
749
+ const slugUnderscore = toUnderscore(page.slug);
750
+ const blocks = [];
751
+ for (const mode of ALL_PAGE_MODES) {
752
+ if (!page.modes.includes(mode)) continue;
753
+ const { handle, fn } = pageNames(page, mode);
754
+ const label = mode === "wp-admin" ? "wp-admin mode" : "full-page mode";
755
+ blocks.push(`/**
756
+ * Register routes for the ${page.slug} page (${label}).
757
+ */
758
+ function ${prefix}_register_${fn}_page_routes() {
759
+ global $${prefix}_${slugUnderscore}_routes_data;
760
+ ${prefix}_register_page_routes( $${prefix}_${slugUnderscore}_routes_data, '${prefix}_register_${fn}_route' );
761
+ }
762
+ add_action( '${handle}_init', '${prefix}_register_${fn}_page_routes' );`);
763
+ }
764
+ return `// Page-specific route registration functions for ${page.slug}.
765
+ ${blocks.join("\n\n")}`;
766
+ }
767
+ function renderRoutesPhp(ctx, pages) {
768
+ const prefix = toUnderscore(ctx.prefix);
769
+ const pageFunctions = pages.map((page) => renderRoutesPageFunctions(ctx, page)).join("\n\n");
770
+ return `<?php
771
+ /**
772
+ * Routes registration - Auto-generated by build process.
773
+ * Registers all routes on their respective page init hooks.
774
+ * Do not edit this file manually.
775
+ *
776
+ * @package ${ctx.prefix}
777
+ */
778
+
779
+ // Load routes registry.
780
+ $routes_file = __DIR__ . '/routes/registry.php';
781
+ if ( ! file_exists( $routes_file ) ) {
782
+ return;
783
+ }
784
+
785
+ $routes = require $routes_file;
786
+
787
+ // Group routes by page and store in globals for page-specific functions.
788
+ $routes_by_page = array();
789
+ foreach ( $routes as $route ) {
790
+ $page_slug = $route['page'];
791
+ if ( ! isset( $routes_by_page[ $page_slug ] ) ) {
792
+ $routes_by_page[ $page_slug ] = array();
793
+ }
794
+ $routes_by_page[ $page_slug ][] = $route;
795
+ }
796
+
797
+ // Store routes data in globals for each page.
798
+ foreach ( $routes_by_page as $page_slug => $page_routes ) {
799
+ $page_slug_underscore = str_replace( '-', '_', $page_slug );
800
+ $GLOBALS[ '${prefix}_' . $page_slug_underscore . '_routes_data' ] = $page_routes;
801
+ }
802
+
803
+ /**
804
+ * Generic helper function to register routes for a page.
805
+ *
806
+ * @param array $page_routes Array of route data for the page.
807
+ * @param string $register_function_name Name of the function to call for registering each route.
808
+ */
809
+ function ${prefix}_register_page_routes( $page_routes, $register_function_name ) {
810
+ // Load build constants.
811
+ $build_constants = require __DIR__ . '/constants.php';
812
+
813
+ foreach ( $page_routes as $route ) {
814
+ $content_handle = null;
815
+ $route_handle = null;
816
+ $module_prefix = ${phpString(ctx.handlePrefix)} . '/routes/' . $route['page'] . '/' . $route['name'];
817
+
818
+ // Register the content module when the build produced one.
819
+ if ( $route['has_content'] ) {
820
+ $content_asset_path = __DIR__ . '/' . $route['dir'] . '/content.asset.php';
821
+ if ( file_exists( $content_asset_path ) ) {
822
+ $content_asset = require $content_asset_path;
823
+ $content_handle = $module_prefix . '/content';
824
+ // Deregister first to override any previously registered version
825
+ // (e.g., Core's default modules when running as a plugin).
826
+ wp_deregister_script_module( $content_handle );
827
+ wp_register_script_module(
828
+ $content_handle,
829
+ $build_constants['build_url'] . $route['dir'] . '/content.js',
830
+ $content_asset['module_dependencies'] ?? array(),
831
+ $content_asset['version'] ?? false
832
+ );
833
+
834
+ // The page stylesheet, when the entry imported any CSS.
835
+ $content_style_path = __DIR__ . '/' . $route['dir'] . '/content.css';
836
+ if ( file_exists( $content_style_path ) ) {
837
+ $style_handle = str_replace( '/', '-', $content_handle );
838
+ wp_register_style(
839
+ $style_handle,
840
+ $build_constants['build_url'] . $route['dir'] . '/content.css',
841
+ array(),
842
+ $content_asset['version'] ?? false
843
+ );
844
+ wp_enqueue_style( $style_handle );
845
+ }
846
+ }
847
+ }
848
+
849
+ // Register the route module when the build produced one.
850
+ if ( $route['has_route'] ) {
851
+ $route_asset_path = __DIR__ . '/' . $route['dir'] . '/route.asset.php';
852
+ if ( file_exists( $route_asset_path ) ) {
853
+ $route_asset = require $route_asset_path;
854
+ $route_handle = $module_prefix . '/route';
855
+ // Deregister first to override any previously registered version
856
+ // (e.g., Core's default modules when running as a plugin).
857
+ wp_deregister_script_module( $route_handle );
858
+ wp_register_script_module(
859
+ $route_handle,
860
+ $build_constants['build_url'] . $route['dir'] . '/route.js',
861
+ $route_asset['module_dependencies'] ?? array(),
862
+ $route_asset['version'] ?? false
863
+ );
864
+ }
865
+ }
866
+
867
+ // Register route with page.
868
+ if ( function_exists( $register_function_name ) ) {
869
+ call_user_func( $register_function_name, $route['path'], $content_handle, $route_handle );
870
+ }
871
+ }
872
+ }
873
+
874
+ ${pageFunctions}
875
+ `;
876
+ }
877
+ function generatePhpFiles(ctx, pages) {
878
+ const files = [
879
+ { fileName: "build.php", source: renderBuildPhp(ctx) },
880
+ { fileName: "constants.php", source: renderConstantsPhp(ctx) }
881
+ ];
882
+ if (pages.length === 0) return files;
883
+ files.push({ fileName: "pages.php", source: renderPagesPhp(ctx, pages) });
884
+ for (const page of pages) {
885
+ if (page.modes.includes("full-page")) {
886
+ files.push({
887
+ fileName: `pages/${page.slug}/page.php`,
888
+ source: renderPagePhp(ctx, page)
889
+ });
890
+ }
891
+ if (page.modes.includes("wp-admin")) {
892
+ files.push({
893
+ fileName: `pages/${page.slug}/page-wp-admin.php`,
894
+ source: renderPageWpAdminPhp(ctx, page)
895
+ });
896
+ }
897
+ }
898
+ const hasRoutes = pages.some((page) => page.routes.length > 0);
899
+ if (hasRoutes) {
900
+ files.push({ fileName: "routes.php", source: renderRoutesPhp(ctx, pages) });
901
+ files.push({
902
+ fileName: "routes/registry.php",
903
+ source: renderRouteRegistryPhp(ctx, pages)
904
+ });
905
+ }
906
+ return files;
907
+ }
908
+
909
+ // src/wp-admin-pages.ts
910
+ var ENTRY_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js"];
911
+ var LOADER_SOURCE = "// Empty module loader for page dependencies.\n";
912
+ function wpAdminPages(options) {
913
+ const buildType = options.buildType ?? "script";
914
+ let pages = [];
915
+ let phpFiles = [];
916
+ return {
917
+ name: "wp-admin-pages",
918
+ enforce: "pre",
919
+ config(userConfig) {
920
+ if (buildType !== "module") return;
921
+ const adminPagesDirAbs = resolve(process.cwd(), options.adminPagesDir);
922
+ pages = discoverPages(adminPagesDirAbs);
923
+ if (pages.length === 0) return;
924
+ if (options.php) {
925
+ const projectRoot = resolve(
926
+ process.cwd(),
927
+ options.projectRoot ?? userConfig?.root ?? "."
928
+ );
929
+ const phpContext = resolvePhpContext(options.php, projectRoot);
930
+ phpFiles = generatePhpFiles(phpContext, pages.map(toGeneratedPage));
931
+ }
932
+ const input = {};
933
+ for (const page of pages) {
934
+ input[`modules/${page.dirName}/content`] = page.content;
935
+ if (page.route) input[`modules/${page.dirName}/route`] = page.route;
936
+ for (const subRoute of page.subRoutes) {
937
+ const base = `modules/${page.dirName}/routes/${subRoute.name}`;
938
+ if (subRoute.content) input[`${base}/content`] = subRoute.content;
939
+ if (subRoute.route) input[`${base}/route`] = subRoute.route;
940
+ }
941
+ }
942
+ return {
943
+ build: {
944
+ rollupOptions: {
945
+ input,
946
+ // Vite defaults app builds to `preserveEntrySignatures:
947
+ // false`, which tree-shakes an entry's exports away. A
948
+ // boot page IS its exports (`export const stage`,
949
+ // `export const route` — imported by
950
+ // `@wordpress/boot` at runtime), so they have to
951
+ // survive.
952
+ preserveEntrySignatures: "exports-only"
953
+ }
954
+ }
955
+ };
956
+ },
957
+ generateBundle(_outputOptions, bundle) {
958
+ if (buildType !== "module" || pages.length === 0) return;
959
+ for (const page of pages) {
960
+ this.emitFile({
961
+ type: "asset",
962
+ fileName: `modules/${page.dirName}/loader.js`,
963
+ source: LOADER_SOURCE
964
+ });
965
+ }
966
+ for (const file of phpFiles) {
967
+ this.emitFile({
968
+ type: "asset",
969
+ fileName: file.fileName,
970
+ source: file.source
971
+ });
972
+ }
973
+ if (!bundle) return;
974
+ const entryNames = /* @__PURE__ */ new Set();
975
+ for (const page of pages) {
976
+ entryNames.add(`modules/${page.dirName}/content`);
977
+ if (page.route) entryNames.add(`modules/${page.dirName}/route`);
978
+ for (const subRoute of page.subRoutes) {
979
+ const base = `modules/${page.dirName}/routes/${subRoute.name}`;
980
+ if (subRoute.content) entryNames.add(`${base}/content`);
981
+ if (subRoute.route) entryNames.add(`${base}/route`);
982
+ }
983
+ }
984
+ const referencingChunks = /* @__PURE__ */ new Map();
985
+ for (const [fileName, output] of Object.entries(bundle)) {
986
+ if (output.type !== "chunk") continue;
987
+ for (const cssFileName of output.viteMetadata?.importedCss ?? []) {
988
+ let chunks = referencingChunks.get(cssFileName);
989
+ if (!chunks) referencingChunks.set(cssFileName, chunks = /* @__PURE__ */ new Set());
990
+ chunks.add(fileName);
991
+ }
992
+ }
993
+ const pageEntries = [];
994
+ for (const [fileName, output] of Object.entries(bundle)) {
995
+ if (output.type !== "chunk" || !output.isEntry) continue;
996
+ if (!entryNames.has(output.name)) continue;
997
+ pageEntries.push([fileName, output]);
998
+ }
999
+ const pageEntryFileNames = new Set(pageEntries.map(([fileName]) => fileName));
1000
+ const reachableFromOtherEntries = /* @__PURE__ */ new Set();
1001
+ for (const [fileName, output] of Object.entries(bundle)) {
1002
+ if (output.type !== "chunk" || !output.isEntry) continue;
1003
+ if (pageEntryFileNames.has(fileName)) continue;
1004
+ for (const chunkFileName of reachableChunks(bundle, fileName)) {
1005
+ reachableFromOtherEntries.add(chunkFileName);
1006
+ }
1007
+ }
1008
+ const foldedIntoPages = /* @__PURE__ */ new Set();
1009
+ for (const [fileName, chunk] of pageEntries) {
1010
+ const cssFileNames = collectImportedCss(bundle, fileName);
1011
+ if (cssFileNames.length === 0) continue;
1012
+ const targetFileName = fileName.replace(/\.m?js$/, ".css");
1013
+ const sources = [];
1014
+ for (const cssFileName of cssFileNames) {
1015
+ const asset = bundle[cssFileName];
1016
+ if (!asset || asset.type !== "asset") continue;
1017
+ sources.push(assetSourceToString(asset.source));
1018
+ if (cssFileName !== targetFileName) foldedIntoPages.add(cssFileName);
1019
+ }
1020
+ if (sources.length === 0) continue;
1021
+ if (!(cssFileNames.length === 1 && cssFileNames[0] === targetFileName)) {
1022
+ this.emitFile({
1023
+ type: "asset",
1024
+ fileName: targetFileName,
1025
+ source: sources.join("\n")
1026
+ });
1027
+ }
1028
+ const importedCss = chunk.viteMetadata?.importedCss;
1029
+ if (importedCss) {
1030
+ for (const cssFileName of [...importedCss]) importedCss.delete(cssFileName);
1031
+ importedCss.add(targetFileName);
1032
+ }
1033
+ }
1034
+ for (const cssFileName of foldedIntoPages) {
1035
+ const referencing = referencingChunks.get(cssFileName) ?? /* @__PURE__ */ new Set();
1036
+ let pagesOnly = true;
1037
+ for (const chunkFileName of referencing) {
1038
+ if (reachableFromOtherEntries.has(chunkFileName)) pagesOnly = false;
1039
+ }
1040
+ if (!pagesOnly) continue;
1041
+ delete bundle[cssFileName];
1042
+ for (const output of Object.values(bundle)) {
1043
+ if (output.type !== "chunk") continue;
1044
+ output.viteMetadata?.importedCss.delete(cssFileName);
1045
+ }
1046
+ }
1047
+ }
1048
+ };
1049
+ }
1050
+ function discoverPages(adminPagesDirAbs) {
1051
+ let dirEntries;
1052
+ try {
1053
+ dirEntries = readdirSync(adminPagesDirAbs, { withFileTypes: true });
1054
+ } catch {
1055
+ return [];
1056
+ }
1057
+ const pages = [];
1058
+ for (const dirEntry of [...dirEntries].sort((a, b) => a.name.localeCompare(b.name))) {
1059
+ if (!dirEntry.isDirectory()) continue;
1060
+ const pageDir = join(adminPagesDirAbs, dirEntry.name);
1061
+ const content = resolveEntryFile(pageDir, "content");
1062
+ if (!content) continue;
1063
+ const route = resolveEntryFile(pageDir, "route");
1064
+ pages.push({
1065
+ dirName: dirEntry.name,
1066
+ content,
1067
+ ...route ? { route } : {},
1068
+ subRoutes: discoverSubRoutes(pageDir),
1069
+ pageConfig: readPageConfig(pageDir)
1070
+ });
1071
+ }
1072
+ return pages;
1073
+ }
1074
+ function discoverSubRoutes(pageDir) {
1075
+ const routesDir = join(pageDir, "routes");
1076
+ let dirEntries;
1077
+ try {
1078
+ dirEntries = readdirSync(routesDir, { withFileTypes: true });
1079
+ } catch {
1080
+ return [];
1081
+ }
1082
+ const subRoutes = [];
1083
+ for (const dirEntry of [...dirEntries].sort((a, b) => a.name.localeCompare(b.name))) {
1084
+ if (!dirEntry.isDirectory()) continue;
1085
+ const routeDir = join(routesDir, dirEntry.name);
1086
+ const content = resolveEntryFile(routeDir, "content");
1087
+ const route = resolveEntryFile(routeDir, "route");
1088
+ if (!content && !route) continue;
1089
+ subRoutes.push({
1090
+ name: dirEntry.name,
1091
+ path: readRoutePath(routeDir, dirEntry.name),
1092
+ ...content ? { content } : {},
1093
+ ...route ? { route } : {}
1094
+ });
1095
+ }
1096
+ return subRoutes;
1097
+ }
1098
+ function readPageConfig(pageDir) {
1099
+ const configPath = join(pageDir, "page.json");
1100
+ if (!existsSync(configPath)) return {};
1101
+ const raw = readJsonFile(configPath);
1102
+ const config = {};
1103
+ if (raw.title !== void 0) {
1104
+ if (typeof raw.title !== "string") {
1105
+ throw new Error(`wpAdminPages: ${configPath} \u2014 "title" must be a string.`);
1106
+ }
1107
+ config.title = raw.title;
1108
+ }
1109
+ if (raw.menu !== void 0) {
1110
+ if (typeof raw.menu !== "object" || raw.menu === null || Array.isArray(raw.menu)) {
1111
+ throw new Error(`wpAdminPages: ${configPath} \u2014 "menu" must be an object.`);
1112
+ }
1113
+ const menu = raw.menu;
1114
+ const parsed = {};
1115
+ if (menu.parent !== void 0) {
1116
+ if (typeof menu.parent !== "string" && menu.parent !== false) {
1117
+ throw new Error(
1118
+ `wpAdminPages: ${configPath} \u2014 "menu.parent" must be a string or false.`
1119
+ );
1120
+ }
1121
+ parsed.parent = menu.parent;
1122
+ }
1123
+ if (menu.capability !== void 0) {
1124
+ if (typeof menu.capability !== "string") {
1125
+ throw new Error(
1126
+ `wpAdminPages: ${configPath} \u2014 "menu.capability" must be a string.`
1127
+ );
1128
+ }
1129
+ parsed.capability = menu.capability;
1130
+ }
1131
+ if (menu.position !== void 0) {
1132
+ if (typeof menu.position !== "number" || !Number.isFinite(menu.position)) {
1133
+ throw new Error(`wpAdminPages: ${configPath} \u2014 "menu.position" must be a number.`);
1134
+ }
1135
+ parsed.position = menu.position;
1136
+ }
1137
+ if (menu.icon !== void 0) {
1138
+ if (typeof menu.icon !== "string") {
1139
+ throw new Error(`wpAdminPages: ${configPath} \u2014 "menu.icon" must be a string.`);
1140
+ }
1141
+ parsed.icon = menu.icon;
1142
+ }
1143
+ config.menu = parsed;
1144
+ }
1145
+ if (raw.init !== void 0) {
1146
+ if (!Array.isArray(raw.init) || raw.init.some((id) => typeof id !== "string")) {
1147
+ throw new Error(`wpAdminPages: ${configPath} \u2014 "init" must be an array of strings.`);
1148
+ }
1149
+ for (const moduleId of raw.init) {
1150
+ if (!/^[A-Za-z0-9@._/-]+$/.test(moduleId)) {
1151
+ throw new Error(
1152
+ `wpAdminPages: ${configPath} \u2014 "init" entry "${moduleId}" is not a valid script module id.`
1153
+ );
1154
+ }
1155
+ }
1156
+ config.init = raw.init;
1157
+ }
1158
+ if (raw.modes !== void 0) {
1159
+ if (!Array.isArray(raw.modes) || raw.modes.length === 0 || raw.modes.some((mode) => !ALL_PAGE_MODES.includes(mode))) {
1160
+ throw new Error(
1161
+ `wpAdminPages: ${configPath} \u2014 "modes" must be a non-empty array of ${ALL_PAGE_MODES.map(
1162
+ (mode) => `"${mode}"`
1163
+ ).join(" / ")}.`
1164
+ );
1165
+ }
1166
+ config.modes = raw.modes;
1167
+ }
1168
+ if (raw.restPreload !== void 0) {
1169
+ if (!Array.isArray(raw.restPreload)) {
1170
+ throw new Error(`wpAdminPages: ${configPath} \u2014 "restPreload" must be an array.`);
1171
+ }
1172
+ const entries = [];
1173
+ for (const entry of raw.restPreload) {
1174
+ if (typeof entry === "string") {
1175
+ entries.push(entry);
1176
+ continue;
1177
+ }
1178
+ if (Array.isArray(entry) && entry.length === 2 && entry.every((part) => typeof part === "string")) {
1179
+ entries.push([entry[0], entry[1]]);
1180
+ continue;
1181
+ }
1182
+ throw new Error(
1183
+ `wpAdminPages: ${configPath} \u2014 "restPreload" entries must be a path string or a [path, method] pair.`
1184
+ );
1185
+ }
1186
+ config.restPreload = entries;
1187
+ }
1188
+ return config;
1189
+ }
1190
+ function readRoutePath(routeDir, name) {
1191
+ const configPath = join(routeDir, "route.json");
1192
+ if (!existsSync(configPath)) return `/${name}`;
1193
+ const raw = readJsonFile(configPath);
1194
+ if (raw.path === void 0) return `/${name}`;
1195
+ if (typeof raw.path !== "string" || !raw.path.startsWith("/")) {
1196
+ throw new Error(`wpAdminPages: ${configPath} \u2014 "path" must be a string starting with "/".`);
1197
+ }
1198
+ return raw.path;
1199
+ }
1200
+ function readJsonFile(filePath) {
1201
+ let parsed;
1202
+ try {
1203
+ parsed = JSON.parse(readFileSync(filePath, "utf8"));
1204
+ } catch (error) {
1205
+ throw new Error(`wpAdminPages: could not parse ${filePath} \u2014 ${error.message}`, {
1206
+ cause: error
1207
+ });
1208
+ }
1209
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1210
+ throw new Error(`wpAdminPages: ${filePath} must contain a JSON object.`);
1211
+ }
1212
+ return parsed;
1213
+ }
1214
+ var PAGE_ROOT_ROUTE_NAME = "index";
1215
+ function toGeneratedPage(page) {
1216
+ if (!isValidSlug(page.dirName)) {
1217
+ throw new Error(
1218
+ `wpAdminPages: page directory "${page.dirName}" is not a valid page slug \u2014 use lowercase letters, digits and dashes.`
1219
+ );
1220
+ }
1221
+ const routes = [
1222
+ {
1223
+ name: PAGE_ROOT_ROUTE_NAME,
1224
+ path: "/",
1225
+ dir: `modules/${page.dirName}`,
1226
+ hasContent: true,
1227
+ hasRoute: !!page.route
1228
+ }
1229
+ ];
1230
+ for (const subRoute of page.subRoutes) {
1231
+ if (!isValidSlug(subRoute.name)) {
1232
+ throw new Error(
1233
+ `wpAdminPages: route directory "${page.dirName}/routes/${subRoute.name}" is not a valid route name \u2014 use lowercase letters, digits and dashes.`
1234
+ );
1235
+ }
1236
+ routes.push({
1237
+ name: subRoute.name,
1238
+ path: subRoute.path,
1239
+ dir: `modules/${page.dirName}/routes/${subRoute.name}`,
1240
+ hasContent: !!subRoute.content,
1241
+ hasRoute: !!subRoute.route
1242
+ });
1243
+ }
1244
+ const config = page.pageConfig;
1245
+ return {
1246
+ slug: page.dirName,
1247
+ title: config.title ?? titleCase(page.dirName),
1248
+ modes: config.modes ?? ALL_PAGE_MODES,
1249
+ initModules: config.init ?? [],
1250
+ restPreload: config.restPreload ?? DEFAULT_REST_PRELOAD,
1251
+ ...config.menu ? { menu: config.menu } : {},
1252
+ routes
1253
+ };
1254
+ }
1255
+ function titleCase(slug) {
1256
+ return slug.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
1257
+ }
1258
+ function resolvePhpContext(php, projectRoot) {
1259
+ const packageJsonPath = join(projectRoot, "package.json");
1260
+ let packageJson = {};
1261
+ if (existsSync(packageJsonPath)) {
1262
+ packageJson = readJsonFile(packageJsonPath);
1263
+ }
1264
+ const packageName = (packageJson.name ?? "").replace(/^@[^/]+\//, "");
1265
+ const prefix = php.prefix ?? packageJson.wpPlugin?.name ?? packageName;
1266
+ if (!prefix) {
1267
+ throw new Error(
1268
+ "wpAdminPages: PHP generation needs an identifier prefix \u2014 set `adminPages.php.prefix`, or `wpPlugin.name` / `name` in package.json."
1269
+ );
1270
+ }
1271
+ if (!isValidPhpIdentifier(prefix)) {
1272
+ throw new Error(
1273
+ `wpAdminPages: "${prefix}" is not a usable PHP identifier prefix. Use letters, digits, underscores and dashes (dashes become underscores), and don't start with a digit. Set \`adminPages.php.prefix\` or \`wpPlugin.name\` in package.json.`
1274
+ );
1275
+ }
1276
+ const handlePrefix = php.handlePrefix ?? packageJson.wpPlugin?.handlePrefix ?? prefix;
1277
+ if (!/^[A-Za-z0-9@._/-]+$/.test(handlePrefix)) {
1278
+ throw new Error(
1279
+ `wpAdminPages: "${handlePrefix}" is not a usable script module id prefix. Use letters, digits and \`@ . _ / -\`.`
1280
+ );
1281
+ }
1282
+ const textdomain = php.textdomain ?? "default";
1283
+ if (!/^[A-Za-z0-9._-]+$/.test(textdomain)) {
1284
+ throw new Error(
1285
+ `wpAdminPages: "${textdomain}" is not a usable text domain. Use letters, digits, dots, underscores and dashes.`
1286
+ );
1287
+ }
1288
+ return {
1289
+ prefix,
1290
+ handlePrefix,
1291
+ baseUrl: php.baseUrl ?? "plugin_dir_url( __FILE__ )",
1292
+ version: packageJson.version ?? "0.0.0",
1293
+ textdomain
1294
+ };
1295
+ }
1296
+ function resolveEntryFile(pageDir, basename) {
1297
+ for (const extension of ENTRY_EXTENSIONS) {
1298
+ const candidate = join(pageDir, `${basename}${extension}`);
1299
+ if (existsSync(candidate)) return candidate;
1300
+ }
1301
+ return null;
1302
+ }
1303
+ function reachableChunks(bundle, entryFileName) {
1304
+ const visited = /* @__PURE__ */ new Set();
1305
+ const queue = [entryFileName];
1306
+ while (queue.length > 0) {
1307
+ const fileName = queue.shift();
1308
+ if (visited.has(fileName)) continue;
1309
+ const output = bundle[fileName];
1310
+ if (!output || output.type !== "chunk") continue;
1311
+ visited.add(fileName);
1312
+ for (const imported of [...output.imports ?? [], ...output.dynamicImports ?? []]) {
1313
+ if (!visited.has(imported)) queue.push(imported);
1314
+ }
1315
+ }
1316
+ return visited;
1317
+ }
1318
+ function collectImportedCss(bundle, entryFileName) {
1319
+ const visited = /* @__PURE__ */ new Set();
1320
+ const seen = /* @__PURE__ */ new Set();
1321
+ const ordered = [];
1322
+ const visit = (fileName) => {
1323
+ if (visited.has(fileName)) return;
1324
+ visited.add(fileName);
1325
+ const output = bundle[fileName];
1326
+ if (!output || output.type !== "chunk") return;
1327
+ for (const imported of [...output.imports ?? [], ...output.dynamicImports ?? []]) {
1328
+ visit(imported);
1329
+ }
1330
+ for (const cssFileName of output.viteMetadata?.importedCss ?? []) {
1331
+ if (seen.has(cssFileName)) continue;
1332
+ seen.add(cssFileName);
1333
+ ordered.push(cssFileName);
1334
+ }
1335
+ };
1336
+ visit(entryFileName);
1337
+ return ordered;
1338
+ }
1339
+ function assetSourceToString(source) {
1340
+ return typeof source === "string" ? source : Buffer.from(source).toString("utf8");
1341
+ }
1342
+ var SCRIPT_FIELDS = ["editorScript", "script", "viewScript"];
1343
+ var MODULE_FIELDS = ["scriptModule", "viewScriptModule"];
1344
+ var STYLE_FIELDS = ["style", "editorStyle", "viewStyle"];
1345
+ var EMPTY_MODULE_PASS_ENTRY = "\0wp-blocks-empty-module-pass";
1346
+ function wpBlocks(options) {
1347
+ const buildType = options.buildType ?? "script";
1348
+ const emitManifest = options.emitManifest ?? true;
1349
+ const skip = new Set(options.skip ?? []);
1350
+ let blocksDirAbs;
1351
+ return {
1352
+ name: "wp-blocks",
1353
+ enforce: "pre",
1354
+ config() {
1355
+ blocksDirAbs = resolve(process.cwd(), options.blocksDir);
1356
+ return {
1357
+ build: {
1358
+ rollupOptions: {
1359
+ input: discoverEntries(blocksDirAbs, buildType, skip)
1360
+ }
1361
+ }
1362
+ };
1363
+ },
1364
+ options(inputOptions) {
1365
+ if (buildType !== "module") return null;
1366
+ const input = inputOptions.input;
1367
+ const isEmpty = input == null || Array.isArray(input) && input.length === 0 || typeof input === "object" && !Array.isArray(input) && Object.keys(input).length === 0;
1368
+ if (!isEmpty) return null;
1369
+ return {
1370
+ ...inputOptions,
1371
+ input: { "wp-blocks-empty-module-pass": EMPTY_MODULE_PASS_ENTRY }
1372
+ };
1373
+ },
1374
+ resolveId(source) {
1375
+ return source === EMPTY_MODULE_PASS_ENTRY ? source : null;
1376
+ },
1377
+ load(id) {
1378
+ return id === EMPTY_MODULE_PASS_ENTRY ? "export {};" : null;
1379
+ },
1380
+ async buildStart() {
1381
+ const blockJsons = await fg(`${blocksDirAbs.replace(/\\/g, "/")}/**/block.json`, {
1382
+ absolute: true
1383
+ });
1384
+ for (const file of blockJsons) {
1385
+ this.addWatchFile(file);
1386
+ }
1387
+ },
1388
+ async generateBundle(_outputOptions, bundle) {
1389
+ for (const [fileName, item] of Object.entries(bundle ?? {})) {
1390
+ if (item.type === "chunk" && item.facadeModuleId === EMPTY_MODULE_PASS_ENTRY) {
1391
+ delete bundle[fileName];
1392
+ }
1393
+ }
1394
+ if (buildType !== "script") return;
1395
+ const files = await fg(
1396
+ [
1397
+ `${blocksDirAbs.replace(/\\/g, "/")}/**/block.json`,
1398
+ `${blocksDirAbs.replace(/\\/g, "/")}/**/*.php`
1399
+ ],
1400
+ { absolute: true }
1401
+ );
1402
+ for (const file of files) {
1403
+ const relativePath = relative(blocksDirAbs, file).replace(/\\/g, "/");
1404
+ const isBlockJson = relativePath.endsWith("block.json");
1405
+ const source = isBlockJson ? transformBlockJson(readFileSync(file, "utf8"), file) : readFileSync(file);
1406
+ this.emitFile({
1407
+ type: "asset",
1408
+ fileName: `blocks/${relativePath}`,
1409
+ source
1410
+ });
1411
+ }
1412
+ if (emitManifest) {
1413
+ const manifest = buildBlocksManifest(blocksDirAbs, skip);
1414
+ if (manifest) {
1415
+ this.emitFile({
1416
+ type: "asset",
1417
+ fileName: "blocks-manifest.php",
1418
+ source: manifest
1419
+ });
1420
+ }
1421
+ }
1422
+ }
1423
+ };
1424
+ }
1425
+ function transformBlockJson(raw, blockJsonPath) {
1426
+ let parsed;
1427
+ try {
1428
+ parsed = JSON.parse(raw);
1429
+ } catch {
1430
+ return raw;
1431
+ }
1432
+ const scriptFields = [
1433
+ "editorScript",
1434
+ "script",
1435
+ "viewScript",
1436
+ "scriptModule",
1437
+ "viewScriptModule"
1438
+ ];
1439
+ const styleFields = ["style", "editorStyle", "viewStyle"];
1440
+ const rewriteRef = (ref, builtExtension) => {
1441
+ if (typeof ref !== "string" || !ref.startsWith("file:")) return ref;
1442
+ return ref.replace(/\.(tsx?|jsx?|s?css|sass)$/, builtExtension);
1443
+ };
1444
+ const explicitVersion = typeof parsed.version === "string" && parsed.version.length > 0;
1445
+ if (!explicitVersion) {
1446
+ const blockDir = dirname(blockJsonPath);
1447
+ const versionHash = hashStyleContent(parsed, ["style", "viewStyle"], blockDir);
1448
+ if (versionHash) {
1449
+ parsed.version = versionHash;
1450
+ }
1451
+ }
1452
+ for (const field of scriptFields) {
1453
+ const fieldValue = parsed[field];
1454
+ if (Array.isArray(fieldValue)) {
1455
+ parsed[field] = fieldValue.map((ref) => rewriteRef(ref, ".js"));
1456
+ } else {
1457
+ parsed[field] = rewriteRef(fieldValue, ".js");
1458
+ }
1459
+ }
1460
+ for (const field of styleFields) {
1461
+ const fieldValue = parsed[field];
1462
+ if (Array.isArray(fieldValue)) {
1463
+ parsed[field] = fieldValue.map((ref) => rewriteRef(ref, ".css"));
1464
+ } else {
1465
+ parsed[field] = rewriteRef(fieldValue, ".css");
1466
+ }
1467
+ }
1468
+ return JSON.stringify(parsed, null, " ") + "\n";
1469
+ }
1470
+ function hashStyleContent(metadata, fields, blockDir) {
1471
+ const hash = createHash("sha256");
1472
+ let hashedAnyFile = false;
1473
+ for (const field of fields) {
1474
+ const fieldValue = metadata[field];
1475
+ const refs = Array.isArray(fieldValue) ? fieldValue : [fieldValue];
1476
+ for (const ref of refs) {
1477
+ if (typeof ref !== "string" || !ref.startsWith("file:")) continue;
1478
+ const relativePath = ref.replace(/^file:/, "");
1479
+ const absolutePath = join(blockDir, relativePath);
1480
+ try {
1481
+ hash.update(readFileSync(absolutePath));
1482
+ hashedAnyFile = true;
1483
+ } catch {
1484
+ }
1485
+ }
1486
+ }
1487
+ return hashedAnyFile ? hash.digest("hex") : "";
1488
+ }
1489
+ function buildBlocksManifest(blocksDirAbs, skip) {
1490
+ const blockJsons = fg.sync(`${blocksDirAbs.replace(/\\/g, "/")}/**/block.json`, {
1491
+ absolute: true
1492
+ });
1493
+ const entries = [];
1494
+ for (const file of blockJsons) {
1495
+ try {
1496
+ const metadata = JSON.parse(transformBlockJson(readFileSync(file, "utf8"), file));
1497
+ const blockDirName = relative(blocksDirAbs, file).replace(/[\\/]block\.json$/, "").replace(/\\/g, "/");
1498
+ if (skip.has(blockDirName)) continue;
1499
+ entries.push([blockDirName, metadata]);
1500
+ } catch {
1501
+ }
1502
+ }
1503
+ if (entries.length === 0) return null;
1504
+ const body = entries.map(([blockDirName, metadata]) => ` '${blockDirName}' => ${phpExport(metadata, 1)},`).join("\n");
1505
+ return `<?php
1506
+ // This file is generated. Do not modify it manually.
1507
+ return array(
1508
+ ${body}
1509
+ );
1510
+ `;
1511
+ }
1512
+ function phpExport(value, indent) {
1513
+ const pad = " ".repeat(indent);
1514
+ const padInner = " ".repeat(indent + 1);
1515
+ if (value === null) return "null";
1516
+ if (typeof value === "boolean") return value ? "true" : "false";
1517
+ if (typeof value === "number") return String(value);
1518
+ if (typeof value === "string") return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
1519
+ if (Array.isArray(value)) {
1520
+ if (value.length === 0) return "array()";
1521
+ const items = value.map((item) => `${padInner}${phpExport(item, indent + 1)}`).join(",\n");
1522
+ return `array(
1523
+ ${items},
1524
+ ${pad})`;
1525
+ }
1526
+ if (typeof value === "object") {
1527
+ const keys = Object.keys(value);
1528
+ if (keys.length === 0) return "array()";
1529
+ const items = keys.map((key) => {
1530
+ const entryValue = value[key];
1531
+ const phpKey = `'${key.replace(/'/g, "\\'")}'`;
1532
+ return `${padInner}${phpKey} => ${phpExport(entryValue, indent + 1)}`;
1533
+ }).join(",\n");
1534
+ return `array(
1535
+ ${items},
1536
+ ${pad})`;
1537
+ }
1538
+ return "null";
1539
+ }
1540
+ function discoverEntries(blocksDirAbs, buildType, skip) {
1541
+ const entries = {};
1542
+ const blockJsons = fg.sync(`${blocksDirAbs.replace(/\\/g, "/")}/**/block.json`, {
1543
+ absolute: true
1544
+ });
1545
+ for (const blockJsonPath of blockJsons) {
1546
+ const blockDirName = relative(blocksDirAbs, blockJsonPath).replace(/[\\/]block\.json$/, "").replace(/\\/g, "/");
1547
+ if (skip.has(blockDirName)) continue;
1548
+ let metadata;
1549
+ try {
1550
+ metadata = JSON.parse(readFileSync(blockJsonPath, "utf8"));
1551
+ } catch {
1552
+ continue;
1553
+ }
1554
+ const fields = buildType === "module" ? MODULE_FIELDS : [...SCRIPT_FIELDS, ...STYLE_FIELDS];
1555
+ const refs = [];
1556
+ for (const field of fields) {
1557
+ const fieldValue = metadata[field];
1558
+ if (typeof fieldValue === "string") refs.push(fieldValue);
1559
+ else if (Array.isArray(fieldValue))
1560
+ refs.push(...fieldValue.filter((item) => typeof item === "string"));
1561
+ }
1562
+ for (const ref of refs) {
1563
+ if (!ref.startsWith("file:")) continue;
1564
+ const declaredPath = ref.replace(/^file:/, "");
1565
+ const pathWithoutExtension = declaredPath.replace(
1566
+ /\.(js|ts|tsx|jsx|css|scss|sass)$/,
1567
+ ""
1568
+ );
1569
+ const candidatePaths = [
1570
+ join(dirname(blockJsonPath), `${pathWithoutExtension}.tsx`),
1571
+ join(dirname(blockJsonPath), `${pathWithoutExtension}.ts`),
1572
+ join(dirname(blockJsonPath), `${pathWithoutExtension}.jsx`),
1573
+ join(dirname(blockJsonPath), `${pathWithoutExtension}.js`),
1574
+ join(dirname(blockJsonPath), `${pathWithoutExtension}.scss`),
1575
+ join(dirname(blockJsonPath), `${pathWithoutExtension}.sass`),
1576
+ join(dirname(blockJsonPath), `${pathWithoutExtension}.css`)
1577
+ ];
1578
+ const sourcePath = candidatePaths.find((candidate) => existsSync(candidate));
1579
+ if (!sourcePath) {
1580
+ console.warn(`[wp-blocks] no source file found for ${ref} in ${blockJsonPath}`);
1581
+ continue;
1582
+ }
1583
+ const entryName = `blocks/${relative(blocksDirAbs, sourcePath).replace(extname(sourcePath), "").replace(/\\/g, "/")}`;
1584
+ entries[entryName] = sourcePath;
1585
+ }
1586
+ }
1587
+ return entries;
1588
+ }
1589
+ function wpBlockStyles(options) {
1590
+ let stylesDirAbs;
1591
+ return {
1592
+ name: "wp-block-styles",
1593
+ enforce: "pre",
1594
+ config() {
1595
+ stylesDirAbs = resolve(process.cwd(), options.blocksStylesDir);
1596
+ const stylesheets = fg.sync(
1597
+ `${stylesDirAbs.replace(/\\/g, "/")}/**/*.{css,scss,sass}`,
1598
+ { absolute: true }
1599
+ );
1600
+ const entries = {};
1601
+ for (const filePath of stylesheets) {
1602
+ const blockName = relative(stylesDirAbs, filePath).replace(extname(filePath), "").replace(/\\/g, "/");
1603
+ entries[`autoenqueue/${blockName}`] = filePath;
1604
+ }
1605
+ return {
1606
+ build: {
1607
+ rollupOptions: {
1608
+ input: entries
1609
+ }
1610
+ }
1611
+ };
1612
+ }
1613
+ };
1614
+ }
1615
+ var DEFAULT_PATTERN = "**/*.{jpg,jpeg,png,gif,webp,avif,ico,svg,eot,ttf,woff,woff2,otf}";
1616
+ function wpCopyAssets(options) {
1617
+ const pattern = options.pattern ?? DEFAULT_PATTERN;
1618
+ const buildType = options.buildType ?? "script";
1619
+ let copyDirAbs;
1620
+ return {
1621
+ name: `wp-copy-assets(${buildType})`,
1622
+ enforce: "pre",
1623
+ config() {
1624
+ copyDirAbs = resolve(process.cwd(), options.copyAssetsDir);
1625
+ },
1626
+ async generateBundle() {
1627
+ if (buildType !== "script") return;
1628
+ const matches = await fg(pattern, {
1629
+ cwd: copyDirAbs,
1630
+ absolute: true,
1631
+ onlyFiles: true
1632
+ });
1633
+ for (const file of matches) {
1634
+ const relativePath = relative(copyDirAbs, file).replace(/\\/g, "/");
1635
+ this.emitFile({
1636
+ type: "asset",
1637
+ fileName: relativePath,
1638
+ source: readFileSync(file)
1639
+ });
1640
+ }
1641
+ }
1642
+ };
1643
+ }
1644
+ var cache = /* @__PURE__ */ new Map();
1645
+ function readPackageMetadata(packageName, projectRoot) {
1646
+ const cacheKey = `${packageName}@${projectRoot}`;
1647
+ if (cache.has(cacheKey)) {
1648
+ return cache.get(cacheKey) ?? null;
1649
+ }
1650
+ try {
1651
+ const require2 = createRequire(join(projectRoot, "package.json"));
1652
+ const packageJsonPath = require2.resolve(`${packageName}/package.json`);
1653
+ const metadata = JSON.parse(readFileSync(packageJsonPath, "utf8"));
1654
+ cache.set(cacheKey, metadata);
1655
+ return metadata;
1656
+ } catch {
1657
+ cache.set(cacheKey, null);
1658
+ return null;
1659
+ }
1660
+ }
1661
+ function splitImport(specifier) {
1662
+ const parts = specifier.split("/");
1663
+ if (specifier.startsWith("@")) {
1664
+ if (parts.length < 2) return { name: specifier, subpath: null };
1665
+ const name = `${parts[0]}/${parts[1]}`;
1666
+ const subpath = parts.length > 2 ? parts.slice(2).join("/") : null;
1667
+ return { name, subpath };
1668
+ }
1669
+ return {
1670
+ name: parts[0] ?? specifier,
1671
+ subpath: parts.length > 1 ? parts.slice(1).join("/") : null
1672
+ };
1673
+ }
1674
+ function isScriptModule(metadata, subpath) {
1675
+ const exports = metadata.wpScriptModuleExports;
1676
+ if (!exports) return false;
1677
+ if (subpath === null) {
1678
+ if (typeof exports === "string") return true;
1679
+ return typeof exports === "object" && "." in exports;
1680
+ }
1681
+ return typeof exports === "object" && `./${subpath}` in exports;
1682
+ }
1683
+ function camelize(kebabCaseName) {
1684
+ return kebabCaseName.replace(/-(.)/g, (_match, letter) => letter.toUpperCase());
1685
+ }
1686
+
1687
+ // src/wp-externals.ts
1688
+ var DEFAULT_VENDOR = {
1689
+ react: { global: "React", handle: "react" },
1690
+ "react-dom": { global: "ReactDOM", handle: "react-dom" },
1691
+ "react-dom/client": { global: "ReactDOM", handle: "react-dom" },
1692
+ "react/jsx-runtime": { global: "ReactJSXRuntime", handle: "react-jsx-runtime" },
1693
+ "react/jsx-dev-runtime": { global: "ReactJSXRuntime", handle: "react-jsx-runtime" },
1694
+ moment: { global: "moment", handle: "moment" },
1695
+ jquery: { global: "jQuery", handle: "jquery" },
1696
+ lodash: { global: "lodash", handle: "lodash" },
1697
+ "lodash-es": { global: "lodash", handle: "lodash" }
1698
+ };
1699
+ var CJS_GLOBAL_SHIM_PREFIX = "\0wp-external-global:";
1700
+ var JS_IDENTIFIER = "[A-Za-z_$][\\w$]*";
1701
+ function rewriteImport(importStatement, globalAccess) {
1702
+ const match = importStatement.match(/^\s*import\s+(.*?)\s+from\s+['"][^'"]+['"]\s*;?\s*$/s);
1703
+ if (!match || !match[1]) return "";
1704
+ const bindings = match[1].trim();
1705
+ const namespaceImport = bindings.match(new RegExp(`^\\*\\s+as\\s+(${JS_IDENTIFIER})$`));
1706
+ if (namespaceImport) return `const ${namespaceImport[1]} = ${globalAccess};`;
1707
+ const defaultWithNamed = bindings.match(
1708
+ new RegExp(`^(${JS_IDENTIFIER})\\s*,\\s*\\{(.+)\\}$`, "s")
1709
+ );
1710
+ if (defaultWithNamed) {
1711
+ return [
1712
+ `const ${defaultWithNamed[1]} = (${globalAccess} && ${globalAccess}.default) || ${globalAccess};`,
1713
+ `const { ${namedImportsToDestructuring(defaultWithNamed[2].trim())} } = ${globalAccess};`
1714
+ ].join(" ");
1715
+ }
1716
+ if (bindings.startsWith("{")) {
1717
+ const namedImports = bindings.replace(/^\{|\}$/g, "").trim();
1718
+ return `const { ${namedImportsToDestructuring(namedImports)} } = ${globalAccess};`;
1719
+ }
1720
+ if (new RegExp(`^${JS_IDENTIFIER}$`).test(bindings)) {
1721
+ return `const ${bindings} = (${globalAccess} && ${globalAccess}.default) || ${globalAccess};`;
1722
+ }
1723
+ return null;
1724
+ }
1725
+ function namedImportsToDestructuring(namedImports) {
1726
+ const aliasPattern = new RegExp(`^(${JS_IDENTIFIER})\\s+as\\s+(${JS_IDENTIFIER})$`);
1727
+ return namedImports.split(",").map((binding) => binding.trim()).filter(Boolean).map((binding) => {
1728
+ const aliased = binding.match(aliasPattern);
1729
+ return aliased ? `${aliased[1]}: ${aliased[2]}` : binding;
1730
+ }).join(", ");
1731
+ }
1732
+ function wpExternals(options = {}) {
1733
+ const projectRoot = options.projectRoot ?? process.cwd();
1734
+ const externalNamespaces = options.externalNamespaces ?? {};
1735
+ const vendorExternals = options.additionalExternals ?? DEFAULT_VENDOR;
1736
+ const buildType = options.buildType ?? "script";
1737
+ const moduleEntryMatchers = options.moduleEntryMatchers ?? [];
1738
+ const wpBuildAssetMatchers = options.wpBuildAssetMatchers ?? [];
1739
+ const scriptModuleNamespaces = options.scriptModuleNamespaces ?? [];
1740
+ function matchesScriptModuleNamespace(packageName) {
1741
+ if (!packageName.startsWith("@")) return false;
1742
+ const slashIndex = packageName.indexOf("/");
1743
+ if (slashIndex < 0) return false;
1744
+ const namespace = packageName.slice(1, slashIndex);
1745
+ for (const pattern of scriptModuleNamespaces) {
1746
+ if (pattern.endsWith("*")) {
1747
+ if (namespace.startsWith(pattern.slice(0, -1))) return true;
1748
+ } else if (namespace === pattern) {
1749
+ return true;
1750
+ }
1751
+ }
1752
+ return false;
1753
+ }
1754
+ function buildModeForFile(moduleId) {
1755
+ const filePath = moduleId.split("?")[0] ?? moduleId;
1756
+ for (const pattern of moduleEntryMatchers) {
1757
+ if (filePath.includes(pattern)) return "module";
1758
+ }
1759
+ return buildType;
1760
+ }
1761
+ function usesWpBuildAssetShape(facadeModuleId) {
1762
+ if (!facadeModuleId) return false;
1763
+ const filePath = (facadeModuleId.split("?")[0] ?? facadeModuleId).replace(/\\/g, "/");
1764
+ for (const pattern of wpBuildAssetMatchers) {
1765
+ if (filePath.includes(pattern.replace(/\\/g, "/"))) return true;
1766
+ }
1767
+ return false;
1768
+ }
1769
+ function resolveExternal(specifier) {
1770
+ if (vendorExternals[specifier]) {
1771
+ const vendor = vendorExternals[specifier];
1772
+ return {
1773
+ global: vendor.global,
1774
+ handle: vendor.handle,
1775
+ specifier,
1776
+ moduleEligible: false,
1777
+ scriptEligible: true
1778
+ };
1779
+ }
1780
+ const { name, subpath } = splitImport(specifier);
1781
+ if (name.startsWith("@wordpress/")) {
1782
+ const meta = readPackageMetadata(name, projectRoot);
1783
+ if (!meta) {
1784
+ return null;
1785
+ }
1786
+ const shortName = name.slice("@wordpress/".length);
1787
+ const moduleEligible = isScriptModule(meta, subpath);
1788
+ const scriptEligible = !!meta.wpScript;
1789
+ if (!moduleEligible && !scriptEligible) {
1790
+ return null;
1791
+ }
1792
+ return {
1793
+ global: scriptEligible ? `wp.${camelize(shortName)}` : null,
1794
+ handle: scriptEligible ? `wp-${shortName}` : null,
1795
+ specifier,
1796
+ moduleEligible,
1797
+ scriptEligible
1798
+ };
1799
+ }
1800
+ for (const [namespace, config] of Object.entries(externalNamespaces)) {
1801
+ if (name.startsWith(`@${namespace}/`)) {
1802
+ const shortName = name.slice(`@${namespace}/`.length);
1803
+ return {
1804
+ global: `${config.global}.${camelize(shortName)}`,
1805
+ handle: `${config.handlePrefix}-${shortName}`,
1806
+ specifier,
1807
+ moduleEligible: false,
1808
+ scriptEligible: true
1809
+ };
1810
+ }
1811
+ }
1812
+ if (matchesScriptModuleNamespace(name)) {
1813
+ return {
1814
+ global: null,
1815
+ handle: null,
1816
+ specifier,
1817
+ moduleEligible: true,
1818
+ scriptEligible: false
1819
+ };
1820
+ }
1821
+ return null;
1822
+ }
1823
+ function moduleImportKind(specifier, isDynamic) {
1824
+ if (specifier === "@wordpress/interactivity") return "static";
1825
+ return isDynamic ? "dynamic" : "static";
1826
+ }
1827
+ const staticDepsByModule = /* @__PURE__ */ new Map();
1828
+ const dynamicDepsByModule = /* @__PURE__ */ new Map();
1829
+ const handleDepsByModule = /* @__PURE__ */ new Map();
1830
+ function recordDeps(map, moduleId, values) {
1831
+ let set = map.get(moduleId);
1832
+ if (!set) {
1833
+ set = /* @__PURE__ */ new Set();
1834
+ map.set(moduleId, set);
1835
+ }
1836
+ for (const value of values) set.add(value);
1837
+ }
1838
+ const resolverPlugin = {
1839
+ name: `wp-externals-resolver(${buildType})`,
1840
+ enforce: "pre",
1841
+ resolveId(source, _importer, options2) {
1842
+ const isRequireCall = options2?.kind === "require-call";
1843
+ const external = resolveExternal(source);
1844
+ if (external) {
1845
+ if (isRequireCall) {
1846
+ if (external.global) {
1847
+ return CJS_GLOBAL_SHIM_PREFIX + source;
1848
+ }
1849
+ if (typeof this?.warn === "function") {
1850
+ this.warn(
1851
+ `${source} is required from CommonJS but is module-only (no wpScript in its package.json, so there is no window global to read). The bundled \`require('${source}')\` will throw at runtime \u2014 import it from ESM instead.`
1852
+ );
1853
+ }
1854
+ }
1855
+ return { id: source, external: true };
1856
+ }
1857
+ const commonjsMatch = source.match(/^\0([^?]+)\?commonjs-external$/);
1858
+ if (commonjsMatch && commonjsMatch[1] && resolveExternal(commonjsMatch[1])) {
1859
+ return source;
1860
+ }
1861
+ return null;
1862
+ },
1863
+ load(id) {
1864
+ if (id.startsWith(CJS_GLOBAL_SHIM_PREFIX)) {
1865
+ const specifier = id.slice(CJS_GLOBAL_SHIM_PREFIX.length);
1866
+ const external2 = resolveExternal(specifier);
1867
+ if (!external2 || !external2.global) return null;
1868
+ if (external2.handle) recordDeps(handleDepsByModule, id, [external2.handle]);
1869
+ return `module.exports = (typeof window !== "undefined" ? window.${external2.global} : {});
1870
+ `;
1871
+ }
1872
+ const commonjsMatch = id.match(/^\0([^?]+)\?commonjs-external$/);
1873
+ if (!commonjsMatch || !commonjsMatch[1]) return null;
1874
+ const external = resolveExternal(commonjsMatch[1]);
1875
+ if (!external) return null;
1876
+ if (buildModeForFile(id) === "module" && external.moduleEligible) {
1877
+ return `export default {};`;
1878
+ }
1879
+ if (!external.global) {
1880
+ return `export default {};`;
1881
+ }
1882
+ if (external.handle) recordDeps(handleDepsByModule, id, [external.handle]);
1883
+ return `const __wpExternal = (typeof window !== "undefined" ? window.${external.global} : {}); export default __wpExternal;`;
1884
+ }
1885
+ };
1886
+ const transformPlugin = {
1887
+ name: `wp-externals(${buildType})`,
1888
+ enforce: "post",
1889
+ async transform(code, id) {
1890
+ if (!code.includes("@wordpress/") && !Object.keys(vendorExternals).some((packageName) => code.includes(packageName)) && !Object.keys(externalNamespaces).some(
1891
+ (namespace) => code.includes(`@${namespace}/`)
1892
+ ) && !scriptModuleNamespaces.some(
1893
+ (pattern) => code.includes(`@${pattern.endsWith("*") ? pattern.slice(0, -1) : pattern}`)
1894
+ )) {
1895
+ return null;
1896
+ }
1897
+ await init;
1898
+ let imports;
1899
+ try {
1900
+ [imports] = parse(code);
1901
+ } catch {
1902
+ return null;
1903
+ }
1904
+ if (imports.length === 0) return null;
1905
+ const magicString = new MagicString(code);
1906
+ let codeChanged = false;
1907
+ const fileMode = buildModeForFile(id);
1908
+ const staticDeps = /* @__PURE__ */ new Set();
1909
+ const dynamicDeps = /* @__PURE__ */ new Set();
1910
+ const handleDeps = /* @__PURE__ */ new Set();
1911
+ const rewriteToGlobal = (external, importRecord) => {
1912
+ const importStatement = code.slice(importRecord.ss, importRecord.se);
1913
+ const rewritten = rewriteImport(importStatement, `(window.${external.global})`);
1914
+ if (rewritten === null) return false;
1915
+ magicString.overwrite(importRecord.ss, importRecord.se, rewritten);
1916
+ if (external.handle) handleDeps.add(external.handle);
1917
+ return true;
1918
+ };
1919
+ for (const importRecord of imports) {
1920
+ const importedSpecifier = importRecord.n;
1921
+ if (importedSpecifier === void 0) continue;
1922
+ const external = resolveExternal(importedSpecifier);
1923
+ if (!external) continue;
1924
+ const isDynamic = importRecord.d > -1;
1925
+ if (fileMode === "script") {
1926
+ if (isDynamic || !external.scriptEligible) {
1927
+ if (!external.scriptEligible && !isDynamic) {
1928
+ this.warn(
1929
+ `${importedSpecifier} is module-only (no wpScript in its package.json) but imported into a script-mode entry. Move this import to a viewScriptModule entry, or this script will fail at runtime.`
1930
+ );
1931
+ }
1932
+ continue;
1933
+ }
1934
+ if (rewriteToGlobal(external, importRecord)) codeChanged = true;
1935
+ } else if (!external.moduleEligible) {
1936
+ if (isDynamic) {
1937
+ this.warn(
1938
+ `${importedSpecifier} is not Script-Module-eligible (no wpScriptModuleExports in its package.json) and can't be dynamically imported from a module-mode entry. Import it statically (it gets read off its window global) or move it to a script-mode entry.`
1939
+ );
1940
+ continue;
1941
+ }
1942
+ if (rewriteToGlobal(external, importRecord)) codeChanged = true;
1943
+ } else {
1944
+ const importKind = moduleImportKind(importedSpecifier, isDynamic);
1945
+ if (importKind === "static") {
1946
+ staticDeps.add(external.specifier);
1947
+ } else {
1948
+ dynamicDeps.add(external.specifier);
1949
+ }
1950
+ }
1951
+ }
1952
+ if (staticDeps.size > 0) recordDeps(staticDepsByModule, id, staticDeps);
1953
+ if (dynamicDeps.size > 0) recordDeps(dynamicDepsByModule, id, dynamicDeps);
1954
+ if (handleDeps.size > 0) recordDeps(handleDepsByModule, id, handleDeps);
1955
+ if (!codeChanged) return null;
1956
+ return {
1957
+ code: magicString.toString(),
1958
+ map: magicString.generateMap({ hires: true })
1959
+ };
1960
+ },
1961
+ generateBundle(_outputOptions, bundle) {
1962
+ const chunksByFileName = /* @__PURE__ */ new Map();
1963
+ for (const [fileName, output] of Object.entries(bundle)) {
1964
+ if (output.type === "chunk") chunksByFileName.set(fileName, output);
1965
+ }
1966
+ for (const [fileName, output] of Object.entries(bundle)) {
1967
+ if (output.type !== "chunk" || !output.isEntry) continue;
1968
+ if (!fileName.endsWith(".js") && !fileName.endsWith(".mjs")) continue;
1969
+ const reachableChunks2 = [];
1970
+ const visited = /* @__PURE__ */ new Set([fileName]);
1971
+ const queue = [fileName];
1972
+ while (queue.length > 0) {
1973
+ const currentFileName = queue.shift();
1974
+ const chunk = chunksByFileName.get(currentFileName);
1975
+ if (!chunk) continue;
1976
+ reachableChunks2.push(chunk);
1977
+ for (const imported of [
1978
+ ...chunk.imports ?? [],
1979
+ ...chunk.dynamicImports ?? []
1980
+ ]) {
1981
+ if (!chunksByFileName.has(imported) || visited.has(imported)) continue;
1982
+ visited.add(imported);
1983
+ queue.push(imported);
1984
+ }
1985
+ }
1986
+ const entryHandles = /* @__PURE__ */ new Set();
1987
+ const entryStaticDeps = /* @__PURE__ */ new Set();
1988
+ const entryDynamicDeps = /* @__PURE__ */ new Set();
1989
+ for (const chunk of reachableChunks2) {
1990
+ for (const moduleId of Object.keys(chunk.modules ?? {})) {
1991
+ const handles2 = handleDepsByModule.get(moduleId);
1992
+ if (handles2) for (const handle of handles2) entryHandles.add(handle);
1993
+ const staticDeps = staticDepsByModule.get(moduleId);
1994
+ if (staticDeps) for (const dep of staticDeps) entryStaticDeps.add(dep);
1995
+ const dynamicDeps = dynamicDepsByModule.get(moduleId);
1996
+ if (dynamicDeps) for (const dep of dynamicDeps) entryDynamicDeps.add(dep);
1997
+ }
1998
+ }
1999
+ let entryMode = buildType;
2000
+ for (const moduleId of Object.keys(output.modules ?? {})) {
2001
+ if (buildModeForFile(moduleId) === "module") entryMode = "module";
2002
+ }
2003
+ const hash = crypto.createHash("md5").update(output.code);
2004
+ const cssFileNames = /* @__PURE__ */ new Set();
2005
+ for (const chunk of reachableChunks2) {
2006
+ for (const cssFileName of chunk.viteMetadata?.importedCss ?? []) {
2007
+ cssFileNames.add(cssFileName);
2008
+ }
2009
+ }
2010
+ for (const cssFileName of [...cssFileNames].sort()) {
2011
+ const cssAsset = bundle[cssFileName];
2012
+ if (!cssAsset || cssAsset.type !== "asset") continue;
2013
+ hash.update(cssAsset.source);
2014
+ }
2015
+ const version = hash.digest("hex").slice(0, 20);
2016
+ const handles = [...entryHandles].sort();
2017
+ const phpList = (items) => items.length > 0 ? ` ${items.join(", ")} ` : "";
2018
+ const moduleImportKinds = /* @__PURE__ */ new Map();
2019
+ for (const dep of entryDynamicDeps) moduleImportKinds.set(dep, "dynamic");
2020
+ for (const dep of entryStaticDeps) moduleImportKinds.set(dep, "static");
2021
+ const sortedModuleDeps = [...moduleImportKinds.entries()].sort(
2022
+ ([a], [b]) => a < b ? -1 : a > b ? 1 : 0
2023
+ );
2024
+ const fields = [];
2025
+ if (usesWpBuildAssetShape(output.facadeModuleId)) {
2026
+ fields.push(
2027
+ `'dependencies' => array(${phpList(handles.map((handle) => `'${handle}'`))})`
2028
+ );
2029
+ const moduleDependencies = sortedModuleDeps.map(
2030
+ ([dep, kind]) => `array( 'id' => '${dep}', 'import' => '${kind}' )`
2031
+ );
2032
+ if (moduleDependencies.length > 0) {
2033
+ fields.push(
2034
+ `'module_dependencies' => array(${phpList(moduleDependencies)})`
2035
+ );
2036
+ }
2037
+ } else if (entryMode === "module") {
2038
+ const moduleDependencies = sortedModuleDeps.map(
2039
+ ([dep, kind]) => kind === "static" ? `'${dep}'` : `array( 'id' => '${dep}', 'import' => 'dynamic' )`
2040
+ );
2041
+ fields.push(`'dependencies' => array(${phpList(moduleDependencies)})`);
2042
+ if (handles.length > 0) {
2043
+ fields.push(
2044
+ `'script_dependencies' => array(${phpList(
2045
+ handles.map((handle) => `'${handle}'`)
2046
+ )})`
2047
+ );
2048
+ }
2049
+ } else {
2050
+ fields.push(
2051
+ `'dependencies' => array(${phpList(handles.map((handle) => `'${handle}'`))})`
2052
+ );
2053
+ }
2054
+ fields.push(`'version' => '${version}'`);
2055
+ if (entryMode === "module") fields.push(`'type' => 'module'`);
2056
+ this.emitFile({
2057
+ type: "asset",
2058
+ fileName: fileName.replace(/\.m?js$/, ".asset.php"),
2059
+ source: `<?php return array( ${fields.join(", ")} );
2060
+ `
2061
+ });
2062
+ }
2063
+ }
2064
+ };
2065
+ return [resolverPlugin, transformPlugin];
2066
+ }
2067
+
2068
+ // src/wp-script-iife.ts
2069
+ function wpScriptIife() {
2070
+ return {
2071
+ name: "wp-script-iife",
2072
+ async writeBundle(options, bundle) {
2073
+ const { readFile, writeFile } = await import('fs/promises');
2074
+ const { join: join4 } = await import('path');
2075
+ const outDir = options.dir ?? ".";
2076
+ for (const [fileName, output] of Object.entries(bundle)) {
2077
+ if (output.type !== "chunk") continue;
2078
+ if (!fileName.endsWith(".js") && !fileName.endsWith(".mjs")) continue;
2079
+ if (!output.isEntry && !output.isDynamicEntry) continue;
2080
+ const filePath = join4(outDir, fileName);
2081
+ let chunkSource = await readFile(filePath, "utf8");
2082
+ const sideEffectImport = /^\s*import\s+["'][^"']+["'];?\s*\n?/;
2083
+ while (sideEffectImport.test(chunkSource)) {
2084
+ chunkSource = chunkSource.replace(sideEffectImport, "");
2085
+ }
2086
+ await writeFile(filePath, `(()=>{
2087
+ ${chunkSource}
2088
+ })();
2089
+ `);
2090
+ }
2091
+ }
2092
+ };
2093
+ }
2094
+
2095
+ // src/wp-clean-css-chunks.ts
2096
+ function wpCleanCssChunks() {
2097
+ return {
2098
+ name: "wp-clean-css-chunks",
2099
+ generateBundle(_options, bundle) {
2100
+ for (const [fileName, output] of Object.entries(bundle)) {
2101
+ if (output.type !== "chunk") continue;
2102
+ const facadeModuleId = output.facadeModuleId ?? "";
2103
+ if (/\.(s?css|sass)$/.test(facadeModuleId)) {
2104
+ delete bundle[fileName];
2105
+ }
2106
+ }
2107
+ }
2108
+ };
2109
+ }
2110
+
2111
+ // src/wp.ts
2112
+ function wp(options = {}) {
2113
+ const buildType = options.buildType ?? "script";
2114
+ const plugins = [];
2115
+ if (options.blocks !== false) {
2116
+ const blocksOptions = typeof options.blocks === "object" ? options.blocks : void 0;
2117
+ const blocksDir = blocksOptions?.blocksDir ?? options.blocksDir;
2118
+ if (blocksDir) {
2119
+ plugins.push(
2120
+ wpBlocks({
2121
+ blocksDir,
2122
+ buildType,
2123
+ ...blocksOptions
2124
+ })
2125
+ );
2126
+ }
2127
+ }
2128
+ if (options.blockStyles !== false) {
2129
+ const blockStylesOptions = typeof options.blockStyles === "object" ? options.blockStyles : void 0;
2130
+ const blocksStylesDir = blockStylesOptions?.blocksStylesDir ?? options.blocksStylesDir;
2131
+ if (blocksStylesDir) {
2132
+ plugins.push(
2133
+ wpBlockStyles({
2134
+ blocksStylesDir,
2135
+ ...blockStylesOptions
2136
+ })
2137
+ );
2138
+ }
2139
+ }
2140
+ if (options.copyAssets !== false) {
2141
+ const copyAssetsOptions = typeof options.copyAssets === "object" ? options.copyAssets : void 0;
2142
+ const copyAssetsDir = copyAssetsOptions?.copyAssetsDir ?? options.copyAssetsDir;
2143
+ if (copyAssetsDir) {
2144
+ plugins.push(
2145
+ wpCopyAssets({
2146
+ copyAssetsDir,
2147
+ buildType,
2148
+ ...copyAssetsOptions
2149
+ })
2150
+ );
2151
+ }
2152
+ }
2153
+ let activeAdminPagesDir;
2154
+ if (options.adminPages !== false) {
2155
+ const adminPagesOptions = typeof options.adminPages === "object" ? options.adminPages : void 0;
2156
+ const adminPagesDir = adminPagesOptions?.adminPagesDir ?? options.adminPagesDir;
2157
+ if (adminPagesDir) {
2158
+ activeAdminPagesDir = adminPagesDir;
2159
+ plugins.push(
2160
+ wpAdminPages({
2161
+ adminPagesDir,
2162
+ buildType,
2163
+ ...adminPagesOptions
2164
+ })
2165
+ );
2166
+ }
2167
+ }
2168
+ if (options.externals !== false) {
2169
+ const externalsOptions = typeof options.externals === "object" ? options.externals : void 0;
2170
+ const wpBuildAssetMatchers = [...externalsOptions?.wpBuildAssetMatchers ?? []];
2171
+ if (activeAdminPagesDir) {
2172
+ const matcher = resolve(process.cwd(), activeAdminPagesDir).replace(/\\/g, "/");
2173
+ if (!wpBuildAssetMatchers.includes(matcher)) wpBuildAssetMatchers.push(matcher);
2174
+ }
2175
+ plugins.push(
2176
+ ...wpExternals({
2177
+ buildType,
2178
+ ...externalsOptions,
2179
+ ...wpBuildAssetMatchers.length > 0 ? { wpBuildAssetMatchers } : {}
2180
+ })
2181
+ );
2182
+ }
2183
+ if (buildType === "script" && options.iife !== false) {
2184
+ plugins.push(wpScriptIife());
2185
+ }
2186
+ if (options.cleanCssChunks !== false) {
2187
+ plugins.push(wpCleanCssChunks());
2188
+ }
2189
+ return plugins;
2190
+ }
2191
+
2192
+ export { wp, wpAdminPages, wpBlockStyles, wpBlocks, wpCleanCssChunks, wpCopyAssets, wpExternals, wpScriptIife };
2193
+ //# sourceMappingURL=index.js.map
2194
+ //# sourceMappingURL=index.js.map