Skip to content

Commit 237fa54

Browse files
committed
chore: improve exit handling
1 parent 4e9195c commit 237fa54

File tree

1 file changed

+114
-44
lines changed

1 file changed

+114
-44
lines changed

src/index.ts

+114-44
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ function getPackageVersions() {
3232
)
3333
viteVersion = JSON.parse(vitePackageJson).version
3434
}
35-
// eslint-disable-next-line unused-imports/no-unused-vars
36-
catch (error) {
35+
catch {
3736
// Fallback to package.json dependencies
3837
viteVersion = packageJson.devDependencies?.vite?.replace('^', '') || '0.0.0'
3938
}
@@ -47,6 +46,26 @@ function getPackageVersions() {
4746

4847
const execAsync = promisify(exec)
4948

49+
async function checkInitialSudo(): Promise<boolean> {
50+
try {
51+
await execAsync('sudo -n true')
52+
return true
53+
}
54+
catch {
55+
return false
56+
}
57+
}
58+
59+
async function getSudoAccess(): Promise<boolean> {
60+
try {
61+
await execAsync('sudo true')
62+
return true
63+
}
64+
catch {
65+
return false
66+
}
67+
}
68+
5069
async function needsSudoAccess(options: VitePluginLocalOptions, domain: string): Promise<boolean> {
5170
try {
5271
// Check if we need to generate certificates
@@ -78,7 +97,7 @@ async function needsSudoAccess(options: VitePluginLocalOptions, domain: string):
7897
}
7998
catch (error) {
8099
console.error('Error checking sudo requirements:', error)
81-
return false // Changed to false - if we can't check, don't assume we need sudo
100+
return false // If we can't check, don't assume we need sudo
82101
}
83102
}
84103

@@ -91,61 +110,84 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
91110
certs: false,
92111
},
93112
} = options
113+
94114
let domains: string[] | undefined
95115
let proxyUrl: string | undefined
96116
let originalConsole: typeof console
97-
let cleanupPromise: Promise<void> | null = null
98117
let isCleaningUp = false
118+
let hasSudoAccess = false
119+
let cleanupPromise: Promise<void> | null = null
120+
let server: ViteDevServer | undefined
99121

100122
const debug = (...args: any[]) => {
101123
if (verbose)
102124
originalConsole.log('[vite-plugin-local]', ...args)
103125
}
104126

