vite-plugin-phpsandbox 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -22
- package/dist/index.cjs +185 -77
- package/dist/index.d.cts +2 -32
- package/dist/index.d.ts +2 -32
- package/dist/index.js +185 -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,161 @@ 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
|
-
if (
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
+
};
|
|
113
151
|
}
|
|
114
|
-
function
|
|
115
|
-
|
|
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 needsHmrServer = false;
|
|
204
|
+
let startupLog;
|
|
205
|
+
function setManagedHmrServer(server) {
|
|
206
|
+
if (managedHmrServer === server) return;
|
|
207
|
+
releaseManagedHmrServer?.();
|
|
208
|
+
managedHmrServer = server;
|
|
209
|
+
if (!server) {
|
|
210
|
+
releaseManagedHmrServer = void 0;
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
let released = false;
|
|
214
|
+
releaseManagedHmrServer = () => {
|
|
215
|
+
if (released) return;
|
|
216
|
+
released = true;
|
|
217
|
+
if (managedHmrServer === server) {
|
|
218
|
+
managedHmrServer = void 0;
|
|
219
|
+
releaseManagedHmrServer = void 0;
|
|
220
|
+
}
|
|
221
|
+
releaseServer(getGlobalState(), HMR_PORT, server);
|
|
222
|
+
};
|
|
223
|
+
}
|
|
117
224
|
return {
|
|
118
225
|
name: "vite-plugin-phpsandbox",
|
|
119
226
|
apply: "serve",
|
|
120
227
|
enforce: "post",
|
|
121
228
|
config(userConfig) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (!sandboxType)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
229
|
+
const env = process.env;
|
|
230
|
+
sandboxType = normalizeSandboxType(env.PHPSANDBOX_TYPE);
|
|
231
|
+
if (!sandboxType) {
|
|
232
|
+
needsHmrServer = false;
|
|
233
|
+
startupLog = void 0;
|
|
234
|
+
return {};
|
|
235
|
+
}
|
|
128
236
|
const isLaravel = sandboxType === "laravel";
|
|
237
|
+
const publicHost = resolvePublicHost(env);
|
|
129
238
|
const existingServer = userConfig.server ?? {};
|
|
130
|
-
|
|
131
|
-
const hmrDisabled = existingServer.hmr === false;
|
|
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;
|
|
239
|
+
needsHmrServer = shouldCreateHmrServer(existingServer, isLaravel);
|
|
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
|
+
void 0
|
|
247
|
+
)
|
|
160
248
|
};
|
|
161
249
|
},
|
|
162
|
-
|
|
163
|
-
if (!
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
250
|
+
configResolved(config) {
|
|
251
|
+
if (!sandboxType) return;
|
|
252
|
+
const nextStartupLog = formatStartupLog(
|
|
253
|
+
sandboxType,
|
|
254
|
+
config.server,
|
|
255
|
+
needsHmrServer
|
|
256
|
+
);
|
|
257
|
+
if (nextStartupLog === startupLog) return;
|
|
258
|
+
startupLog = nextStartupLog;
|
|
259
|
+
console.info(nextStartupLog);
|
|
260
|
+
},
|
|
261
|
+
async configureServer(server) {
|
|
262
|
+
if (!sandboxType) return;
|
|
263
|
+
if (needsHmrServer) {
|
|
264
|
+
const hmrServer = managedHmrServer ?? await acquireServer(getGlobalState(), HMR_PORT);
|
|
265
|
+
setManagedHmrServer(hmrServer);
|
|
266
|
+
const hmrConfig = server.config.server.hmr;
|
|
267
|
+
if (isRecord(hmrConfig)) {
|
|
268
|
+
hmrConfig.server = hmrServer;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const release = releaseManagedHmrServer;
|
|
272
|
+
if (!release) return;
|
|
273
|
+
server.httpServer?.once("close", release);
|
|
274
|
+
},
|
|
275
|
+
closeBundle() {
|
|
276
|
+
releaseManagedHmrServer?.();
|
|
169
277
|
}
|
|
170
278
|
};
|
|
171
279
|
}
|
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,161 @@ 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
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
+
};
|
|
77
115
|
}
|
|
78
|
-
function
|
|
79
|
-
|
|
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 needsHmrServer = false;
|
|
168
|
+
let startupLog;
|
|
169
|
+
function setManagedHmrServer(server) {
|
|
170
|
+
if (managedHmrServer === server) return;
|
|
171
|
+
releaseManagedHmrServer?.();
|
|
172
|
+
managedHmrServer = server;
|
|
173
|
+
if (!server) {
|
|
174
|
+
releaseManagedHmrServer = void 0;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
let released = false;
|
|
178
|
+
releaseManagedHmrServer = () => {
|
|
179
|
+
if (released) return;
|
|
180
|
+
released = true;
|
|
181
|
+
if (managedHmrServer === server) {
|
|
182
|
+
managedHmrServer = void 0;
|
|
183
|
+
releaseManagedHmrServer = void 0;
|
|
184
|
+
}
|
|
185
|
+
releaseServer(getGlobalState(), HMR_PORT, server);
|
|
186
|
+
};
|
|
187
|
+
}
|
|
81
188
|
return {
|
|
82
189
|
name: "vite-plugin-phpsandbox",
|
|
83
190
|
apply: "serve",
|
|
84
191
|
enforce: "post",
|
|
85
192
|
config(userConfig) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (!sandboxType)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
193
|
+
const env = process.env;
|
|
194
|
+
sandboxType = normalizeSandboxType(env.PHPSANDBOX_TYPE);
|
|
195
|
+
if (!sandboxType) {
|
|
196
|
+
needsHmrServer = false;
|
|
197
|
+
startupLog = void 0;
|
|
198
|
+
return {};
|
|
199
|
+
}
|
|
92
200
|
const isLaravel = sandboxType === "laravel";
|
|
201
|
+
const publicHost = resolvePublicHost(env);
|
|
93
202
|
const existingServer = userConfig.server ?? {};
|
|
94
|
-
|
|
95
|
-
const hmrDisabled = existingServer.hmr === false;
|
|
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;
|
|
203
|
+
needsHmrServer = shouldCreateHmrServer(existingServer, isLaravel);
|
|
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
|
+
void 0
|
|
211
|
+
)
|
|
124
212
|
};
|
|
125
213
|
},
|
|
126
|
-
|
|
127
|
-
if (!
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
214
|
+
configResolved(config) {
|
|
215
|
+
if (!sandboxType) return;
|
|
216
|
+
const nextStartupLog = formatStartupLog(
|
|
217
|
+
sandboxType,
|
|
218
|
+
config.server,
|
|
219
|
+
needsHmrServer
|
|
220
|
+
);
|
|
221
|
+
if (nextStartupLog === startupLog) return;
|
|
222
|
+
startupLog = nextStartupLog;
|
|
223
|
+
console.info(nextStartupLog);
|
|
224
|
+
},
|
|
225
|
+
async configureServer(server) {
|
|
226
|
+
if (!sandboxType) return;
|
|
227
|
+
if (needsHmrServer) {
|
|
228
|
+
const hmrServer = managedHmrServer ?? await acquireServer(getGlobalState(), HMR_PORT);
|
|
229
|
+
setManagedHmrServer(hmrServer);
|
|
230
|
+
const hmrConfig = server.config.server.hmr;
|
|
231
|
+
if (isRecord(hmrConfig)) {
|
|
232
|
+
hmrConfig.server = hmrServer;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const release = releaseManagedHmrServer;
|
|
236
|
+
if (!release) return;
|
|
237
|
+
server.httpServer?.once("close", release);
|
|
238
|
+
},
|
|
239
|
+
closeBundle() {
|
|
240
|
+
releaseManagedHmrServer?.();
|
|
133
241
|
}
|
|
134
242
|
};
|
|
135
243
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-phpsandbox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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": {
|