diff --git a/packages/vite/src/node/cli.ts b/packages/vite/src/node/cli.ts
index b520b9b5144b46..27981ac4004f92 100644
--- a/packages/vite/src/node/cli.ts
+++ b/packages/vite/src/node/cli.ts
@@ -169,6 +169,9 @@ cli
'--force',
`[boolean] force the optimizer to ignore the cache and re-bundle`,
)
+ .option('--watchStdin', `[boolean] watch stdin and exit on EOF`, {
+ default: !process.stdin.isTTY,
+ })
.action(async (root: string, options: ServerOptions & GlobalCLIOptions) => {
filterDuplicateOptions(options)
// output structure is preserved even after bundling so require()
@@ -364,6 +367,9 @@ cli
.option('--strictPort', `[boolean] exit if specified port is already in use`)
.option('--open [path]', `[boolean | string] open browser on startup`)
.option('--outDir
', `[string] output directory (default: dist)`)
+ .option('--watchStdin', `[boolean] watch stdin and exit on EOF`, {
+ default: !process.stdin.isTTY,
+ })
.action(
async (
root: string,
@@ -373,6 +379,7 @@ cli
open?: boolean | string
strictPort?: boolean
outDir?: string
+ watchStdin?: boolean
} & GlobalCLIOptions,
) => {
filterDuplicateOptions(options)
@@ -392,6 +399,7 @@ cli
strictPort: options.strictPort,
host: options.host,
open: options.open,
+ watchStdin: options.watchStdin,
},
})
server.printUrls()
diff --git a/packages/vite/src/node/http.ts b/packages/vite/src/node/http.ts
index ec1bf5e645641d..89560162811c99 100644
--- a/packages/vite/src/node/http.ts
+++ b/packages/vite/src/node/http.ts
@@ -67,6 +67,10 @@ export interface CommonServerOptions {
* Specify server response headers.
*/
headers?: HttpServerHeaders
+ /**
+ * Watch stdin and exit on EOF
+ */
+ watchStdin?: boolean
}
/**
diff --git a/packages/vite/src/node/preview.ts b/packages/vite/src/node/preview.ts
index 614c16f8aaac06..c9c76bde95df43 100644
--- a/packages/vite/src/node/preview.ts
+++ b/packages/vite/src/node/preview.ts
@@ -60,6 +60,7 @@ export function resolvePreviewOptions(
proxy: preview?.proxy ?? server.proxy,
cors: preview?.cors ?? server.cors,
headers: preview?.headers ?? server.headers,
+ watchStdin: preview?.watchStdin ?? server.watchStdin,
}
}
@@ -151,7 +152,7 @@ export async function preview(
// Promise used by `server.close()` to ensure `closeServer()` is only called once
let closeServerPromise: Promise | undefined
const closeServer = async () => {
- teardownSIGTERMListener(closeServerAndExit)
+ teardownSIGTERMListener(config.preview.watchStdin, closeServerAndExit)
await closeHttpServer()
server.resolvedUrls = null
}
@@ -188,7 +189,7 @@ export async function preview(
}
}
- setupSIGTERMListener(closeServerAndExit)
+ setupSIGTERMListener(config.preview.watchStdin, closeServerAndExit)
// apply server hooks from plugins
const postHooks: ((() => void) | void)[] = []
diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts
index 486b245d9c71de..badb21b910ca28 100644
--- a/packages/vite/src/node/server/index.ts
+++ b/packages/vite/src/node/server/index.ts
@@ -527,7 +527,7 @@ export async function _createServer(
let closeServerPromise: Promise | undefined
const closeServer = async () => {
if (!middlewareMode) {
- teardownSIGTERMListener(closeServerAndExit)
+ teardownSIGTERMListener(config.server.watchStdin, closeServerAndExit)
}
await Promise.allSettled([
@@ -766,7 +766,7 @@ export async function _createServer(
}
if (!middlewareMode) {
- setupSIGTERMListener(closeServerAndExit)
+ setupSIGTERMListener(config.server.watchStdin, closeServerAndExit)
}
const onHMRUpdate = async (
@@ -1045,6 +1045,7 @@ export const serverConfigDefaults = Object.freeze({
host: 'localhost',
https: undefined,
open: false,
+ watchStdin: false,
proxy: undefined,
cors: true,
headers: {},
diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts
index 582a3c2c6e43ef..c94a25e90bd422 100644
--- a/packages/vite/src/node/utils.ts
+++ b/packages/vite/src/node/utils.ts
@@ -1544,24 +1544,28 @@ const parentSigtermCallback: SigtermCallback = async (signal, exitCode) => {
}
export const setupSIGTERMListener = (
+ watchStdin: boolean,
callback: (signal?: 'SIGTERM', exitCode?: number) => Promise,
): void => {
if (sigtermCallbacks.size === 0) {
process.once('SIGTERM', parentSigtermCallback)
- if (process.env.CI !== 'true') {
+ if (watchStdin) {
process.stdin.on('end', parentSigtermCallback)
+ // resume stdin to allow the server to exit on EOF
+ process.stdin.resume()
}
}
sigtermCallbacks.add(callback)
}
export const teardownSIGTERMListener = (
- callback: Parameters[0],
+ watchStdin: boolean,
+ callback: Parameters[1],
): void => {
sigtermCallbacks.delete(callback)
if (sigtermCallbacks.size === 0) {
process.off('SIGTERM', parentSigtermCallback)
- if (process.env.CI !== 'true') {
+ if (watchStdin) {
process.stdin.off('end', parentSigtermCallback)
}
}