vite-plugin-phpsandbox 0.1.1 → 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 -77
- package/dist/index.d.cts +2 -32
- package/dist/index.d.ts +2 -32
- package/dist/index.js +176 -77
- 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,83 +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
|
-
merged.add(`.${notebookHost}`);
|
|
108
|
-
}
|
|
109
|
-
if (publicHost) {
|
|
110
|
-
merged.add(publicHost);
|
|
111
|
-
}
|
|
112
|
-
return merged.size > 0 ? Array.from(merged) : existingAllowedHosts;
|
|
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;
|
|
113
138
|
}
|
|
114
|
-
function
|
|
115
|
-
|
|
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}`;
|
|
183
|
+
}
|
|
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() {
|
|
116
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
|
+
}
|
|
117
223
|
return {
|
|
118
224
|
name: "vite-plugin-phpsandbox",
|
|
119
225
|
apply: "serve",
|
|
120
226
|
enforce: "post",
|
|
121
|
-
config(userConfig) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (!sandboxType)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
+
}
|
|
128
235
|
const isLaravel = sandboxType === "laravel";
|
|
236
|
+
const publicHost = resolvePublicHost(env);
|
|
129
237
|
const existingServer = userConfig.server ?? {};
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
const allowedHosts = resolveAllowedHosts(
|
|
133
|
-
existingServer.allowedHosts,
|
|
134
|
-
options.allowedHosts,
|
|
135
|
-
env.NOTEBOOK_HOST,
|
|
136
|
-
publicHost
|
|
137
|
-
);
|
|
138
|
-
const hmrServer = isLaravel || hmrDisabled ? void 0 : acquireServer(getGlobalState(), hmrPort);
|
|
139
|
-
managedHmrPort = hmrServer ? hmrPort : void 0;
|
|
140
|
-
managedHmrServer = hmrServer;
|
|
238
|
+
const hmrServer = shouldCreateHmrServer(existingServer, isLaravel) ? managedHmrServer ?? await acquireServer(getGlobalState(), HMR_PORT) : void 0;
|
|
239
|
+
setManagedHmrServer(hmrServer);
|
|
141
240
|
return {
|
|
142
|
-
server:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
port: hmrPort,
|
|
150
|
-
...hmrServer ? { server: hmrServer } : {},
|
|
151
|
-
...publicHost ? {
|
|
152
|
-
protocol: "wss",
|
|
153
|
-
host: publicHost,
|
|
154
|
-
clientPort: publicClientPort
|
|
155
|
-
} : {}
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
allowedHosts
|
|
159
|
-
}
|
|
241
|
+
server: resolveServerConfig(
|
|
242
|
+
existingServer,
|
|
243
|
+
env,
|
|
244
|
+
isLaravel,
|
|
245
|
+
publicHost,
|
|
246
|
+
hmrServer
|
|
247
|
+
)
|
|
160
248
|
};
|
|
161
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
|
+
},
|
|
162
261
|
configureServer(server) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
262
|
+
const release = releaseManagedHmrServer;
|
|
263
|
+
if (!release) return;
|
|
264
|
+
server.httpServer?.once("close", release);
|
|
265
|
+
},
|
|
266
|
+
closeBundle() {
|
|
267
|
+
releaseManagedHmrServer?.();
|
|
169
268
|
}
|
|
170
269
|
};
|
|
171
270
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,36 +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 `.${NOTEBOOK_HOST}` and the derived public host into
|
|
11
|
-
* `server.allowedHosts`.
|
|
12
|
-
* - true: disable host checks entirely
|
|
13
|
-
* - array: merge with existing allowedHosts
|
|
14
|
-
*/
|
|
15
|
-
allowedHosts?: true | string[];
|
|
16
|
-
/**
|
|
17
|
-
* Explicitly disable the plugin.
|
|
18
|
-
*/
|
|
19
|
-
enabled?: boolean;
|
|
20
|
-
/**
|
|
21
|
-
* Defaults to 5173.
|
|
22
|
-
*/
|
|
23
|
-
hmrPort?: number;
|
|
24
|
-
/**
|
|
25
|
-
* Defaults to `${SUBDOMAIN}-${port}.${NOTEBOOK_HOST}` when env values exist.
|
|
26
|
-
*/
|
|
27
|
-
publicHost?: string;
|
|
28
|
-
/**
|
|
29
|
-
* Defaults to 443.
|
|
30
|
-
*/
|
|
31
|
-
publicClientPort?: number;
|
|
32
|
-
};
|
|
33
|
-
declare function phpSandboxPlugin(options?: PhpSandboxOptions, env?: PhpSandboxEnv): Plugin;
|
|
3
|
+
declare function phpSandboxPlugin(): Plugin;
|
|
34
4
|
declare const phpSandbox: typeof phpSandboxPlugin;
|
|
35
5
|
|
|
36
|
-
export {
|
|
6
|
+
export { phpSandboxPlugin as default, phpSandbox, phpSandboxPlugin };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,36 +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 `.${NOTEBOOK_HOST}` and the derived public host into
|
|
11
|
-
* `server.allowedHosts`.
|
|
12
|
-
* - true: disable host checks entirely
|
|
13
|
-
* - array: merge with existing allowedHosts
|
|
14
|
-
*/
|
|
15
|
-
allowedHosts?: true | string[];
|
|
16
|
-
/**
|
|
17
|
-
* Explicitly disable the plugin.
|
|
18
|
-
*/
|
|
19
|
-
enabled?: boolean;
|
|
20
|
-
/**
|
|
21
|
-
* Defaults to 5173.
|
|
22
|
-
*/
|
|
23
|
-
hmrPort?: number;
|
|
24
|
-
/**
|
|
25
|
-
* Defaults to `${SUBDOMAIN}-${port}.${NOTEBOOK_HOST}` when env values exist.
|
|
26
|
-
*/
|
|
27
|
-
publicHost?: string;
|
|
28
|
-
/**
|
|
29
|
-
* Defaults to 443.
|
|
30
|
-
*/
|
|
31
|
-
publicClientPort?: number;
|
|
32
|
-
};
|
|
33
|
-
declare function phpSandboxPlugin(options?: PhpSandboxOptions, env?: PhpSandboxEnv): Plugin;
|
|
3
|
+
declare function phpSandboxPlugin(): Plugin;
|
|
34
4
|
declare const phpSandbox: typeof phpSandboxPlugin;
|
|
35
5
|
|
|
36
|
-
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,83 +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
|
-
merged.add(`.${notebookHost}`);
|
|
72
|
-
}
|
|
73
|
-
if (publicHost) {
|
|
74
|
-
merged.add(publicHost);
|
|
75
|
-
}
|
|
76
|
-
return merged.size > 0 ? Array.from(merged) : existingAllowedHosts;
|
|
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;
|
|
77
102
|
}
|
|
78
|
-
function
|
|
79
|
-
|
|
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}`;
|
|
147
|
+
}
|
|
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() {
|
|
80
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
|
+
}
|
|
81
187
|
return {
|
|
82
188
|
name: "vite-plugin-phpsandbox",
|
|
83
189
|
apply: "serve",
|
|
84
190
|
enforce: "post",
|
|
85
|
-
config(userConfig) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (!sandboxType)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
+
}
|
|
92
199
|
const isLaravel = sandboxType === "laravel";
|
|
200
|
+
const publicHost = resolvePublicHost(env);
|
|
93
201
|
const existingServer = userConfig.server ?? {};
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
const allowedHosts = resolveAllowedHosts(
|
|
97
|
-
existingServer.allowedHosts,
|
|
98
|
-
options.allowedHosts,
|
|
99
|
-
env.NOTEBOOK_HOST,
|
|
100
|
-
publicHost
|
|
101
|
-
);
|
|
102
|
-
const hmrServer = isLaravel || hmrDisabled ? void 0 : acquireServer(getGlobalState(), hmrPort);
|
|
103
|
-
managedHmrPort = hmrServer ? hmrPort : void 0;
|
|
104
|
-
managedHmrServer = hmrServer;
|
|
202
|
+
const hmrServer = shouldCreateHmrServer(existingServer, isLaravel) ? managedHmrServer ?? await acquireServer(getGlobalState(), HMR_PORT) : void 0;
|
|
203
|
+
setManagedHmrServer(hmrServer);
|
|
105
204
|
return {
|
|
106
|
-
server:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
port: hmrPort,
|
|
114
|
-
...hmrServer ? { server: hmrServer } : {},
|
|
115
|
-
...publicHost ? {
|
|
116
|
-
protocol: "wss",
|
|
117
|
-
host: publicHost,
|
|
118
|
-
clientPort: publicClientPort
|
|
119
|
-
} : {}
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
allowedHosts
|
|
123
|
-
}
|
|
205
|
+
server: resolveServerConfig(
|
|
206
|
+
existingServer,
|
|
207
|
+
env,
|
|
208
|
+
isLaravel,
|
|
209
|
+
publicHost,
|
|
210
|
+
hmrServer
|
|
211
|
+
)
|
|
124
212
|
};
|
|
125
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
|
+
},
|
|
126
225
|
configureServer(server) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
226
|
+
const release = releaseManagedHmrServer;
|
|
227
|
+
if (!release) return;
|
|
228
|
+
server.httpServer?.once("close", release);
|
|
229
|
+
},
|
|
230
|
+
closeBundle() {
|
|
231
|
+
releaseManagedHmrServer?.();
|
|
133
232
|
}
|
|
134
233
|
};
|
|
135
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 version
|
|
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": {
|