@10up/wp-vite-plugins 1.0.0-alpha.1 → 1.0.0-alpha.3
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/README.md +53 -3
- package/dist/build.d.ts +2 -2
- package/dist/build.js +18 -1
- package/dist/build.js.map +1 -1
- package/dist/cli.js +85 -7
- package/dist/cli.js.map +1 -1
- package/dist/dev.d.ts +35 -0
- package/dist/dev.js +384 -0
- package/dist/dev.js.map +1 -0
- package/dist/index.d.ts +153 -2
- package/dist/index.js +1645 -158
- package/dist/index.js.map +1 -1
- package/dist/{runner-WUYCS-nx.d.ts → runner-jUVlAHlZ.d.ts} +8 -3
- package/dist/watch.d.ts +2 -2
- package/dist/watch.js +18 -1
- package/dist/watch.js.map +1 -1
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { resolve, join, relative, dirname, extname } from 'path';
|
|
2
|
-
import { readdirSync, existsSync, readFileSync, realpathSync } from 'fs';
|
|
1
|
+
import { resolve, join, relative, dirname, extname, isAbsolute } from 'path';
|
|
2
|
+
import { readdirSync, existsSync, readFileSync, rmSync, mkdirSync, writeFileSync, realpathSync } from 'fs';
|
|
3
3
|
import crypto, { createHash } from 'crypto';
|
|
4
4
|
import fg from 'fast-glob';
|
|
5
5
|
import { init, parse } from 'es-module-lexer';
|
|
@@ -874,6 +874,599 @@ function ${prefix}_register_page_routes( $page_routes, $register_function_name )
|
|
|
874
874
|
${pageFunctions}
|
|
875
875
|
`;
|
|
876
876
|
}
|
|
877
|
+
function renderDevServerPhp(ctx) {
|
|
878
|
+
const prefixUnderscore = toUnderscore(ctx.prefix);
|
|
879
|
+
const fn = `${prefixUnderscore}_wp_vite_dev`;
|
|
880
|
+
const client = `${ctx.prefix}-vite-client`;
|
|
881
|
+
const checkConstant = `${prefixUnderscore.toUpperCase()}_WP_VITE_CHECK_DEV_SERVER`;
|
|
882
|
+
const checkFilter = `${prefixUnderscore}_wp_vite_check_dev_server`;
|
|
883
|
+
return `<?php
|
|
884
|
+
/**
|
|
885
|
+
* Vite dev-server bridge.
|
|
886
|
+
* Auto-generated by build process. Do not edit this file manually.
|
|
887
|
+
*
|
|
888
|
+
* Require it once, unconditionally, next to the rest of your dist requires:
|
|
889
|
+
*
|
|
890
|
+
* $wp_vite_dev = __DIR__ . '/dist/dev-server.php';
|
|
891
|
+
* if ( file_exists( $wp_vite_dev ) ) {
|
|
892
|
+
* require_once $wp_vite_dev;
|
|
893
|
+
* }
|
|
894
|
+
*
|
|
895
|
+
* With no dev server running it costs a single \`file_exists()\` and returns.
|
|
896
|
+
* When ${ctx.hotFile} is sitting next to it, it repoints every script,
|
|
897
|
+
* stylesheet and Script Module WordPress would have loaded out of this
|
|
898
|
+
* directory at the running Vite server instead.
|
|
899
|
+
*
|
|
900
|
+
* @package ${ctx.prefix}
|
|
901
|
+
*/
|
|
902
|
+
|
|
903
|
+
/*
|
|
904
|
+
* Two loaders, a theme that also boots its own mu-plugin: requiring this file
|
|
905
|
+
* twice must not redeclare anything or stack a second set of filters.
|
|
906
|
+
*
|
|
907
|
+
* The guard has to *wrap* the declarations rather than \`return\` above them.
|
|
908
|
+
* PHP binds an unconditionally-declared top-level function when it compiles the
|
|
909
|
+
* file \u2014 before a single statement in it runs \u2014 so an early return would fatal
|
|
910
|
+
* on the second require anyway. Conditional declarations are bound at run time,
|
|
911
|
+
* which is what makes the check mean something.
|
|
912
|
+
*/
|
|
913
|
+
if ( ! function_exists( '${fn}_hot' ) ) :
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Whether ${fn}_hot() should verify the dev server is actually reachable
|
|
917
|
+
* before trusting the hot file's mere presence.
|
|
918
|
+
*
|
|
919
|
+
* Off by default. Every other check in this bridge is a single
|
|
920
|
+
* \`file_exists()\` because a running dev server keeps that contract
|
|
921
|
+
* deliberately \u2014 write on 'listening', delete on exit, exit handlers bound
|
|
922
|
+
* once. The one hole is a \`kill -9\`'d process, which leaves the file behind
|
|
923
|
+
* pointing at a socket nothing is listening on any more. Closing that hole
|
|
924
|
+
* costs a real HTTP request \u2014 timed short, but still a round trip \u2014 on every
|
|
925
|
+
* single request this bridge participates in, so it stays opt-in rather than
|
|
926
|
+
* something every site pays for.
|
|
927
|
+
*
|
|
928
|
+
* Enable it with the \`${checkConstant}\` constant, or the
|
|
929
|
+
* \`${checkFilter}\` filter for anything more dynamic (e.g. only inside a
|
|
930
|
+
* container where \`kill -9\`ing the dev server is routine).
|
|
931
|
+
*
|
|
932
|
+
* @return bool
|
|
933
|
+
*/
|
|
934
|
+
function ${fn}_should_verify_dev_server() {
|
|
935
|
+
$default = defined( '${checkConstant}' ) && constant( '${checkConstant}' );
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Filters whether the dev-server bridge verifies the running server is
|
|
939
|
+
* reachable before trusting the hot file.
|
|
940
|
+
*
|
|
941
|
+
* @param bool $verify Whether to probe. Default from the
|
|
942
|
+
* ${checkConstant} constant, else false.
|
|
943
|
+
*/
|
|
944
|
+
return (bool) apply_filters( '${checkFilter}', $default );
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* The decoded hot file, or null when no dev server is running.
|
|
949
|
+
*
|
|
950
|
+
* The presence of the file is the entire protocol \u2014 a running server writes it
|
|
951
|
+
* on 'listening' and deletes it on exit. Cached in a static because this is on
|
|
952
|
+
* the request path for every enqueued asset.
|
|
953
|
+
*
|
|
954
|
+
* @return array|null Array with 'origin', 'base' and 'plugins' keys, or null.
|
|
955
|
+
*/
|
|
956
|
+
function ${fn}_hot() {
|
|
957
|
+
static $hot = false;
|
|
958
|
+
|
|
959
|
+
if ( false !== $hot ) {
|
|
960
|
+
return $hot;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
$hot = null;
|
|
964
|
+
$hot_file = __DIR__ . '/${ctx.hotFile}';
|
|
965
|
+
|
|
966
|
+
if ( ! file_exists( $hot_file ) ) {
|
|
967
|
+
return $hot;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
|
|
971
|
+
$contents = file_get_contents( $hot_file );
|
|
972
|
+
$decoded = is_string( $contents ) ? json_decode( $contents, true ) : null;
|
|
973
|
+
|
|
974
|
+
if ( ! is_array( $decoded ) || empty( $decoded['origin'] ) || ! is_string( $decoded['origin'] ) ) {
|
|
975
|
+
return $hot;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
$base = isset( $decoded['base'] ) && is_string( $decoded['base'] ) ? $decoded['base'] : '/';
|
|
979
|
+
$base = '/' . ltrim( $base, '/' );
|
|
980
|
+
if ( '/' !== substr( $base, -1 ) ) {
|
|
981
|
+
$base .= '/';
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
$hot = array(
|
|
985
|
+
'origin' => rtrim( $decoded['origin'], '/' ),
|
|
986
|
+
'base' => $base,
|
|
987
|
+
'plugins' => isset( $decoded['plugins'] ) && is_array( $decoded['plugins'] ) ? $decoded['plugins'] : array(),
|
|
988
|
+
);
|
|
989
|
+
|
|
990
|
+
// Stale hot file: the file is there, but nothing answers for it (a
|
|
991
|
+
// \`kill -9\`'d dev server). See ${fn}_should_verify_dev_server() above \u2014
|
|
992
|
+
// off by default, so this whole block is skipped on the common path.
|
|
993
|
+
if ( ${fn}_should_verify_dev_server() && function_exists( 'wp_remote_head' ) ) {
|
|
994
|
+
$response = wp_remote_head(
|
|
995
|
+
$hot['origin'] . $hot['base'] . '@vite/client',
|
|
996
|
+
array( 'timeout' => 0.2, 'blocking' => true )
|
|
997
|
+
);
|
|
998
|
+
|
|
999
|
+
$status_code = is_wp_error( $response ) ? 0 : (int) wp_remote_retrieve_response_code( $response );
|
|
1000
|
+
|
|
1001
|
+
if ( $status_code < 200 || $status_code >= 400 ) {
|
|
1002
|
+
$hot = null;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
return $hot;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Turn a project-relative source path into the URL the dev server serves it at.
|
|
1011
|
+
*
|
|
1012
|
+
* @param string $source Project-relative path from an .asset.php 'source' key.
|
|
1013
|
+
* @return string Absolute URL, or '' when no dev server is running.
|
|
1014
|
+
*/
|
|
1015
|
+
function ${fn}_url( $source ) {
|
|
1016
|
+
$hot = ${fn}_hot();
|
|
1017
|
+
|
|
1018
|
+
if ( null === $hot ) {
|
|
1019
|
+
return '';
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
return $hot['origin'] . $hot['base'] . ltrim( (string) $source, '/' );
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* URL of the directory this file lives in.
|
|
1027
|
+
*
|
|
1028
|
+
* Derived from __DIR__ rather than configured, so the same generated file works
|
|
1029
|
+
* whether it was built into a theme, a plugin or an mu-plugin. The candidates
|
|
1030
|
+
* are tried most-specific first: content_url() is the general answer, but a
|
|
1031
|
+
* theme under a custom theme root \u2014 or anything reached through a symlink \u2014 is
|
|
1032
|
+
* only spelled correctly by its own helper.
|
|
1033
|
+
*
|
|
1034
|
+
* @return string Trailing-slashed URL, or '' when this directory is outside
|
|
1035
|
+
* every location WordPress can build a URL for.
|
|
1036
|
+
*/
|
|
1037
|
+
function ${fn}_dist_url() {
|
|
1038
|
+
static $dist_url = false;
|
|
1039
|
+
|
|
1040
|
+
if ( false !== $dist_url ) {
|
|
1041
|
+
return $dist_url;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
$dist_url = '';
|
|
1045
|
+
$dir = rtrim( str_replace( '\\\\', '/', __DIR__ ), '/' );
|
|
1046
|
+
$roots = array();
|
|
1047
|
+
|
|
1048
|
+
if ( function_exists( 'get_stylesheet_directory' ) && function_exists( 'get_stylesheet_directory_uri' ) ) {
|
|
1049
|
+
$roots[] = array( get_stylesheet_directory(), get_stylesheet_directory_uri() );
|
|
1050
|
+
}
|
|
1051
|
+
if ( function_exists( 'get_template_directory' ) && function_exists( 'get_template_directory_uri' ) ) {
|
|
1052
|
+
$roots[] = array( get_template_directory(), get_template_directory_uri() );
|
|
1053
|
+
}
|
|
1054
|
+
if ( defined( 'WPMU_PLUGIN_DIR' ) && defined( 'WPMU_PLUGIN_URL' ) ) {
|
|
1055
|
+
$roots[] = array( WPMU_PLUGIN_DIR, WPMU_PLUGIN_URL );
|
|
1056
|
+
}
|
|
1057
|
+
if ( defined( 'WP_PLUGIN_DIR' ) && defined( 'WP_PLUGIN_URL' ) ) {
|
|
1058
|
+
$roots[] = array( WP_PLUGIN_DIR, WP_PLUGIN_URL );
|
|
1059
|
+
}
|
|
1060
|
+
if ( defined( 'WP_CONTENT_DIR' ) && function_exists( 'content_url' ) ) {
|
|
1061
|
+
$roots[] = array( WP_CONTENT_DIR, content_url() );
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
foreach ( $roots as $root ) {
|
|
1065
|
+
list( $root_dir, $root_url ) = $root;
|
|
1066
|
+
|
|
1067
|
+
if ( ! is_string( $root_dir ) || ! is_string( $root_url ) || '' === $root_dir || '' === $root_url ) {
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
$root_dir = rtrim( str_replace( '\\\\', '/', $root_dir ), '/' );
|
|
1072
|
+
|
|
1073
|
+
if ( '' === $root_dir || 0 !== strpos( $dir . '/', $root_dir . '/' ) ) {
|
|
1074
|
+
continue;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
$dist_url = rtrim( $root_url, '/' ) . substr( $dir, strlen( $root_dir ) ) . '/';
|
|
1078
|
+
break;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
return $dist_url;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/**
|
|
1085
|
+
* Map an asset URL pointing into this directory back to the file the dev
|
|
1086
|
+
* server serves it from.
|
|
1087
|
+
*
|
|
1088
|
+
* The lookup is the .asset.php sidecar next to where the built file would have
|
|
1089
|
+
* been: dist-relative URL, extension stripped, '.asset.php' appended. Its
|
|
1090
|
+
* 'source' key \u2014 written by both a build and a running dev server \u2014 is the
|
|
1091
|
+
* project-relative path of the file Vite compiles on demand.
|
|
1092
|
+
*
|
|
1093
|
+
* @param string $url Asset URL as WordPress is about to print it, '?ver=' and all.
|
|
1094
|
+
* @return string|null Project-relative source path, or null when this URL is
|
|
1095
|
+
* not ours or has no sidecar.
|
|
1096
|
+
*/
|
|
1097
|
+
function ${fn}_source( $url ) {
|
|
1098
|
+
static $cache = array();
|
|
1099
|
+
|
|
1100
|
+
if ( ! is_string( $url ) || '' === $url ) {
|
|
1101
|
+
return null;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
$dist_url = ${fn}_dist_url();
|
|
1105
|
+
|
|
1106
|
+
if ( '' === $dist_url ) {
|
|
1107
|
+
return null;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// The filters hand us the versioned URL; the sidecar lives at the
|
|
1111
|
+
// unversioned path.
|
|
1112
|
+
$query = strpos( $url, '?' );
|
|
1113
|
+
if ( false !== $query ) {
|
|
1114
|
+
$url = substr( $url, 0, $query );
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
if ( array_key_exists( $url, $cache ) ) {
|
|
1118
|
+
return $cache[ $url ];
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
$cache[ $url ] = null;
|
|
1122
|
+
$relative = null;
|
|
1123
|
+
|
|
1124
|
+
// A protocol-relative src, and an http/https mismatch between the enqueued
|
|
1125
|
+
// URL and content_url(), are both routine; neither should stop a match. The
|
|
1126
|
+
// path-only form additionally catches root-relative and CDN-fronted srcs.
|
|
1127
|
+
$path_only = preg_replace( '#^([a-z][a-z0-9+.-]*:)?//[^/]*#i', '', $dist_url );
|
|
1128
|
+
foreach ( array( $dist_url, is_string( $path_only ) ? $path_only : '' ) as $prefix ) {
|
|
1129
|
+
if ( '' === $prefix ) {
|
|
1130
|
+
continue;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
$bare_prefix = preg_replace( '#^[a-z][a-z0-9+.-]*:#i', '', $prefix );
|
|
1134
|
+
$bare_url = preg_replace( '#^[a-z][a-z0-9+.-]*:#i', '', $url );
|
|
1135
|
+
|
|
1136
|
+
if ( 0 !== strpos( $bare_url, $bare_prefix ) ) {
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
$relative = substr( $bare_url, strlen( $bare_prefix ) );
|
|
1141
|
+
break;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// The relative path reaches a require(), and it came out of a URL. Nothing
|
|
1145
|
+
// but the shape a build actually emits gets through.
|
|
1146
|
+
if ( null === $relative || ! preg_match( '#^[A-Za-z0-9_][A-Za-z0-9_./-]*$#', $relative ) || false !== strpos( $relative, '..' ) ) {
|
|
1147
|
+
return null;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
$asset_file = __DIR__ . '/' . preg_replace( '/\\.(js|mjs|css)$/', '', $relative ) . '.asset.php';
|
|
1151
|
+
|
|
1152
|
+
if ( ! file_exists( $asset_file ) ) {
|
|
1153
|
+
return null;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
$asset = require $asset_file;
|
|
1157
|
+
|
|
1158
|
+
if ( ! is_array( $asset ) || empty( $asset['source'] ) || ! is_string( $asset['source'] ) ) {
|
|
1159
|
+
return null;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
$cache[ $url ] = $asset['source'];
|
|
1163
|
+
|
|
1164
|
+
return $cache[ $url ];
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/**
|
|
1168
|
+
* Registry of handles whose <script> tag has to be stamped type="module".
|
|
1169
|
+
*
|
|
1170
|
+
* Everything the dev server serves is ESM \u2014 that is the point of dev mode \u2014 so
|
|
1171
|
+
* every handle this file repoints joins the set.
|
|
1172
|
+
*
|
|
1173
|
+
* @param string $handle Script handle.
|
|
1174
|
+
* @param bool $mark Add the handle to the registry.
|
|
1175
|
+
* @return bool Whether the handle prints as a module.
|
|
1176
|
+
*/
|
|
1177
|
+
function ${fn}_is_module( $handle, $mark = false ) {
|
|
1178
|
+
static $handles = array();
|
|
1179
|
+
|
|
1180
|
+
if ( $mark ) {
|
|
1181
|
+
$handles[ $handle ] = true;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
return isset( $handles[ $handle ] );
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// Nothing below this line matters when no dev server is running.
|
|
1188
|
+
if ( null === ${fn}_hot() ) {
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
/**
|
|
1193
|
+
* Register and enqueue Vite's HMR client, early.
|
|
1194
|
+
*
|
|
1195
|
+
* It opens the websocket, applies hot updates, and renders the error overlay.
|
|
1196
|
+
* Every handle this file repoints gets it as a dependency, so it is always the
|
|
1197
|
+
* first module in the document.
|
|
1198
|
+
*/
|
|
1199
|
+
function ${fn}_enqueue_client() {
|
|
1200
|
+
$src = ${fn}_url( '@vite/client' );
|
|
1201
|
+
|
|
1202
|
+
if ( '' === $src ) {
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
wp_register_script( '${client}', $src, array(), null, false );
|
|
1207
|
+
wp_enqueue_script( '${client}' );
|
|
1208
|
+
${fn}_is_module( '${client}', true );
|
|
1209
|
+
|
|
1210
|
+
${fn}_enqueue_react_refresh_preamble();
|
|
1211
|
+
}
|
|
1212
|
+
add_action( 'wp_enqueue_scripts', '${fn}_enqueue_client', -1000 );
|
|
1213
|
+
add_action( 'admin_enqueue_scripts', '${fn}_enqueue_client', -1000 );
|
|
1214
|
+
|
|
1215
|
+
/**
|
|
1216
|
+
* Attach the React Fast Refresh preamble to the vite-client handle, when the
|
|
1217
|
+
* running dev server reports \`vite:react-refresh\` in the hot file's plugin
|
|
1218
|
+
* list.
|
|
1219
|
+
*
|
|
1220
|
+
* The preamble has to run before any React component module evaluates \u2014
|
|
1221
|
+
* @vitejs/plugin-react's transform assumes \`window.$RefreshReg$\` already
|
|
1222
|
+
* exists, and every transformed component module throws otherwise. Attaching
|
|
1223
|
+
* it to the vite-client handle rather than to each rewritten script gets that
|
|
1224
|
+
* ordering for free, two different ways depending on how the component reaches
|
|
1225
|
+
* the page:
|
|
1226
|
+
*
|
|
1227
|
+
* - **Classic scripts.** ${fn}_prepare() (below) makes the client a
|
|
1228
|
+
* dependency of every classic handle this bridge repoints, so WordPress's
|
|
1229
|
+
* own dependency graph orders the client \u2014 and the inline block
|
|
1230
|
+
* \`wp_add_inline_script( \u2026, 'after' )\` attaches to it \u2014 before them.
|
|
1231
|
+
* - **Script Modules.** There is no cross-system dependency a classic handle
|
|
1232
|
+
* can declare on a Script Module, so this relies on where WordPress prints
|
|
1233
|
+
* each group instead: the client is a classic-registered handle (its
|
|
1234
|
+
* \`type="module"\` is only a printed attribute, stamped by
|
|
1235
|
+
* ${fn}_script_tag() below), and every generated page in this package
|
|
1236
|
+
* prints classic scripts \u2014 the client included \u2014 before it prints Script
|
|
1237
|
+
* Modules. Same-document module scripts without \`async\` execute in that
|
|
1238
|
+
* print order, so the preamble still runs first.
|
|
1239
|
+
*
|
|
1240
|
+
* Same mechanics vite-for-wp uses for the classic-script half.
|
|
1241
|
+
*
|
|
1242
|
+
* Guarded by a static so a request that somehow fires both enqueue hooks
|
|
1243
|
+
* cannot attach the preamble, and its type="module" filter, twice.
|
|
1244
|
+
*/
|
|
1245
|
+
function ${fn}_enqueue_react_refresh_preamble() {
|
|
1246
|
+
static $attached = false;
|
|
1247
|
+
|
|
1248
|
+
if ( $attached ) {
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
$hot = ${fn}_hot();
|
|
1253
|
+
|
|
1254
|
+
if ( null === $hot || ! in_array( 'vite:react-refresh', $hot['plugins'], true ) ) {
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
$attached = true;
|
|
1259
|
+
|
|
1260
|
+
$preamble = sprintf(
|
|
1261
|
+
'import RefreshRuntime from "%1$s"
|
|
1262
|
+
RefreshRuntime.injectIntoGlobalHook(window)
|
|
1263
|
+
window.$RefreshReg$ = () => {}
|
|
1264
|
+
window.$RefreshSig$ = () => (type) => type
|
|
1265
|
+
window.__vite_plugin_react_preamble_installed__ = true',
|
|
1266
|
+
esc_js( ${fn}_url( '@react-refresh' ) )
|
|
1267
|
+
);
|
|
1268
|
+
|
|
1269
|
+
wp_add_inline_script( '${client}', $preamble, 'after' );
|
|
1270
|
+
add_filter( 'wp_inline_script_attributes', '${fn}_react_refresh_inline_attributes' );
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
/**
|
|
1274
|
+
* Stamp type="module" on exactly the inline block ${fn}_enqueue_react_refresh_preamble()
|
|
1275
|
+
* attached \u2014 the preamble is a top-level \`import\`, so it must not print as a
|
|
1276
|
+
* classic inline script the way an 'after' block otherwise would.
|
|
1277
|
+
*
|
|
1278
|
+
* WordPress assigns an inline block printed at the 'after' position the id
|
|
1279
|
+
* \`<handle>-js-after\`; matching on it (rather than the handle alone) is what
|
|
1280
|
+
* keeps this from touching some other inline script attached to the same
|
|
1281
|
+
* vite-client handle by unrelated code.
|
|
1282
|
+
*
|
|
1283
|
+
* @param array $attributes Attributes about to be printed on the inline <script> tag.
|
|
1284
|
+
* @return array
|
|
1285
|
+
*/
|
|
1286
|
+
function ${fn}_react_refresh_inline_attributes( $attributes ) {
|
|
1287
|
+
if ( is_array( $attributes ) && isset( $attributes['id'] ) && '${client}-js-after' === $attributes['id'] ) {
|
|
1288
|
+
$attributes['type'] = 'module';
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
return $attributes;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/**
|
|
1295
|
+
* The one pass that has to happen while the queues are still mutable: wire the
|
|
1296
|
+
* HMR client into everything about to be repointed, and swap this directory's
|
|
1297
|
+
* stylesheets for the JS modules Vite serves them as.
|
|
1298
|
+
*
|
|
1299
|
+
* Runs last on the enqueue hooks \u2014 the URL rewriting itself happens later, in
|
|
1300
|
+
* script_loader_src, which also catches handles registered after this point.
|
|
1301
|
+
*/
|
|
1302
|
+
function ${fn}_prepare() {
|
|
1303
|
+
$claimed = array();
|
|
1304
|
+
|
|
1305
|
+
if ( function_exists( 'wp_scripts' ) && function_exists( 'wp_script_is' ) ) {
|
|
1306
|
+
$has_client = wp_script_is( '${client}', 'registered' );
|
|
1307
|
+
|
|
1308
|
+
foreach ( wp_scripts()->registered as $handle => $script ) {
|
|
1309
|
+
if ( '${client}' === $handle || empty( $script->src ) ) {
|
|
1310
|
+
continue;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
$source = ${fn}_source( $script->src );
|
|
1314
|
+
|
|
1315
|
+
if ( null === $source ) {
|
|
1316
|
+
continue;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
$claimed[ $source ] = true;
|
|
1320
|
+
|
|
1321
|
+
// Ordering only. A missing client handle would make WordPress drop
|
|
1322
|
+
// the script entirely, so never depend on one that isn't there.
|
|
1323
|
+
if ( $has_client && is_array( $script->deps ) && ! in_array( '${client}', $script->deps, true ) ) {
|
|
1324
|
+
$script->deps[] = '${client}';
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
if ( ! function_exists( 'wp_styles' ) ) {
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
$styles = wp_styles();
|
|
1334
|
+
|
|
1335
|
+
foreach ( $styles->queue as $handle ) {
|
|
1336
|
+
if ( ! isset( $styles->registered[ $handle ] ) || empty( $styles->registered[ $handle ]->src ) ) {
|
|
1337
|
+
continue;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
$source = ${fn}_source( $styles->registered[ $handle ]->src );
|
|
1341
|
+
|
|
1342
|
+
if ( null === $source ) {
|
|
1343
|
+
continue;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
wp_dequeue_style( $handle );
|
|
1347
|
+
|
|
1348
|
+
// A stylesheet whose source is already served by a repointed script is
|
|
1349
|
+
// covered: in dev the entry imports its own CSS.
|
|
1350
|
+
if ( isset( $claimed[ $source ] ) ) {
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
$claimed[ $source ] = true;
|
|
1355
|
+
$shim = $handle . '-vite-dev';
|
|
1356
|
+
|
|
1357
|
+
wp_register_script(
|
|
1358
|
+
$shim,
|
|
1359
|
+
${fn}_url( $source ),
|
|
1360
|
+
wp_script_is( '${client}', 'registered' ) ? array( '${client}' ) : array(),
|
|
1361
|
+
null,
|
|
1362
|
+
false
|
|
1363
|
+
);
|
|
1364
|
+
wp_enqueue_script( $shim );
|
|
1365
|
+
${fn}_is_module( $shim, true );
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
add_action( 'wp_enqueue_scripts', '${fn}_prepare', PHP_INT_MAX );
|
|
1369
|
+
add_action( 'admin_enqueue_scripts', '${fn}_prepare', PHP_INT_MAX );
|
|
1370
|
+
|
|
1371
|
+
/**
|
|
1372
|
+
* Repoint a classic script at the dev server.
|
|
1373
|
+
*
|
|
1374
|
+
* @param string $src Script URL.
|
|
1375
|
+
* @param string $handle Script handle.
|
|
1376
|
+
* @return string Rewritten URL, or the original.
|
|
1377
|
+
*/
|
|
1378
|
+
function ${fn}_script_src( $src, $handle ) {
|
|
1379
|
+
$source = ${fn}_source( $src );
|
|
1380
|
+
|
|
1381
|
+
if ( null === $source ) {
|
|
1382
|
+
return $src;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
// script_loader_src always runs before script_loader_tag for the same
|
|
1386
|
+
// handle (both live in WP_Scripts::do_item()), so marking here is enough.
|
|
1387
|
+
${fn}_is_module( $handle, true );
|
|
1388
|
+
|
|
1389
|
+
return ${fn}_url( $source );
|
|
1390
|
+
}
|
|
1391
|
+
add_filter( 'script_loader_src', '${fn}_script_src', 10, 2 );
|
|
1392
|
+
|
|
1393
|
+
/**
|
|
1394
|
+
* Stamp type="module" on the handles this file repointed.
|
|
1395
|
+
*
|
|
1396
|
+
* @param string $tag Script tag HTML.
|
|
1397
|
+
* @param string $handle Script handle.
|
|
1398
|
+
* @return string Rewritten tag.
|
|
1399
|
+
*/
|
|
1400
|
+
function ${fn}_script_tag( $tag, $handle ) {
|
|
1401
|
+
if ( ! is_string( $tag ) || ! ${fn}_is_module( $handle ) ) {
|
|
1402
|
+
return $tag;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// WP 6.2+. Without it, the crude rewrite is still better than letting an
|
|
1406
|
+
// ESM file load as a classic script and throw on its first import.
|
|
1407
|
+
if ( ! class_exists( 'WP_HTML_Tag_Processor' ) ) {
|
|
1408
|
+
return false === strpos( $tag, 'type="module"' )
|
|
1409
|
+
? str_replace( '<script ', '<script type="module" ', $tag )
|
|
1410
|
+
: $tag;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
$processor = new WP_HTML_Tag_Processor( $tag );
|
|
1414
|
+
|
|
1415
|
+
while ( $processor->next_tag( 'script' ) ) {
|
|
1416
|
+
// Inline blocks attached to the same handle stay classic; only the
|
|
1417
|
+
// src'd tag is the module.
|
|
1418
|
+
if ( null === $processor->get_attribute( 'src' ) ) {
|
|
1419
|
+
continue;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
$processor->set_attribute( 'type', 'module' );
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
return $processor->get_updated_html();
|
|
1426
|
+
}
|
|
1427
|
+
add_filter( 'script_loader_tag', '${fn}_script_tag', 10, 2 );
|
|
1428
|
+
|
|
1429
|
+
/**
|
|
1430
|
+
* Repoint a Script Module at the dev server.
|
|
1431
|
+
*
|
|
1432
|
+
* script_module_loader_src (WP 6.5+) is the single choke point inside
|
|
1433
|
+
* WP_Script_Modules::get_src() for all three Script Module outputs \u2014 the
|
|
1434
|
+
* <script type="module"> tag, its <link rel="modulepreload">, and the import
|
|
1435
|
+
* map \u2014 so one filter keeps all three consistent and no consumer registration
|
|
1436
|
+
* has to change.
|
|
1437
|
+
*
|
|
1438
|
+
* @param string $src Script Module URL.
|
|
1439
|
+
* @param string $id Script Module identifier.
|
|
1440
|
+
* @return string Rewritten URL, or the original.
|
|
1441
|
+
*/
|
|
1442
|
+
function ${fn}_script_module_src( $src, $id ) {
|
|
1443
|
+
$source = ${fn}_source( $src );
|
|
1444
|
+
|
|
1445
|
+
return null === $source ? $src : ${fn}_url( $source );
|
|
1446
|
+
}
|
|
1447
|
+
add_filter( 'script_module_loader_src', '${fn}_script_module_src', 10, 2 );
|
|
1448
|
+
|
|
1449
|
+
/**
|
|
1450
|
+
* Last resort for a stylesheet ${fn}_prepare() never saw \u2014 one registered
|
|
1451
|
+
* after the enqueue hooks, or pulled in as another stylesheet's dependency.
|
|
1452
|
+
*
|
|
1453
|
+
* Vite's '?direct' serves the compiled CSS with a text/css content type instead
|
|
1454
|
+
* of the JS module a bare .css request returns. Correct styles, no hot reload.
|
|
1455
|
+
*
|
|
1456
|
+
* @param string $src Stylesheet URL.
|
|
1457
|
+
* @param string $handle Stylesheet handle.
|
|
1458
|
+
* @return string Rewritten URL, or the original.
|
|
1459
|
+
*/
|
|
1460
|
+
function ${fn}_style_src( $src, $handle ) {
|
|
1461
|
+
$source = ${fn}_source( $src );
|
|
1462
|
+
|
|
1463
|
+
return null === $source ? $src : ${fn}_url( $source ) . '?direct';
|
|
1464
|
+
}
|
|
1465
|
+
add_filter( 'style_loader_src', '${fn}_style_src', 10, 2 );
|
|
1466
|
+
|
|
1467
|
+
endif;
|
|
1468
|
+
`;
|
|
1469
|
+
}
|
|
877
1470
|
function generatePhpFiles(ctx, pages) {
|
|
878
1471
|
const files = [
|
|
879
1472
|
{ fileName: "build.php", source: renderBuildPhp(ctx) },
|
|
@@ -1815,37 +2408,135 @@ function isBarePackageName(name) {
|
|
|
1815
2408
|
}
|
|
1816
2409
|
return name.length > 0 && !name.includes("/") && !name.startsWith(".");
|
|
1817
2410
|
}
|
|
2411
|
+
function entrySourcePath(facadeModuleId, root) {
|
|
2412
|
+
if (!facadeModuleId) return null;
|
|
2413
|
+
const relativePath = relative(root, facadeModuleId).replace(/\\/g, "/");
|
|
2414
|
+
if (relativePath === "" || relativePath === ".." || relativePath.startsWith("../")) {
|
|
2415
|
+
return null;
|
|
2416
|
+
}
|
|
2417
|
+
if (isAbsolute(relativePath)) return null;
|
|
2418
|
+
return relativePath;
|
|
2419
|
+
}
|
|
2420
|
+
function phpSingleQuoteEscape(value) {
|
|
2421
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
2422
|
+
}
|
|
1818
2423
|
var DEFAULT_VENDOR = {
|
|
1819
2424
|
react: { global: "React", handle: "react" },
|
|
1820
2425
|
"react-dom": { global: "ReactDOM", handle: "react-dom" },
|
|
1821
2426
|
"react-dom/client": { global: "ReactDOM", handle: "react-dom" },
|
|
1822
|
-
"react/jsx-runtime": {
|
|
1823
|
-
|
|
2427
|
+
"react/jsx-runtime": {
|
|
2428
|
+
global: "ReactJSXRuntime",
|
|
2429
|
+
handle: "react-jsx-runtime"
|
|
2430
|
+
},
|
|
2431
|
+
"react/jsx-dev-runtime": {
|
|
2432
|
+
global: "ReactJSXRuntime",
|
|
2433
|
+
handle: "react-jsx-runtime"
|
|
2434
|
+
},
|
|
1824
2435
|
moment: { global: "moment", handle: "moment" },
|
|
1825
2436
|
jquery: { global: "jQuery", handle: "jquery" },
|
|
1826
2437
|
lodash: { global: "lodash", handle: "lodash" },
|
|
1827
2438
|
"lodash-es": { global: "lodash", handle: "lodash" }
|
|
1828
2439
|
};
|
|
1829
2440
|
var CJS_GLOBAL_SHIM_PREFIX = "\0wp-external-global:";
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
const
|
|
1833
|
-
if (
|
|
1834
|
-
const
|
|
1835
|
-
const
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
2441
|
+
function matchesScriptModuleNamespace(packageName, scriptModuleNamespaces) {
|
|
2442
|
+
if (!packageName.startsWith("@")) return false;
|
|
2443
|
+
const slashIndex = packageName.indexOf("/");
|
|
2444
|
+
if (slashIndex < 0) return false;
|
|
2445
|
+
const namespace = packageName.slice(1, slashIndex);
|
|
2446
|
+
for (const pattern of scriptModuleNamespaces) {
|
|
2447
|
+
if (pattern.endsWith("*")) {
|
|
2448
|
+
if (namespace.startsWith(pattern.slice(0, -1))) return true;
|
|
2449
|
+
} else if (namespace === pattern) {
|
|
2450
|
+
return true;
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
return false;
|
|
2454
|
+
}
|
|
2455
|
+
function resolveExternal(specifier, options) {
|
|
2456
|
+
const projectRoot = options.projectRoot;
|
|
2457
|
+
const vendorExternals = options.vendorExternals ?? DEFAULT_VENDOR;
|
|
2458
|
+
const externalNamespaces = options.externalNamespaces ?? {};
|
|
2459
|
+
const scriptModuleNamespaces = options.scriptModuleNamespaces ?? [];
|
|
2460
|
+
const bundledPackages = options.bundledPackages instanceof Set ? options.bundledPackages : new Set(options.bundledPackages ?? []);
|
|
2461
|
+
if (bundledPackages.has(splitImport(specifier).name)) return null;
|
|
2462
|
+
if (vendorExternals[specifier]) {
|
|
2463
|
+
const vendor = vendorExternals[specifier];
|
|
2464
|
+
return {
|
|
2465
|
+
global: vendor.global,
|
|
2466
|
+
handle: vendor.handle,
|
|
2467
|
+
specifier,
|
|
2468
|
+
moduleEligible: false,
|
|
2469
|
+
scriptEligible: true
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
const { name, subpath } = splitImport(specifier);
|
|
2473
|
+
if (name.startsWith("@wordpress/")) {
|
|
2474
|
+
const meta = readPackageMetadata(name, projectRoot);
|
|
2475
|
+
if (!meta) {
|
|
2476
|
+
return null;
|
|
2477
|
+
}
|
|
2478
|
+
const shortName = name.slice("@wordpress/".length);
|
|
2479
|
+
const moduleEligible = isScriptModule(meta, subpath);
|
|
2480
|
+
const scriptEligible = !!meta.wpScript;
|
|
2481
|
+
if (!moduleEligible && !scriptEligible) {
|
|
2482
|
+
return null;
|
|
2483
|
+
}
|
|
2484
|
+
return {
|
|
2485
|
+
global: scriptEligible ? `wp.${camelize(shortName)}` : null,
|
|
2486
|
+
handle: scriptEligible ? `wp-${shortName}` : null,
|
|
2487
|
+
specifier,
|
|
2488
|
+
moduleEligible,
|
|
2489
|
+
scriptEligible
|
|
2490
|
+
};
|
|
2491
|
+
}
|
|
2492
|
+
for (const [namespace, config] of Object.entries(externalNamespaces)) {
|
|
2493
|
+
if (name.startsWith(`@${namespace}/`)) {
|
|
2494
|
+
const shortName = name.slice(`@${namespace}/`.length);
|
|
2495
|
+
return {
|
|
2496
|
+
global: `${config.global}.${camelize(shortName)}`,
|
|
2497
|
+
handle: `${config.handlePrefix}-${shortName}`,
|
|
2498
|
+
specifier,
|
|
2499
|
+
moduleEligible: false,
|
|
2500
|
+
scriptEligible: true
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
if (matchesScriptModuleNamespace(name, scriptModuleNamespaces)) {
|
|
2505
|
+
return {
|
|
2506
|
+
global: null,
|
|
2507
|
+
handle: null,
|
|
2508
|
+
specifier,
|
|
2509
|
+
moduleEligible: true,
|
|
2510
|
+
scriptEligible: false
|
|
2511
|
+
};
|
|
2512
|
+
}
|
|
2513
|
+
return null;
|
|
2514
|
+
}
|
|
2515
|
+
var JS_IDENTIFIER = "[A-Za-z_$][\\w$]*";
|
|
2516
|
+
var SIDE_EFFECT_IMPORT = /^\s*import\s*['"][^'"]+['"]\s*;?\s*$/;
|
|
2517
|
+
function rewriteImport(importStatement, globalAccess) {
|
|
2518
|
+
if (SIDE_EFFECT_IMPORT.test(importStatement)) return "";
|
|
2519
|
+
const match = importStatement.match(/^\s*import\s*(.*?)\s*from\s*['"][^'"]+['"]\s*;?\s*$/s);
|
|
2520
|
+
if (!match || !match[1]) return null;
|
|
2521
|
+
const bindings = match[1].trim();
|
|
2522
|
+
const namespaceImport = bindings.match(new RegExp(`^\\*\\s*as\\s+(${JS_IDENTIFIER})$`));
|
|
2523
|
+
if (namespaceImport) return `const ${namespaceImport[1]} = ${globalAccess};`;
|
|
2524
|
+
const defaultWithNamed = bindings.match(
|
|
2525
|
+
new RegExp(`^(${JS_IDENTIFIER})\\s*,\\s*\\{(.+)\\}$`, "s")
|
|
2526
|
+
);
|
|
2527
|
+
if (defaultWithNamed) {
|
|
2528
|
+
const destructured = namedImportsToDestructuring(defaultWithNamed[2].trim());
|
|
2529
|
+
if (destructured === null) return null;
|
|
2530
|
+
return [
|
|
2531
|
+
`const ${defaultWithNamed[1]} = (${globalAccess} && ${globalAccess}.default) || ${globalAccess};`,
|
|
2532
|
+
`const { ${destructured} } = ${globalAccess};`
|
|
2533
|
+
].join(" ");
|
|
1845
2534
|
}
|
|
1846
2535
|
if (bindings.startsWith("{")) {
|
|
1847
2536
|
const namedImports = bindings.replace(/^\{|\}$/g, "").trim();
|
|
1848
|
-
|
|
2537
|
+
const destructured = namedImportsToDestructuring(namedImports);
|
|
2538
|
+
if (destructured === null) return null;
|
|
2539
|
+
return `const { ${destructured} } = ${globalAccess};`;
|
|
1849
2540
|
}
|
|
1850
2541
|
if (new RegExp(`^${JS_IDENTIFIER}$`).test(bindings)) {
|
|
1851
2542
|
return `const ${bindings} = (${globalAccess} && ${globalAccess}.default) || ${globalAccess};`;
|
|
@@ -1854,10 +2545,23 @@ function rewriteImport(importStatement, globalAccess) {
|
|
|
1854
2545
|
}
|
|
1855
2546
|
function namedImportsToDestructuring(namedImports) {
|
|
1856
2547
|
const aliasPattern = new RegExp(`^(${JS_IDENTIFIER})\\s+as\\s+(${JS_IDENTIFIER})$`);
|
|
1857
|
-
|
|
2548
|
+
const plainPattern = new RegExp(`^${JS_IDENTIFIER}$`);
|
|
2549
|
+
const bindings = [];
|
|
2550
|
+
for (const raw of namedImports.split(",")) {
|
|
2551
|
+
const binding = raw.trim();
|
|
2552
|
+
if (!binding) continue;
|
|
1858
2553
|
const aliased = binding.match(aliasPattern);
|
|
1859
|
-
|
|
1860
|
-
|
|
2554
|
+
if (aliased) {
|
|
2555
|
+
bindings.push(`${aliased[1]}: ${aliased[2]}`);
|
|
2556
|
+
continue;
|
|
2557
|
+
}
|
|
2558
|
+
if (plainPattern.test(binding)) {
|
|
2559
|
+
bindings.push(binding);
|
|
2560
|
+
continue;
|
|
2561
|
+
}
|
|
2562
|
+
return null;
|
|
2563
|
+
}
|
|
2564
|
+
return bindings.join(", ");
|
|
1861
2565
|
}
|
|
1862
2566
|
function wpExternals(options = {}) {
|
|
1863
2567
|
const projectRoot = options.projectRoot ?? process.cwd();
|
|
@@ -1867,6 +2571,7 @@ function wpExternals(options = {}) {
|
|
|
1867
2571
|
const moduleEntryMatchers = options.moduleEntryMatchers ?? [];
|
|
1868
2572
|
const wpBuildAssetMatchers = options.wpBuildAssetMatchers ?? [];
|
|
1869
2573
|
const scriptModuleNamespaces = options.scriptModuleNamespaces ?? [];
|
|
2574
|
+
const globalsSink = options.globalsSink;
|
|
1870
2575
|
for (const name of options.bundle ?? []) {
|
|
1871
2576
|
if (isBarePackageName(name)) continue;
|
|
1872
2577
|
const { name: rootName, subpath } = splitImport(name);
|
|
@@ -1875,24 +2580,22 @@ function wpExternals(options = {}) {
|
|
|
1875
2580
|
);
|
|
1876
2581
|
}
|
|
1877
2582
|
const bundledPackages = /* @__PURE__ */ new Set([...DEFAULT_BUNDLED_PACKAGES, ...options.bundle ?? []]);
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
}
|
|
1890
|
-
return false;
|
|
2583
|
+
const resolveExternalOptions = {
|
|
2584
|
+
projectRoot,
|
|
2585
|
+
vendorExternals,
|
|
2586
|
+
externalNamespaces,
|
|
2587
|
+
scriptModuleNamespaces,
|
|
2588
|
+
bundledPackages
|
|
2589
|
+
};
|
|
2590
|
+
function resolveExternalForThisPlugin(specifier) {
|
|
2591
|
+
const external = resolveExternal(specifier, resolveExternalOptions);
|
|
2592
|
+
if (external?.global) globalsSink?.set(external.specifier, external.global);
|
|
2593
|
+
return external;
|
|
1891
2594
|
}
|
|
1892
2595
|
function buildModeForFile(moduleId) {
|
|
1893
|
-
const filePath = moduleId.split("?")[0] ?? moduleId;
|
|
2596
|
+
const filePath = (moduleId.split("?")[0] ?? moduleId).replace(/\\/g, "/");
|
|
1894
2597
|
for (const pattern of moduleEntryMatchers) {
|
|
1895
|
-
if (filePath.includes(pattern)) return "module";
|
|
2598
|
+
if (filePath.includes(pattern.replace(/\\/g, "/"))) return "module";
|
|
1896
2599
|
}
|
|
1897
2600
|
return buildType;
|
|
1898
2601
|
}
|
|
@@ -1904,61 +2607,6 @@ function wpExternals(options = {}) {
|
|
|
1904
2607
|
}
|
|
1905
2608
|
return false;
|
|
1906
2609
|
}
|
|
1907
|
-
function resolveExternal(specifier) {
|
|
1908
|
-
if (bundledPackages.has(splitImport(specifier).name)) return null;
|
|
1909
|
-
if (vendorExternals[specifier]) {
|
|
1910
|
-
const vendor = vendorExternals[specifier];
|
|
1911
|
-
return {
|
|
1912
|
-
global: vendor.global,
|
|
1913
|
-
handle: vendor.handle,
|
|
1914
|
-
specifier,
|
|
1915
|
-
moduleEligible: false,
|
|
1916
|
-
scriptEligible: true
|
|
1917
|
-
};
|
|
1918
|
-
}
|
|
1919
|
-
const { name, subpath } = splitImport(specifier);
|
|
1920
|
-
if (name.startsWith("@wordpress/")) {
|
|
1921
|
-
const meta = readPackageMetadata(name, projectRoot);
|
|
1922
|
-
if (!meta) {
|
|
1923
|
-
return null;
|
|
1924
|
-
}
|
|
1925
|
-
const shortName = name.slice("@wordpress/".length);
|
|
1926
|
-
const moduleEligible = isScriptModule(meta, subpath);
|
|
1927
|
-
const scriptEligible = !!meta.wpScript;
|
|
1928
|
-
if (!moduleEligible && !scriptEligible) {
|
|
1929
|
-
return null;
|
|
1930
|
-
}
|
|
1931
|
-
return {
|
|
1932
|
-
global: scriptEligible ? `wp.${camelize(shortName)}` : null,
|
|
1933
|
-
handle: scriptEligible ? `wp-${shortName}` : null,
|
|
1934
|
-
specifier,
|
|
1935
|
-
moduleEligible,
|
|
1936
|
-
scriptEligible
|
|
1937
|
-
};
|
|
1938
|
-
}
|
|
1939
|
-
for (const [namespace, config] of Object.entries(externalNamespaces)) {
|
|
1940
|
-
if (name.startsWith(`@${namespace}/`)) {
|
|
1941
|
-
const shortName = name.slice(`@${namespace}/`.length);
|
|
1942
|
-
return {
|
|
1943
|
-
global: `${config.global}.${camelize(shortName)}`,
|
|
1944
|
-
handle: `${config.handlePrefix}-${shortName}`,
|
|
1945
|
-
specifier,
|
|
1946
|
-
moduleEligible: false,
|
|
1947
|
-
scriptEligible: true
|
|
1948
|
-
};
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
if (matchesScriptModuleNamespace(name)) {
|
|
1952
|
-
return {
|
|
1953
|
-
global: null,
|
|
1954
|
-
handle: null,
|
|
1955
|
-
specifier,
|
|
1956
|
-
moduleEligible: true,
|
|
1957
|
-
scriptEligible: false
|
|
1958
|
-
};
|
|
1959
|
-
}
|
|
1960
|
-
return null;
|
|
1961
|
-
}
|
|
1962
2610
|
function moduleImportKind(specifier, isDynamic) {
|
|
1963
2611
|
if (specifier === "@wordpress/interactivity") return "static";
|
|
1964
2612
|
return isDynamic ? "dynamic" : "static";
|
|
@@ -1979,7 +2627,7 @@ function wpExternals(options = {}) {
|
|
|
1979
2627
|
enforce: "pre",
|
|
1980
2628
|
resolveId(source, _importer, options2) {
|
|
1981
2629
|
const isRequireCall = options2?.kind === "require-call";
|
|
1982
|
-
const external =
|
|
2630
|
+
const external = resolveExternalForThisPlugin(source);
|
|
1983
2631
|
if (external) {
|
|
1984
2632
|
if (isRequireCall) {
|
|
1985
2633
|
if (external.global) {
|
|
@@ -1994,7 +2642,7 @@ function wpExternals(options = {}) {
|
|
|
1994
2642
|
return { id: source, external: true };
|
|
1995
2643
|
}
|
|
1996
2644
|
const commonjsMatch = source.match(/^\0([^?]+)\?commonjs-external$/);
|
|
1997
|
-
if (commonjsMatch && commonjsMatch[1] &&
|
|
2645
|
+
if (commonjsMatch && commonjsMatch[1] && resolveExternalForThisPlugin(commonjsMatch[1])) {
|
|
1998
2646
|
return source;
|
|
1999
2647
|
}
|
|
2000
2648
|
return null;
|
|
@@ -2002,7 +2650,7 @@ function wpExternals(options = {}) {
|
|
|
2002
2650
|
load(id) {
|
|
2003
2651
|
if (id.startsWith(CJS_GLOBAL_SHIM_PREFIX)) {
|
|
2004
2652
|
const specifier = id.slice(CJS_GLOBAL_SHIM_PREFIX.length);
|
|
2005
|
-
const external2 =
|
|
2653
|
+
const external2 = resolveExternalForThisPlugin(specifier);
|
|
2006
2654
|
if (!external2 || !external2.global) return null;
|
|
2007
2655
|
if (external2.handle) recordDeps(handleDepsByModule, id, [external2.handle]);
|
|
2008
2656
|
return `module.exports = (typeof window !== "undefined" ? window.${external2.global} : {});
|
|
@@ -2010,7 +2658,7 @@ function wpExternals(options = {}) {
|
|
|
2010
2658
|
}
|
|
2011
2659
|
const commonjsMatch = id.match(/^\0([^?]+)\?commonjs-external$/);
|
|
2012
2660
|
if (!commonjsMatch || !commonjsMatch[1]) return null;
|
|
2013
|
-
const external =
|
|
2661
|
+
const external = resolveExternalForThisPlugin(commonjsMatch[1]);
|
|
2014
2662
|
if (!external) return null;
|
|
2015
2663
|
if (buildModeForFile(id) === "module" && external.moduleEligible) {
|
|
2016
2664
|
return `export default {};`;
|
|
@@ -2022,9 +2670,13 @@ function wpExternals(options = {}) {
|
|
|
2022
2670
|
return `const __wpExternal = (typeof window !== "undefined" ? window.${external.global} : {}); export default __wpExternal;`;
|
|
2023
2671
|
}
|
|
2024
2672
|
};
|
|
2673
|
+
let resolvedRoot = projectRoot;
|
|
2025
2674
|
const transformPlugin = {
|
|
2026
2675
|
name: `wp-externals(${buildType})`,
|
|
2027
2676
|
enforce: "post",
|
|
2677
|
+
configResolved(config) {
|
|
2678
|
+
resolvedRoot = config.root;
|
|
2679
|
+
},
|
|
2028
2680
|
async transform(code, id) {
|
|
2029
2681
|
if (!code.includes("@wordpress/") && !Object.keys(vendorExternals).some((packageName) => code.includes(packageName)) && !Object.keys(externalNamespaces).some(
|
|
2030
2682
|
(namespace) => code.includes(`@${namespace}/`)
|
|
@@ -2064,7 +2716,7 @@ Most likely cause: JSX in a file that isn't .jsx/.tsx. JSX must live in .jsx/.ts
|
|
|
2064
2716
|
for (const importRecord of imports) {
|
|
2065
2717
|
const importedSpecifier = importRecord.n;
|
|
2066
2718
|
if (importedSpecifier === void 0) continue;
|
|
2067
|
-
const external =
|
|
2719
|
+
const external = resolveExternalForThisPlugin(importedSpecifier);
|
|
2068
2720
|
if (!external) continue;
|
|
2069
2721
|
const isDynamic = importRecord.d > -1;
|
|
2070
2722
|
if (fileMode === "script") {
|
|
@@ -2198,6 +2850,10 @@ Most likely cause: JSX in a file that isn't .jsx/.tsx. JSX must live in .jsx/.ts
|
|
|
2198
2850
|
}
|
|
2199
2851
|
fields.push(`'version' => '${version}'`);
|
|
2200
2852
|
if (entryMode === "module") fields.push(`'type' => 'module'`);
|
|
2853
|
+
const sourcePath = entrySourcePath(output.facadeModuleId, resolvedRoot);
|
|
2854
|
+
if (sourcePath) {
|
|
2855
|
+
fields.push(`'source' => '${phpSingleQuoteEscape(sourcePath)}'`);
|
|
2856
|
+
}
|
|
2201
2857
|
this.emitFile({
|
|
2202
2858
|
type: "asset",
|
|
2203
2859
|
fileName: fileName.replace(/\.m?js$/, ".asset.php"),
|
|
@@ -2326,15 +2982,47 @@ function parseImportClause(statement) {
|
|
|
2326
2982
|
}
|
|
2327
2983
|
return rest === "" ? clause : null;
|
|
2328
2984
|
}
|
|
2985
|
+
function exportClauseRange(code, exports) {
|
|
2986
|
+
let firstName = Number.POSITIVE_INFINITY;
|
|
2987
|
+
let lastName = -1;
|
|
2988
|
+
for (const record of exports) {
|
|
2989
|
+
firstName = Math.min(firstName, record.s, record.ls >= 0 ? record.ls : record.s);
|
|
2990
|
+
lastName = Math.max(lastName, record.e, record.le >= 0 ? record.le : record.e);
|
|
2991
|
+
}
|
|
2992
|
+
if (lastName < 0) return null;
|
|
2993
|
+
const brace = code.lastIndexOf("{", firstName);
|
|
2994
|
+
if (brace < 0) return null;
|
|
2995
|
+
const beforeBrace = code.slice(0, brace).trimEnd();
|
|
2996
|
+
if (!beforeBrace.endsWith("export")) return null;
|
|
2997
|
+
const start = beforeBrace.length - "export".length;
|
|
2998
|
+
if (start > 0 && /[\w$]/.test(code[start - 1])) return null;
|
|
2999
|
+
const close = code.indexOf("}", lastName);
|
|
3000
|
+
if (close < 0) return null;
|
|
3001
|
+
let end = close + 1;
|
|
3002
|
+
while (end < code.length && /\s/.test(code[end])) end += 1;
|
|
3003
|
+
if (code[end] === ";") end += 1;
|
|
3004
|
+
if (!/^export\s*\{[^{}]*\}\s*;?$/.test(code.slice(start, end))) return null;
|
|
3005
|
+
return { start, end };
|
|
3006
|
+
}
|
|
2329
3007
|
function toInlineExpression(code) {
|
|
2330
3008
|
const [imports, exports] = parse(code);
|
|
2331
3009
|
if (imports.length > 0) return null;
|
|
2332
|
-
|
|
2333
|
-
if (
|
|
2334
|
-
|
|
3010
|
+
let body = code;
|
|
3011
|
+
if (exports.length > 0) {
|
|
3012
|
+
const range = exportClauseRange(code, exports);
|
|
3013
|
+
if (!range) return null;
|
|
3014
|
+
body = code.slice(0, range.start) + code.slice(range.end);
|
|
3015
|
+
}
|
|
3016
|
+
try {
|
|
3017
|
+
const [bodyImports, bodyExports] = parse(body);
|
|
3018
|
+
if (bodyImports.length > 0 || bodyExports.length > 0) return null;
|
|
3019
|
+
} catch {
|
|
3020
|
+
return null;
|
|
3021
|
+
}
|
|
2335
3022
|
const returned = exports.map((record) => `${objectKey(record.n)}: ${record.ln ?? record.n}`).join(", ");
|
|
2336
3023
|
return `(() => {
|
|
2337
3024
|
${body.trim()}
|
|
3025
|
+
;
|
|
2338
3026
|
return { ${returned} };
|
|
2339
3027
|
})()`;
|
|
2340
3028
|
}
|
|
@@ -2363,32 +3051,46 @@ function wpScriptDedupe() {
|
|
|
2363
3051
|
// and normal buckets.
|
|
2364
3052
|
enforce: "pre",
|
|
2365
3053
|
apply: "build",
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
3054
|
+
resolveId: {
|
|
3055
|
+
// `enforce: 'pre'` alone only puts this plugin in the pre bucket —
|
|
3056
|
+
// *within* it, array position decides, and any pre-bucket resolver
|
|
3057
|
+
// registered earlier answers first and hands back an untagged id.
|
|
3058
|
+
// wpExternals' force-bundle resolver is exactly that: it answers
|
|
3059
|
+
// `wp-vite-bundled:@wordpress/icons` itself, so the barrel came back
|
|
3060
|
+
// unpartitioned and its whole subtree inherited one marker minted
|
|
3061
|
+
// from the barrel — identical for every entry, i.e. a shared chunk
|
|
3062
|
+
// (issue #23). Hook-level `order: 'pre'` puts this resolver ahead of
|
|
3063
|
+
// every other resolveId hook no matter how the plugins are composed;
|
|
3064
|
+
// it delegates through `this.resolve`, so those resolvers still
|
|
3065
|
+
// decide *what* an id resolves to. This plugin only tags the answer.
|
|
3066
|
+
order: "pre",
|
|
3067
|
+
async handler(source, importer, options) {
|
|
3068
|
+
if (!importer) return null;
|
|
3069
|
+
if (source.includes("\0")) return null;
|
|
3070
|
+
const key = `${importer}
|
|
2370
3071
|
${source}`;
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
3072
|
+
if (inFlight.has(key)) return null;
|
|
3073
|
+
const marker = markerOf(importer) ?? mintMarker(importer);
|
|
3074
|
+
inFlight.add(key);
|
|
3075
|
+
let resolved;
|
|
3076
|
+
try {
|
|
3077
|
+
resolved = await this.resolve(
|
|
3078
|
+
stripEntryMarker(source),
|
|
3079
|
+
stripEntryMarker(importer),
|
|
3080
|
+
{
|
|
3081
|
+
// `kind` rides along so plugins keying on `require-call`
|
|
3082
|
+
// (wpExternals' CJS shim) still see the truth.
|
|
3083
|
+
...options,
|
|
3084
|
+
skipSelf: true
|
|
3085
|
+
}
|
|
3086
|
+
);
|
|
3087
|
+
} finally {
|
|
3088
|
+
inFlight.delete(key);
|
|
3089
|
+
}
|
|
3090
|
+
if (!resolved || resolved.external) return resolved;
|
|
3091
|
+
if (!isPartitionable(resolved.id)) return resolved;
|
|
3092
|
+
return { ...resolved, id: withMarker(resolved.id, marker) };
|
|
2388
3093
|
}
|
|
2389
|
-
if (!resolved || resolved.external) return resolved;
|
|
2390
|
-
if (!isPartitionable(resolved.id)) return resolved;
|
|
2391
|
-
return { ...resolved, id: withMarker(resolved.id, marker) };
|
|
2392
3094
|
},
|
|
2393
3095
|
generateBundle: {
|
|
2394
3096
|
// Load-bearing, not cosmetic: wpExternals emits its `.asset.php`
|
|
@@ -2512,7 +3214,14 @@ var CLASSIC_SCRIPT_DEFINE = {
|
|
|
2512
3214
|
"import.meta.resolve": "undefined"
|
|
2513
3215
|
};
|
|
2514
3216
|
var LIVE_MODULE_STATEMENT = /^[ \t]*(?:import\s*(?:[{*"']|[A-Za-z_$][\w$]*\s*(?:,|from\b))|export\s*(?:[{*]|(?:default|const|let|var|function|class|async)\b))[^\n]*/m;
|
|
2515
|
-
|
|
3217
|
+
var ROLLDOWN_REQUIRE_SHIM = /typeof\s+require\s*(?:<|!==?|===?)/;
|
|
3218
|
+
var REQUIRE_CALL = /(?:^|[^.\w$])require\s*\(/;
|
|
3219
|
+
function buildRequireShim(globals) {
|
|
3220
|
+
const cases = [...globals.entries()].map(([specifier, global]) => `case ${JSON.stringify(specifier)}:return window.${global};`).join("");
|
|
3221
|
+
return `const require=(id)=>{switch(id){${cases}}throw new Error('require("'+id+'") is not mapped to a global in this classic script.');};`;
|
|
3222
|
+
}
|
|
3223
|
+
function wpScriptIife(options = {}) {
|
|
3224
|
+
const globals = options.globals ?? /* @__PURE__ */ new Map();
|
|
2516
3225
|
return {
|
|
2517
3226
|
name: "wp-script-iife",
|
|
2518
3227
|
// Build-only. Vite filters plugins by `apply` before it runs `config`,
|
|
@@ -2528,34 +3237,68 @@ function wpScriptIife() {
|
|
|
2528
3237
|
}
|
|
2529
3238
|
return Object.keys(define).length > 0 ? { define } : null;
|
|
2530
3239
|
},
|
|
2531
|
-
async writeBundle(
|
|
3240
|
+
async writeBundle(options2, bundle) {
|
|
2532
3241
|
const { readFile, writeFile } = await import('fs/promises');
|
|
2533
|
-
const { join:
|
|
2534
|
-
const outDir =
|
|
3242
|
+
const { join: join6 } = await import('path');
|
|
3243
|
+
const outDir = options2.dir ?? ".";
|
|
3244
|
+
const pending = [];
|
|
3245
|
+
const failures = [];
|
|
2535
3246
|
for (const [fileName, output] of Object.entries(bundle)) {
|
|
2536
3247
|
if (output.type !== "chunk") continue;
|
|
2537
3248
|
if (!fileName.endsWith(".js") && !fileName.endsWith(".mjs")) continue;
|
|
2538
3249
|
if (!output.isEntry && !output.isDynamicEntry) continue;
|
|
2539
|
-
const filePath =
|
|
3250
|
+
const filePath = join6(outDir, fileName);
|
|
2540
3251
|
let chunkSource = await readFile(filePath, "utf8");
|
|
2541
|
-
const sideEffectImport = /^\s*import\s
|
|
3252
|
+
const sideEffectImport = /^\s*import\s*["'][^"']+["'];?\s*\n?/;
|
|
2542
3253
|
while (sideEffectImport.test(chunkSource)) {
|
|
2543
3254
|
chunkSource = chunkSource.replace(sideEffectImport, "");
|
|
2544
3255
|
}
|
|
2545
|
-
|
|
2546
|
-
|
|
3256
|
+
const failure = classicScriptFailure(
|
|
3257
|
+
fileName,
|
|
3258
|
+
chunkSource,
|
|
3259
|
+
output.isEntry !== true
|
|
3260
|
+
);
|
|
3261
|
+
if (failure) {
|
|
3262
|
+
failures.push(failure);
|
|
3263
|
+
continue;
|
|
3264
|
+
}
|
|
3265
|
+
if (ROLLDOWN_REQUIRE_SHIM.test(chunkSource)) {
|
|
3266
|
+
pending.push({
|
|
3267
|
+
filePath,
|
|
3268
|
+
body: `${buildRequireShim(globals)}(()=>{
|
|
2547
3269
|
${chunkSource}
|
|
3270
|
+
})();`
|
|
3271
|
+
});
|
|
3272
|
+
continue;
|
|
3273
|
+
}
|
|
3274
|
+
if (globals.size > 0 && REQUIRE_CALL.test(chunkSource)) {
|
|
3275
|
+
this.warn(
|
|
3276
|
+
`${fileName} calls \`require()\` but carries none of Rolldown's runtime require shim, so no global mapping was injected. If this is a CJS dependency reaching for an external, it will throw at runtime \u2014 please report it, as the shim's shape may have changed.`
|
|
3277
|
+
);
|
|
3278
|
+
}
|
|
3279
|
+
pending.push({ filePath, body: chunkSource });
|
|
3280
|
+
}
|
|
3281
|
+
if (failures.length > 0) {
|
|
3282
|
+
throw new Error(
|
|
3283
|
+
`wp-script-iife: ${failures.length} chunk(s) are not valid as classic scripts, so nothing was wrapped:
|
|
3284
|
+
|
|
3285
|
+
${failures.join("\n\n")}`
|
|
3286
|
+
);
|
|
3287
|
+
}
|
|
3288
|
+
for (const { filePath, body } of pending) {
|
|
3289
|
+
await writeFile(filePath, `(()=>{
|
|
3290
|
+
${body}
|
|
2548
3291
|
})();
|
|
2549
3292
|
`);
|
|
2550
3293
|
}
|
|
2551
3294
|
}
|
|
2552
3295
|
};
|
|
2553
3296
|
}
|
|
2554
|
-
function
|
|
3297
|
+
function classicScriptFailure(fileName, code, isDynamicOnly = false) {
|
|
2555
3298
|
let parseError;
|
|
2556
3299
|
try {
|
|
2557
3300
|
new Function(code);
|
|
2558
|
-
return;
|
|
3301
|
+
return null;
|
|
2559
3302
|
} catch (error) {
|
|
2560
3303
|
if (!(error instanceof SyntaxError)) throw error;
|
|
2561
3304
|
parseError = error;
|
|
@@ -2563,24 +3306,19 @@ function assertClassicScript(fileName, code, isDynamicOnly = false) {
|
|
|
2563
3306
|
const liveStatement = code.match(LIVE_MODULE_STATEMENT);
|
|
2564
3307
|
if (liveStatement) {
|
|
2565
3308
|
const specifiers = [...code.matchAll(/^[ \t]*(?:import|export)\b[^\n]*?["']([^"']+)["']/gm)].map((match) => match[1]).filter((specifier, index, all) => all.indexOf(specifier) === index);
|
|
2566
|
-
|
|
2567
|
-
`wp-script-iife: ${fileName} still contains a top-level module statement, which is a SyntaxError once WordPress enqueues it as a classic script:
|
|
3309
|
+
return `wp-script-iife: ${fileName} still contains a top-level module statement, which is a SyntaxError once WordPress enqueues it as a classic script:
|
|
2568
3310
|
${liveStatement[0].trim()}
|
|
2569
3311
|
` + (specifiers.length > 0 ? ` Imported chunk(s): ${specifiers.join(", ")}
|
|
2570
|
-
` : "") + (isDynamicOnly ? ` This file is a dynamic-import chunk, so it needs its \`export\`s and cannot be an IIFE. A script-mode entry can't lazily load one either (WP has no module context to resolve it in) \u2014 drop the dynamic \`import()\`, or build this entry in module mode.` : ` This is almost always a shared chunk: a module two or more script-mode entries import gets hoisted into its own chunk instead of being duplicated into each entry. Add the wpScriptDedupe() plugin (auto-included by wp() in script mode \u2014 check you haven't set \`scriptDedupe: false\`), or build this entry in module mode
|
|
2571
|
-
|
|
3312
|
+
` : "") + (isDynamicOnly ? ` This file is a dynamic-import chunk, so it needs its \`export\`s and cannot be an IIFE. A script-mode entry can't lazily load one either (WP has no module context to resolve it in) \u2014 drop the dynamic \`import()\`, or build this entry in module mode.` : ` This is almost always a shared chunk: a module two or more script-mode entries import gets hoisted into its own chunk instead of being duplicated into each entry. Add the wpScriptDedupe() plugin (auto-included by wp() in script mode \u2014 check you haven't set \`scriptDedupe: false\`), or build this entry in module mode.
|
|
3313
|
+
If the specifier above is a bare \`@wordpress/*\` package rather than a relative chunk path, this is instead a module-only package (no \`wpScript\` in its package.json) imported into a script-mode entry \u2014 wpExternals warns about it by name earlier in the build. Move that import to a viewScriptModule entry, or build this entry in module mode.`);
|
|
2572
3314
|
}
|
|
2573
3315
|
if (/\bimport\.meta\b/.test(code)) {
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
Vite injects its module-preload helper (which reads \`import.meta.url\`) whenever the bundled graph contains a dynamic \`import()\` \u2014 \`build.modulePreload: false\` does not remove it. This plugin's \`config()\` hook defines \`import.meta.url\`/\`import.meta.resolve\` away; a surviving reference means either that define was overridden, or the code touches another \`import.meta\` member. Extend \`define\` in your Vite config, or move this entry to module mode.`
|
|
2577
|
-
);
|
|
3316
|
+
return `wp-script-iife: ${fileName} references \`import.meta\`, which is a SyntaxError once WordPress enqueues it as a classic script.
|
|
3317
|
+
Vite injects its module-preload helper (which reads \`import.meta.url\`) whenever the bundled graph contains a dynamic \`import()\` \u2014 \`build.modulePreload: false\` does not remove it. This plugin's \`config()\` hook defines \`import.meta.url\`/\`import.meta.resolve\` away; a surviving reference means either that define was overridden, or the code touches another \`import.meta\` member. Extend \`define\` in your Vite config, or move this entry to module mode.`;
|
|
2578
3318
|
}
|
|
2579
|
-
|
|
2580
|
-
`wp-script-iife: ${fileName} does not parse as a classic script, so WordPress could not load it:
|
|
3319
|
+
return `wp-script-iife: ${fileName} does not parse as a classic script, so WordPress could not load it:
|
|
2581
3320
|
${parseError.message}
|
|
2582
|
-
Nothing in this plugin's usual suspects (a shared chunk's \`import\`, an \`import.meta\` reference) explains it \u2014 inspect the written file, and report it upstream if the source is valid
|
|
2583
|
-
);
|
|
3321
|
+
Nothing in this plugin's usual suspects (a shared chunk's \`import\`, an \`import.meta\` reference) explains it \u2014 inspect the written file, and report it upstream if the source is valid.`;
|
|
2584
3322
|
}
|
|
2585
3323
|
|
|
2586
3324
|
// src/wp-clean-css-chunks.ts
|
|
@@ -2599,17 +3337,37 @@ function wpCleanCssChunks() {
|
|
|
2599
3337
|
};
|
|
2600
3338
|
}
|
|
2601
3339
|
var STYLESHEET_EXTENSION = /\.(css|scss|sass)$/i;
|
|
3340
|
+
function tryRealpath(path) {
|
|
3341
|
+
try {
|
|
3342
|
+
return realpathSync(path);
|
|
3343
|
+
} catch {
|
|
3344
|
+
return path;
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
function entrySourcePath2(sourcePath, root) {
|
|
3348
|
+
const relativePath = relative(tryRealpath(root), tryRealpath(sourcePath)).replace(/\\/g, "/");
|
|
3349
|
+
if (relativePath === "" || relativePath === ".." || relativePath.startsWith("../")) {
|
|
3350
|
+
return null;
|
|
3351
|
+
}
|
|
3352
|
+
if (isAbsolute(relativePath)) return null;
|
|
3353
|
+
return relativePath;
|
|
3354
|
+
}
|
|
3355
|
+
function phpSingleQuoteEscape2(value) {
|
|
3356
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
3357
|
+
}
|
|
2602
3358
|
function wpCssAssetInfo() {
|
|
2603
3359
|
const cssEntries = /* @__PURE__ */ new Map();
|
|
3360
|
+
let resolvedRoot = process.cwd();
|
|
2604
3361
|
return {
|
|
2605
3362
|
name: "wp-css-asset-info",
|
|
2606
3363
|
configResolved(config) {
|
|
2607
3364
|
cssEntries.clear();
|
|
3365
|
+
resolvedRoot = config.root;
|
|
2608
3366
|
const input = config.build?.rollupOptions?.input;
|
|
2609
3367
|
if (!input || typeof input !== "object" || Array.isArray(input)) return;
|
|
2610
3368
|
for (const [entryName, source] of Object.entries(input)) {
|
|
2611
3369
|
if (typeof source === "string" && STYLESHEET_EXTENSION.test(source)) {
|
|
2612
|
-
cssEntries.set(entryName, source);
|
|
3370
|
+
cssEntries.set(entryName, resolve(config.root, source));
|
|
2613
3371
|
}
|
|
2614
3372
|
}
|
|
2615
3373
|
},
|
|
@@ -2626,10 +3384,13 @@ function wpCssAssetInfo() {
|
|
|
2626
3384
|
const assetPhpFileName = cssAsset.fileName.replace(/\.css$/i, ".asset.php");
|
|
2627
3385
|
if (bundle[assetPhpFileName]) continue;
|
|
2628
3386
|
const version = crypto.createHash("md5").update(cssAsset.source).digest("hex");
|
|
3387
|
+
const fields = [`'dependencies' => array()`, `'version' => '${version}'`];
|
|
3388
|
+
const source = entrySourcePath2(sourcePath, resolvedRoot);
|
|
3389
|
+
if (source) fields.push(`'source' => '${phpSingleQuoteEscape2(source)}'`);
|
|
2629
3390
|
this.emitFile({
|
|
2630
3391
|
type: "asset",
|
|
2631
3392
|
fileName: assetPhpFileName,
|
|
2632
|
-
source: `<?php return array(
|
|
3393
|
+
source: `<?php return array( ${fields.join(", ")} );
|
|
2633
3394
|
`
|
|
2634
3395
|
});
|
|
2635
3396
|
}
|
|
@@ -2647,6 +3408,681 @@ function findCssAsset(bundle, expectedFileName, sourcePath) {
|
|
|
2647
3408
|
}
|
|
2648
3409
|
return void 0;
|
|
2649
3410
|
}
|
|
3411
|
+
var HOT_FILE_NAME = ".wp-vite-dev.json";
|
|
3412
|
+
var DEV_SERVER_PHP_NAME = "dev-server.php";
|
|
3413
|
+
var DEFAULT_PHP_PREFIX = "wp-vite";
|
|
3414
|
+
var REPORTED_PLUGINS = ["vite:react-refresh"];
|
|
3415
|
+
var SCAN_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
3416
|
+
var STYLESHEET_EXTENSION2 = /\.(css|scss|sass)$/i;
|
|
3417
|
+
var JSX_SOURCE_EXTENSION = /\.[jt]sx$/i;
|
|
3418
|
+
var JSX_RUNTIME_SPECIFIER = "react/jsx-runtime";
|
|
3419
|
+
var DEFAULT_MAX_SCAN_DEPTH = 24;
|
|
3420
|
+
var REDISCOVER_DEBOUNCE_MS = 50;
|
|
3421
|
+
function wpDevServer(options = {}) {
|
|
3422
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
3423
|
+
const emitManifest = options.emitManifest ?? true;
|
|
3424
|
+
const buildType = options.buildType ?? "script";
|
|
3425
|
+
const skip = new Set(options.skip ?? []);
|
|
3426
|
+
const maxScanDepth = options.maxScanDepth ?? DEFAULT_MAX_SCAN_DEPTH;
|
|
3427
|
+
const moduleEntryMatchers = options.moduleEntryMatchers;
|
|
3428
|
+
const wpBuildAssetMatchers = (options.wpBuildAssetMatchers ?? []).map(toPosix);
|
|
3429
|
+
const resolveExternalOptions = {
|
|
3430
|
+
projectRoot,
|
|
3431
|
+
...options.additionalExternals ? { vendorExternals: options.additionalExternals } : {},
|
|
3432
|
+
...options.externalNamespaces ? { externalNamespaces: options.externalNamespaces } : {},
|
|
3433
|
+
...options.scriptModuleNamespaces ? { scriptModuleNamespaces: options.scriptModuleNamespaces } : {},
|
|
3434
|
+
...options.bundledPackages ? { bundledPackages: options.bundledPackages } : {}
|
|
3435
|
+
};
|
|
3436
|
+
const blocksDirAbs = options.blocksDir ? resolve(process.cwd(), options.blocksDir) : null;
|
|
3437
|
+
const blockStylesDirAbs = options.blocksStylesDir ? resolve(process.cwd(), options.blocksStylesDir) : null;
|
|
3438
|
+
const adminPagesDirAbs = options.adminPagesDir ? resolve(process.cwd(), options.adminPagesDir) : null;
|
|
3439
|
+
let config;
|
|
3440
|
+
let outDirAbs = "";
|
|
3441
|
+
let entries = [];
|
|
3442
|
+
let configuredEntries = {};
|
|
3443
|
+
let entriesByFile = /* @__PURE__ */ new Map();
|
|
3444
|
+
let writtenSidecars = /* @__PURE__ */ new Set();
|
|
3445
|
+
let writtenBlockFiles = /* @__PURE__ */ new Set();
|
|
3446
|
+
const collectEntries = () => discoverDevEntries({
|
|
3447
|
+
blocksDirAbs,
|
|
3448
|
+
blockStylesDirAbs,
|
|
3449
|
+
adminPagesDirAbs,
|
|
3450
|
+
skip,
|
|
3451
|
+
configuredEntries,
|
|
3452
|
+
buildType
|
|
3453
|
+
});
|
|
3454
|
+
const syncModuleEntryMatchers = () => {
|
|
3455
|
+
if (!moduleEntryMatchers) return;
|
|
3456
|
+
for (const entry of entries) {
|
|
3457
|
+
if (entry.mode !== "module" || entry.kind !== "js") continue;
|
|
3458
|
+
const matcher = toPosix(entry.file);
|
|
3459
|
+
if (!moduleEntryMatchers.includes(matcher)) moduleEntryMatchers.push(matcher);
|
|
3460
|
+
}
|
|
3461
|
+
};
|
|
3462
|
+
return {
|
|
3463
|
+
name: "wp-dev-server",
|
|
3464
|
+
// Build is untouched: every write below exists only because there is no
|
|
3465
|
+
// bundle to emit into.
|
|
3466
|
+
apply: "serve",
|
|
3467
|
+
/**
|
|
3468
|
+
* WordPress ships the *production* JSX runtime and nothing else. Core's
|
|
3469
|
+
* `react-jsx-runtime` handle is built from `react/jsx-runtime`, so
|
|
3470
|
+
* `window.ReactJSXRuntime` carries `jsx`, `jsxs` and `Fragment` — there
|
|
3471
|
+
* is no `jsxDEV` on it, in `SCRIPT_DEBUG` or out of it.
|
|
3472
|
+
*
|
|
3473
|
+
* Vite's serve default is the opposite: JSX compiles against the dev
|
|
3474
|
+
* runtime, `react/jsx-dev-runtime`. `wpExternals` then does exactly what
|
|
3475
|
+
* it is supposed to and rewrites that import to the global WordPress
|
|
3476
|
+
* registers for it, so every block editor script with JSX in it comes
|
|
3477
|
+
* out as `const { jsxDEV: _jsxDEV } = (window.ReactJSXRuntime)` —
|
|
3478
|
+
* `undefined`, and a `_jsxDEV is not a function` the moment anything
|
|
3479
|
+
* renders. Builds never see it (`development` is off there), which makes
|
|
3480
|
+
* it a dev-only cliff that a passing build tells you nothing about.
|
|
3481
|
+
*
|
|
3482
|
+
* Filled in only where the user has no opinion, the same rule
|
|
3483
|
+
* `wpScriptIife`'s `define` injection and `wpModuleEntrySignatures`
|
|
3484
|
+
* follow. The cost is the `__source`/`__self` debug props; the benefit is
|
|
3485
|
+
* that the editor script runs at all.
|
|
3486
|
+
*/
|
|
3487
|
+
config(userConfig) {
|
|
3488
|
+
const oxc = userConfig.oxc;
|
|
3489
|
+
if (oxc === false) return null;
|
|
3490
|
+
const jsx = oxc?.jsx;
|
|
3491
|
+
if (jsx === "preserve") return null;
|
|
3492
|
+
if (jsx && typeof jsx === "object" && jsx.development !== void 0) return null;
|
|
3493
|
+
return { oxc: { jsx: { development: false } } };
|
|
3494
|
+
},
|
|
3495
|
+
configResolved(resolvedConfig) {
|
|
3496
|
+
config = resolvedConfig;
|
|
3497
|
+
outDirAbs = resolve(resolvedConfig.root, resolvedConfig.build.outDir);
|
|
3498
|
+
configuredEntries = readConfiguredEntries(resolvedConfig);
|
|
3499
|
+
entries = collectEntries();
|
|
3500
|
+
syncModuleEntryMatchers();
|
|
3501
|
+
},
|
|
3502
|
+
/**
|
|
3503
|
+
* Answer for the bare specifiers the escape above hands back to the
|
|
3504
|
+
* browser.
|
|
3505
|
+
*
|
|
3506
|
+
* `installBareSpecifierEscape` rewrites the served code *after*
|
|
3507
|
+
* import-analysis, which means Vite's module graph still holds the
|
|
3508
|
+
* `/@id/@wordpress/interactivity` form it produced — and Vite eagerly
|
|
3509
|
+
* pre-transforms every import it recorded. That request resolves to a
|
|
3510
|
+
* bare specifier nothing on disk backs, and the console gets a red
|
|
3511
|
+
* `Pre-transform error: Failed to load url @wordpress/interactivity` for
|
|
3512
|
+
* every Script Module entry, on a server that is working perfectly.
|
|
3513
|
+
*
|
|
3514
|
+
* An empty module makes the warm-up succeed and changes nothing else:
|
|
3515
|
+
* the URL is not in the code any browser receives, so it is never
|
|
3516
|
+
* fetched — WordPress's import map resolves the specifier instead.
|
|
3517
|
+
* Narrow by construction: only ids `resolveExternal()` recognizes (i.e.
|
|
3518
|
+
* an installed package whose own metadata says it is externalizable),
|
|
3519
|
+
* and never a path, so a genuinely missing module still fails loudly.
|
|
3520
|
+
*/
|
|
3521
|
+
load(id) {
|
|
3522
|
+
if (id.startsWith(".") || id.startsWith("/") || id.startsWith("\0")) return null;
|
|
3523
|
+
if (isAbsolute(id) || !resolveExternal(id, resolveExternalOptions)) return null;
|
|
3524
|
+
return "export {};\n";
|
|
3525
|
+
},
|
|
3526
|
+
async configureServer(server) {
|
|
3527
|
+
installBareSpecifierEscape(server, config, resolveExternalOptions);
|
|
3528
|
+
const refreshAll = async () => {
|
|
3529
|
+
const { index, written } = await writeAllSidecars({
|
|
3530
|
+
entries,
|
|
3531
|
+
outDirAbs,
|
|
3532
|
+
root: config.root,
|
|
3533
|
+
resolveExternalOptions,
|
|
3534
|
+
wpBuildAssetMatchers,
|
|
3535
|
+
maxScanDepth
|
|
3536
|
+
});
|
|
3537
|
+
entriesByFile = index;
|
|
3538
|
+
pruneStale(writtenSidecars, written);
|
|
3539
|
+
writtenSidecars = written;
|
|
3540
|
+
};
|
|
3541
|
+
const refreshFor = async (file) => {
|
|
3542
|
+
const affected = entriesByFile.get(toPosix(file));
|
|
3543
|
+
if (!affected || affected.size === 0) return;
|
|
3544
|
+
const subset = entries.filter((entry) => affected.has(entry.name));
|
|
3545
|
+
const { index: rescanned } = await writeAllSidecars({
|
|
3546
|
+
entries: subset,
|
|
3547
|
+
outDirAbs,
|
|
3548
|
+
root: config.root,
|
|
3549
|
+
resolveExternalOptions,
|
|
3550
|
+
wpBuildAssetMatchers,
|
|
3551
|
+
maxScanDepth
|
|
3552
|
+
});
|
|
3553
|
+
for (const [scannedFile, names] of rescanned) {
|
|
3554
|
+
const existing = entriesByFile.get(scannedFile);
|
|
3555
|
+
if (existing) for (const name of names) existing.add(name);
|
|
3556
|
+
else entriesByFile.set(scannedFile, new Set(names));
|
|
3557
|
+
}
|
|
3558
|
+
};
|
|
3559
|
+
const refreshBlocksTree = () => {
|
|
3560
|
+
const written = writeBlocksTree({ blocksDirAbs, outDirAbs, emitManifest, skip });
|
|
3561
|
+
pruneStale(writtenBlockFiles, written);
|
|
3562
|
+
writtenBlockFiles = written;
|
|
3563
|
+
};
|
|
3564
|
+
const addWatchDirs = () => {
|
|
3565
|
+
for (const dir of [blocksDirAbs, blockStylesDirAbs, adminPagesDirAbs]) {
|
|
3566
|
+
if (dir && existsSync(dir)) server.watcher.add(dir);
|
|
3567
|
+
}
|
|
3568
|
+
};
|
|
3569
|
+
const rediscover = () => {
|
|
3570
|
+
addWatchDirs();
|
|
3571
|
+
refreshBlocksTree();
|
|
3572
|
+
configuredEntries = readConfiguredEntries(config);
|
|
3573
|
+
entries = collectEntries();
|
|
3574
|
+
syncModuleEntryMatchers();
|
|
3575
|
+
return refreshAll();
|
|
3576
|
+
};
|
|
3577
|
+
refreshBlocksTree();
|
|
3578
|
+
writeFileTree(
|
|
3579
|
+
join(outDirAbs, DEV_SERVER_PHP_NAME),
|
|
3580
|
+
renderDevServerPhp({
|
|
3581
|
+
prefix: resolvePhpPrefix(options.phpPrefix, config.root),
|
|
3582
|
+
hotFile: HOT_FILE_NAME
|
|
3583
|
+
})
|
|
3584
|
+
);
|
|
3585
|
+
await refreshAll();
|
|
3586
|
+
addWatchDirs();
|
|
3587
|
+
watchSources({
|
|
3588
|
+
server,
|
|
3589
|
+
sourceDirs: [blocksDirAbs, blockStylesDirAbs, adminPagesDirAbs],
|
|
3590
|
+
blocksDirAbs,
|
|
3591
|
+
onRediscover: rediscover,
|
|
3592
|
+
onFileChange: (file) => refreshFor(file)
|
|
3593
|
+
});
|
|
3594
|
+
const hotFilePath = join(outDirAbs, HOT_FILE_NAME);
|
|
3595
|
+
const httpServer = server.httpServer;
|
|
3596
|
+
if (httpServer) {
|
|
3597
|
+
httpServer.once("listening", () => {
|
|
3598
|
+
const hotFile = buildHotFile(config, httpServer.address());
|
|
3599
|
+
writeFileTree(hotFilePath, `${JSON.stringify(hotFile, null, " ")}
|
|
3600
|
+
`);
|
|
3601
|
+
registerHotFileCleanup(hotFilePath);
|
|
3602
|
+
});
|
|
3603
|
+
}
|
|
3604
|
+
},
|
|
3605
|
+
/**
|
|
3606
|
+
* Vite closes the plugin container on `server.close()`, which is what
|
|
3607
|
+
* makes this fire for a programmatically-created server (tests, a
|
|
3608
|
+
* wrapper process). The `process` handlers registered above cover the
|
|
3609
|
+
* interactive case, where nothing calls `close()` at all.
|
|
3610
|
+
*/
|
|
3611
|
+
closeBundle() {
|
|
3612
|
+
removeHotFile(join(outDirAbs, HOT_FILE_NAME));
|
|
3613
|
+
}
|
|
3614
|
+
};
|
|
3615
|
+
}
|
|
3616
|
+
function wpDevServerPhp(options = {}) {
|
|
3617
|
+
let projectRoot = options.projectRoot ?? process.cwd();
|
|
3618
|
+
return {
|
|
3619
|
+
name: "wp-dev-server-php",
|
|
3620
|
+
apply: "build",
|
|
3621
|
+
configResolved(resolvedConfig) {
|
|
3622
|
+
projectRoot = options.projectRoot ?? resolvedConfig.root;
|
|
3623
|
+
},
|
|
3624
|
+
generateBundle() {
|
|
3625
|
+
this.emitFile({
|
|
3626
|
+
type: "asset",
|
|
3627
|
+
fileName: DEV_SERVER_PHP_NAME,
|
|
3628
|
+
source: renderDevServerPhp({
|
|
3629
|
+
prefix: resolvePhpPrefix(options.phpPrefix, projectRoot),
|
|
3630
|
+
hotFile: HOT_FILE_NAME
|
|
3631
|
+
})
|
|
3632
|
+
});
|
|
3633
|
+
}
|
|
3634
|
+
};
|
|
3635
|
+
}
|
|
3636
|
+
function resolvePhpPrefix(explicit, projectRoot) {
|
|
3637
|
+
const fromOption = sanitizePhpPrefix(explicit);
|
|
3638
|
+
if (fromOption) return fromOption;
|
|
3639
|
+
let packageJson = {};
|
|
3640
|
+
try {
|
|
3641
|
+
packageJson = JSON.parse(readFileSync(join(projectRoot, "package.json"), "utf8"));
|
|
3642
|
+
} catch {
|
|
3643
|
+
}
|
|
3644
|
+
return sanitizePhpPrefix(packageJson.wpPlugin?.name) ?? sanitizePhpPrefix(packageJson.name?.replace(/^@[^/]+\//, "")) ?? DEFAULT_PHP_PREFIX;
|
|
3645
|
+
}
|
|
3646
|
+
function sanitizePhpPrefix(value) {
|
|
3647
|
+
if (!value) return null;
|
|
3648
|
+
const cleaned = value.replace(/[^A-Za-z0-9_-]/g, "-").replace(/^-+|-+$/g, "");
|
|
3649
|
+
return cleaned !== "" && isValidPhpIdentifier(cleaned) ? cleaned : null;
|
|
3650
|
+
}
|
|
3651
|
+
function buildHotFile(config, address) {
|
|
3652
|
+
return {
|
|
3653
|
+
origin: resolveDevOrigin(config, address),
|
|
3654
|
+
base: config.base,
|
|
3655
|
+
plugins: REPORTED_PLUGINS.filter(
|
|
3656
|
+
(name) => config.plugins.some((plugin) => plugin.name === name)
|
|
3657
|
+
)
|
|
3658
|
+
};
|
|
3659
|
+
}
|
|
3660
|
+
var UNPUBLISHABLE_ADDRESSES = /* @__PURE__ */ new Set([
|
|
3661
|
+
"0.0.0.0",
|
|
3662
|
+
"::",
|
|
3663
|
+
"::0",
|
|
3664
|
+
"0000:0000:0000:0000:0000:0000:0000:0000",
|
|
3665
|
+
"127.0.0.1",
|
|
3666
|
+
"::1",
|
|
3667
|
+
"0000:0000:0000:0000:0000:0000:0000:0001"
|
|
3668
|
+
]);
|
|
3669
|
+
function resolveDevOrigin(config, address) {
|
|
3670
|
+
const explicitOrigin = config.server?.origin;
|
|
3671
|
+
if (typeof explicitOrigin === "string" && explicitOrigin.length > 0) {
|
|
3672
|
+
return explicitOrigin.replace(/\/+$/, "");
|
|
3673
|
+
}
|
|
3674
|
+
const hmr = typeof config.server?.hmr === "object" ? config.server.hmr : null;
|
|
3675
|
+
const hmrProtocol = hmr && "protocol" in hmr ? hmr.protocol : void 0;
|
|
3676
|
+
const protocol = hmrProtocol ? hmrProtocol === "wss" ? "https" : "http" : config.server?.https ? "https" : "http";
|
|
3677
|
+
const hmrHost = hmr && "host" in hmr && typeof hmr.host === "string" ? hmr.host : void 0;
|
|
3678
|
+
const configHost = typeof config.server?.host === "string" ? config.server.host : void 0;
|
|
3679
|
+
const boundHost = address && typeof address === "object" ? address.address : void 0;
|
|
3680
|
+
const host = hmrHost ?? configHost ?? (boundHost ? normalizeBoundHost(boundHost) : "localhost");
|
|
3681
|
+
const hmrClientPort = hmr && "clientPort" in hmr && typeof hmr.clientPort === "number" ? hmr.clientPort : void 0;
|
|
3682
|
+
const boundPort = address && typeof address === "object" ? address.port : void 0;
|
|
3683
|
+
const port = hmrClientPort ?? boundPort;
|
|
3684
|
+
return port === void 0 ? `${protocol}://${host}` : `${protocol}://${host}:${port}`;
|
|
3685
|
+
}
|
|
3686
|
+
function normalizeBoundHost(host) {
|
|
3687
|
+
if (UNPUBLISHABLE_ADDRESSES.has(host)) return "localhost";
|
|
3688
|
+
if (host.includes(":") && !host.startsWith("[")) return `[${host}]`;
|
|
3689
|
+
return host;
|
|
3690
|
+
}
|
|
3691
|
+
var pendingHotFiles = /* @__PURE__ */ new Set();
|
|
3692
|
+
var hotFileHandlersBound = false;
|
|
3693
|
+
function registerHotFileCleanup(hotFilePath) {
|
|
3694
|
+
pendingHotFiles.add(hotFilePath);
|
|
3695
|
+
if (hotFileHandlersBound) return;
|
|
3696
|
+
hotFileHandlersBound = true;
|
|
3697
|
+
const cleanAll = () => {
|
|
3698
|
+
for (const filePath of pendingHotFiles) removeHotFile(filePath);
|
|
3699
|
+
pendingHotFiles.clear();
|
|
3700
|
+
};
|
|
3701
|
+
process.on("exit", cleanAll);
|
|
3702
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
3703
|
+
process.on(signal, () => {
|
|
3704
|
+
cleanAll();
|
|
3705
|
+
process.exit();
|
|
3706
|
+
});
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
function removeHotFile(hotFilePath) {
|
|
3710
|
+
pendingHotFiles.delete(hotFilePath);
|
|
3711
|
+
try {
|
|
3712
|
+
rmSync(hotFilePath, { force: true });
|
|
3713
|
+
} catch {
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3716
|
+
var decoratedEnvironments = /* @__PURE__ */ new WeakSet();
|
|
3717
|
+
function installBareSpecifierEscape(server, config, resolveExternalOptions) {
|
|
3718
|
+
const base = config.base ?? "/";
|
|
3719
|
+
const wrappedPrefix = `${base.replace(/\/+$/, "")}/@id/`;
|
|
3720
|
+
const pattern = new RegExp(`(["'])${escapeRegExp2(wrappedPrefix)}([^"'\\s]+)\\1`, "g");
|
|
3721
|
+
const unwrap = (code) => code.replace(
|
|
3722
|
+
pattern,
|
|
3723
|
+
(match, quote, specifier) => resolveExternal(specifier, resolveExternalOptions) ? `${quote}${specifier}${quote}` : match
|
|
3724
|
+
);
|
|
3725
|
+
for (const environment of Object.values(server.environments)) {
|
|
3726
|
+
if (decoratedEnvironments.has(environment)) continue;
|
|
3727
|
+
decoratedEnvironments.add(environment);
|
|
3728
|
+
const original = environment.transformRequest.bind(environment);
|
|
3729
|
+
environment.transformRequest = async (url) => {
|
|
3730
|
+
const result = await original(url);
|
|
3731
|
+
if (!result || typeof result.code !== "string") return result;
|
|
3732
|
+
if (!result.code.includes(wrappedPrefix)) return result;
|
|
3733
|
+
const code = unwrap(result.code);
|
|
3734
|
+
return code === result.code ? result : { ...result, code };
|
|
3735
|
+
};
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
function escapeRegExp2(value) {
|
|
3739
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3740
|
+
}
|
|
3741
|
+
function discoverDevEntries(options) {
|
|
3742
|
+
const { blocksDirAbs, blockStylesDirAbs, adminPagesDirAbs, skip } = options;
|
|
3743
|
+
const byName = /* @__PURE__ */ new Map();
|
|
3744
|
+
const add = (entry) => {
|
|
3745
|
+
if (!byName.has(entry.name)) byName.set(entry.name, entry);
|
|
3746
|
+
};
|
|
3747
|
+
if (blocksDirAbs && existsSync(blocksDirAbs)) {
|
|
3748
|
+
for (const [name, file] of Object.entries(discoverEntries(blocksDirAbs, "module", skip))) {
|
|
3749
|
+
add({ name, file, mode: "module", kind: entryKind(file) });
|
|
3750
|
+
}
|
|
3751
|
+
for (const [name, file] of Object.entries(discoverEntries(blocksDirAbs, "script", skip))) {
|
|
3752
|
+
add({ name, file, mode: "script", kind: entryKind(file) });
|
|
3753
|
+
}
|
|
3754
|
+
}
|
|
3755
|
+
if (blockStylesDirAbs && existsSync(blockStylesDirAbs)) {
|
|
3756
|
+
const stylesheets = fg.sync(`${toPosix(blockStylesDirAbs)}/**/*.{css,scss,sass}`, {
|
|
3757
|
+
absolute: true
|
|
3758
|
+
});
|
|
3759
|
+
for (const file of stylesheets) {
|
|
3760
|
+
const blockName = relative(blockStylesDirAbs, file).replace(extname(file), "").replace(/\\/g, "/");
|
|
3761
|
+
add({ name: `autoenqueue/${blockName}`, file, mode: "script", kind: "css" });
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
if (adminPagesDirAbs && existsSync(adminPagesDirAbs)) {
|
|
3765
|
+
for (const page of discoverPages(adminPagesDirAbs)) {
|
|
3766
|
+
add({
|
|
3767
|
+
name: `modules/${page.dirName}/content`,
|
|
3768
|
+
file: page.content,
|
|
3769
|
+
mode: "module",
|
|
3770
|
+
kind: "js"
|
|
3771
|
+
});
|
|
3772
|
+
if (page.route) {
|
|
3773
|
+
add({
|
|
3774
|
+
name: `modules/${page.dirName}/route`,
|
|
3775
|
+
file: page.route,
|
|
3776
|
+
mode: "module",
|
|
3777
|
+
kind: "js"
|
|
3778
|
+
});
|
|
3779
|
+
}
|
|
3780
|
+
for (const subRoute of page.subRoutes) {
|
|
3781
|
+
const base = `modules/${page.dirName}/routes/${subRoute.name}`;
|
|
3782
|
+
if (subRoute.content) {
|
|
3783
|
+
add({
|
|
3784
|
+
name: `${base}/content`,
|
|
3785
|
+
file: subRoute.content,
|
|
3786
|
+
mode: "module",
|
|
3787
|
+
kind: "js"
|
|
3788
|
+
});
|
|
3789
|
+
}
|
|
3790
|
+
if (subRoute.route) {
|
|
3791
|
+
add({
|
|
3792
|
+
name: `${base}/route`,
|
|
3793
|
+
file: subRoute.route,
|
|
3794
|
+
mode: "module",
|
|
3795
|
+
kind: "js"
|
|
3796
|
+
});
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
for (const [name, file] of Object.entries(options.configuredEntries ?? {})) {
|
|
3802
|
+
add({ name, file, mode: options.buildType ?? "script", kind: entryKind(file) });
|
|
3803
|
+
}
|
|
3804
|
+
return [...byName.values()].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
3805
|
+
}
|
|
3806
|
+
function readConfiguredEntries(config) {
|
|
3807
|
+
const input = config.build?.rollupOptions?.input;
|
|
3808
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return {};
|
|
3809
|
+
const entries = {};
|
|
3810
|
+
for (const [name, source] of Object.entries(input)) {
|
|
3811
|
+
if (typeof source !== "string") continue;
|
|
3812
|
+
const file = resolve(config.root, source);
|
|
3813
|
+
if (existsSync(file)) entries[name] = file;
|
|
3814
|
+
}
|
|
3815
|
+
return entries;
|
|
3816
|
+
}
|
|
3817
|
+
function entryKind(file) {
|
|
3818
|
+
return STYLESHEET_EXTENSION2.test(file) ? "css" : "js";
|
|
3819
|
+
}
|
|
3820
|
+
async function scanEntryDependencies(entryFile, options) {
|
|
3821
|
+
const maxDepth = options.maxDepth ?? DEFAULT_MAX_SCAN_DEPTH;
|
|
3822
|
+
const moduleIds = /* @__PURE__ */ new Set();
|
|
3823
|
+
const dynamicModuleIds = /* @__PURE__ */ new Set();
|
|
3824
|
+
const handles = /* @__PURE__ */ new Set();
|
|
3825
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3826
|
+
const files = [];
|
|
3827
|
+
let hasJsxSource = false;
|
|
3828
|
+
await init;
|
|
3829
|
+
const queue = [{ file: entryFile, depth: 0 }];
|
|
3830
|
+
while (queue.length > 0) {
|
|
3831
|
+
const { file, depth } = queue.shift();
|
|
3832
|
+
const key = toPosix(file);
|
|
3833
|
+
if (visited.has(key)) continue;
|
|
3834
|
+
visited.add(key);
|
|
3835
|
+
files.push(key);
|
|
3836
|
+
if (JSX_SOURCE_EXTENSION.test(key)) hasJsxSource = true;
|
|
3837
|
+
if (depth >= maxDepth) continue;
|
|
3838
|
+
if (STYLESHEET_EXTENSION2.test(file)) continue;
|
|
3839
|
+
let code;
|
|
3840
|
+
try {
|
|
3841
|
+
code = readFileSync(file, "utf8");
|
|
3842
|
+
} catch {
|
|
3843
|
+
continue;
|
|
3844
|
+
}
|
|
3845
|
+
for (const { specifier, isDynamic } of lexImports(code, file)) {
|
|
3846
|
+
if (specifier.startsWith(".") || isAbsolute(specifier)) {
|
|
3847
|
+
const resolved = resolveLocalImport(specifier, file);
|
|
3848
|
+
if (resolved) queue.push({ file: resolved, depth: depth + 1 });
|
|
3849
|
+
continue;
|
|
3850
|
+
}
|
|
3851
|
+
const external = resolveExternal(specifier, options.resolveExternalOptions);
|
|
3852
|
+
if (!external) continue;
|
|
3853
|
+
if (options.mode === "module" && external.moduleEligible) {
|
|
3854
|
+
if (isDynamic && specifier !== "@wordpress/interactivity") {
|
|
3855
|
+
dynamicModuleIds.add(external.specifier);
|
|
3856
|
+
} else {
|
|
3857
|
+
moduleIds.add(external.specifier);
|
|
3858
|
+
}
|
|
3859
|
+
continue;
|
|
3860
|
+
}
|
|
3861
|
+
if (external.handle) handles.add(external.handle);
|
|
3862
|
+
}
|
|
3863
|
+
}
|
|
3864
|
+
if (hasJsxSource) {
|
|
3865
|
+
const jsxRuntime = resolveExternal(JSX_RUNTIME_SPECIFIER, options.resolveExternalOptions);
|
|
3866
|
+
if (jsxRuntime?.handle) handles.add(jsxRuntime.handle);
|
|
3867
|
+
}
|
|
3868
|
+
for (const id of moduleIds) dynamicModuleIds.delete(id);
|
|
3869
|
+
return {
|
|
3870
|
+
moduleIds: [...moduleIds].sort(),
|
|
3871
|
+
dynamicModuleIds: [...dynamicModuleIds].sort(),
|
|
3872
|
+
handles: [...handles].sort(),
|
|
3873
|
+
files
|
|
3874
|
+
};
|
|
3875
|
+
}
|
|
3876
|
+
function lexImports(code, file) {
|
|
3877
|
+
try {
|
|
3878
|
+
const [imports] = parse(code, file);
|
|
3879
|
+
const found = [];
|
|
3880
|
+
for (const importRecord of imports) {
|
|
3881
|
+
if (importRecord.n === void 0) continue;
|
|
3882
|
+
found.push({ specifier: importRecord.n, isDynamic: importRecord.d > -1 });
|
|
3883
|
+
}
|
|
3884
|
+
return found;
|
|
3885
|
+
} catch {
|
|
3886
|
+
return regexImports(code);
|
|
3887
|
+
}
|
|
3888
|
+
}
|
|
3889
|
+
function stripComments(code) {
|
|
3890
|
+
return code.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:"'`\\])\/\/[^\n]*/g, "$1");
|
|
3891
|
+
}
|
|
3892
|
+
var DYNAMIC_IMPORT_RE = /\bimport\s*\(\s*(['"])([^'"]+)\1\s*\)/g;
|
|
3893
|
+
var STATIC_FROM_RE = /(?:^|[\s;}(])(?:import|export)\b[^;]*?\bfrom\s*(['"])([^'"]+)\1/g;
|
|
3894
|
+
var BARE_IMPORT_RE = /(?:^|[\s;}])import\s*(['"])([^'"]+)\1/g;
|
|
3895
|
+
function regexImports(code) {
|
|
3896
|
+
const source = stripComments(code);
|
|
3897
|
+
const found = /* @__PURE__ */ new Map();
|
|
3898
|
+
const record = (specifier, isDynamic) => {
|
|
3899
|
+
const existing = found.get(specifier);
|
|
3900
|
+
if (existing && !existing.isDynamic) return;
|
|
3901
|
+
found.set(specifier, { specifier, isDynamic });
|
|
3902
|
+
};
|
|
3903
|
+
for (const match of source.matchAll(DYNAMIC_IMPORT_RE)) record(match[2], true);
|
|
3904
|
+
for (const match of source.matchAll(STATIC_FROM_RE)) record(match[2], false);
|
|
3905
|
+
for (const match of source.matchAll(BARE_IMPORT_RE)) record(match[2], false);
|
|
3906
|
+
return [...found.values()];
|
|
3907
|
+
}
|
|
3908
|
+
function resolveLocalImport(specifier, importerFile) {
|
|
3909
|
+
const withoutQuery = specifier.split("?")[0] ?? specifier;
|
|
3910
|
+
const target = isAbsolute(withoutQuery) ? withoutQuery : join(dirname(importerFile), withoutQuery);
|
|
3911
|
+
if (existsSync(target) && !isDirectory(target)) return target;
|
|
3912
|
+
const candidates = [];
|
|
3913
|
+
const withoutJsExtension = target.replace(/\.[mc]?js$/, "");
|
|
3914
|
+
for (const extension of SCAN_EXTENSIONS) {
|
|
3915
|
+
candidates.push(`${target}${extension}`);
|
|
3916
|
+
if (withoutJsExtension !== target) candidates.push(`${withoutJsExtension}${extension}`);
|
|
3917
|
+
}
|
|
3918
|
+
for (const extension of SCAN_EXTENSIONS) candidates.push(join(target, `index${extension}`));
|
|
3919
|
+
return candidates.find((candidate) => existsSync(candidate)) ?? null;
|
|
3920
|
+
}
|
|
3921
|
+
function isDirectory(filePath) {
|
|
3922
|
+
try {
|
|
3923
|
+
return existsSync(filePath) && extname(filePath) === "";
|
|
3924
|
+
} catch {
|
|
3925
|
+
return false;
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
function renderDevAssetPhp(input) {
|
|
3929
|
+
const { scan, mode, kind, source } = input;
|
|
3930
|
+
const phpList = (items) => items.length > 0 ? ` ${items.join(", ")} ` : "";
|
|
3931
|
+
const quoted = (items) => items.map((item) => `'${phpSingleQuoteEscape3(item)}'`);
|
|
3932
|
+
const fields = [];
|
|
3933
|
+
if (kind === "css") {
|
|
3934
|
+
fields.push(`'dependencies' => array()`);
|
|
3935
|
+
} else if (input.wpBuildShape) {
|
|
3936
|
+
fields.push(`'dependencies' => array(${phpList(quoted(scan.handles))})`);
|
|
3937
|
+
const moduleDependencies = [
|
|
3938
|
+
...scan.moduleIds.map(
|
|
3939
|
+
(id) => `array( 'id' => '${phpSingleQuoteEscape3(id)}', 'import' => 'static' )`
|
|
3940
|
+
),
|
|
3941
|
+
...scan.dynamicModuleIds.map(
|
|
3942
|
+
(id) => `array( 'id' => '${phpSingleQuoteEscape3(id)}', 'import' => 'dynamic' )`
|
|
3943
|
+
)
|
|
3944
|
+
];
|
|
3945
|
+
if (moduleDependencies.length > 0) {
|
|
3946
|
+
fields.push(`'module_dependencies' => array(${phpList(moduleDependencies)})`);
|
|
3947
|
+
}
|
|
3948
|
+
} else if (mode === "module") {
|
|
3949
|
+
const moduleDependencies = [
|
|
3950
|
+
...quoted(scan.moduleIds),
|
|
3951
|
+
...scan.dynamicModuleIds.map(
|
|
3952
|
+
(id) => `array( 'id' => '${phpSingleQuoteEscape3(id)}', 'import' => 'dynamic' )`
|
|
3953
|
+
)
|
|
3954
|
+
];
|
|
3955
|
+
fields.push(`'dependencies' => array(${phpList(moduleDependencies)})`);
|
|
3956
|
+
if (scan.handles.length > 0) {
|
|
3957
|
+
fields.push(`'script_dependencies' => array(${phpList(quoted(scan.handles))})`);
|
|
3958
|
+
}
|
|
3959
|
+
} else {
|
|
3960
|
+
fields.push(`'dependencies' => array(${phpList(quoted(scan.handles))})`);
|
|
3961
|
+
}
|
|
3962
|
+
fields.push(`'version' => null`);
|
|
3963
|
+
if (kind === "js" && mode === "module") fields.push(`'type' => 'module'`);
|
|
3964
|
+
if (source) fields.push(`'source' => '${phpSingleQuoteEscape3(source)}'`);
|
|
3965
|
+
fields.push(`'dev' => true`);
|
|
3966
|
+
return `<?php return array( ${fields.join(", ")} );
|
|
3967
|
+
`;
|
|
3968
|
+
}
|
|
3969
|
+
async function writeAllSidecars(options) {
|
|
3970
|
+
const index = /* @__PURE__ */ new Map();
|
|
3971
|
+
const written = /* @__PURE__ */ new Set();
|
|
3972
|
+
for (const entry of options.entries) {
|
|
3973
|
+
const scan = entry.kind === "css" ? { moduleIds: [], dynamicModuleIds: [], handles: [], files: [toPosix(entry.file)] } : await scanEntryDependencies(entry.file, {
|
|
3974
|
+
mode: entry.mode,
|
|
3975
|
+
resolveExternalOptions: options.resolveExternalOptions,
|
|
3976
|
+
maxDepth: options.maxScanDepth
|
|
3977
|
+
});
|
|
3978
|
+
const php = renderDevAssetPhp({
|
|
3979
|
+
scan,
|
|
3980
|
+
mode: entry.mode,
|
|
3981
|
+
kind: entry.kind,
|
|
3982
|
+
source: projectRelativeSource(entry.file, options.root),
|
|
3983
|
+
wpBuildShape: options.wpBuildAssetMatchers.some(
|
|
3984
|
+
(matcher) => toPosix(entry.file).includes(matcher)
|
|
3985
|
+
)
|
|
3986
|
+
});
|
|
3987
|
+
const sidecarPath = join(options.outDirAbs, `${entry.name}.asset.php`);
|
|
3988
|
+
writeFileTree(sidecarPath, php);
|
|
3989
|
+
written.add(sidecarPath);
|
|
3990
|
+
for (const file of scan.files) {
|
|
3991
|
+
let names = index.get(file);
|
|
3992
|
+
if (!names) index.set(file, names = /* @__PURE__ */ new Set());
|
|
3993
|
+
names.add(entry.name);
|
|
3994
|
+
}
|
|
3995
|
+
}
|
|
3996
|
+
return { index, written };
|
|
3997
|
+
}
|
|
3998
|
+
function pruneStale(previous, current) {
|
|
3999
|
+
for (const filePath of previous) {
|
|
4000
|
+
if (current.has(filePath)) continue;
|
|
4001
|
+
try {
|
|
4002
|
+
rmSync(filePath, { force: true });
|
|
4003
|
+
} catch {
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
function projectRelativeSource(file, root) {
|
|
4008
|
+
const relativePath = relative(root, file).replace(/\\/g, "/");
|
|
4009
|
+
if (relativePath === "" || relativePath === ".." || relativePath.startsWith("../")) return null;
|
|
4010
|
+
if (isAbsolute(relativePath)) return null;
|
|
4011
|
+
return relativePath;
|
|
4012
|
+
}
|
|
4013
|
+
function phpSingleQuoteEscape3(value) {
|
|
4014
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
4015
|
+
}
|
|
4016
|
+
function writeBlocksTree(options) {
|
|
4017
|
+
const { blocksDirAbs, outDirAbs, emitManifest, skip } = options;
|
|
4018
|
+
const written = /* @__PURE__ */ new Set();
|
|
4019
|
+
if (!blocksDirAbs || !existsSync(blocksDirAbs)) return written;
|
|
4020
|
+
const files = fg.sync(
|
|
4021
|
+
[`${toPosix(blocksDirAbs)}/**/block.json`, `${toPosix(blocksDirAbs)}/**/*.php`],
|
|
4022
|
+
{ absolute: true }
|
|
4023
|
+
);
|
|
4024
|
+
for (const file of files) {
|
|
4025
|
+
const relativePath = relative(blocksDirAbs, file).replace(/\\/g, "/");
|
|
4026
|
+
const blockDirName = relativePath.replace(/\/?[^/]+$/, "");
|
|
4027
|
+
if (blockDirName && skip.has(blockDirName)) continue;
|
|
4028
|
+
const source = relativePath.endsWith("block.json") ? transformBlockJson(readFileSync(file, "utf8"), file) : readFileSync(file);
|
|
4029
|
+
const target = join(outDirAbs, "blocks", relativePath);
|
|
4030
|
+
writeFileTree(target, source);
|
|
4031
|
+
written.add(target);
|
|
4032
|
+
}
|
|
4033
|
+
if (!emitManifest) return written;
|
|
4034
|
+
const manifest = buildBlocksManifest(blocksDirAbs, skip);
|
|
4035
|
+
if (manifest) {
|
|
4036
|
+
const target = join(outDirAbs, "blocks-manifest.php");
|
|
4037
|
+
writeFileTree(target, manifest);
|
|
4038
|
+
written.add(target);
|
|
4039
|
+
}
|
|
4040
|
+
return written;
|
|
4041
|
+
}
|
|
4042
|
+
function watchSources(options) {
|
|
4043
|
+
const { server, blocksDirAbs } = options;
|
|
4044
|
+
const sourceDirs = options.sourceDirs.filter((dir) => !!dir).map((dir) => `${toPosix(dir)}/`);
|
|
4045
|
+
const isUnderSourceDir = (posixFile) => sourceDirs.some((dir) => posixFile.startsWith(dir));
|
|
4046
|
+
const isBlocksMetadata = (posixFile) => {
|
|
4047
|
+
if (!blocksDirAbs) return false;
|
|
4048
|
+
if (!posixFile.startsWith(`${toPosix(blocksDirAbs)}/`)) return false;
|
|
4049
|
+
return posixFile.endsWith("/block.json") || posixFile.endsWith(".php");
|
|
4050
|
+
};
|
|
4051
|
+
const run = (work, file) => {
|
|
4052
|
+
void Promise.resolve(work).catch((error) => {
|
|
4053
|
+
server.config.logger.error(
|
|
4054
|
+
`[wp-dev-server] failed to refresh after ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
4055
|
+
);
|
|
4056
|
+
});
|
|
4057
|
+
};
|
|
4058
|
+
let pending = null;
|
|
4059
|
+
const scheduleRediscover = (file) => {
|
|
4060
|
+
if (pending) clearTimeout(pending);
|
|
4061
|
+
pending = setTimeout(() => {
|
|
4062
|
+
pending = null;
|
|
4063
|
+
run(options.onRediscover(), file);
|
|
4064
|
+
}, REDISCOVER_DEBOUNCE_MS);
|
|
4065
|
+
pending.unref?.();
|
|
4066
|
+
};
|
|
4067
|
+
const handle = (event, file) => {
|
|
4068
|
+
const posixFile = toPosix(file);
|
|
4069
|
+
if (isBlocksMetadata(posixFile) || event !== "change" && isUnderSourceDir(posixFile)) {
|
|
4070
|
+
scheduleRediscover(file);
|
|
4071
|
+
return;
|
|
4072
|
+
}
|
|
4073
|
+
run(options.onFileChange(file), file);
|
|
4074
|
+
};
|
|
4075
|
+
for (const event of ["add", "change", "unlink"]) {
|
|
4076
|
+
server.watcher.on(event, (file) => handle(event, file));
|
|
4077
|
+
}
|
|
4078
|
+
}
|
|
4079
|
+
function writeFileTree(filePath, contents) {
|
|
4080
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
4081
|
+
writeFileSync(filePath, contents);
|
|
4082
|
+
}
|
|
4083
|
+
function toPosix(filePath) {
|
|
4084
|
+
return filePath.replace(/\\/g, "/");
|
|
4085
|
+
}
|
|
2650
4086
|
|
|
2651
4087
|
// src/wp.ts
|
|
2652
4088
|
function wpModuleEntrySignatures() {
|
|
@@ -2654,17 +4090,27 @@ function wpModuleEntrySignatures() {
|
|
|
2654
4090
|
name: "wp-module-entry-signatures",
|
|
2655
4091
|
config(userConfig) {
|
|
2656
4092
|
if (userConfig.build?.rollupOptions?.preserveEntrySignatures !== void 0) return null;
|
|
2657
|
-
return {
|
|
4093
|
+
return {
|
|
4094
|
+
build: { rollupOptions: { preserveEntrySignatures: "exports-only" } }
|
|
4095
|
+
};
|
|
2658
4096
|
}
|
|
2659
4097
|
};
|
|
2660
4098
|
}
|
|
2661
4099
|
function wp(options = {}) {
|
|
2662
4100
|
const buildType = options.buildType ?? "script";
|
|
2663
4101
|
const plugins = [];
|
|
4102
|
+
const globalsSink = /* @__PURE__ */ new Map();
|
|
4103
|
+
let activeBlocksDir;
|
|
4104
|
+
let activeBlocksSkip;
|
|
4105
|
+
let activeEmitManifest;
|
|
4106
|
+
let activeBlockStylesDir;
|
|
2664
4107
|
if (options.blocks !== false) {
|
|
2665
4108
|
const blocksOptions = typeof options.blocks === "object" ? options.blocks : void 0;
|
|
2666
4109
|
const blocksDir = blocksOptions?.blocksDir ?? options.blocksDir;
|
|
2667
4110
|
if (blocksDir) {
|
|
4111
|
+
activeBlocksDir = blocksDir;
|
|
4112
|
+
activeBlocksSkip = blocksOptions?.skip;
|
|
4113
|
+
activeEmitManifest = blocksOptions?.emitManifest;
|
|
2668
4114
|
plugins.push(
|
|
2669
4115
|
wpBlocks({
|
|
2670
4116
|
blocksDir,
|
|
@@ -2678,6 +4124,7 @@ function wp(options = {}) {
|
|
|
2678
4124
|
const blockStylesOptions = typeof options.blockStyles === "object" ? options.blockStyles : void 0;
|
|
2679
4125
|
const blocksStylesDir = blockStylesOptions?.blocksStylesDir ?? options.blocksStylesDir;
|
|
2680
4126
|
if (blocksStylesDir) {
|
|
4127
|
+
activeBlockStylesDir = blocksStylesDir;
|
|
2681
4128
|
plugins.push(
|
|
2682
4129
|
wpBlockStyles({
|
|
2683
4130
|
blocksStylesDir,
|
|
@@ -2714,26 +4161,66 @@ function wp(options = {}) {
|
|
|
2714
4161
|
);
|
|
2715
4162
|
}
|
|
2716
4163
|
}
|
|
4164
|
+
const externalsOptions = typeof options.externals === "object" ? options.externals : void 0;
|
|
4165
|
+
const wpBuildAssetMatchers = [...externalsOptions?.wpBuildAssetMatchers ?? []];
|
|
4166
|
+
if (activeAdminPagesDir) {
|
|
4167
|
+
const matcher = resolve(process.cwd(), activeAdminPagesDir).replace(/\\/g, "/");
|
|
4168
|
+
if (!wpBuildAssetMatchers.includes(matcher)) wpBuildAssetMatchers.push(matcher);
|
|
4169
|
+
}
|
|
4170
|
+
const moduleEntryMatchers = [...externalsOptions?.moduleEntryMatchers ?? []];
|
|
2717
4171
|
if (options.externals !== false) {
|
|
2718
|
-
const externalsOptions = typeof options.externals === "object" ? options.externals : void 0;
|
|
2719
|
-
const wpBuildAssetMatchers = [...externalsOptions?.wpBuildAssetMatchers ?? []];
|
|
2720
|
-
if (activeAdminPagesDir) {
|
|
2721
|
-
const matcher = resolve(process.cwd(), activeAdminPagesDir).replace(/\\/g, "/");
|
|
2722
|
-
if (!wpBuildAssetMatchers.includes(matcher)) wpBuildAssetMatchers.push(matcher);
|
|
2723
|
-
}
|
|
2724
4172
|
plugins.push(
|
|
2725
4173
|
...wpExternals({
|
|
2726
4174
|
buildType,
|
|
2727
4175
|
...externalsOptions,
|
|
4176
|
+
moduleEntryMatchers,
|
|
4177
|
+
// After the spread, deliberately: `globalsSink` is an internal
|
|
4178
|
+
// out-param, not user config. A consumer passing their own Map
|
|
4179
|
+
// through `externals: { globalsSink }` would otherwise sever the
|
|
4180
|
+
// wiring — wpExternals would fill their map, wpScriptIife would
|
|
4181
|
+
// read an empty one, and every shimmed `require()` would throw
|
|
4182
|
+
// at runtime with nothing said at build time.
|
|
4183
|
+
globalsSink,
|
|
2728
4184
|
...wpBuildAssetMatchers.length > 0 ? { wpBuildAssetMatchers } : {}
|
|
2729
4185
|
})
|
|
2730
4186
|
);
|
|
2731
4187
|
}
|
|
4188
|
+
if (options.devServer !== false) {
|
|
4189
|
+
const devServerOptions = typeof options.devServer === "object" ? options.devServer : void 0;
|
|
4190
|
+
plugins.push(
|
|
4191
|
+
wpDevServer({
|
|
4192
|
+
// The mode the *declared* entries (`build.rollupOptions.input`)
|
|
4193
|
+
// are served in. Block and admin-page entries carry their own
|
|
4194
|
+
// verdict from discovery, so this only decides the project's own.
|
|
4195
|
+
buildType,
|
|
4196
|
+
...activeBlocksDir ? { blocksDir: activeBlocksDir } : {},
|
|
4197
|
+
...activeBlockStylesDir ? { blocksStylesDir: activeBlockStylesDir } : {},
|
|
4198
|
+
...activeAdminPagesDir ? { adminPagesDir: activeAdminPagesDir } : {},
|
|
4199
|
+
...activeBlocksSkip ? { skip: activeBlocksSkip } : {},
|
|
4200
|
+
...activeEmitManifest !== void 0 ? { emitManifest: activeEmitManifest } : {},
|
|
4201
|
+
// The dev dependency scan has to reach the same externalization
|
|
4202
|
+
// verdicts `wpExternals` does, so it gets the same inputs.
|
|
4203
|
+
...externalsOptions?.projectRoot ? { projectRoot: externalsOptions.projectRoot } : {},
|
|
4204
|
+
...externalsOptions?.externalNamespaces ? { externalNamespaces: externalsOptions.externalNamespaces } : {},
|
|
4205
|
+
...externalsOptions?.additionalExternals ? { additionalExternals: externalsOptions.additionalExternals } : {},
|
|
4206
|
+
...externalsOptions?.scriptModuleNamespaces ? { scriptModuleNamespaces: externalsOptions.scriptModuleNamespaces } : {},
|
|
4207
|
+
...externalsOptions?.bundle ? { bundledPackages: [...DEFAULT_BUNDLED_PACKAGES, ...externalsOptions.bundle] } : {},
|
|
4208
|
+
moduleEntryMatchers,
|
|
4209
|
+
...wpBuildAssetMatchers.length > 0 ? { wpBuildAssetMatchers } : {},
|
|
4210
|
+
...devServerOptions
|
|
4211
|
+
})
|
|
4212
|
+
);
|
|
4213
|
+
plugins.push(
|
|
4214
|
+
wpDevServerPhp(
|
|
4215
|
+
devServerOptions?.phpPrefix ? { phpPrefix: devServerOptions.phpPrefix } : {}
|
|
4216
|
+
)
|
|
4217
|
+
);
|
|
4218
|
+
}
|
|
2732
4219
|
if (buildType === "script" && options.scriptDedupe !== false) {
|
|
2733
4220
|
plugins.push(wpScriptDedupe());
|
|
2734
4221
|
}
|
|
2735
4222
|
if (buildType === "script" && options.iife !== false) {
|
|
2736
|
-
plugins.push(wpScriptIife());
|
|
4223
|
+
plugins.push(wpScriptIife({ globals: globalsSink }));
|
|
2737
4224
|
}
|
|
2738
4225
|
if (options.cleanCssChunks !== false) {
|
|
2739
4226
|
plugins.push(wpCleanCssChunks());
|
|
@@ -2747,6 +4234,6 @@ function wp(options = {}) {
|
|
|
2747
4234
|
return plugins;
|
|
2748
4235
|
}
|
|
2749
4236
|
|
|
2750
|
-
export { DEFAULT_BUNDLED_PACKAGES, wp, wpAdminPages, wpBlockStyles, wpBlocks, wpCleanCssChunks, wpCopyAssets, wpCssAssetInfo, wpExternals, wpScriptDedupe, wpScriptIife };
|
|
4237
|
+
export { DEFAULT_BUNDLED_PACKAGES, DEV_SERVER_PHP_NAME, HOT_FILE_NAME, wp, wpAdminPages, wpBlockStyles, wpBlocks, wpCleanCssChunks, wpCopyAssets, wpCssAssetInfo, wpDevServer, wpDevServerPhp, wpExternals, wpScriptDedupe, wpScriptIife };
|
|
2751
4238
|
//# sourceMappingURL=index.js.map
|
|
2752
4239
|
//# sourceMappingURL=index.js.map
|