105-
// Add cleanup handler for process exit
106127
const exitHandler = async () => {
107-
if (domains?.length && !isCleaningUp) {
108-
isCleaningUp = true
109-
debug('Cleaning up...')
128+
if (!domains?.length || isCleaningUp) {
129+
debug('Skipping cleanup - no domains or already cleaning')
130+
return
131+
}
132+
133+
isCleaningUp = true
134+
debug('Starting cleanup process')
135+
136+
try {
137+
// Store the cleanup promise
110138
cleanupPromise = cleanup({
111139
domains,
112140
hosts: typeof cleanupOpts === 'boolean' ? cleanupOpts : cleanupOpts?.hosts,
113141
certs: typeof cleanupOpts === 'boolean' ? cleanupOpts : cleanupOpts?.certs,
114142
verbose,
115143
})
144+
116145
await cleanupPromise
117146
domains = undefined
118-
debug('Cleanup complete')
147+
debug('Cleanup completed successfully')
148+
}
149+
catch (error) {
150+
console.error('Error during cleanup:', error)
151+
throw error // Re-throw to ensure process exits with error
152+
}
153+
finally {
119154
isCleaningUp = false
155+
cleanupPromise = null
120156
}
121157
}
122158

123-
// Override the library's process.exit
124-
const originalExit = process.exit
125-
process.exit = ((code?: number) => {
126-
if (cleanupPromise || domains?.length) {
127-
exitHandler().finally(() => {
128-
process.exit = originalExit
129-
process.exit(code)
130-
})
131-
return undefined as never
159+
const handleSignal = async (signal: string) => {
160+
debug(`Received ${signal}, initiating cleanup...`)
161+
162+
try {
163+
await exitHandler()
164+
debug(`Cleanup after ${signal} completed successfully`)
165+
}
166+
catch (error) {
167+
console.error(`Cleanup failed after ${signal}:`, error)
168+
process.exit(1)
132169
}
133-
return originalExit(code)
134-
}) as (code?: number) => never
135170

136-
// Handle cleanup for different termination signals
137-
process.on('SIGINT', exitHandler)
138-
process.on('SIGTERM', exitHandler)
139-
process.on('beforeExit', exitHandler)
171+
if (server?.httpServer) {
172+
server.httpServer.close()
173+
}
174+
175+
// Only exit if we're handling a signal
176+
if (signal !== 'CLOSE') {
177+
process.exit(0)
178+
}
179+
}
140180

141181
return {
142182
name: 'vite-plugin-local',
143183
enforce: 'pre',
144184

145-
configureServer(server: ViteDevServer) {
185+
configureServer(viteServer: ViteDevServer) {
146186
if (!enabled)
147187
return
148188

189+
server = viteServer
190+
149191
// Override console.log immediately to prevent VitePress initial messages
150192
const originalLog = console.log
151193
console.log = (...args) => {
@@ -163,6 +205,29 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
163205

164206
server.printUrls = () => { }
165207

208+
// Add cleanup handlers for the server
209+
server.httpServer?.on('close', () => {
210+
debug('Server closing, cleaning up...')
211+
handleSignal('CLOSE')
212+
})
213+
214+
// Register signal handlers
215+
process.once('SIGINT', () => handleSignal('SIGINT'))
216+
process.once('SIGTERM', () => handleSignal('SIGTERM'))
217+
process.once('beforeExit', () => handleSignal('beforeExit'))
218+
process.once('exit', async () => {
219+
// If there's a pending cleanup, wait for it
220+
if (cleanupPromise) {
221+
try {
222+
await cleanupPromise
223+
}
224+
catch (error) {
225+
console.error('Cleanup failed during exit:', error)
226+
process.exit(1)
227+
}
228+
}
229+
})
230+
166231
const setupPlugin = async () => {
167232
try {
168233
const config = buildConfig(options, 'localhost:5173')
@@ -173,27 +238,26 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
173238

174239
if (needsSudo) {
175240
debug('Sudo access required')
176-
// Only show sudo message if we actually need it
177-
console.log('\nSudo access required for proxy setup.')
178-
console.log('Please enter your password when prompted.\n')
241+
hasSudoAccess = await checkInitialSudo()
179242

180-
try {
181-
await execAsync('sudo true')
182-
}
183-
// eslint-disable-next-line unused-imports/no-unused-vars
184-
catch (error) {
185-
console.error('Failed to get sudo access. Please try again.')
186-
process.exit(1)
243+
if (!hasSudoAccess) {
244+
console.log('\nSudo access required for proxy setup.')
245+
hasSudoAccess = await getSudoAccess()
246+
247+
if (!hasSudoAccess) {
248+
console.error('Failed to get sudo access. Please try again.')
249+
process.exit(1)
250+
}
187251
}
188252
}
189253

190254
const setupProxy = async () => {
191255
try {
192-
const host = typeof server.config.server.host === 'boolean'
256+
const host = typeof server!.config.server.host === 'boolean'
193257
? 'localhost'
194-
: server.config.server.host || 'localhost'
258+
: server!.config.server.host || 'localhost'
195259

196-
const port = server.config.server.port || 5173
260+
const port = server!.config.server.port || 5173
197261
const serverUrl = `${host}:${port}`
198262

199263
const config = buildConfig(options, serverUrl)
@@ -204,9 +268,9 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
204268

205269
await startProxies(config)
206270

207-
server.printUrls = function () {
271+
server!.printUrls = function () {
208272
const protocol = options.https ? 'https' : 'http'
209-
const port = server.config.server.port || 5173
273+
const port = server!.config.server.port || 5173
210274
const localUrl = `http://localhost:${port}/`
211275
const proxiedUrl = `${protocol}://${proxyUrl}/`
212276
const colorUrl = (url: string) =>
@@ -240,7 +304,7 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
240304
console.log(`\n ${green(dim('➜'))} ${dim('press')} ${bold('h')} ${dim('to show help')}\n`)
241305
}
242306

243-
server.printUrls()
307+
server!.printUrls()
244308
debug('Proxy setup complete')
245309
}
246310
catch (error) {
@@ -249,8 +313,8 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
249313
}
250314
}
251315

252-
server.httpServer?.once('listening', setupProxy)
253-
if (server.httpServer?.listening) {
316+
server?.httpServer?.once('listening', setupProxy)
317+
if (server?.httpServer?.listening) {
254318
debug('Server already listening, setting up proxy immediately')
255319
await setupProxy()
256320
}
@@ -261,7 +325,13 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
261325
}
262326
}
263327

264-
return setupPlugin()
328+
setupPlugin()
329+
},
330+
331+
// Add a closeBundle hook to ensure cleanup happens
332+
async closeBundle() {
333+
debug('Bundle closing, initiating cleanup...')
334+
await handleSignal('CLOSE')
265335
},
266336
}
267337
}

0 commit comments

Comments
 (0)