neex 0.6.46 → 0.6.50
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/dist/src/build-manager.js +69 -70
- package/dist/src/commands/build-commands.js +83 -13
- package/dist/src/commands/start-commands.js +230 -35
- package/dist/src/start-manager.js +362 -73
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.BuildManager = void 0;
|
|
7
|
-
// src/build-manager.ts - Build manager for TypeScript projects using
|
|
7
|
+
// src/build-manager.ts - Build manager for TypeScript projects using tsc
|
|
8
8
|
const child_process_1 = require("child_process");
|
|
9
9
|
const chokidar_1 = require("chokidar");
|
|
10
10
|
const logger_manager_js_1 = require("./logger-manager.js");
|
|
@@ -19,7 +19,7 @@ class BuildManager {
|
|
|
19
19
|
this.buildProcess = null;
|
|
20
20
|
this.isBuilding = false;
|
|
21
21
|
this.buildCount = 0;
|
|
22
|
-
this.debouncedBuild = this.debounce(this.runBuild.bind(this),
|
|
22
|
+
this.debouncedBuild = this.debounce(this.runBuild.bind(this), 300);
|
|
23
23
|
this.options = options;
|
|
24
24
|
}
|
|
25
25
|
async cleanOutputDirectory() {
|
|
@@ -78,49 +78,28 @@ class BuildManager {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
args.push('--sourcemap');
|
|
97
|
-
}
|
|
98
|
-
if (this.options.format === 'esm') {
|
|
99
|
-
args.push('--format', 'esm');
|
|
100
|
-
}
|
|
101
|
-
if (this.options.external.length > 0) {
|
|
102
|
-
args.push('--external', this.options.external.join(','));
|
|
103
|
-
}
|
|
104
|
-
return { command: 'tsx', args };
|
|
81
|
+
getTscCommand() {
|
|
82
|
+
const args = [
|
|
83
|
+
'--project',
|
|
84
|
+
this.options.tsconfig,
|
|
85
|
+
'--outDir',
|
|
86
|
+
this.options.output,
|
|
87
|
+
'--target',
|
|
88
|
+
this.options.target,
|
|
89
|
+
'--declaration'
|
|
90
|
+
];
|
|
91
|
+
if (this.options.sourcemap) {
|
|
92
|
+
args.push('--sourceMap');
|
|
93
|
+
}
|
|
94
|
+
if (this.options.format === 'esm') {
|
|
95
|
+
args.push('--module', 'es2020', '--moduleResolution', 'node');
|
|
105
96
|
}
|
|
106
97
|
else {
|
|
107
|
-
|
|
108
|
-
const args = [
|
|
109
|
-
'--project',
|
|
110
|
-
this.options.tsconfig,
|
|
111
|
-
'--outDir',
|
|
112
|
-
this.options.output,
|
|
113
|
-
'--target',
|
|
114
|
-
this.options.target
|
|
115
|
-
];
|
|
116
|
-
if (this.options.sourcemap) {
|
|
117
|
-
args.push('--sourceMap');
|
|
118
|
-
}
|
|
119
|
-
if (this.options.format === 'esm') {
|
|
120
|
-
args.push('--module', 'es2020');
|
|
121
|
-
}
|
|
122
|
-
return { command: 'tsc', args };
|
|
98
|
+
args.push('--module', 'commonjs');
|
|
123
99
|
}
|
|
100
|
+
// Always include these for better compatibility
|
|
101
|
+
args.push('--esModuleInterop', '--allowSyntheticDefaultImports', '--strict');
|
|
102
|
+
return { command: 'tsc', args };
|
|
124
103
|
}
|
|
125
104
|
async runBuild() {
|
|
126
105
|
if (this.isBuilding) {
|
|
@@ -135,31 +114,28 @@ class BuildManager {
|
|
|
135
114
|
}
|
|
136
115
|
try {
|
|
137
116
|
await this.ensureOutputDirectory();
|
|
138
|
-
const { command, args } = this.
|
|
117
|
+
const { command, args } = this.getTscCommand();
|
|
139
118
|
if (this.options.verbose) {
|
|
140
119
|
logger_manager_js_1.loggerManager.printLine(`Executing: ${command} ${args.join(' ')}`, 'info');
|
|
141
120
|
}
|
|
142
121
|
return new Promise((resolve, reject) => {
|
|
143
122
|
var _a, _b;
|
|
144
123
|
this.buildProcess = (0, child_process_1.spawn)(command, args, {
|
|
145
|
-
stdio:
|
|
124
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
146
125
|
shell: false,
|
|
147
126
|
env: {
|
|
148
127
|
...process.env,
|
|
149
|
-
FORCE_COLOR:
|
|
128
|
+
FORCE_COLOR: '0' // Disable TSC colors to avoid log pollution
|
|
150
129
|
}
|
|
151
130
|
});
|
|
131
|
+
let stdout = '';
|
|
152
132
|
let stderr = '';
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
133
|
+
// Capture all output but don't display TSC logs
|
|
134
|
+
(_a = this.buildProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
|
|
135
|
+
stdout += data.toString();
|
|
136
|
+
});
|
|
158
137
|
(_b = this.buildProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
|
|
159
138
|
stderr += data.toString();
|
|
160
|
-
if (this.options.verbose) {
|
|
161
|
-
process.stderr.write(data);
|
|
162
|
-
}
|
|
163
139
|
});
|
|
164
140
|
this.buildProcess.on('error', (error) => {
|
|
165
141
|
this.buildProcess = null;
|
|
@@ -182,11 +158,15 @@ class BuildManager {
|
|
|
182
158
|
resolve();
|
|
183
159
|
}
|
|
184
160
|
else {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
161
|
+
// Only show meaningful errors, filter out TSC verbosity
|
|
162
|
+
const meaningfulErrors = this.filterTscErrors(stderr);
|
|
163
|
+
if (meaningfulErrors) {
|
|
164
|
+
logger_manager_js_1.loggerManager.printLine(`Build failed:\n${meaningfulErrors}`, 'error');
|
|
188
165
|
}
|
|
189
|
-
|
|
166
|
+
else {
|
|
167
|
+
logger_manager_js_1.loggerManager.printLine(`Build failed with code ${code}`, 'error');
|
|
168
|
+
}
|
|
169
|
+
reject(new Error(`Build failed with code ${code}`));
|
|
190
170
|
}
|
|
191
171
|
});
|
|
192
172
|
});
|
|
@@ -196,14 +176,28 @@ class BuildManager {
|
|
|
196
176
|
throw error;
|
|
197
177
|
}
|
|
198
178
|
}
|
|
179
|
+
filterTscErrors(stderr) {
|
|
180
|
+
if (!stderr)
|
|
181
|
+
return '';
|
|
182
|
+
const lines = stderr.split('\n');
|
|
183
|
+
const meaningfulLines = lines.filter(line => {
|
|
184
|
+
const trimmed = line.trim();
|
|
185
|
+
// Filter out TSC verbose output, keep only actual errors
|
|
186
|
+
return trimmed &&
|
|
187
|
+
!trimmed.includes('message TS') &&
|
|
188
|
+
!trimmed.includes('Found 0 errors') &&
|
|
189
|
+
!trimmed.match(/^\s*\d+\s*$/) && // Filter line numbers
|
|
190
|
+
!trimmed.includes('Watching for file changes');
|
|
191
|
+
});
|
|
192
|
+
return meaningfulLines.join('\n').trim();
|
|
193
|
+
}
|
|
199
194
|
async analyzeBuild() {
|
|
200
195
|
try {
|
|
201
|
-
const stats = await promises_1.default.stat(this.options.output);
|
|
202
196
|
const files = await promises_1.default.readdir(this.options.output, { withFileTypes: true });
|
|
203
197
|
let totalSize = 0;
|
|
204
198
|
const fileStats = [];
|
|
205
199
|
for (const file of files) {
|
|
206
|
-
if (file.isFile()) {
|
|
200
|
+
if (file.isFile() && (file.name.endsWith('.js') || file.name.endsWith('.d.ts'))) {
|
|
207
201
|
const filePath = path_1.default.join(this.options.output, file.name);
|
|
208
202
|
const stat = await promises_1.default.stat(filePath);
|
|
209
203
|
totalSize += stat.size;
|
|
@@ -211,11 +205,13 @@ class BuildManager {
|
|
|
211
205
|
}
|
|
212
206
|
}
|
|
213
207
|
fileStats.sort((a, b) => b.size - a.size);
|
|
214
|
-
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)}
|
|
208
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Build Analysis:`, 'info');
|
|
215
209
|
logger_manager_js_1.loggerManager.printLine(`Total size: ${chalk_1.default.cyan(this.formatBytes(totalSize))}`, 'info');
|
|
216
|
-
logger_manager_js_1.loggerManager.printLine(`
|
|
217
|
-
if (this.options.verbose) {
|
|
218
|
-
fileStats.slice(0,
|
|
210
|
+
logger_manager_js_1.loggerManager.printLine(`Generated files: ${fileStats.length}`, 'info');
|
|
211
|
+
if (this.options.verbose && fileStats.length > 0) {
|
|
212
|
+
const topFiles = fileStats.slice(0, 5);
|
|
213
|
+
logger_manager_js_1.loggerManager.printLine('Largest files:', 'info');
|
|
214
|
+
topFiles.forEach(file => {
|
|
219
215
|
logger_manager_js_1.loggerManager.printLine(` ${file.name}: ${this.formatBytes(file.size)}`, 'info');
|
|
220
216
|
});
|
|
221
217
|
}
|
|
@@ -237,7 +233,7 @@ class BuildManager {
|
|
|
237
233
|
this.buildProcess = null;
|
|
238
234
|
const cleanup = () => {
|
|
239
235
|
if (!this.options.quiet) {
|
|
240
|
-
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.square)}
|
|
236
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.square)} Build process stopped`, 'info');
|
|
241
237
|
}
|
|
242
238
|
resolve();
|
|
243
239
|
};
|
|
@@ -257,7 +253,7 @@ class BuildManager {
|
|
|
257
253
|
// Ignore
|
|
258
254
|
}
|
|
259
255
|
}
|
|
260
|
-
},
|
|
256
|
+
}, 3000);
|
|
261
257
|
}
|
|
262
258
|
}
|
|
263
259
|
catch (error) {
|
|
@@ -286,12 +282,13 @@ class BuildManager {
|
|
|
286
282
|
ignoreInitial: true,
|
|
287
283
|
followSymlinks: false,
|
|
288
284
|
usePolling: false,
|
|
289
|
-
atomic:
|
|
285
|
+
atomic: 200,
|
|
290
286
|
ignored: [
|
|
291
287
|
'**/node_modules/**',
|
|
292
288
|
'**/.git/**',
|
|
293
289
|
`**/${this.options.output}/**`,
|
|
294
|
-
'**/*.log'
|
|
290
|
+
'**/*.log',
|
|
291
|
+
'**/*.map'
|
|
295
292
|
]
|
|
296
293
|
});
|
|
297
294
|
this.watcher.on('change', (filePath) => {
|
|
@@ -342,7 +339,7 @@ class BuildManager {
|
|
|
342
339
|
if (this.options.watch) {
|
|
343
340
|
this.setupWatcher();
|
|
344
341
|
if (!this.options.quiet) {
|
|
345
|
-
logger_manager_js_1.loggerManager.printLine(
|
|
342
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Watching for changes...`, 'info');
|
|
346
343
|
}
|
|
347
344
|
}
|
|
348
345
|
}
|
|
@@ -354,13 +351,15 @@ class BuildManager {
|
|
|
354
351
|
}
|
|
355
352
|
}
|
|
356
353
|
async stop() {
|
|
357
|
-
|
|
354
|
+
if (!this.options.quiet) {
|
|
355
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Stopping build process...`, 'info');
|
|
356
|
+
}
|
|
358
357
|
if (this.watcher) {
|
|
359
358
|
await this.watcher.close();
|
|
360
359
|
this.watcher = null;
|
|
361
360
|
}
|
|
362
361
|
await this.stopProcess();
|
|
363
|
-
if (this.buildCount > 0) {
|
|
362
|
+
if (this.buildCount > 0 && !this.options.quiet) {
|
|
364
363
|
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Build process stopped after ${this.buildCount} build(s)`, 'info');
|
|
365
364
|
}
|
|
366
365
|
}
|
|
@@ -7,6 +7,7 @@ exports.addBuildCommands = void 0;
|
|
|
7
7
|
const build_manager_js_1 = require("../build-manager.js");
|
|
8
8
|
const logger_manager_js_1 = require("../logger-manager.js");
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const figures_1 = __importDefault(require("figures"));
|
|
10
11
|
function addBuildCommands(program) {
|
|
11
12
|
let buildManager = null;
|
|
12
13
|
// Build command for TypeScript projects
|
|
@@ -15,13 +16,10 @@ function addBuildCommands(program) {
|
|
|
15
16
|
.description('Build TypeScript project for production (default: src)')
|
|
16
17
|
.option('-o, --output <dir>', 'Output directory', 'dist')
|
|
17
18
|
.option('-w, --watch', 'Watch mode for continuous building')
|
|
18
|
-
.option('-c, --clean', 'Clean output directory before build')
|
|
19
|
-
.option('-m, --minify', 'Minify output')
|
|
19
|
+
.option('-c, --clean', 'Clean output directory before build', true)
|
|
20
20
|
.option('-s, --sourcemap', 'Generate source maps')
|
|
21
21
|
.option('-t, --target <target>', 'TypeScript target (es2020, es2022, etc.)', 'es2020')
|
|
22
22
|
.option('-f, --format <format>', 'Output format (cjs, esm)', 'cjs')
|
|
23
|
-
.option('--no-bundle', 'Don\'t bundle, just compile')
|
|
24
|
-
.option('--external <packages>', 'External packages (comma-separated)')
|
|
25
23
|
.option('--tsconfig <file>', 'TypeScript config file', 'tsconfig.json')
|
|
26
24
|
.option('-v, --verbose', 'Verbose output')
|
|
27
25
|
.option('-q, --quiet', 'Quiet output')
|
|
@@ -30,18 +28,20 @@ function addBuildCommands(program) {
|
|
|
30
28
|
.action(async (source, options) => {
|
|
31
29
|
try {
|
|
32
30
|
const sourceDir = source || 'src';
|
|
33
|
-
|
|
31
|
+
if (!options.quiet) {
|
|
32
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Building TypeScript project from ${chalk_1.default.cyan(sourceDir)}`, 'info');
|
|
33
|
+
}
|
|
34
34
|
buildManager = new build_manager_js_1.BuildManager({
|
|
35
35
|
source: sourceDir,
|
|
36
36
|
output: options.output,
|
|
37
37
|
watch: options.watch,
|
|
38
38
|
clean: options.clean,
|
|
39
|
-
minify:
|
|
39
|
+
minify: false,
|
|
40
40
|
sourcemap: options.sourcemap,
|
|
41
41
|
target: options.target,
|
|
42
42
|
format: options.format,
|
|
43
|
-
bundle:
|
|
44
|
-
external:
|
|
43
|
+
bundle: false,
|
|
44
|
+
external: [],
|
|
45
45
|
tsconfig: options.tsconfig,
|
|
46
46
|
verbose: options.verbose,
|
|
47
47
|
quiet: options.quiet,
|
|
@@ -49,24 +49,94 @@ function addBuildCommands(program) {
|
|
|
49
49
|
analyze: options.analyze
|
|
50
50
|
});
|
|
51
51
|
await buildManager.build();
|
|
52
|
+
// If not in watch mode, show completion message
|
|
53
|
+
if (!options.watch && !options.quiet) {
|
|
54
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Build completed successfully`, 'info');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (error instanceof Error) {
|
|
59
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Build failed: ${error.message}`, 'error');
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} An unknown build error occurred`, 'error');
|
|
63
|
+
}
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
// Add a quick build command without options
|
|
68
|
+
program
|
|
69
|
+
.command('compile [source]')
|
|
70
|
+
.alias('tsc')
|
|
71
|
+
.description('Quick TypeScript compilation (alias for build)')
|
|
72
|
+
.action(async (source) => {
|
|
73
|
+
try {
|
|
74
|
+
const sourceDir = source || 'src';
|
|
75
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Compiling TypeScript...`, 'info');
|
|
76
|
+
buildManager = new build_manager_js_1.BuildManager({
|
|
77
|
+
source: sourceDir,
|
|
78
|
+
output: 'dist',
|
|
79
|
+
watch: false,
|
|
80
|
+
clean: true,
|
|
81
|
+
minify: false,
|
|
82
|
+
sourcemap: false,
|
|
83
|
+
target: 'es2020',
|
|
84
|
+
format: 'cjs',
|
|
85
|
+
bundle: false,
|
|
86
|
+
external: [],
|
|
87
|
+
tsconfig: 'tsconfig.json',
|
|
88
|
+
verbose: false,
|
|
89
|
+
quiet: false,
|
|
90
|
+
color: true,
|
|
91
|
+
analyze: false
|
|
92
|
+
});
|
|
93
|
+
await buildManager.build();
|
|
94
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Compilation completed`, 'info');
|
|
52
95
|
}
|
|
53
96
|
catch (error) {
|
|
54
97
|
if (error instanceof Error) {
|
|
55
|
-
logger_manager_js_1.loggerManager.printLine(
|
|
98
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Compilation failed: ${error.message}`, 'error');
|
|
56
99
|
}
|
|
57
100
|
else {
|
|
58
|
-
logger_manager_js_1.loggerManager.printLine(
|
|
101
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} An unknown compilation error occurred`, 'error');
|
|
59
102
|
}
|
|
60
103
|
process.exit(1);
|
|
61
104
|
}
|
|
62
105
|
});
|
|
63
106
|
// Cleanup function
|
|
64
|
-
const cleanupBuild = () => {
|
|
107
|
+
const cleanupBuild = async () => {
|
|
108
|
+
if (buildManager) {
|
|
109
|
+
try {
|
|
110
|
+
await buildManager.stop();
|
|
111
|
+
buildManager = null;
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
// Ignore cleanup errors
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
// Handle process termination
|
|
119
|
+
const handleExit = (signal) => {
|
|
65
120
|
if (buildManager) {
|
|
66
|
-
|
|
67
|
-
|
|
121
|
+
logger_manager_js_1.loggerManager.printLine(`\n${chalk_1.default.yellow(figures_1.default.warning)} Received ${signal}, stopping build process...`, 'info');
|
|
122
|
+
cleanupBuild().then(() => {
|
|
123
|
+
process.exit(0);
|
|
124
|
+
}).catch(() => {
|
|
125
|
+
process.exit(1);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
process.exit(0);
|
|
68
130
|
}
|
|
69
131
|
};
|
|
132
|
+
// Register signal handlers
|
|
133
|
+
process.on('SIGINT', () => handleExit('SIGINT'));
|
|
134
|
+
process.on('SIGTERM', () => handleExit('SIGTERM'));
|
|
135
|
+
process.on('exit', () => {
|
|
136
|
+
if (buildManager) {
|
|
137
|
+
cleanupBuild();
|
|
138
|
+
}
|
|
139
|
+
});
|
|
70
140
|
return { cleanupBuild };
|
|
71
141
|
}
|
|
72
142
|
exports.addBuildCommands = addBuildCommands;
|
|
@@ -6,55 +6,250 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.addStartCommands = void 0;
|
|
7
7
|
const start_manager_js_1 = require("../start-manager.js");
|
|
8
8
|
const logger_manager_js_1 = require("../logger-manager.js");
|
|
9
|
-
const
|
|
10
|
-
const
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const figures_1 = __importDefault(require("figures"));
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
11
12
|
function addStartCommands(program) {
|
|
12
|
-
let
|
|
13
|
-
|
|
13
|
+
let startManager = null;
|
|
14
|
+
// Main start command
|
|
15
|
+
program
|
|
14
16
|
.command('start [entry]')
|
|
15
|
-
.description('Start
|
|
16
|
-
.option('-
|
|
17
|
-
.option('-
|
|
18
|
-
.option('--
|
|
19
|
-
.option('--
|
|
20
|
-
.option('--
|
|
21
|
-
.option('--
|
|
17
|
+
.description('Start the production server (default: dist/index.js)')
|
|
18
|
+
.option('-p, --port <port>', 'Port to run the server on', '3000')
|
|
19
|
+
.option('-h, --host <host>', 'Host to bind the server to', '0.0.0.0')
|
|
20
|
+
.option('-e, --env <env>', 'Environment mode', 'production')
|
|
21
|
+
.option('-w, --watch', 'Watch for file changes and restart')
|
|
22
|
+
.option('-c, --cluster', 'Enable cluster mode')
|
|
23
|
+
.option('--workers <count>', 'Number of worker processes (default: CPU count)', os_1.default.cpus().length.toString())
|
|
24
|
+
.option('-m, --memory <mb>', 'Memory limit in MB', '512')
|
|
25
|
+
.option('-t, --timeout <ms>', 'Startup timeout in milliseconds', '30000')
|
|
26
|
+
.option('--graceful-timeout <ms>', 'Graceful shutdown timeout in milliseconds', '5000')
|
|
27
|
+
.option('-v, --verbose', 'Verbose output')
|
|
28
|
+
.option('-q, --quiet', 'Quiet output')
|
|
29
|
+
.option('--no-color', 'Disable colored output')
|
|
30
|
+
.option('--log-level <level>', 'Log level (error, warn, info, debug)', 'info')
|
|
31
|
+
.option('--node-args <args>', 'Additional Node.js arguments (comma-separated)')
|
|
32
|
+
.option('--inspect', 'Enable Node.js inspector')
|
|
33
|
+
.option('--inspect-port <port>', 'Inspector port', '9229')
|
|
34
|
+
.option('--pid <file>', 'Write process ID to file')
|
|
35
|
+
.option('--restart', 'Auto-restart on failure', true)
|
|
36
|
+
.option('--restart-delay <ms>', 'Delay between restarts in milliseconds', '1000')
|
|
37
|
+
.option('--max-restarts <count>', 'Maximum restart attempts', '10')
|
|
22
38
|
.action(async (entry, options) => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
39
|
+
try {
|
|
40
|
+
const entryFile = entry || 'dist/index.js';
|
|
41
|
+
if (!options.quiet) {
|
|
42
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Starting production server...`, 'info');
|
|
43
|
+
}
|
|
44
|
+
const nodeArgs = options.nodeArgs ?
|
|
45
|
+
options.nodeArgs.split(',').map((arg) => arg.trim()) : [];
|
|
46
|
+
startManager = new start_manager_js_1.StartManager({
|
|
47
|
+
entry: entryFile,
|
|
48
|
+
port: parseInt(options.port),
|
|
49
|
+
host: options.host,
|
|
50
|
+
env: options.env,
|
|
51
|
+
watch: options.watch,
|
|
52
|
+
cluster: options.cluster,
|
|
53
|
+
workers: parseInt(options.workers),
|
|
54
|
+
memory: parseInt(options.memory),
|
|
55
|
+
timeout: parseInt(options.timeout),
|
|
56
|
+
gracefulTimeout: parseInt(options.gracefulTimeout),
|
|
57
|
+
verbose: options.verbose,
|
|
58
|
+
quiet: options.quiet,
|
|
59
|
+
color: options.color,
|
|
60
|
+
logLevel: options.logLevel,
|
|
61
|
+
nodeArgs,
|
|
62
|
+
inspect: options.inspect,
|
|
63
|
+
inspectPort: parseInt(options.inspectPort),
|
|
64
|
+
pid: options.pid,
|
|
65
|
+
restart: options.restart,
|
|
66
|
+
restartDelay: parseInt(options.restartDelay),
|
|
67
|
+
maxRestarts: parseInt(options.maxRestarts)
|
|
68
|
+
});
|
|
69
|
+
await startManager.start();
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof Error) {
|
|
73
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Start failed: ${error.message}`, 'error');
|
|
28
74
|
}
|
|
29
75
|
else {
|
|
30
|
-
logger_manager_js_1.loggerManager.printLine(
|
|
31
|
-
|
|
76
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} An unknown start error occurred`, 'error');
|
|
77
|
+
}
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
// Quick start command for development
|
|
82
|
+
program
|
|
83
|
+
.command('serve [entry]')
|
|
84
|
+
.description('Quick start with watch mode (development)')
|
|
85
|
+
.option('-p, --port <port>', 'Port to run the server on', '3000')
|
|
86
|
+
.option('-v, --verbose', 'Verbose output')
|
|
87
|
+
.action(async (entry, options) => {
|
|
88
|
+
try {
|
|
89
|
+
const entryFile = entry || 'dist/index.js';
|
|
90
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Starting development server...`, 'info');
|
|
91
|
+
startManager = new start_manager_js_1.StartManager({
|
|
92
|
+
entry: entryFile,
|
|
93
|
+
port: parseInt(options.port),
|
|
94
|
+
host: '127.0.0.1',
|
|
95
|
+
env: 'development',
|
|
96
|
+
watch: true,
|
|
97
|
+
cluster: false,
|
|
98
|
+
workers: 1,
|
|
99
|
+
memory: 256,
|
|
100
|
+
timeout: 15000,
|
|
101
|
+
gracefulTimeout: 3000,
|
|
102
|
+
verbose: options.verbose,
|
|
103
|
+
quiet: false,
|
|
104
|
+
color: true,
|
|
105
|
+
logLevel: 'info',
|
|
106
|
+
nodeArgs: [],
|
|
107
|
+
inspect: false,
|
|
108
|
+
inspectPort: 9229,
|
|
109
|
+
pid: '',
|
|
110
|
+
restart: true,
|
|
111
|
+
restartDelay: 500,
|
|
112
|
+
maxRestarts: 50
|
|
113
|
+
});
|
|
114
|
+
await startManager.start();
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (error instanceof Error) {
|
|
118
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Serve failed: ${error.message}`, 'error');
|
|
32
119
|
}
|
|
120
|
+
else {
|
|
121
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} An unknown serve error occurred`, 'error');
|
|
122
|
+
}
|
|
123
|
+
process.exit(1);
|
|
33
124
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
125
|
+
});
|
|
126
|
+
// Production deployment command
|
|
127
|
+
program
|
|
128
|
+
.command('deploy [entry]')
|
|
129
|
+
.description('Deploy to production with optimal settings')
|
|
130
|
+
.option('-p, --port <port>', 'Port to run the server on', process.env.PORT || '3000')
|
|
131
|
+
.option('-w, --workers <count>', 'Number of worker processes', os_1.default.cpus().length.toString())
|
|
132
|
+
.option('-m, --memory <mb>', 'Memory limit in MB', '1024')
|
|
133
|
+
.option('--pid <file>', 'Write process ID to file', 'server.pid')
|
|
134
|
+
.option('-v, --verbose', 'Verbose output')
|
|
135
|
+
.action(async (entry, options) => {
|
|
44
136
|
try {
|
|
45
|
-
|
|
137
|
+
const entryFile = entry || 'dist/index.js';
|
|
138
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Deploying to production...`, 'info');
|
|
139
|
+
startManager = new start_manager_js_1.StartManager({
|
|
140
|
+
entry: entryFile,
|
|
141
|
+
port: parseInt(options.port),
|
|
142
|
+
host: '0.0.0.0',
|
|
143
|
+
env: 'production',
|
|
144
|
+
watch: false,
|
|
145
|
+
cluster: true,
|
|
146
|
+
workers: parseInt(options.workers),
|
|
147
|
+
memory: parseInt(options.memory),
|
|
148
|
+
timeout: 30000,
|
|
149
|
+
gracefulTimeout: 10000,
|
|
150
|
+
verbose: options.verbose,
|
|
151
|
+
quiet: false,
|
|
152
|
+
color: false,
|
|
153
|
+
logLevel: 'warn',
|
|
154
|
+
nodeArgs: ['--optimize-for-size'],
|
|
155
|
+
inspect: false,
|
|
156
|
+
inspectPort: 9229,
|
|
157
|
+
pid: options.pid,
|
|
158
|
+
restart: true,
|
|
159
|
+
restartDelay: 2000,
|
|
160
|
+
maxRestarts: 5
|
|
161
|
+
});
|
|
162
|
+
await startManager.start();
|
|
46
163
|
}
|
|
47
164
|
catch (error) {
|
|
48
|
-
|
|
165
|
+
if (error instanceof Error) {
|
|
166
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Deploy failed: ${error.message}`, 'error');
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} An unknown deploy error occurred`, 'error');
|
|
170
|
+
}
|
|
49
171
|
process.exit(1);
|
|
50
172
|
}
|
|
51
173
|
});
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
174
|
+
// Health check command
|
|
175
|
+
program
|
|
176
|
+
.command('health [url]')
|
|
177
|
+
.description('Check server health')
|
|
178
|
+
.option('-t, --timeout <ms>', 'Request timeout in milliseconds', '5000')
|
|
179
|
+
.option('-i, --interval <ms>', 'Check interval in milliseconds', '1000')
|
|
180
|
+
.option('-c, --count <number>', 'Number of checks', '1')
|
|
181
|
+
.action(async (url, options) => {
|
|
182
|
+
const checkUrl = url || 'http://localhost:3000/health';
|
|
183
|
+
const timeout = parseInt(options.timeout);
|
|
184
|
+
const interval = parseInt(options.interval);
|
|
185
|
+
const count = parseInt(options.count);
|
|
186
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Health check: ${checkUrl}`, 'info');
|
|
187
|
+
for (let i = 0; i < count; i++) {
|
|
188
|
+
try {
|
|
189
|
+
const controller = new AbortController();
|
|
190
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
191
|
+
const response = await fetch(checkUrl, {
|
|
192
|
+
signal: controller.signal
|
|
193
|
+
});
|
|
194
|
+
clearTimeout(timeoutId);
|
|
195
|
+
if (response.ok) {
|
|
196
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Health check ${i + 1}/${count}: OK (${response.status})`, 'info');
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Health check ${i + 1}/${count}: Failed (${response.status})`, 'error');
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Health check ${i + 1}/${count}: Error (${error.message})`, 'error');
|
|
204
|
+
}
|
|
205
|
+
if (i < count - 1) {
|
|
206
|
+
await new Promise(resolve => setTimeout(resolve, interval));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
// Cleanup function
|
|
211
|
+
const cleanupStart = async () => {
|
|
212
|
+
if (startManager) {
|
|
213
|
+
try {
|
|
214
|
+
await startManager.stop();
|
|
215
|
+
startManager = null;
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
// Ignore cleanup errors
|
|
56
219
|
}
|
|
57
|
-
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
// Handle process termination
|
|
223
|
+
const handleExit = (signal) => {
|
|
224
|
+
if (startManager) {
|
|
225
|
+
logger_manager_js_1.loggerManager.printLine(`\n${chalk_1.default.yellow(figures_1.default.warning)} Received ${signal}, shutting down gracefully...`, 'info');
|
|
226
|
+
cleanupStart().then(() => {
|
|
227
|
+
process.exit(0);
|
|
228
|
+
}).catch(() => {
|
|
229
|
+
process.exit(1);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
process.exit(0);
|
|
234
|
+
}
|
|
58
235
|
};
|
|
236
|
+
// Register signal handlers
|
|
237
|
+
process.on('SIGINT', () => handleExit('SIGINT'));
|
|
238
|
+
process.on('SIGTERM', () => handleExit('SIGTERM'));
|
|
239
|
+
process.on('SIGQUIT', () => handleExit('SIGQUIT'));
|
|
240
|
+
// Handle uncaught exceptions
|
|
241
|
+
process.on('uncaughtException', (error) => {
|
|
242
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Uncaught exception: ${error.message}`, 'error');
|
|
243
|
+
cleanupStart().then(() => {
|
|
244
|
+
process.exit(1);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
248
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Unhandled rejection at: ${promise} reason: ${reason}`, 'error');
|
|
249
|
+
cleanupStart().then(() => {
|
|
250
|
+
process.exit(1);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
return { cleanupStart };
|
|
59
254
|
}
|
|
60
255
|
exports.addStartCommands = addStartCommands;
|
|
@@ -4,118 +4,407 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.StartManager = void 0;
|
|
7
|
-
// src/start-manager.ts - Production
|
|
7
|
+
// src/start-manager.ts - Production server manager for built TypeScript projects
|
|
8
8
|
const child_process_1 = require("child_process");
|
|
9
|
+
const chokidar_1 = require("chokidar");
|
|
9
10
|
const logger_manager_js_1 = require("./logger-manager.js");
|
|
10
11
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
12
|
const figures_1 = __importDefault(require("figures"));
|
|
12
|
-
const fs_1 = __importDefault(require("fs"));
|
|
13
13
|
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
15
|
+
const fs_1 = require("fs");
|
|
16
|
+
const perf_hooks_1 = require("perf_hooks");
|
|
14
17
|
class StartManager {
|
|
15
18
|
constructor(options) {
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
19
|
+
this.serverProcess = null;
|
|
20
|
+
this.watcher = null;
|
|
21
|
+
this.isStarting = false;
|
|
22
|
+
this.restartCount = 0;
|
|
23
|
+
this.startTime = 0;
|
|
24
|
+
this.isShuttingDown = false;
|
|
25
|
+
this.restartTimer = null;
|
|
18
26
|
this.options = options;
|
|
19
27
|
}
|
|
20
|
-
async
|
|
21
|
-
|
|
22
|
-
if
|
|
23
|
-
|
|
28
|
+
async validateEntry() {
|
|
29
|
+
let entryPath = this.options.entry;
|
|
30
|
+
// Check if entry exists as provided
|
|
31
|
+
if ((0, fs_1.existsSync)(entryPath)) {
|
|
32
|
+
return path_1.default.resolve(entryPath);
|
|
24
33
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
32
|
-
shell: false,
|
|
33
|
-
env: {
|
|
34
|
-
...process.env,
|
|
35
|
-
NODE_ENV: 'production',
|
|
36
|
-
FORCE_COLOR: this.options.color ? '1' : '0'
|
|
37
|
-
},
|
|
38
|
-
detached: true
|
|
39
|
-
});
|
|
40
|
-
const appName = this.options.name || path_1.default.basename(this.options.entry);
|
|
41
|
-
if (!this.options.quiet) {
|
|
42
|
-
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.play)} Starting ${chalk_1.default.cyan(appName)} in production mode...`, 'info');
|
|
34
|
+
// Try with .js extension
|
|
35
|
+
if (!entryPath.endsWith('.js')) {
|
|
36
|
+
const jsPath = entryPath + '.js';
|
|
37
|
+
if ((0, fs_1.existsSync)(jsPath)) {
|
|
38
|
+
return path_1.default.resolve(jsPath);
|
|
39
|
+
}
|
|
43
40
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
// Try in dist directory
|
|
42
|
+
const distPath = path_1.default.join('dist', entryPath);
|
|
43
|
+
if ((0, fs_1.existsSync)(distPath)) {
|
|
44
|
+
return path_1.default.resolve(distPath);
|
|
45
|
+
}
|
|
46
|
+
// Try in dist with .js extension
|
|
47
|
+
if (!entryPath.endsWith('.js')) {
|
|
48
|
+
const distJsPath = path_1.default.join('dist', entryPath + '.js');
|
|
49
|
+
if ((0, fs_1.existsSync)(distJsPath)) {
|
|
50
|
+
return path_1.default.resolve(distJsPath);
|
|
47
51
|
}
|
|
48
|
-
}
|
|
49
|
-
|
|
52
|
+
}
|
|
53
|
+
// Try common entry points
|
|
54
|
+
const commonEntries = [
|
|
55
|
+
'dist/index.js',
|
|
56
|
+
'dist/server.js',
|
|
57
|
+
'dist/app.js',
|
|
58
|
+
'dist/main.js',
|
|
59
|
+
'index.js',
|
|
60
|
+
'server.js'
|
|
61
|
+
];
|
|
62
|
+
for (const entry of commonEntries) {
|
|
63
|
+
if ((0, fs_1.existsSync)(entry)) {
|
|
64
|
+
if (!this.options.quiet) {
|
|
65
|
+
logger_manager_js_1.loggerManager.printLine(`Using entry point: ${entry}`, 'info');
|
|
66
|
+
}
|
|
67
|
+
return path_1.default.resolve(entry);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`Entry file not found: ${entryPath}`);
|
|
71
|
+
}
|
|
72
|
+
async loadPackageJson() {
|
|
73
|
+
try {
|
|
74
|
+
const packageJsonPath = path_1.default.join(process.cwd(), 'package.json');
|
|
75
|
+
if ((0, fs_1.existsSync)(packageJsonPath)) {
|
|
76
|
+
const content = await promises_1.default.readFile(packageJsonPath, 'utf8');
|
|
77
|
+
return JSON.parse(content);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
// Ignore package.json errors
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
getNodeArgs() {
|
|
86
|
+
const args = [];
|
|
87
|
+
// Add custom node arguments
|
|
88
|
+
if (this.options.nodeArgs.length > 0) {
|
|
89
|
+
args.push(...this.options.nodeArgs);
|
|
90
|
+
}
|
|
91
|
+
// Memory limit
|
|
92
|
+
if (this.options.memory > 0) {
|
|
93
|
+
args.push(`--max-old-space-size=${this.options.memory}`);
|
|
94
|
+
}
|
|
95
|
+
// Debugging
|
|
96
|
+
if (this.options.inspect) {
|
|
97
|
+
args.push(`--inspect=${this.options.inspectPort}`);
|
|
98
|
+
}
|
|
99
|
+
// Production optimizations
|
|
100
|
+
if (this.options.env === 'production') {
|
|
101
|
+
args.push('--optimize-for-size');
|
|
102
|
+
}
|
|
103
|
+
return args;
|
|
104
|
+
}
|
|
105
|
+
getEnvironment() {
|
|
106
|
+
const env = {
|
|
107
|
+
...process.env,
|
|
108
|
+
NODE_ENV: this.options.env,
|
|
109
|
+
PORT: this.options.port.toString(),
|
|
110
|
+
HOST: this.options.host,
|
|
111
|
+
LOG_LEVEL: this.options.logLevel
|
|
112
|
+
};
|
|
113
|
+
// Add color support
|
|
114
|
+
if (this.options.color) {
|
|
115
|
+
env.FORCE_COLOR = '1';
|
|
116
|
+
}
|
|
117
|
+
return env;
|
|
118
|
+
}
|
|
119
|
+
async writePidFile(pid) {
|
|
120
|
+
if (this.options.pid) {
|
|
121
|
+
try {
|
|
122
|
+
await promises_1.default.writeFile(this.options.pid, pid.toString());
|
|
123
|
+
if (this.options.verbose) {
|
|
124
|
+
logger_manager_js_1.loggerManager.printLine(`PID file written: ${this.options.pid}`, 'info');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
logger_manager_js_1.loggerManager.printLine(`Failed to write PID file: ${error.message}`, 'warn');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async removePidFile() {
|
|
133
|
+
if (this.options.pid && (0, fs_1.existsSync)(this.options.pid)) {
|
|
134
|
+
try {
|
|
135
|
+
await promises_1.default.unlink(this.options.pid);
|
|
136
|
+
if (this.options.verbose) {
|
|
137
|
+
logger_manager_js_1.loggerManager.printLine(`PID file removed: ${this.options.pid}`, 'info');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
// Ignore cleanup errors
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async startServer() {
|
|
146
|
+
if (this.isStarting || this.isShuttingDown) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
this.isStarting = true;
|
|
150
|
+
this.startTime = perf_hooks_1.performance.now();
|
|
151
|
+
try {
|
|
152
|
+
const entryPath = await this.validateEntry();
|
|
153
|
+
const nodeArgs = this.getNodeArgs();
|
|
154
|
+
const env = this.getEnvironment();
|
|
50
155
|
if (!this.options.quiet) {
|
|
51
|
-
|
|
156
|
+
const restartInfo = this.restartCount > 0 ? ` (restart #${this.restartCount})` : '';
|
|
157
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Starting server${restartInfo}...`, 'info');
|
|
158
|
+
if (this.options.verbose) {
|
|
159
|
+
logger_manager_js_1.loggerManager.printLine(`Entry: ${entryPath}`, 'info');
|
|
160
|
+
logger_manager_js_1.loggerManager.printLine(`Environment: ${this.options.env}`, 'info');
|
|
161
|
+
logger_manager_js_1.loggerManager.printLine(`Host: ${this.options.host}:${this.options.port}`, 'info');
|
|
162
|
+
if (nodeArgs.length > 0) {
|
|
163
|
+
logger_manager_js_1.loggerManager.printLine(`Node args: ${nodeArgs.join(' ')}`, 'info');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
52
166
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
167
|
+
const args = [...nodeArgs, entryPath];
|
|
168
|
+
this.serverProcess = (0, child_process_1.spawn)('node', args, {
|
|
169
|
+
stdio: this.options.verbose ? ['ignore', 'pipe', 'pipe'] : ['ignore', 'ignore', 'pipe'],
|
|
170
|
+
env,
|
|
171
|
+
detached: false,
|
|
172
|
+
cwd: process.cwd()
|
|
173
|
+
});
|
|
174
|
+
if (this.serverProcess.pid) {
|
|
175
|
+
await this.writePidFile(this.serverProcess.pid);
|
|
176
|
+
}
|
|
177
|
+
// Handle server output
|
|
178
|
+
if (this.options.verbose && this.serverProcess.stdout) {
|
|
179
|
+
this.serverProcess.stdout.on('data', (data) => {
|
|
180
|
+
const output = data.toString().trim();
|
|
181
|
+
if (output) {
|
|
182
|
+
logger_manager_js_1.loggerManager.printLine(`[SERVER] ${output}`, 'info');
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
// Handle server errors
|
|
187
|
+
if (this.serverProcess.stderr) {
|
|
188
|
+
this.serverProcess.stderr.on('data', (data) => {
|
|
189
|
+
const error = data.toString().trim();
|
|
190
|
+
if (error && !this.options.quiet) {
|
|
191
|
+
logger_manager_js_1.loggerManager.printLine(`[SERVER] ${error}`, 'error');
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
// Handle process events
|
|
196
|
+
this.serverProcess.on('error', (error) => {
|
|
197
|
+
this.isStarting = false;
|
|
198
|
+
if (!this.isShuttingDown) {
|
|
199
|
+
logger_manager_js_1.loggerManager.printLine(`Server process error: ${error.message}`, 'error');
|
|
200
|
+
this.handleServerExit(1);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
this.serverProcess.on('exit', (code, signal) => {
|
|
204
|
+
this.isStarting = false;
|
|
205
|
+
if (!this.isShuttingDown) {
|
|
206
|
+
this.handleServerExit(code || 0, signal || undefined);
|
|
64
207
|
}
|
|
208
|
+
});
|
|
209
|
+
// Wait for server to start
|
|
210
|
+
await this.waitForServerStart();
|
|
211
|
+
this.isStarting = false;
|
|
212
|
+
const duration = Math.round(perf_hooks_1.performance.now() - this.startTime);
|
|
213
|
+
if (!this.options.quiet) {
|
|
214
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Server started successfully in ${duration}ms`, 'info');
|
|
215
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Running on ${chalk_1.default.cyan(`http://${this.options.host}:${this.options.port}`)}`, 'info');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
this.isStarting = false;
|
|
220
|
+
throw error;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async waitForServerStart() {
|
|
224
|
+
return new Promise((resolve, reject) => {
|
|
225
|
+
const timeout = setTimeout(() => {
|
|
226
|
+
reject(new Error(`Server startup timeout after ${this.options.timeout}ms`));
|
|
227
|
+
}, this.options.timeout);
|
|
228
|
+
if (this.serverProcess) {
|
|
229
|
+
// If process exits immediately, it's likely an error
|
|
230
|
+
const exitHandler = (code) => {
|
|
231
|
+
clearTimeout(timeout);
|
|
232
|
+
if (code !== 0) {
|
|
233
|
+
reject(new Error(`Server exited with code ${code}`));
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
resolve();
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
this.serverProcess.once('exit', exitHandler);
|
|
240
|
+
// Give some time for the server to start
|
|
241
|
+
setTimeout(() => {
|
|
242
|
+
if (this.serverProcess && !this.serverProcess.killed) {
|
|
243
|
+
this.serverProcess.removeListener('exit', exitHandler);
|
|
244
|
+
clearTimeout(timeout);
|
|
245
|
+
resolve();
|
|
246
|
+
}
|
|
247
|
+
}, 1000);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
clearTimeout(timeout);
|
|
251
|
+
reject(new Error('Server process not created'));
|
|
65
252
|
}
|
|
66
253
|
});
|
|
67
254
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
255
|
+
handleServerExit(code, signal) {
|
|
256
|
+
this.removePidFile();
|
|
257
|
+
if (this.isShuttingDown) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const exitMessage = signal
|
|
261
|
+
? `Server process killed with signal ${signal}`
|
|
262
|
+
: `Server process exited with code ${code}`;
|
|
263
|
+
if (code !== 0 && !this.options.quiet) {
|
|
264
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} ${exitMessage}`, 'error');
|
|
265
|
+
}
|
|
266
|
+
// Handle restart logic
|
|
267
|
+
if (this.options.restart && code !== 0 && this.restartCount < this.options.maxRestarts) {
|
|
268
|
+
this.scheduleRestart();
|
|
71
269
|
}
|
|
72
|
-
|
|
270
|
+
else if (this.restartCount >= this.options.maxRestarts) {
|
|
271
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Maximum restart attempts reached (${this.options.maxRestarts})`, 'error');
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
else if (code !== 0) {
|
|
275
|
+
process.exit(code);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
scheduleRestart() {
|
|
279
|
+
this.restartCount++;
|
|
280
|
+
const delay = this.options.restartDelay * Math.min(this.restartCount, 5); // Exponential backoff
|
|
73
281
|
if (!this.options.quiet) {
|
|
74
|
-
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.
|
|
282
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Restarting server in ${delay}ms... (${this.restartCount}/${this.options.maxRestarts})`, 'info');
|
|
75
283
|
}
|
|
284
|
+
this.restartTimer = setTimeout(() => {
|
|
285
|
+
this.restartTimer = null;
|
|
286
|
+
this.startServer().catch(error => {
|
|
287
|
+
logger_manager_js_1.loggerManager.printLine(`Restart failed: ${error.message}`, 'error');
|
|
288
|
+
this.handleServerExit(1);
|
|
289
|
+
});
|
|
290
|
+
}, delay);
|
|
76
291
|
}
|
|
77
|
-
|
|
78
|
-
this.
|
|
79
|
-
|
|
80
|
-
|
|
292
|
+
setupWatcher() {
|
|
293
|
+
if (!this.options.watch)
|
|
294
|
+
return;
|
|
295
|
+
const watchPatterns = [
|
|
296
|
+
'dist/**/*.js',
|
|
297
|
+
'package.json',
|
|
298
|
+
'dist/package.json'
|
|
299
|
+
];
|
|
300
|
+
this.watcher = (0, chokidar_1.watch)(watchPatterns, {
|
|
301
|
+
ignoreInitial: true,
|
|
302
|
+
followSymlinks: false,
|
|
303
|
+
usePolling: false,
|
|
304
|
+
atomic: 500,
|
|
305
|
+
ignored: [
|
|
306
|
+
'**/node_modules/**',
|
|
307
|
+
'**/.git/**',
|
|
308
|
+
'**/*.log',
|
|
309
|
+
'**/*.map'
|
|
310
|
+
]
|
|
311
|
+
});
|
|
312
|
+
this.watcher.on('change', (filePath) => {
|
|
313
|
+
if (this.options.verbose) {
|
|
314
|
+
logger_manager_js_1.loggerManager.printLine(`File changed: ${path_1.default.relative(process.cwd(), filePath)}`, 'info');
|
|
315
|
+
}
|
|
316
|
+
this.restart();
|
|
317
|
+
});
|
|
318
|
+
this.watcher.on('error', (error) => {
|
|
319
|
+
logger_manager_js_1.loggerManager.printLine(`Watcher error: ${error.message}`, 'error');
|
|
320
|
+
});
|
|
321
|
+
if (this.options.verbose) {
|
|
322
|
+
logger_manager_js_1.loggerManager.printLine(`Watching: ${watchPatterns.join(', ')}`, 'info');
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async restart() {
|
|
326
|
+
if (this.isShuttingDown)
|
|
81
327
|
return;
|
|
328
|
+
if (!this.options.quiet) {
|
|
329
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Restarting server...`, 'info');
|
|
82
330
|
}
|
|
83
|
-
this.
|
|
331
|
+
await this.stopServer();
|
|
332
|
+
// Small delay before restart
|
|
333
|
+
setTimeout(() => {
|
|
334
|
+
this.startServer().catch(error => {
|
|
335
|
+
logger_manager_js_1.loggerManager.printLine(`Restart failed: ${error.message}`, 'error');
|
|
336
|
+
process.exit(1);
|
|
337
|
+
});
|
|
338
|
+
}, 100);
|
|
339
|
+
}
|
|
340
|
+
async stopServer() {
|
|
341
|
+
if (!this.serverProcess)
|
|
342
|
+
return;
|
|
84
343
|
return new Promise((resolve) => {
|
|
85
|
-
|
|
86
|
-
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Stopping application ${appName}...`, 'info');
|
|
87
|
-
proc.on('exit', () => {
|
|
88
|
-
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.square)} Application stopped.`, 'info');
|
|
344
|
+
if (!this.serverProcess) {
|
|
89
345
|
resolve();
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const proc = this.serverProcess;
|
|
349
|
+
this.serverProcess = null;
|
|
350
|
+
if (this.restartTimer) {
|
|
351
|
+
clearTimeout(this.restartTimer);
|
|
352
|
+
this.restartTimer = null;
|
|
353
|
+
}
|
|
354
|
+
const cleanup = () => {
|
|
355
|
+
this.removePidFile();
|
|
94
356
|
resolve();
|
|
95
|
-
}
|
|
357
|
+
};
|
|
358
|
+
proc.on('exit', cleanup);
|
|
359
|
+
proc.on('error', cleanup);
|
|
96
360
|
try {
|
|
97
361
|
if (proc.pid) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// Set a timeout to force kill if it doesn't terminate gracefully
|
|
362
|
+
// Graceful shutdown
|
|
363
|
+
proc.kill('SIGTERM');
|
|
364
|
+
// Force kill after timeout
|
|
102
365
|
setTimeout(() => {
|
|
103
|
-
if (!proc.killed) {
|
|
366
|
+
if (proc.pid && !proc.killed) {
|
|
104
367
|
try {
|
|
105
|
-
|
|
368
|
+
proc.kill('SIGKILL');
|
|
106
369
|
}
|
|
107
370
|
catch (e) {
|
|
108
|
-
// Ignore
|
|
371
|
+
// Ignore
|
|
109
372
|
}
|
|
110
373
|
}
|
|
111
|
-
},
|
|
374
|
+
}, this.options.gracefulTimeout);
|
|
112
375
|
}
|
|
113
376
|
}
|
|
114
|
-
catch (
|
|
115
|
-
|
|
116
|
-
resolve();
|
|
377
|
+
catch (error) {
|
|
378
|
+
cleanup();
|
|
117
379
|
}
|
|
118
380
|
});
|
|
119
381
|
}
|
|
382
|
+
async start() {
|
|
383
|
+
try {
|
|
384
|
+
await this.startServer();
|
|
385
|
+
this.setupWatcher();
|
|
386
|
+
if (this.options.watch && !this.options.quiet) {
|
|
387
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Watching for changes...`, 'info');
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
logger_manager_js_1.loggerManager.printLine(`Failed to start server: ${error.message}`, 'error');
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
async stop() {
|
|
396
|
+
this.isShuttingDown = true;
|
|
397
|
+
if (!this.options.quiet) {
|
|
398
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Stopping server...`, 'info');
|
|
399
|
+
}
|
|
400
|
+
if (this.watcher) {
|
|
401
|
+
await this.watcher.close();
|
|
402
|
+
this.watcher = null;
|
|
403
|
+
}
|
|
404
|
+
await this.stopServer();
|
|
405
|
+
if (!this.options.quiet) {
|
|
406
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Server stopped successfully`, 'info');
|
|
407
|
+
}
|
|
408
|
+
}
|
|
120
409
|
}
|
|
121
410
|
exports.StartManager = StartManager;
|