neex 0.7.11 → 0.7.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/src/runner.js +71 -35
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/src/runner.js
CHANGED
|
@@ -37,7 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.Runner = void 0;
|
|
40
|
-
// src/runner.ts -
|
|
40
|
+
// src/runner.ts - Fixed version
|
|
41
41
|
const child_process_1 = require("child_process");
|
|
42
42
|
const fsPromises = __importStar(require("fs/promises"));
|
|
43
43
|
const path = __importStar(require("path"));
|
|
@@ -52,8 +52,29 @@ class Runner {
|
|
|
52
52
|
this.serverInfo = new Map();
|
|
53
53
|
this.portRegex = /listening on (?:port |http:\/\/localhost:|https:\/\/localhost:)(\d+)/i;
|
|
54
54
|
this.urlRegex = /(https?:\/\/localhost:[0-9]+(?:\/[^\s]*)?)/i;
|
|
55
|
+
this.isCleaningUp = false;
|
|
55
56
|
this.options = options;
|
|
56
57
|
this.activeProcesses = new Map();
|
|
58
|
+
this.setupSignalHandlers();
|
|
59
|
+
}
|
|
60
|
+
setupSignalHandlers() {
|
|
61
|
+
const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT'];
|
|
62
|
+
signals.forEach((signal) => {
|
|
63
|
+
process.on(signal, () => {
|
|
64
|
+
if (!this.isCleaningUp) {
|
|
65
|
+
this.isCleaningUp = true;
|
|
66
|
+
logger_1.default.printLine(`\nReceived ${signal}. Cleaning up...`, 'warn');
|
|
67
|
+
this.cleanup(signal);
|
|
68
|
+
process.exit(0);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
// Handle unexpected exits
|
|
73
|
+
process.on('beforeExit', () => {
|
|
74
|
+
if (!this.isCleaningUp) {
|
|
75
|
+
this.cleanup();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
57
78
|
}
|
|
58
79
|
async expandWildcardCommands(commands) {
|
|
59
80
|
const expandedCommands = [];
|
|
@@ -75,13 +96,13 @@ class Runner {
|
|
|
75
96
|
let foundMatch = false;
|
|
76
97
|
for (const scriptName in packageJson.scripts) {
|
|
77
98
|
if (pattern.test(scriptName)) {
|
|
78
|
-
expandedCommands.push(scriptName);
|
|
99
|
+
expandedCommands.push(scriptName);
|
|
79
100
|
foundMatch = true;
|
|
80
101
|
}
|
|
81
102
|
}
|
|
82
103
|
if (!foundMatch) {
|
|
83
104
|
logger_1.default.printLine(`No scripts found in package.json matching wildcard: ${command}`, 'warn');
|
|
84
|
-
expandedCommands.push(command);
|
|
105
|
+
expandedCommands.push(command);
|
|
85
106
|
}
|
|
86
107
|
}
|
|
87
108
|
else {
|
|
@@ -105,13 +126,11 @@ class Runner {
|
|
|
105
126
|
return { executableCommand: commandToExecute, executionCwd: targetCwd };
|
|
106
127
|
}
|
|
107
128
|
else {
|
|
108
|
-
// It's a script from package.json, but no 'cd ... && ...' pattern
|
|
109
129
|
return { executableCommand: scriptValue, executionCwd: baseDir };
|
|
110
130
|
}
|
|
111
131
|
}
|
|
112
132
|
}
|
|
113
133
|
catch (error) {
|
|
114
|
-
// Errors like package.json not found, or script not in package.json
|
|
115
134
|
// Will treat as direct command
|
|
116
135
|
}
|
|
117
136
|
return { executableCommand: scriptNameOrCommand, executionCwd: undefined };
|
|
@@ -119,7 +138,6 @@ class Runner {
|
|
|
119
138
|
detectServerInfo(command, data) {
|
|
120
139
|
if (!this.options.isServerMode)
|
|
121
140
|
return;
|
|
122
|
-
// Get or create server info
|
|
123
141
|
let serverInfo = this.serverInfo.get(command);
|
|
124
142
|
if (!serverInfo) {
|
|
125
143
|
serverInfo = {
|
|
@@ -128,25 +146,20 @@ class Runner {
|
|
|
128
146
|
};
|
|
129
147
|
this.serverInfo.set(command, serverInfo);
|
|
130
148
|
}
|
|
131
|
-
// Try to detect port from output
|
|
132
149
|
const portMatch = data.match(this.portRegex);
|
|
133
150
|
if (portMatch && portMatch[1]) {
|
|
134
151
|
serverInfo.port = parseInt(portMatch[1], 10);
|
|
135
152
|
serverInfo.status = 'running';
|
|
136
|
-
// Only log if we just discovered the port
|
|
137
153
|
if (!serverInfo.url) {
|
|
138
154
|
logger_1.default.printLine(`Server ${command} running on port ${serverInfo.port}`, 'info');
|
|
139
155
|
}
|
|
140
156
|
}
|
|
141
|
-
// Try to detect full URL from output
|
|
142
157
|
const urlMatch = data.match(this.urlRegex);
|
|
143
158
|
if (urlMatch && urlMatch[1]) {
|
|
144
159
|
serverInfo.url = urlMatch[1];
|
|
145
160
|
serverInfo.status = 'running';
|
|
146
|
-
// Log the full URL once we detect it
|
|
147
161
|
logger_1.default.printLine(`Server ${command} available at ${chalk_1.default.cyan(serverInfo.url)}`, 'info');
|
|
148
162
|
}
|
|
149
|
-
// Update server info
|
|
150
163
|
this.serverInfo.set(command, serverInfo);
|
|
151
164
|
}
|
|
152
165
|
async runCommand(originalCommand, currentRetry = 0) {
|
|
@@ -172,11 +185,11 @@ class Runner {
|
|
|
172
185
|
...(0, npm_run_path_1.npmRunPathEnv)(),
|
|
173
186
|
FORCE_COLOR: this.options.color ? '1' : '0'
|
|
174
187
|
};
|
|
188
|
+
// Fix: Remove detached and handle process groups properly
|
|
175
189
|
const proc = (0, child_process_1.spawn)(cmd, args, {
|
|
176
190
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
177
191
|
shell: true,
|
|
178
192
|
env,
|
|
179
|
-
detached: true,
|
|
180
193
|
cwd
|
|
181
194
|
});
|
|
182
195
|
this.activeProcesses.set(originalCommand, proc);
|
|
@@ -313,9 +326,7 @@ class Runner {
|
|
|
313
326
|
logger_1.default.printLine('No commands to run after wildcard expansion.', 'warn');
|
|
314
327
|
return [];
|
|
315
328
|
}
|
|
316
|
-
// Initialize logger with the final list of commands
|
|
317
329
|
logger_1.default.setCommands(commands);
|
|
318
|
-
// Run in parallel or sequential mode
|
|
319
330
|
if (this.options.parallel) {
|
|
320
331
|
if (this.options.isServerMode) {
|
|
321
332
|
logger_1.default.printLine('Starting servers in parallel mode', 'info');
|
|
@@ -330,36 +341,61 @@ class Runner {
|
|
|
330
341
|
}
|
|
331
342
|
}
|
|
332
343
|
cleanup(signal = 'SIGTERM') {
|
|
344
|
+
if (this.isCleaningUp)
|
|
345
|
+
return;
|
|
346
|
+
this.isCleaningUp = true;
|
|
333
347
|
logger_1.default.printLine('Cleaning up child processes...', 'warn');
|
|
348
|
+
const promises = [];
|
|
334
349
|
this.activeProcesses.forEach((proc, command) => {
|
|
335
350
|
if (proc.pid && !proc.killed) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
351
|
+
promises.push(this.killProcess(proc, command, signal));
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
// Wait for all processes to be killed
|
|
355
|
+
Promise.all(promises).then(() => {
|
|
356
|
+
this.activeProcesses.clear();
|
|
357
|
+
if (this.options.isServerMode && this.serverInfo.size > 0) {
|
|
358
|
+
logger_1.default.printLine('Server shutdown summary:', 'info');
|
|
359
|
+
this.serverInfo.forEach((info, command) => {
|
|
360
|
+
const statusColor = info.status === 'running' ? chalk_1.default.green :
|
|
361
|
+
info.status === 'error' ? chalk_1.default.red : chalk_1.default.yellow;
|
|
362
|
+
logger_1.default.printLine(` ${command}: ${statusColor(info.status)}`, 'info');
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}).catch((error) => {
|
|
366
|
+
logger_1.default.printLine(`Error during cleanup: ${error.message}`, 'error');
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
async killProcess(proc, command, signal) {
|
|
370
|
+
return new Promise((resolve) => {
|
|
371
|
+
const timeout = setTimeout(() => {
|
|
372
|
+
if (proc.pid && !proc.killed) {
|
|
343
373
|
try {
|
|
344
|
-
proc.kill(
|
|
345
|
-
logger_1.default.printLine(`
|
|
374
|
+
proc.kill('SIGKILL');
|
|
375
|
+
logger_1.default.printLine(`Force killed process ${proc.pid} (${command}) with SIGKILL`, 'warn');
|
|
346
376
|
}
|
|
347
|
-
catch (
|
|
348
|
-
logger_1.default.printLine(`Failed to kill process ${proc.pid} (${command}): ${
|
|
377
|
+
catch (error) {
|
|
378
|
+
logger_1.default.printLine(`Failed to force kill process ${proc.pid} (${command}): ${error.message}`, 'error');
|
|
349
379
|
}
|
|
350
380
|
}
|
|
381
|
+
resolve();
|
|
382
|
+
}, 5000); // 5 second timeout
|
|
383
|
+
proc.on('exit', () => {
|
|
384
|
+
clearTimeout(timeout);
|
|
385
|
+
logger_1.default.printLine(`Process ${proc.pid} (${command}) exited gracefully`, 'info');
|
|
386
|
+
resolve();
|
|
387
|
+
});
|
|
388
|
+
try {
|
|
389
|
+
// Try to kill the process gracefully first
|
|
390
|
+
proc.kill(signal);
|
|
391
|
+
logger_1.default.printLine(`Sent ${signal} to process ${proc.pid} (${command})`, 'info');
|
|
392
|
+
}
|
|
393
|
+
catch (error) {
|
|
394
|
+
logger_1.default.printLine(`Failed to send ${signal} to process ${proc.pid} (${command}): ${error.message}`, 'error');
|
|
395
|
+
clearTimeout(timeout);
|
|
396
|
+
resolve();
|
|
351
397
|
}
|
|
352
398
|
});
|
|
353
|
-
this.activeProcesses.clear();
|
|
354
|
-
// Print server status summary if in server mode
|
|
355
|
-
if (this.options.isServerMode && this.serverInfo.size > 0) {
|
|
356
|
-
logger_1.default.printLine('Server shutdown summary:', 'info');
|
|
357
|
-
this.serverInfo.forEach((info, command) => {
|
|
358
|
-
const statusColor = info.status === 'running' ? chalk_1.default.green :
|
|
359
|
-
info.status === 'error' ? chalk_1.default.red : chalk_1.default.yellow;
|
|
360
|
-
logger_1.default.printLine(` ${command}: ${statusColor(info.status)}`, 'info');
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
399
|
}
|
|
364
400
|
}
|
|
365
401
|
exports.Runner = Runner;
|
package/package.json
CHANGED