@@ -32,8 +32,7 @@ function getPackageVersions() {
32
32
)
33
33
viteVersion = JSON . parse ( vitePackageJson ) . version
34
34
}
35
- // eslint-disable-next-line unused-imports/no-unused-vars
36
- catch ( error ) {
35
+ catch {
37
36
// Fallback to package.json dependencies
38
37
viteVersion = packageJson . devDependencies ?. vite ?. replace ( '^' , '' ) || '0.0.0'
39
38
}
@@ -47,6 +46,26 @@ function getPackageVersions() {
47
46
48
47
const execAsync = promisify ( exec )
49
48
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
+
50
69
async function needsSudoAccess ( options : VitePluginLocalOptions , domain : string ) : Promise < boolean > {
51
70
try {
52
71
// Check if we need to generate certificates
@@ -78,7 +97,7 @@ async function needsSudoAccess(options: VitePluginLocalOptions, domain: string):
78
97
}
79
98
catch ( error ) {
80
99
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
82
101
}
83
102
}
84
103
@@ -91,61 +110,84 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
91
110
certs : false ,
92
111
} ,
93
112
} = options
113
+
94
114
let domains : string [ ] | undefined
95
115
let proxyUrl : string | undefined
96
116
let originalConsole : typeof console
97
- let cleanupPromise : Promise < void > | null = null
98
117
let isCleaningUp = false
118
+ let hasSudoAccess = false
119
+ let cleanupPromise : Promise < void > | null = null
120
+ let server : ViteDevServer | undefined
99
121
100
122
const debug = ( ...args : any [ ] ) => {
101
123
if ( verbose )
102
124
originalConsole . log ( '[vite-plugin-local]' , ...args )
103
125
}
104
126
105
- // Add cleanup handler for process exit
106
127
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
110
138
cleanupPromise = cleanup ( {
111
139
domains,
112
140
hosts : typeof cleanupOpts === 'boolean' ? cleanupOpts : cleanupOpts ?. hosts ,
113
141
certs : typeof cleanupOpts === 'boolean' ? cleanupOpts : cleanupOpts ?. certs ,
114
142
verbose,
115
143
} )
144
+
116
145
await cleanupPromise
117
146
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 {
119
154
isCleaningUp = false
155
+ cleanupPromise = null
120
156
}
121
157
}
122
158
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 )
132
169
}
133
- return originalExit ( code )
134
- } ) as ( code ?: number ) => never
135
170
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
+ }
140
180
141
181
return {
142
182
name : 'vite-plugin-local' ,
143
183
enforce : 'pre' ,
144
184
145
- configureServer ( server : ViteDevServer ) {
185
+ configureServer ( viteServer : ViteDevServer ) {
146
186
if ( ! enabled )
147
187
return
148
188
189
+ server = viteServer
190
+
149
191
// Override console.log immediately to prevent VitePress initial messages
150
192
const originalLog = console . log
151
193
console . log = ( ...args ) => {
@@ -163,6 +205,29 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
163
205
164
206
server . printUrls = ( ) => { }
165
207
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
+
166
231
const setupPlugin = async ( ) => {
167
232
try {
168
233
const config = buildConfig ( options , 'localhost:5173' )
@@ -173,27 +238,26 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
173
238
174
239
if ( needsSudo ) {
175
240
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 ( )
179
242
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
+ }
187
251
}
188
252
}
189
253
190
254
const setupProxy = async ( ) => {
191
255
try {
192
- const host = typeof server . config . server . host === 'boolean'
256
+ const host = typeof server ! . config . server . host === 'boolean'
193
257
? 'localhost'
194
- : server . config . server . host || 'localhost'
258
+ : server ! . config . server . host || 'localhost'
195
259
196
- const port = server . config . server . port || 5173
260
+ const port = server ! . config . server . port || 5173
197
261
const serverUrl = `${ host } :${ port } `
198
262
199
263
const config = buildConfig ( options , serverUrl )
@@ -204,9 +268,9 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
204
268
205
269
await startProxies ( config )
206
270
207
- server . printUrls = function ( ) {
271
+ server ! . printUrls = function ( ) {
208
272
const protocol = options . https ? 'https' : 'http'
209
- const port = server . config . server . port || 5173
273
+ const port = server ! . config . server . port || 5173
210
274
const localUrl = `http://localhost:${ port } /`
211
275
const proxiedUrl = `${ protocol } ://${ proxyUrl } /`
212
276
const colorUrl = ( url : string ) =>
@@ -240,7 +304,7 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
240
304
console . log ( `\n ${ green ( dim ( '➜' ) ) } ${ dim ( 'press' ) } ${ bold ( 'h' ) } ${ dim ( 'to show help' ) } \n` )
241
305
}
242
306
243
- server . printUrls ( )
307
+ server ! . printUrls ( )
244
308
debug ( 'Proxy setup complete' )
245
309
}
246
310
catch ( error ) {
@@ -249,8 +313,8 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
249
313
}
250
314
}
251
315
252
- server . httpServer ?. once ( 'listening' , setupProxy )
253
- if ( server . httpServer ?. listening ) {
316
+ server ? .httpServer ?. once ( 'listening' , setupProxy )
317
+ if ( server ? .httpServer ?. listening ) {
254
318
debug ( 'Server already listening, setting up proxy immediately' )
255
319
await setupProxy ( )
256
320
}
@@ -261,7 +325,13 @@ export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
261
325
}
262
326
}
263
327
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' )
265
335
} ,
266
336
}
267
337
}
0 commit comments