fix: 修复热更新问题 #AI commit#

This commit is contained in:
winixt 2026-05-06 19:41:02 +08:00
parent b6b37e90c7
commit 39bf1646e4

View File

@ -1,8 +1,10 @@
import type { IPluginAPI } from '@fesjs/shared'; import type { IPluginAPI } from '@fesjs/shared';
import type { Server as HttpsServer } from 'node:https';
import type { ViteDevServer } from 'vite'; import type { ViteDevServer } from 'vite';
import os from 'node:os';
import process from 'node:process'; import process from 'node:process';
import { createServer } from 'vite';
import pc from 'picocolors'; import pc from 'picocolors';
import { createServer as createViteServer } from 'vite';
import getDevConfig from './getDevConfig'; import getDevConfig from './getDevConfig';
interface Args { interface Args {
@ -10,6 +12,52 @@ interface Args {
rawArgv?: Record<string, any>; rawArgv?: Record<string, any>;
options?: Record<string, any>; options?: Record<string, any>;
program?: any; program?: any;
https?: boolean;
}
// Vite 8 uses http2.createSecureServer when HTTPS is enabled, which creates
// Http2ServerRequest/Http2ServerResponse objects that are incompatible with
// Connect middleware (parseurl, finalhandler, etc.).
// This function replaces the HTTP/2 server with a standard HTTPS server
// that produces compatible IncomingMessage/ServerResponse objects.
async function replaceHttp2WithHttps(server: ViteDevServer): Promise<HttpsServer | undefined> {
const httpServer = server.httpServer;
if (!httpServer || (httpServer as any).constructor.name !== 'Http2SecureServer') {
return undefined;
}
// Extract the TLS credentials from the HTTP/2 server
const secureContext = (httpServer as any)._sharedCreds;
if (!secureContext?.context) {
return undefined;
}
// Get the resolved HTTPS options from Vite config
const httpsOptions = (server.config as any).server?.https;
if (!httpsOptions) {
return undefined;
}
// Migrate upgrade listeners (for WebSocket/HMR) from the old server
// before closing it, so they can be re-attached to the new server.
const upgradeListeners = (httpServer as any).listeners('upgrade').slice();
// Create a standard HTTPS server
const https = await import('node:https');
const newServer = https.createServer(httpsOptions, server.middlewares);
// Re-attach upgrade listeners to the new server
for (const listener of upgradeListeners) {
newServer.on('upgrade', listener);
}
// Close the HTTP/2 server (it hasn't started listening yet)
httpServer.close();
// Replace the httpServer reference on the Vite server object
(server as any).httpServer = newServer;
return newServer;
} }
export default (api: IPluginAPI) => { export default (api: IPluginAPI) => {
@ -19,8 +67,10 @@ export default (api: IPluginAPI) => {
} = api; } = api;
let server: ViteDevServer | undefined; let server: ViteDevServer | undefined;
let customHttpsServer: HttpsServer | undefined;
function destroy() { function destroy() {
customHttpsServer?.close();
if (server) { if (server) {
server.close().catch(() => {}); server.close().catch(() => {});
} }
@ -49,8 +99,45 @@ export default (api: IPluginAPI) => {
await api.startWatch(); await api.startWatch();
server = await createServer(await getDevConfig(api, args)); server = await createViteServer(await getDevConfig(api, args));
await server.listen();
// Replace HTTP/2 server with standard HTTPS if applicable
customHttpsServer = await replaceHttp2WithHttps(server);
if (customHttpsServer) {
// For custom HTTPS server, we need to listen manually
// because Vite's internal listen() uses the old httpServer closure
const port = server.config.server.port;
const host = server.config.server.host;
await new Promise<void>((resolve, reject) => {
customHttpsServer!.listen(
port,
host === true ? undefined : (host as string) || undefined,
() => resolve(),
);
customHttpsServer!.on('error', reject);
});
// Set resolvedUrls for printUrls()
const resolvedPort = (customHttpsServer.address() as any).port;
const base = server.config.base === './' || server.config.base === '' ? '/' : server.config.base;
const networkInterfaces = os.networkInterfaces();
const networkUrls: string[] = [];
for (const nInterface of Object.values(networkInterfaces)) {
for (const detail of nInterface ?? []) {
if (detail.family === 'IPv4' && detail.address && !detail.address.includes('127.0.0.1')) {
networkUrls.push(`https://${detail.address}:${resolvedPort}${base}`);
}
}
}
(server as any).resolvedUrls = {
local: [`https://localhost:${resolvedPort}${base}`],
network: networkUrls,
};
}
else {
await server.listen();
}
server.printUrls(); server.printUrls();