Skip to content

Commit 2207a68

Browse files
authored
fix: gracefully shutdown preview server on SIGTERM (fix #12990) (#17333)
1 parent 6db2515 commit 2207a68

File tree

4 files changed

+58
-22
lines changed

4 files changed

+58
-22
lines changed

packages/vite/src/node/preview.ts

+23-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@ import { htmlFallbackMiddleware } from './server/middlewares/htmlFallback'
2525
import { indexHtmlMiddleware } from './server/middlewares/indexHtml'
2626
import { notFoundMiddleware } from './server/middlewares/notFound'
2727
import { proxyMiddleware } from './server/middlewares/proxy'
28-
import { resolveHostname, resolveServerUrls, shouldServeFile } from './utils'
28+
import {
29+
resolveHostname,
30+
resolveServerUrls,
31+
setupSIGTERMListener,
32+
shouldServeFile,
33+
teardownSIGTERMListener,
34+
} from './utils'
2935
import { printServerUrls } from './logger'
3036
import { bindCLIShortcuts } from './shortcuts'
3137
import type { BindCLIShortcutsOptions } from './shortcuts'
@@ -137,11 +143,16 @@ export async function preview(
137143
const options = config.preview
138144
const logger = config.logger
139145

146+
const closeHttpServer = createServerCloseFn(httpServer)
147+
140148
const server: PreviewServer = {
141149
config,
142150
middlewares: app,
143151
httpServer,
144-
close: createServerCloseFn(httpServer),
152+
async close() {
153+
teardownSIGTERMListener(closeServerAndExit)
154+
await closeHttpServer()
155+
},
145156
resolvedUrls: null,
146157
printUrls() {
147158
if (server.resolvedUrls) {
@@ -155,6 +166,16 @@ export async function preview(
155166
},
156167
}
157168

169+
const closeServerAndExit = async () => {
170+
try {
171+
await server.close()
172+
} finally {
173+
process.exit()
174+
}
175+
}
176+
177+
setupSIGTERMListener(closeServerAndExit)
178+
158179
// apply server hooks from plugins
159180
const postHooks: ((() => void) | void)[] = []
160181
for (const hook of config.getSortedPluginHooks('configurePreviewServer')) {

packages/vite/src/node/server/index.ts

+12-17
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import {
3535
promiseWithResolvers,
3636
resolveHostname,
3737
resolveServerUrls,
38+
setupSIGTERMListener,
39+
teardownSIGTERMListener,
3840
} from '../utils'
3941
import { getFsUtils } from '../fsUtils'
4042
import { ssrLoadModule } from '../ssr/ssrModuleLoader'
@@ -502,8 +504,6 @@ export async function _createServer(
502504
const container = await createPluginContainer(config, moduleGraph, watcher)
503505
const closeHttpServer = createServerCloseFn(httpServer)
504506

505-
let exitProcess: () => void
506-
507507
const devHtmlTransformFn = createDevHtmlTransformFn(config)
508508

509509
const onCrawlEndCallbacks: (() => void)[] = []
@@ -638,10 +638,7 @@ export async function _createServer(
638638
},
639639
async close() {
640640
if (!middlewareMode) {
641-
process.off('SIGTERM', exitProcess)
642-
if (process.env.CI !== 'true') {
643-
process.stdin.off('end', exitProcess)
644-
}
641+
teardownSIGTERMListener(closeServerAndExit)
645642
}
646643
await Promise.allSettled([
647644
watcher.close(),
@@ -736,20 +733,18 @@ export async function _createServer(
736733
},
737734
})
738735

739-
if (!middlewareMode) {
740-
exitProcess = async () => {
741-
try {
742-
await server.close()
743-
} finally {
744-
process.exit()
745-
}
746-
}
747-
process.once('SIGTERM', exitProcess)
748-
if (process.env.CI !== 'true') {
749-
process.stdin.on('end', exitProcess)
736+
const closeServerAndExit = async () => {
737+
try {
738+
await server.close()
739+
} finally {
740+
process.exit()
750741
}
751742
}
752743

744+
if (!middlewareMode) {
745+
setupSIGTERMListener(closeServerAndExit)
746+
}
747+
753748
const onHMRUpdate = async (
754749
type: 'create' | 'delete' | 'update',
755750
file: string,

packages/vite/src/node/shortcuts.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,11 @@ const BASE_DEV_SHORTCUTS: CLIShortcut<ViteDevServer>[] = [
126126
key: 'q',
127127
description: 'quit',
128128
async action(server) {
129-
await server.close().finally(() => process.exit())
129+
try {
130+
await server.close()
131+
} finally {
132+
process.exit()
133+
}
130134
},
131135
},
132136
]
@@ -148,9 +152,9 @@ const BASE_PREVIEW_SHORTCUTS: CLIShortcut<PreviewServer>[] = [
148152
{
149153
key: 'q',
150154
description: 'quit',
151-
action(server) {
155+
async action(server) {
152156
try {
153-
server.httpServer.close()
157+
await server.close()
154158
} finally {
155159
process.exit()
156160
}

packages/vite/src/node/utils.ts

+16
Original file line numberDiff line numberDiff line change
@@ -1438,3 +1438,19 @@ export function partialEncodeURIPath(uri: string): string {
14381438
const postfix = filePath !== uri ? uri.slice(filePath.length) : ''
14391439
return filePath.replaceAll('%', '%25') + postfix
14401440
}
1441+
1442+
export const setupSIGTERMListener = (callback: () => Promise<void>): void => {
1443+
process.once('SIGTERM', callback)
1444+
if (process.env.CI !== 'true') {
1445+
process.stdin.on('end', callback)
1446+
}
1447+
}
1448+
1449+
export const teardownSIGTERMListener = (
1450+
callback: () => Promise<void>,
1451+
): void => {
1452+
process.off('SIGTERM', callback)
1453+
if (process.env.CI !== 'true') {
1454+
process.stdin.off('end', callback)
1455+
}
1456+
}

0 commit comments

Comments
 (0)