vite-plugin-phpsandbox 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -22
- package/dist/index.cjs +176 -73
- package/dist/index.d.cts +2 -31
- package/dist/index.d.ts +2 -31
- package/dist/index.js +176 -73
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -23,18 +23,6 @@ export default defineConfig({
|
|
|
23
23
|
|
|
24
24
|
The plugin only applies when `PHPSANDBOX_TYPE` is set.
|
|
25
25
|
|
|
26
|
-
## Options
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
type PhpSandboxOptions = {
|
|
30
|
-
allowedHosts?: true | string[];
|
|
31
|
-
enabled?: boolean;
|
|
32
|
-
hmrPort?: number;
|
|
33
|
-
publicHost?: string;
|
|
34
|
-
publicClientPort?: number;
|
|
35
|
-
};
|
|
36
|
-
```
|
|
37
|
-
|
|
38
26
|
## Environment Variables
|
|
39
27
|
|
|
40
28
|
The plugin reads these environment variables by default:
|
|
@@ -46,14 +34,16 @@ The plugin reads these environment variables by default:
|
|
|
46
34
|
## Behavior
|
|
47
35
|
|
|
48
36
|
- Does nothing unless `PHPSANDBOX_TYPE` is set.
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
37
|
+
- Uses `5173` as the stable HMR port.
|
|
38
|
+
- Derives the public host as `${SUBDOMAIN}-5173.${NOTEBOOK_HOST}` when both values exist.
|
|
39
|
+
- Uses `443` as the public HMR client port.
|
|
52
40
|
- Defaults `server.host` to `true` only when the project has not set `server.host`.
|
|
53
|
-
-
|
|
54
|
-
- Creates a stable local HMR server on `0.0.0.0
|
|
41
|
+
- Adds `.${NOTEBOOK_HOST}` and the derived public host to `server.allowedHosts` unless the project has set `server.allowedHosts = true`.
|
|
42
|
+
- Creates a stable local HMR server on `0.0.0.0:5173` unless `PHPSANDBOX_TYPE=laravel`.
|
|
43
|
+
- Fails startup if the local HMR server cannot bind to `0.0.0.0:5173`.
|
|
55
44
|
- Advertises HMR over `wss://${publicHost}:${publicClientPort}` when a public host is available.
|
|
56
45
|
- Preserves `server.hmr = false` if the consuming project disables HMR explicitly.
|
|
46
|
+
- Logs the resolved sandbox type, host, HMR endpoint, and allowed hosts after config resolution.
|
|
57
47
|
|
|
58
48
|
## Laravel Mode
|
|
59
49
|
|
|
@@ -62,9 +52,5 @@ When `PHPSANDBOX_TYPE=laravel`, the plugin does not create a standalone HMR serv
|
|
|
62
52
|
## API
|
|
63
53
|
|
|
64
54
|
```ts
|
|
65
|
-
import phpSandboxPlugin, {
|
|
66
|
-
phpSandbox,
|
|
67
|
-
type PhpSandboxEnv,
|
|
68
|
-
type PhpSandboxOptions,
|
|
69
|
-
} from "vite-plugin-phpsandbox";
|
|
55
|
+
import phpSandboxPlugin, { phpSandbox } from "vite-plugin-phpsandbox";
|
|
70
56
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -36,8 +36,8 @@ __export(index_exports, {
|
|
|
36
36
|
});
|
|
37
37
|
module.exports = __toCommonJS(index_exports);
|
|
38
38
|
var import_node_http = __toESM(require("http"), 1);
|
|
39
|
-
var
|
|
40
|
-
var
|
|
39
|
+
var HMR_PORT = 5173;
|
|
40
|
+
var PUBLIC_CLIENT_PORT = 443;
|
|
41
41
|
var GLOBAL_KEY = "__phpSandboxViteState__";
|
|
42
42
|
function getGlobalState() {
|
|
43
43
|
const g = globalThis;
|
|
@@ -52,30 +52,60 @@ function normalizeSandboxType(value) {
|
|
|
52
52
|
const normalized = value?.trim().toLowerCase();
|
|
53
53
|
return normalized ? normalized : void 0;
|
|
54
54
|
}
|
|
55
|
-
function
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
function resolvePublicClientPort(options) {
|
|
59
|
-
return options.publicClientPort ?? DEFAULT_PUBLIC_CLIENT_PORT;
|
|
60
|
-
}
|
|
61
|
-
function resolvePublicHost(options, env, hmrPort) {
|
|
62
|
-
return options.publicHost ?? (env.SUBDOMAIN && env.NOTEBOOK_HOST ? `${env.SUBDOMAIN}-${hmrPort}.${env.NOTEBOOK_HOST}` : void 0);
|
|
55
|
+
function resolvePublicHost(env) {
|
|
56
|
+
return env.SUBDOMAIN && env.NOTEBOOK_HOST ? `${env.SUBDOMAIN}-${HMR_PORT}.${env.NOTEBOOK_HOST}` : void 0;
|
|
63
57
|
}
|
|
64
58
|
function isRecord(value) {
|
|
65
59
|
return typeof value === "object" && value !== null;
|
|
66
60
|
}
|
|
67
|
-
function
|
|
61
|
+
function toListenError(error, port) {
|
|
62
|
+
const code = isRecord(error) ? error.code : void 0;
|
|
63
|
+
if (code === "EADDRINUSE") {
|
|
64
|
+
return new Error(
|
|
65
|
+
`[vite-plugin-phpsandbox] HMR port ${port} is already in use.`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
69
|
+
return new Error(
|
|
70
|
+
`[vite-plugin-phpsandbox] Failed to bind HMR server on 0.0.0.0:${port}: ${message}`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
async function listen(server, port) {
|
|
74
|
+
await new Promise((resolve, reject) => {
|
|
75
|
+
const cleanup = () => {
|
|
76
|
+
server.off("error", onError);
|
|
77
|
+
server.off("listening", onListening);
|
|
78
|
+
};
|
|
79
|
+
const onError = (error) => {
|
|
80
|
+
cleanup();
|
|
81
|
+
reject(toListenError(error, port));
|
|
82
|
+
};
|
|
83
|
+
const onListening = () => {
|
|
84
|
+
cleanup();
|
|
85
|
+
resolve();
|
|
86
|
+
};
|
|
87
|
+
server.once("error", onError);
|
|
88
|
+
server.once("listening", onListening);
|
|
89
|
+
try {
|
|
90
|
+
server.listen(port, "0.0.0.0");
|
|
91
|
+
} catch (error) {
|
|
92
|
+
cleanup();
|
|
93
|
+
reject(toListenError(error, port));
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function logServerError(error) {
|
|
98
|
+
console.error("[vite-plugin-phpsandbox] HMR server error:", error);
|
|
99
|
+
}
|
|
100
|
+
async function acquireServer(state, port) {
|
|
68
101
|
const existing = state.servers.get(port);
|
|
69
102
|
if (existing) {
|
|
70
103
|
state.refs.set(port, (state.refs.get(port) ?? 0) + 1);
|
|
71
104
|
return existing;
|
|
72
105
|
}
|
|
73
106
|
const server = import_node_http.default.createServer();
|
|
74
|
-
server
|
|
75
|
-
|
|
76
|
-
console.error("[vite-plugin-phpsandbox] HMR server error:", error);
|
|
77
|
-
});
|
|
78
|
-
server.listen(port, "0.0.0.0");
|
|
107
|
+
await listen(server, port);
|
|
108
|
+
server.on("error", logServerError);
|
|
79
109
|
state.servers.set(port, server);
|
|
80
110
|
state.refs.set(port, 1);
|
|
81
111
|
return server;
|
|
@@ -89,79 +119,152 @@ function releaseServer(state, port, server) {
|
|
|
89
119
|
state.refs.delete(port);
|
|
90
120
|
if (state.servers.get(port) !== server) return;
|
|
91
121
|
state.servers.delete(port);
|
|
122
|
+
server.off("error", logServerError);
|
|
92
123
|
if (server.listening) {
|
|
93
124
|
server.close();
|
|
94
125
|
}
|
|
95
126
|
}
|
|
96
|
-
function resolveAllowedHosts(existingAllowedHosts,
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
127
|
+
function resolveAllowedHosts(existingAllowedHosts, notebookHost, publicHost) {
|
|
128
|
+
if (existingAllowedHosts === true) return true;
|
|
129
|
+
const hosts = [
|
|
130
|
+
...Array.isArray(existingAllowedHosts) ? existingAllowedHosts : [],
|
|
131
|
+
...notebookHost ? [`.${notebookHost}`] : [],
|
|
132
|
+
...publicHost ? [publicHost] : []
|
|
133
|
+
];
|
|
134
|
+
return hosts.length > 0 ? Array.from(new Set(hosts)) : existingAllowedHosts;
|
|
135
|
+
}
|
|
136
|
+
function resolveExistingHmrOptions(serverOptions) {
|
|
137
|
+
return isRecord(serverOptions.hmr) ? serverOptions.hmr : void 0;
|
|
138
|
+
}
|
|
139
|
+
function resolveHmrOptions(existingServer, publicHost, hmrServer) {
|
|
140
|
+
if (existingServer.hmr === false) return false;
|
|
141
|
+
return {
|
|
142
|
+
...resolveExistingHmrOptions(existingServer),
|
|
143
|
+
port: HMR_PORT,
|
|
144
|
+
...hmrServer ? { server: hmrServer } : {},
|
|
145
|
+
...publicHost ? {
|
|
146
|
+
protocol: "wss",
|
|
147
|
+
host: publicHost,
|
|
148
|
+
clientPort: PUBLIC_CLIENT_PORT
|
|
149
|
+
} : {}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function shouldCreateHmrServer(existingServer, isLaravel) {
|
|
153
|
+
return !isLaravel && existingServer.hmr !== false;
|
|
154
|
+
}
|
|
155
|
+
function resolveServerConfig(existingServer, env, isLaravel, publicHost, hmrServer) {
|
|
156
|
+
return {
|
|
157
|
+
...existingServer,
|
|
158
|
+
host: existingServer.host ?? true,
|
|
159
|
+
...isLaravel ? { port: HMR_PORT } : {},
|
|
160
|
+
hmr: resolveHmrOptions(existingServer, publicHost, hmrServer),
|
|
161
|
+
allowedHosts: resolveAllowedHosts(
|
|
162
|
+
existingServer.allowedHosts,
|
|
163
|
+
env.NOTEBOOK_HOST,
|
|
164
|
+
publicHost
|
|
165
|
+
)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function formatHost(host) {
|
|
169
|
+
if (host === true) return "0.0.0.0";
|
|
170
|
+
if (host === false || host === void 0) return "vite default";
|
|
171
|
+
return String(host);
|
|
172
|
+
}
|
|
173
|
+
function formatAllowedHosts(allowedHosts) {
|
|
174
|
+
if (allowedHosts === true) return "all";
|
|
175
|
+
if (Array.isArray(allowedHosts)) return allowedHosts.join(", ") || "none";
|
|
176
|
+
return "vite default";
|
|
177
|
+
}
|
|
178
|
+
function formatHmrEndpoint(hmr) {
|
|
179
|
+
if (!hmr || !hmr.host) return "none";
|
|
180
|
+
const protocol = hmr.protocol ?? "ws";
|
|
181
|
+
const port = hmr.clientPort ?? hmr.port;
|
|
182
|
+
return port ? `${protocol}://${hmr.host}:${port}` : `${protocol}://${hmr.host}`;
|
|
110
183
|
}
|
|
111
|
-
function
|
|
112
|
-
|
|
184
|
+
function formatHmrSummary(serverOptions, hasManagedServer) {
|
|
185
|
+
if (serverOptions.hmr === false) return "disabled";
|
|
186
|
+
const hmr = resolveExistingHmrOptions(serverOptions);
|
|
187
|
+
const localEndpoint = hasManagedServer ? `0.0.0.0:${HMR_PORT}` : `vite:${serverOptions.port ?? HMR_PORT}`;
|
|
188
|
+
return `${localEndpoint}, public=${formatHmrEndpoint(hmr)}`;
|
|
189
|
+
}
|
|
190
|
+
function formatStartupLog(sandboxType, serverOptions, hasManagedServer) {
|
|
191
|
+
return [
|
|
192
|
+
"[vite-plugin-phpsandbox] enabled",
|
|
193
|
+
`type=${sandboxType}`,
|
|
194
|
+
`host=${formatHost(serverOptions.host)}`,
|
|
195
|
+
`hmr=${formatHmrSummary(serverOptions, hasManagedServer)}`,
|
|
196
|
+
`allowedHosts=${formatAllowedHosts(serverOptions.allowedHosts)}`
|
|
197
|
+
].join(" | ");
|
|
198
|
+
}
|
|
199
|
+
function phpSandboxPlugin() {
|
|
113
200
|
let managedHmrServer;
|
|
201
|
+
let releaseManagedHmrServer;
|
|
202
|
+
let sandboxType;
|
|
203
|
+
let startupLog;
|
|
204
|
+
function setManagedHmrServer(server) {
|
|
205
|
+
if (managedHmrServer === server) return;
|
|
206
|
+
releaseManagedHmrServer?.();
|
|
207
|
+
managedHmrServer = server;
|
|
208
|
+
if (!server) {
|
|
209
|
+
releaseManagedHmrServer = void 0;
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
let released = false;
|
|
213
|
+
releaseManagedHmrServer = () => {
|
|
214
|
+
if (released) return;
|
|
215
|
+
released = true;
|
|
216
|
+
if (managedHmrServer === server) {
|
|
217
|
+
managedHmrServer = void 0;
|
|
218
|
+
releaseManagedHmrServer = void 0;
|
|
219
|
+
}
|
|
220
|
+
releaseServer(getGlobalState(), HMR_PORT, server);
|
|
221
|
+
};
|
|
222
|
+
}
|
|
114
223
|
return {
|
|
115
224
|
name: "vite-plugin-phpsandbox",
|
|
116
225
|
apply: "serve",
|
|
117
226
|
enforce: "post",
|
|
118
|
-
config(userConfig) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (!sandboxType)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
227
|
+
async config(userConfig) {
|
|
228
|
+
const env = process.env;
|
|
229
|
+
sandboxType = normalizeSandboxType(env.PHPSANDBOX_TYPE);
|
|
230
|
+
if (!sandboxType) {
|
|
231
|
+
setManagedHmrServer(void 0);
|
|
232
|
+
startupLog = void 0;
|
|
233
|
+
return {};
|
|
234
|
+
}
|
|
125
235
|
const isLaravel = sandboxType === "laravel";
|
|
236
|
+
const publicHost = resolvePublicHost(env);
|
|
126
237
|
const existingServer = userConfig.server ?? {};
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
const allowedHosts = resolveAllowedHosts(
|
|
130
|
-
existingServer.allowedHosts,
|
|
131
|
-
options.allowedHosts,
|
|
132
|
-
publicHost
|
|
133
|
-
);
|
|
134
|
-
const hmrServer = isLaravel || hmrDisabled ? void 0 : acquireServer(getGlobalState(), hmrPort);
|
|
135
|
-
managedHmrPort = hmrServer ? hmrPort : void 0;
|
|
136
|
-
managedHmrServer = hmrServer;
|
|
238
|
+
const hmrServer = shouldCreateHmrServer(existingServer, isLaravel) ? managedHmrServer ?? await acquireServer(getGlobalState(), HMR_PORT) : void 0;
|
|
239
|
+
setManagedHmrServer(hmrServer);
|
|
137
240
|
return {
|
|
138
|
-
server:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
port: hmrPort,
|
|
146
|
-
...hmrServer ? { server: hmrServer } : {},
|
|
147
|
-
...publicHost ? {
|
|
148
|
-
protocol: "wss",
|
|
149
|
-
host: publicHost,
|
|
150
|
-
clientPort: publicClientPort
|
|
151
|
-
} : {}
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
allowedHosts
|
|
155
|
-
}
|
|
241
|
+
server: resolveServerConfig(
|
|
242
|
+
existingServer,
|
|
243
|
+
env,
|
|
244
|
+
isLaravel,
|
|
245
|
+
publicHost,
|
|
246
|
+
hmrServer
|
|
247
|
+
)
|
|
156
248
|
};
|
|
157
249
|
},
|
|
250
|
+
configResolved(config) {
|
|
251
|
+
if (!sandboxType) return;
|
|
252
|
+
const nextStartupLog = formatStartupLog(
|
|
253
|
+
sandboxType,
|
|
254
|
+
config.server,
|
|
255
|
+
Boolean(managedHmrServer)
|
|
256
|
+
);
|
|
257
|
+
if (nextStartupLog === startupLog) return;
|
|
258
|
+
startupLog = nextStartupLog;
|
|
259
|
+
console.info(nextStartupLog);
|
|
260
|
+
},
|
|
158
261
|
configureServer(server) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
262
|
+
const release = releaseManagedHmrServer;
|
|
263
|
+
if (!release) return;
|
|
264
|
+
server.httpServer?.once("close", release);
|
|
265
|
+
},
|
|
266
|
+
closeBundle() {
|
|
267
|
+
releaseManagedHmrServer?.();
|
|
165
268
|
}
|
|
166
269
|
};
|
|
167
270
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,35 +1,6 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
SUBDOMAIN?: string;
|
|
5
|
-
NOTEBOOK_HOST?: string;
|
|
6
|
-
PHPSANDBOX_TYPE?: string;
|
|
7
|
-
};
|
|
8
|
-
type PhpSandboxOptions = {
|
|
9
|
-
/**
|
|
10
|
-
* Defaults to merging the derived public host into `server.allowedHosts`.
|
|
11
|
-
* - true: disable host checks entirely
|
|
12
|
-
* - array: merge with existing allowedHosts
|
|
13
|
-
*/
|
|
14
|
-
allowedHosts?: true | string[];
|
|
15
|
-
/**
|
|
16
|
-
* Explicitly disable the plugin.
|
|
17
|
-
*/
|
|
18
|
-
enabled?: boolean;
|
|
19
|
-
/**
|
|
20
|
-
* Defaults to 5173.
|
|
21
|
-
*/
|
|
22
|
-
hmrPort?: number;
|
|
23
|
-
/**
|
|
24
|
-
* Defaults to `${SUBDOMAIN}-${port}.${NOTEBOOK_HOST}` when env values exist.
|
|
25
|
-
*/
|
|
26
|
-
publicHost?: string;
|
|
27
|
-
/**
|
|
28
|
-
* Defaults to 443.
|
|
29
|
-
*/
|
|
30
|
-
publicClientPort?: number;
|
|
31
|
-
};
|
|
32
|
-
declare function phpSandboxPlugin(options?: PhpSandboxOptions, env?: PhpSandboxEnv): Plugin;
|
|
3
|
+
declare function phpSandboxPlugin(): Plugin;
|
|
33
4
|
declare const phpSandbox: typeof phpSandboxPlugin;
|
|
34
5
|
|
|
35
|
-
export {
|
|
6
|
+
export { phpSandboxPlugin as default, phpSandbox, phpSandboxPlugin };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,35 +1,6 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
SUBDOMAIN?: string;
|
|
5
|
-
NOTEBOOK_HOST?: string;
|
|
6
|
-
PHPSANDBOX_TYPE?: string;
|
|
7
|
-
};
|
|
8
|
-
type PhpSandboxOptions = {
|
|
9
|
-
/**
|
|
10
|
-
* Defaults to merging the derived public host into `server.allowedHosts`.
|
|
11
|
-
* - true: disable host checks entirely
|
|
12
|
-
* - array: merge with existing allowedHosts
|
|
13
|
-
*/
|
|
14
|
-
allowedHosts?: true | string[];
|
|
15
|
-
/**
|
|
16
|
-
* Explicitly disable the plugin.
|
|
17
|
-
*/
|
|
18
|
-
enabled?: boolean;
|
|
19
|
-
/**
|
|
20
|
-
* Defaults to 5173.
|
|
21
|
-
*/
|
|
22
|
-
hmrPort?: number;
|
|
23
|
-
/**
|
|
24
|
-
* Defaults to `${SUBDOMAIN}-${port}.${NOTEBOOK_HOST}` when env values exist.
|
|
25
|
-
*/
|
|
26
|
-
publicHost?: string;
|
|
27
|
-
/**
|
|
28
|
-
* Defaults to 443.
|
|
29
|
-
*/
|
|
30
|
-
publicClientPort?: number;
|
|
31
|
-
};
|
|
32
|
-
declare function phpSandboxPlugin(options?: PhpSandboxOptions, env?: PhpSandboxEnv): Plugin;
|
|
3
|
+
declare function phpSandboxPlugin(): Plugin;
|
|
33
4
|
declare const phpSandbox: typeof phpSandboxPlugin;
|
|
34
5
|
|
|
35
|
-
export {
|
|
6
|
+
export { phpSandboxPlugin as default, phpSandbox, phpSandboxPlugin };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import http from "http";
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var HMR_PORT = 5173;
|
|
4
|
+
var PUBLIC_CLIENT_PORT = 443;
|
|
5
5
|
var GLOBAL_KEY = "__phpSandboxViteState__";
|
|
6
6
|
function getGlobalState() {
|
|
7
7
|
const g = globalThis;
|
|
@@ -16,30 +16,60 @@ function normalizeSandboxType(value) {
|
|
|
16
16
|
const normalized = value?.trim().toLowerCase();
|
|
17
17
|
return normalized ? normalized : void 0;
|
|
18
18
|
}
|
|
19
|
-
function
|
|
20
|
-
return
|
|
21
|
-
}
|
|
22
|
-
function resolvePublicClientPort(options) {
|
|
23
|
-
return options.publicClientPort ?? DEFAULT_PUBLIC_CLIENT_PORT;
|
|
24
|
-
}
|
|
25
|
-
function resolvePublicHost(options, env, hmrPort) {
|
|
26
|
-
return options.publicHost ?? (env.SUBDOMAIN && env.NOTEBOOK_HOST ? `${env.SUBDOMAIN}-${hmrPort}.${env.NOTEBOOK_HOST}` : void 0);
|
|
19
|
+
function resolvePublicHost(env) {
|
|
20
|
+
return env.SUBDOMAIN && env.NOTEBOOK_HOST ? `${env.SUBDOMAIN}-${HMR_PORT}.${env.NOTEBOOK_HOST}` : void 0;
|
|
27
21
|
}
|
|
28
22
|
function isRecord(value) {
|
|
29
23
|
return typeof value === "object" && value !== null;
|
|
30
24
|
}
|
|
31
|
-
function
|
|
25
|
+
function toListenError(error, port) {
|
|
26
|
+
const code = isRecord(error) ? error.code : void 0;
|
|
27
|
+
if (code === "EADDRINUSE") {
|
|
28
|
+
return new Error(
|
|
29
|
+
`[vite-plugin-phpsandbox] HMR port ${port} is already in use.`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
33
|
+
return new Error(
|
|
34
|
+
`[vite-plugin-phpsandbox] Failed to bind HMR server on 0.0.0.0:${port}: ${message}`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
async function listen(server, port) {
|
|
38
|
+
await new Promise((resolve, reject) => {
|
|
39
|
+
const cleanup = () => {
|
|
40
|
+
server.off("error", onError);
|
|
41
|
+
server.off("listening", onListening);
|
|
42
|
+
};
|
|
43
|
+
const onError = (error) => {
|
|
44
|
+
cleanup();
|
|
45
|
+
reject(toListenError(error, port));
|
|
46
|
+
};
|
|
47
|
+
const onListening = () => {
|
|
48
|
+
cleanup();
|
|
49
|
+
resolve();
|
|
50
|
+
};
|
|
51
|
+
server.once("error", onError);
|
|
52
|
+
server.once("listening", onListening);
|
|
53
|
+
try {
|
|
54
|
+
server.listen(port, "0.0.0.0");
|
|
55
|
+
} catch (error) {
|
|
56
|
+
cleanup();
|
|
57
|
+
reject(toListenError(error, port));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function logServerError(error) {
|
|
62
|
+
console.error("[vite-plugin-phpsandbox] HMR server error:", error);
|
|
63
|
+
}
|
|
64
|
+
async function acquireServer(state, port) {
|
|
32
65
|
const existing = state.servers.get(port);
|
|
33
66
|
if (existing) {
|
|
34
67
|
state.refs.set(port, (state.refs.get(port) ?? 0) + 1);
|
|
35
68
|
return existing;
|
|
36
69
|
}
|
|
37
70
|
const server = http.createServer();
|
|
38
|
-
server
|
|
39
|
-
|
|
40
|
-
console.error("[vite-plugin-phpsandbox] HMR server error:", error);
|
|
41
|
-
});
|
|
42
|
-
server.listen(port, "0.0.0.0");
|
|
71
|
+
await listen(server, port);
|
|
72
|
+
server.on("error", logServerError);
|
|
43
73
|
state.servers.set(port, server);
|
|
44
74
|
state.refs.set(port, 1);
|
|
45
75
|
return server;
|
|
@@ -53,79 +83,152 @@ function releaseServer(state, port, server) {
|
|
|
53
83
|
state.refs.delete(port);
|
|
54
84
|
if (state.servers.get(port) !== server) return;
|
|
55
85
|
state.servers.delete(port);
|
|
86
|
+
server.off("error", logServerError);
|
|
56
87
|
if (server.listening) {
|
|
57
88
|
server.close();
|
|
58
89
|
}
|
|
59
90
|
}
|
|
60
|
-
function resolveAllowedHosts(existingAllowedHosts,
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
91
|
+
function resolveAllowedHosts(existingAllowedHosts, notebookHost, publicHost) {
|
|
92
|
+
if (existingAllowedHosts === true) return true;
|
|
93
|
+
const hosts = [
|
|
94
|
+
...Array.isArray(existingAllowedHosts) ? existingAllowedHosts : [],
|
|
95
|
+
...notebookHost ? [`.${notebookHost}`] : [],
|
|
96
|
+
...publicHost ? [publicHost] : []
|
|
97
|
+
];
|
|
98
|
+
return hosts.length > 0 ? Array.from(new Set(hosts)) : existingAllowedHosts;
|
|
99
|
+
}
|
|
100
|
+
function resolveExistingHmrOptions(serverOptions) {
|
|
101
|
+
return isRecord(serverOptions.hmr) ? serverOptions.hmr : void 0;
|
|
102
|
+
}
|
|
103
|
+
function resolveHmrOptions(existingServer, publicHost, hmrServer) {
|
|
104
|
+
if (existingServer.hmr === false) return false;
|
|
105
|
+
return {
|
|
106
|
+
...resolveExistingHmrOptions(existingServer),
|
|
107
|
+
port: HMR_PORT,
|
|
108
|
+
...hmrServer ? { server: hmrServer } : {},
|
|
109
|
+
...publicHost ? {
|
|
110
|
+
protocol: "wss",
|
|
111
|
+
host: publicHost,
|
|
112
|
+
clientPort: PUBLIC_CLIENT_PORT
|
|
113
|
+
} : {}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function shouldCreateHmrServer(existingServer, isLaravel) {
|
|
117
|
+
return !isLaravel && existingServer.hmr !== false;
|
|
118
|
+
}
|
|
119
|
+
function resolveServerConfig(existingServer, env, isLaravel, publicHost, hmrServer) {
|
|
120
|
+
return {
|
|
121
|
+
...existingServer,
|
|
122
|
+
host: existingServer.host ?? true,
|
|
123
|
+
...isLaravel ? { port: HMR_PORT } : {},
|
|
124
|
+
hmr: resolveHmrOptions(existingServer, publicHost, hmrServer),
|
|
125
|
+
allowedHosts: resolveAllowedHosts(
|
|
126
|
+
existingServer.allowedHosts,
|
|
127
|
+
env.NOTEBOOK_HOST,
|
|
128
|
+
publicHost
|
|
129
|
+
)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function formatHost(host) {
|
|
133
|
+
if (host === true) return "0.0.0.0";
|
|
134
|
+
if (host === false || host === void 0) return "vite default";
|
|
135
|
+
return String(host);
|
|
136
|
+
}
|
|
137
|
+
function formatAllowedHosts(allowedHosts) {
|
|
138
|
+
if (allowedHosts === true) return "all";
|
|
139
|
+
if (Array.isArray(allowedHosts)) return allowedHosts.join(", ") || "none";
|
|
140
|
+
return "vite default";
|
|
141
|
+
}
|
|
142
|
+
function formatHmrEndpoint(hmr) {
|
|
143
|
+
if (!hmr || !hmr.host) return "none";
|
|
144
|
+
const protocol = hmr.protocol ?? "ws";
|
|
145
|
+
const port = hmr.clientPort ?? hmr.port;
|
|
146
|
+
return port ? `${protocol}://${hmr.host}:${port}` : `${protocol}://${hmr.host}`;
|
|
74
147
|
}
|
|
75
|
-
function
|
|
76
|
-
|
|
148
|
+
function formatHmrSummary(serverOptions, hasManagedServer) {
|
|
149
|
+
if (serverOptions.hmr === false) return "disabled";
|
|
150
|
+
const hmr = resolveExistingHmrOptions(serverOptions);
|
|
151
|
+
const localEndpoint = hasManagedServer ? `0.0.0.0:${HMR_PORT}` : `vite:${serverOptions.port ?? HMR_PORT}`;
|
|
152
|
+
return `${localEndpoint}, public=${formatHmrEndpoint(hmr)}`;
|
|
153
|
+
}
|
|
154
|
+
function formatStartupLog(sandboxType, serverOptions, hasManagedServer) {
|
|
155
|
+
return [
|
|
156
|
+
"[vite-plugin-phpsandbox] enabled",
|
|
157
|
+
`type=${sandboxType}`,
|
|
158
|
+
`host=${formatHost(serverOptions.host)}`,
|
|
159
|
+
`hmr=${formatHmrSummary(serverOptions, hasManagedServer)}`,
|
|
160
|
+
`allowedHosts=${formatAllowedHosts(serverOptions.allowedHosts)}`
|
|
161
|
+
].join(" | ");
|
|
162
|
+
}
|
|
163
|
+
function phpSandboxPlugin() {
|
|
77
164
|
let managedHmrServer;
|
|
165
|
+
let releaseManagedHmrServer;
|
|
166
|
+
let sandboxType;
|
|
167
|
+
let startupLog;
|
|
168
|
+
function setManagedHmrServer(server) {
|
|
169
|
+
if (managedHmrServer === server) return;
|
|
170
|
+
releaseManagedHmrServer?.();
|
|
171
|
+
managedHmrServer = server;
|
|
172
|
+
if (!server) {
|
|
173
|
+
releaseManagedHmrServer = void 0;
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
let released = false;
|
|
177
|
+
releaseManagedHmrServer = () => {
|
|
178
|
+
if (released) return;
|
|
179
|
+
released = true;
|
|
180
|
+
if (managedHmrServer === server) {
|
|
181
|
+
managedHmrServer = void 0;
|
|
182
|
+
releaseManagedHmrServer = void 0;
|
|
183
|
+
}
|
|
184
|
+
releaseServer(getGlobalState(), HMR_PORT, server);
|
|
185
|
+
};
|
|
186
|
+
}
|
|
78
187
|
return {
|
|
79
188
|
name: "vite-plugin-phpsandbox",
|
|
80
189
|
apply: "serve",
|
|
81
190
|
enforce: "post",
|
|
82
|
-
config(userConfig) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (!sandboxType)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
191
|
+
async config(userConfig) {
|
|
192
|
+
const env = process.env;
|
|
193
|
+
sandboxType = normalizeSandboxType(env.PHPSANDBOX_TYPE);
|
|
194
|
+
if (!sandboxType) {
|
|
195
|
+
setManagedHmrServer(void 0);
|
|
196
|
+
startupLog = void 0;
|
|
197
|
+
return {};
|
|
198
|
+
}
|
|
89
199
|
const isLaravel = sandboxType === "laravel";
|
|
200
|
+
const publicHost = resolvePublicHost(env);
|
|
90
201
|
const existingServer = userConfig.server ?? {};
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
const allowedHosts = resolveAllowedHosts(
|
|
94
|
-
existingServer.allowedHosts,
|
|
95
|
-
options.allowedHosts,
|
|
96
|
-
publicHost
|
|
97
|
-
);
|
|
98
|
-
const hmrServer = isLaravel || hmrDisabled ? void 0 : acquireServer(getGlobalState(), hmrPort);
|
|
99
|
-
managedHmrPort = hmrServer ? hmrPort : void 0;
|
|
100
|
-
managedHmrServer = hmrServer;
|
|
202
|
+
const hmrServer = shouldCreateHmrServer(existingServer, isLaravel) ? managedHmrServer ?? await acquireServer(getGlobalState(), HMR_PORT) : void 0;
|
|
203
|
+
setManagedHmrServer(hmrServer);
|
|
101
204
|
return {
|
|
102
|
-
server:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
port: hmrPort,
|
|
110
|
-
...hmrServer ? { server: hmrServer } : {},
|
|
111
|
-
...publicHost ? {
|
|
112
|
-
protocol: "wss",
|
|
113
|
-
host: publicHost,
|
|
114
|
-
clientPort: publicClientPort
|
|
115
|
-
} : {}
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
allowedHosts
|
|
119
|
-
}
|
|
205
|
+
server: resolveServerConfig(
|
|
206
|
+
existingServer,
|
|
207
|
+
env,
|
|
208
|
+
isLaravel,
|
|
209
|
+
publicHost,
|
|
210
|
+
hmrServer
|
|
211
|
+
)
|
|
120
212
|
};
|
|
121
213
|
},
|
|
214
|
+
configResolved(config) {
|
|
215
|
+
if (!sandboxType) return;
|
|
216
|
+
const nextStartupLog = formatStartupLog(
|
|
217
|
+
sandboxType,
|
|
218
|
+
config.server,
|
|
219
|
+
Boolean(managedHmrServer)
|
|
220
|
+
);
|
|
221
|
+
if (nextStartupLog === startupLog) return;
|
|
222
|
+
startupLog = nextStartupLog;
|
|
223
|
+
console.info(nextStartupLog);
|
|
224
|
+
},
|
|
122
225
|
configureServer(server) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
226
|
+
const release = releaseManagedHmrServer;
|
|
227
|
+
if (!release) return;
|
|
228
|
+
server.httpServer?.once("close", release);
|
|
229
|
+
},
|
|
230
|
+
closeBundle() {
|
|
231
|
+
releaseManagedHmrServer?.();
|
|
129
232
|
}
|
|
130
233
|
};
|
|
131
234
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-phpsandbox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Vite plugin for PHP sandbox environments that need stable HMR over public proxy hosts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
38
38
|
"check": "tsc --noEmit",
|
|
39
39
|
"release:check": "npm run check && npm run build && npm pack --dry-run",
|
|
40
|
-
"release": "npm run release:check && npm publish",
|
|
40
|
+
"release": "npm version minor && npm run release:check && npm publish",
|
|
41
41
|
"prepublishOnly": "npm run build && npm run check"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|