neex 0.3.2 → 0.3.4
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/bun.lock +804 -72
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/process-command/process.js +2 -1
- package/dist/commands/process-command/process.js.map +1 -1
- package/dist/commands/project-command/build.js +2 -1
- package/dist/commands/project-command/build.js.map +1 -1
- package/dist/commands/project-command/cache.js +2 -1
- package/dist/commands/project-command/cache.js.map +1 -1
- package/dist/commands/project-command/dev.js +4 -3
- package/dist/commands/project-command/dev.js.map +1 -1
- package/dist/commands/project-command/start.js +2 -1
- package/dist/commands/project-command/start.js.map +1 -1
- package/dist/commands/run-commands/run.js +2 -1
- package/dist/commands/run-commands/run.js.map +1 -1
- package/dist/dev-manager.d.ts +1 -0
- package/dist/dev-manager.js +1 -1
- package/dist/dev-manager.js.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/logger.js +9 -9
- package/dist/logger.js.map +1 -1
- package/dist/process-manager.d.ts +1 -2
- package/dist/process-manager.js +0 -2
- package/dist/process-manager.js.map +1 -1
- package/dist/project-manager.d.ts +13 -1
- package/dist/project-manager.js +362 -94
- package/dist/project-manager.js.map +1 -1
- package/dist/runner.d.ts +1 -0
- package/dist/runner.js +7 -17
- package/dist/runner.js.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.js +67 -0
- package/dist/server.js.map +1 -0
- package/dist/typescript-runner.js.map +1 -1
- package/package.json +4 -1
package/dist/project-manager.js
CHANGED
|
@@ -15,23 +15,13 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) ||
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
35
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
27
|
};
|
|
@@ -40,15 +30,19 @@ exports.ProjectManager = void 0;
|
|
|
40
30
|
const child_process_1 = require("child_process");
|
|
41
31
|
const path_1 = require("path");
|
|
42
32
|
const fs_1 = require("fs");
|
|
33
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
34
|
+
const figures_1 = __importDefault(require("figures"));
|
|
43
35
|
const chokidar_1 = require("chokidar");
|
|
44
36
|
const typescript_runner_js_1 = require("./typescript-runner.js");
|
|
45
|
-
const child_process_2 = require("child_process");
|
|
46
37
|
const logger_js_1 = __importDefault(require("./logger.js"));
|
|
47
38
|
const net = __importStar(require("net"));
|
|
39
|
+
const readline = __importStar(require("readline"));
|
|
48
40
|
class ProjectManager {
|
|
49
41
|
constructor() {
|
|
50
42
|
this.server = null;
|
|
51
43
|
this.watcher = null;
|
|
44
|
+
this.buildWatcher = null;
|
|
45
|
+
this.isRestarting = false;
|
|
52
46
|
this.tsRunner = new typescript_runner_js_1.FastTypeScriptRunner();
|
|
53
47
|
}
|
|
54
48
|
async startDev(options) {
|
|
@@ -57,8 +51,12 @@ class ProjectManager {
|
|
|
57
51
|
if (!(0, fs_1.existsSync)(entryPath)) {
|
|
58
52
|
throw new Error(`Entry file ${options.entry} not found`);
|
|
59
53
|
}
|
|
60
|
-
logger_js_1.default.printLine(`Starting development server with ${
|
|
61
|
-
|
|
54
|
+
logger_js_1.default.printLine(`Starting development server with ${options.compiler === 'auto' ? 'esbuild' : options.compiler} compiler...`, 'info');
|
|
55
|
+
// Initialize cache if enabled
|
|
56
|
+
if (options.cache) {
|
|
57
|
+
this.ensureCacheDir();
|
|
58
|
+
}
|
|
59
|
+
await this.startDevServer(entryPath, options);
|
|
62
60
|
this.setupFileWatcher(options);
|
|
63
61
|
this.setupCleanup();
|
|
64
62
|
}
|
|
@@ -70,22 +68,17 @@ class ProjectManager {
|
|
|
70
68
|
try {
|
|
71
69
|
logger_js_1.default.printLine('Building project...', 'info');
|
|
72
70
|
const startTime = Date.now();
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
else {
|
|
85
|
-
logger_js_1.default.printLine(`Build failed with code ${code}`, 'error');
|
|
86
|
-
process.exit(code ?? 1);
|
|
87
|
-
}
|
|
88
|
-
});
|
|
71
|
+
// Create output directory if it doesn't exist
|
|
72
|
+
if (!(0, fs_1.existsSync)(options.outDir)) {
|
|
73
|
+
(0, fs_1.mkdirSync)(options.outDir, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
const compiler = options.compiler === 'auto' ? 'esbuild' : options.compiler;
|
|
76
|
+
if (options.watch) {
|
|
77
|
+
await this.startBuildWatch(compiler, options);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
await this.executeBuild(compiler, options, startTime);
|
|
81
|
+
}
|
|
89
82
|
}
|
|
90
83
|
catch (error) {
|
|
91
84
|
this.handleError(error);
|
|
@@ -98,7 +91,14 @@ class ProjectManager {
|
|
|
98
91
|
const entryPath = (0, path_1.resolve)(process.cwd(), outDir, entryFile);
|
|
99
92
|
if (!(0, fs_1.existsSync)(entryPath)) {
|
|
100
93
|
logger_js_1.default.printLine('Build not found. Building project first...', 'warn');
|
|
101
|
-
await this.build({
|
|
94
|
+
await this.build({
|
|
95
|
+
outDir: options.outDir,
|
|
96
|
+
minify: true,
|
|
97
|
+
sourcemap: false,
|
|
98
|
+
watch: false,
|
|
99
|
+
compiler: 'esbuild',
|
|
100
|
+
target: 'ES2022'
|
|
101
|
+
});
|
|
102
102
|
}
|
|
103
103
|
await this.startProductionServer(entryPath, options);
|
|
104
104
|
}
|
|
@@ -106,60 +106,135 @@ class ProjectManager {
|
|
|
106
106
|
this.handleError(error);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
-
async
|
|
110
|
-
return new Promise((resolve
|
|
109
|
+
async isPortAvailable(port) {
|
|
110
|
+
return new Promise((resolve) => {
|
|
111
111
|
const server = net.createServer();
|
|
112
112
|
server.unref();
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
113
|
+
server.on('error', () => {
|
|
114
|
+
resolve(false);
|
|
115
|
+
});
|
|
116
|
+
server.on('listening', () => {
|
|
117
|
+
server.close(() => {
|
|
118
|
+
resolve(true);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
server.listen(port);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
async promptForPort() {
|
|
125
|
+
const rl = readline.createInterface({
|
|
126
|
+
input: process.stdin,
|
|
127
|
+
output: process.stdout
|
|
128
|
+
});
|
|
129
|
+
return new Promise((resolve) => {
|
|
130
|
+
logger_js_1.default.printLine('', 'info');
|
|
131
|
+
logger_js_1.default.printLine('All default ports are in use.', 'warn');
|
|
132
|
+
logger_js_1.default.printLine('You can either:', 'info');
|
|
133
|
+
logger_js_1.default.printLine(' 1. Set PORT in your .env file: PORT=your_custom_port', 'info');
|
|
134
|
+
logger_js_1.default.printLine(' 2. Use -p flag: neex dev src/server.ts -p your_custom_port', 'info');
|
|
135
|
+
logger_js_1.default.printLine(' 3. Enter a custom port now', 'info');
|
|
136
|
+
logger_js_1.default.printLine('', 'info');
|
|
137
|
+
const askForPort = () => {
|
|
138
|
+
rl.question(chalk_1.default.cyan('Enter a custom port (or press Ctrl+C to exit): '), async (answer) => {
|
|
139
|
+
const port = parseInt(answer.trim());
|
|
140
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
141
|
+
logger_js_1.default.printLine('Please enter a valid port number (1-65535)', 'error');
|
|
142
|
+
askForPort();
|
|
143
|
+
return;
|
|
124
144
|
}
|
|
125
|
-
|
|
126
|
-
|
|
145
|
+
if (port < 1024) {
|
|
146
|
+
logger_js_1.default.printLine('Warning: Ports below 1024 may require administrator privileges', 'warn');
|
|
127
147
|
}
|
|
148
|
+
const available = await this.isPortAvailable(port);
|
|
149
|
+
if (!available) {
|
|
150
|
+
logger_js_1.default.printLine(`Port ${port} is already in use. Try another port.`, 'error');
|
|
151
|
+
askForPort();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
rl.close();
|
|
155
|
+
resolve(port);
|
|
128
156
|
});
|
|
129
|
-
server.once('listening', () => {
|
|
130
|
-
server.close(() => {
|
|
131
|
-
resolve(port);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
server.listen(port);
|
|
135
157
|
};
|
|
136
|
-
|
|
158
|
+
askForPort();
|
|
137
159
|
});
|
|
138
160
|
}
|
|
139
|
-
async
|
|
161
|
+
async findAvailablePort(requestedPort = 8000, maxTries = 10) {
|
|
162
|
+
const isRequestedPortAvailable = await this.isPortAvailable(requestedPort);
|
|
163
|
+
if (isRequestedPortAvailable) {
|
|
164
|
+
return requestedPort;
|
|
165
|
+
}
|
|
166
|
+
logger_js_1.default.printLine(`Port ${requestedPort} is already in use. Searching for available port...`, 'warn');
|
|
167
|
+
for (let i = 1; i <= maxTries; i++) {
|
|
168
|
+
const tryPort = requestedPort + i;
|
|
169
|
+
logger_js_1.default.printLine(`Checking port ${tryPort}...`, 'info');
|
|
170
|
+
const available = await this.isPortAvailable(tryPort);
|
|
171
|
+
if (available) {
|
|
172
|
+
logger_js_1.default.printLine(`✓ Found available port: ${tryPort}`, 'info');
|
|
173
|
+
return tryPort;
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
logger_js_1.default.printLine(`✗ Port ${tryPort} is in use`, 'warn');
|
|
177
|
+
}
|
|
178
|
+
if (i < maxTries) {
|
|
179
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
logger_js_1.default.printLine(`All ports from ${requestedPort} to ${requestedPort + maxTries} are in use.`, 'warn');
|
|
183
|
+
return await this.promptForPort();
|
|
184
|
+
}
|
|
185
|
+
getRequestedPort(options) {
|
|
186
|
+
let requestedPort;
|
|
187
|
+
if (options.port && options.port !== '') {
|
|
188
|
+
requestedPort = options.port;
|
|
189
|
+
logger_js_1.default.printLine(`Using port from command line: ${requestedPort}`, 'info');
|
|
190
|
+
}
|
|
191
|
+
else if (process.env.PORT) {
|
|
192
|
+
requestedPort = process.env.PORT;
|
|
193
|
+
logger_js_1.default.printLine(`Using port from .env file: ${requestedPort}`, 'info');
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
requestedPort = '8000';
|
|
197
|
+
logger_js_1.default.printLine(`Using default port: ${requestedPort}`, 'info');
|
|
198
|
+
}
|
|
199
|
+
const port = parseInt(requestedPort);
|
|
200
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
201
|
+
logger_js_1.default.printLine(`Invalid port number: ${requestedPort}. Using default port 8000.`, 'warn');
|
|
202
|
+
return 8000;
|
|
203
|
+
}
|
|
204
|
+
return port;
|
|
205
|
+
}
|
|
206
|
+
async startDevServer(entryPath, options) {
|
|
140
207
|
if (this.server) {
|
|
141
208
|
this.server.kill('SIGTERM');
|
|
142
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
209
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
143
210
|
}
|
|
144
211
|
try {
|
|
145
212
|
const startTime = Date.now();
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
213
|
+
const compiler = options.compiler === 'auto' ? 'esbuild' : options.compiler;
|
|
214
|
+
// Build the project first
|
|
215
|
+
const buildOptions = {
|
|
216
|
+
outDir: '.neex/dev',
|
|
217
|
+
minify: false,
|
|
218
|
+
sourcemap: true,
|
|
219
|
+
watch: false,
|
|
220
|
+
compiler,
|
|
221
|
+
target: 'ES2022'
|
|
222
|
+
};
|
|
223
|
+
await this.executeBuild(compiler, buildOptions, startTime, true);
|
|
224
|
+
// Get the built file path
|
|
225
|
+
const entryName = entryPath.split('/').pop()?.replace('.ts', '.js') || 'server.js';
|
|
226
|
+
const builtPath = (0, path_1.resolve)(process.cwd(), '.neex/dev', entryName);
|
|
227
|
+
if (!(0, fs_1.existsSync)(builtPath)) {
|
|
228
|
+
throw new Error(`Built file not found: ${builtPath}`);
|
|
154
229
|
}
|
|
155
230
|
const nodeArgs = [];
|
|
156
231
|
if (options.inspect)
|
|
157
232
|
nodeArgs.push('--inspect');
|
|
158
233
|
if (options.inspectBrk)
|
|
159
234
|
nodeArgs.push('--inspect-brk');
|
|
160
|
-
const requestedPort = options
|
|
161
|
-
const port = await this.findAvailablePort(
|
|
162
|
-
this.server = (0, child_process_1.spawn)('node', [...nodeArgs,
|
|
235
|
+
const requestedPort = this.getRequestedPort(options);
|
|
236
|
+
const port = await this.findAvailablePort(requestedPort);
|
|
237
|
+
this.server = (0, child_process_1.spawn)('node', [...nodeArgs, builtPath], {
|
|
163
238
|
stdio: 'inherit',
|
|
164
239
|
env: {
|
|
165
240
|
...process.env,
|
|
@@ -176,12 +251,86 @@ class ProjectManager {
|
|
|
176
251
|
logger_js_1.default.printLine(`Server crashed with code ${code}`, 'error');
|
|
177
252
|
}
|
|
178
253
|
});
|
|
179
|
-
|
|
254
|
+
const totalTime = Date.now() - startTime;
|
|
255
|
+
logger_js_1.default.printLine(`${chalk_1.default.green(figures_1.default.tick)} Server started successfully on port ${chalk_1.default.bold.cyan(port)} (${totalTime}ms)`, 'info');
|
|
256
|
+
logger_js_1.default.printLine(`${chalk_1.default.gray('Local:')} http://localhost:${port}`, 'info');
|
|
180
257
|
}
|
|
181
258
|
catch (error) {
|
|
182
259
|
this.handleError(error);
|
|
183
260
|
}
|
|
184
261
|
}
|
|
262
|
+
async executeBuild(compiler, options, startTime, isDev = false) {
|
|
263
|
+
const buildCommand = this.getBuildCommand(compiler, options, isDev);
|
|
264
|
+
return new Promise((resolve, reject) => {
|
|
265
|
+
const build = (0, child_process_1.spawn)('sh', ['-c', buildCommand], {
|
|
266
|
+
stdio: isDev ? 'pipe' : 'inherit'
|
|
267
|
+
});
|
|
268
|
+
build.on('close', (code) => {
|
|
269
|
+
if (code === 0) {
|
|
270
|
+
const buildTime = Date.now() - startTime;
|
|
271
|
+
if (!isDev) {
|
|
272
|
+
logger_js_1.default.printLine(`Build completed successfully in ${buildTime}ms`, 'info');
|
|
273
|
+
logger_js_1.default.printLine(`Output: ${options.outDir}`, 'info');
|
|
274
|
+
}
|
|
275
|
+
resolve();
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
const error = new Error(`Build failed with code ${code}`);
|
|
279
|
+
if (!isDev) {
|
|
280
|
+
logger_js_1.default.printLine(`Build failed with code ${code}`, 'error');
|
|
281
|
+
}
|
|
282
|
+
reject(error);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
build.on('error', (err) => {
|
|
286
|
+
reject(err);
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
async startBuildWatch(compiler, options) {
|
|
291
|
+
logger_js_1.default.printLine('Starting build in watch mode...', 'info');
|
|
292
|
+
this.buildWatcher = (0, chokidar_1.watch)(['src/**/*'], {
|
|
293
|
+
ignored: [
|
|
294
|
+
'**/*.map',
|
|
295
|
+
'**/*.d.ts',
|
|
296
|
+
'**/node_modules/**'
|
|
297
|
+
],
|
|
298
|
+
persistent: true,
|
|
299
|
+
ignoreInitial: false,
|
|
300
|
+
awaitWriteFinish: {
|
|
301
|
+
stabilityThreshold: 200,
|
|
302
|
+
pollInterval: 50
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
let buildTimer = null;
|
|
306
|
+
let isBuilding = false;
|
|
307
|
+
const handleBuild = async () => {
|
|
308
|
+
if (isBuilding)
|
|
309
|
+
return;
|
|
310
|
+
if (buildTimer) {
|
|
311
|
+
clearTimeout(buildTimer);
|
|
312
|
+
}
|
|
313
|
+
buildTimer = setTimeout(async () => {
|
|
314
|
+
isBuilding = true;
|
|
315
|
+
try {
|
|
316
|
+
const startTime = Date.now();
|
|
317
|
+
await this.executeBuild(compiler, options, startTime);
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
logger_js_1.default.printLine('Build failed', 'error');
|
|
321
|
+
}
|
|
322
|
+
finally {
|
|
323
|
+
isBuilding = false;
|
|
324
|
+
}
|
|
325
|
+
}, 300);
|
|
326
|
+
};
|
|
327
|
+
this.buildWatcher
|
|
328
|
+
.on('change', handleBuild)
|
|
329
|
+
.on('add', handleBuild)
|
|
330
|
+
.on('unlink', handleBuild);
|
|
331
|
+
// Initial build
|
|
332
|
+
await handleBuild();
|
|
333
|
+
}
|
|
185
334
|
setupFileWatcher(options) {
|
|
186
335
|
this.watcher = (0, chokidar_1.watch)(options.watch, {
|
|
187
336
|
ignored: [
|
|
@@ -189,7 +338,8 @@ class ProjectManager {
|
|
|
189
338
|
/(^|[\/\\])\../,
|
|
190
339
|
'**/*.map',
|
|
191
340
|
'**/*.d.ts',
|
|
192
|
-
'**/node_modules/**'
|
|
341
|
+
'**/node_modules/**',
|
|
342
|
+
'**/.neex/**'
|
|
193
343
|
],
|
|
194
344
|
persistent: true,
|
|
195
345
|
ignoreInitial: true,
|
|
@@ -199,25 +349,33 @@ class ProjectManager {
|
|
|
199
349
|
}
|
|
200
350
|
});
|
|
201
351
|
let restartTimer = null;
|
|
202
|
-
let isRestarting = false;
|
|
203
352
|
const handleFileChange = (path, event) => {
|
|
204
353
|
const ext = path.split('.').pop()?.toLowerCase();
|
|
205
354
|
if (!ext || !options.ext.includes(ext)) {
|
|
206
355
|
return;
|
|
207
356
|
}
|
|
357
|
+
if (this.isRestarting) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
208
360
|
if (restartTimer) {
|
|
209
361
|
clearTimeout(restartTimer);
|
|
210
362
|
}
|
|
211
|
-
isRestarting = true;
|
|
212
|
-
restartTimer = setTimeout(() => {
|
|
363
|
+
this.isRestarting = true;
|
|
364
|
+
restartTimer = setTimeout(async () => {
|
|
213
365
|
if (options.clear) {
|
|
214
366
|
process.stdout.write('\x1Bc');
|
|
215
367
|
}
|
|
216
368
|
logger_js_1.default.printLine(`${event}: ${path}`, 'info');
|
|
217
369
|
logger_js_1.default.printLine('Restarting server...', 'info');
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
370
|
+
try {
|
|
371
|
+
await this.startDevServer((0, path_1.resolve)(process.cwd(), options.entry), options);
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
logger_js_1.default.printLine('Failed to restart server', 'error');
|
|
375
|
+
}
|
|
376
|
+
finally {
|
|
377
|
+
this.isRestarting = false;
|
|
378
|
+
}
|
|
221
379
|
}, parseInt(options.delay));
|
|
222
380
|
};
|
|
223
381
|
this.watcher
|
|
@@ -227,23 +385,50 @@ class ProjectManager {
|
|
|
227
385
|
}
|
|
228
386
|
setupCleanup() {
|
|
229
387
|
const handleSignal = (signal) => {
|
|
388
|
+
logger_js_1.default.printLine('\nShutting down server...', 'info');
|
|
230
389
|
if (this.server) {
|
|
231
390
|
this.server.kill('SIGTERM');
|
|
232
391
|
}
|
|
233
392
|
if (this.watcher) {
|
|
234
393
|
this.watcher.close();
|
|
235
394
|
}
|
|
395
|
+
if (this.buildWatcher) {
|
|
396
|
+
this.buildWatcher.close();
|
|
397
|
+
}
|
|
398
|
+
// Clean up dev build directory
|
|
399
|
+
const devBuildDir = (0, path_1.resolve)(process.cwd(), '.neex/dev');
|
|
400
|
+
if ((0, fs_1.existsSync)(devBuildDir)) {
|
|
401
|
+
try {
|
|
402
|
+
(0, fs_1.rmSync)(devBuildDir, { recursive: true, force: true });
|
|
403
|
+
}
|
|
404
|
+
catch (error) {
|
|
405
|
+
// Ignore cleanup errors
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
process.exit(0);
|
|
236
409
|
};
|
|
237
410
|
process.on('SIGINT', () => handleSignal('SIGINT'));
|
|
238
411
|
process.on('SIGTERM', () => handleSignal('SIGTERM'));
|
|
239
412
|
process.on('SIGQUIT', () => handleSignal('SIGQUIT'));
|
|
240
413
|
}
|
|
241
|
-
getBuildCommand(compiler, options) {
|
|
414
|
+
getBuildCommand(compiler, options, isDev = false) {
|
|
415
|
+
const srcPattern = isDev ? 'src/**/*.ts' : 'src/**/*.ts';
|
|
242
416
|
switch (compiler) {
|
|
243
417
|
case 'swc':
|
|
244
418
|
return `npx swc src -d ${options.outDir} --config-file .swcrc`;
|
|
245
419
|
case 'esbuild':
|
|
246
|
-
|
|
420
|
+
const esbuildFlags = [
|
|
421
|
+
`--outdir=${options.outDir}`,
|
|
422
|
+
'--platform=node',
|
|
423
|
+
`--target=${options.target.toLowerCase()}`,
|
|
424
|
+
'--format=cjs',
|
|
425
|
+
options.sourcemap ? '--sourcemap' : '',
|
|
426
|
+
options.minify && !isDev ? '--minify' : '',
|
|
427
|
+
'--bundle',
|
|
428
|
+
'--external:node_modules/*'
|
|
429
|
+
].filter(Boolean).join(' ');
|
|
430
|
+
return `npx esbuild ${srcPattern} ${esbuildFlags}`;
|
|
431
|
+
case 'tsc':
|
|
247
432
|
default:
|
|
248
433
|
return `npx tsc --outDir ${options.outDir}${options.sourcemap ? ' --sourceMap' : ''}`;
|
|
249
434
|
}
|
|
@@ -255,8 +440,8 @@ class ProjectManager {
|
|
|
255
440
|
nodeArgs.push('--inspect');
|
|
256
441
|
if (options.maxMemory)
|
|
257
442
|
nodeArgs.push(`--max-old-space-size=${options.maxMemory.replace('M', '')}`);
|
|
258
|
-
const requestedPort = options
|
|
259
|
-
const port = await this.findAvailablePort(
|
|
443
|
+
const requestedPort = this.getRequestedPort(options);
|
|
444
|
+
const port = await this.findAvailablePort(requestedPort);
|
|
260
445
|
let serverCommand = 'node';
|
|
261
446
|
let serverArgs = [...nodeArgs, entryPath];
|
|
262
447
|
if (options.cluster) {
|
|
@@ -275,7 +460,14 @@ class ProjectManager {
|
|
|
275
460
|
logger_js_1.default.printLine(`Server error: ${err.message}`, 'error');
|
|
276
461
|
process.exit(1);
|
|
277
462
|
});
|
|
278
|
-
logger_js_1.default.printLine(
|
|
463
|
+
logger_js_1.default.printLine(`${chalk_1.default.green(figures_1.default.tick)} Production server started successfully on port ${chalk_1.default.bold.cyan(port)}`, 'info');
|
|
464
|
+
logger_js_1.default.printLine(`${chalk_1.default.gray('Local:')} http://localhost:${port}`, 'info');
|
|
465
|
+
}
|
|
466
|
+
ensureCacheDir() {
|
|
467
|
+
const cacheDir = this.getCachePath();
|
|
468
|
+
if (!(0, fs_1.existsSync)(cacheDir)) {
|
|
469
|
+
(0, fs_1.mkdirSync)(cacheDir, { recursive: true });
|
|
470
|
+
}
|
|
279
471
|
}
|
|
280
472
|
handleError(error) {
|
|
281
473
|
if (error instanceof Error) {
|
|
@@ -286,11 +478,74 @@ class ProjectManager {
|
|
|
286
478
|
}
|
|
287
479
|
process.exit(1);
|
|
288
480
|
}
|
|
481
|
+
calculateDirectorySize(dirPath) {
|
|
482
|
+
let totalSize = 0;
|
|
483
|
+
try {
|
|
484
|
+
const files = (0, fs_1.readdirSync)(dirPath);
|
|
485
|
+
for (const file of files) {
|
|
486
|
+
const filePath = (0, path_1.join)(dirPath, file);
|
|
487
|
+
const stats = (0, fs_1.statSync)(filePath);
|
|
488
|
+
if (stats.isDirectory()) {
|
|
489
|
+
totalSize += this.calculateDirectorySize(filePath);
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
totalSize += stats.size;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
catch (error) {
|
|
497
|
+
// Ignore errors and return 0
|
|
498
|
+
}
|
|
499
|
+
return totalSize;
|
|
500
|
+
}
|
|
501
|
+
formatFileSize(bytes) {
|
|
502
|
+
if (bytes === 0)
|
|
503
|
+
return '0B';
|
|
504
|
+
const k = 1024;
|
|
505
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
506
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
507
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + sizes[i];
|
|
508
|
+
}
|
|
509
|
+
countFiles(dirPath) {
|
|
510
|
+
let count = 0;
|
|
511
|
+
try {
|
|
512
|
+
const files = (0, fs_1.readdirSync)(dirPath);
|
|
513
|
+
for (const file of files) {
|
|
514
|
+
const filePath = (0, path_1.join)(dirPath, file);
|
|
515
|
+
const stats = (0, fs_1.statSync)(filePath);
|
|
516
|
+
if (stats.isDirectory()) {
|
|
517
|
+
count += this.countFiles(filePath);
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
count++;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
catch (error) {
|
|
525
|
+
// Ignore errors
|
|
526
|
+
}
|
|
527
|
+
return count;
|
|
528
|
+
}
|
|
529
|
+
// Public methods for cache management
|
|
289
530
|
clearCache() {
|
|
531
|
+
const cacheDir = this.getCachePath();
|
|
532
|
+
if ((0, fs_1.existsSync)(cacheDir)) {
|
|
533
|
+
try {
|
|
534
|
+
(0, fs_1.rmSync)(cacheDir, { recursive: true, force: true });
|
|
535
|
+
logger_js_1.default.printLine('Cache cleared successfully', 'info');
|
|
536
|
+
}
|
|
537
|
+
catch (error) {
|
|
538
|
+
logger_js_1.default.printLine('Failed to clear cache', 'error');
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
logger_js_1.default.printLine('Cache directory does not exist', 'warn');
|
|
543
|
+
}
|
|
544
|
+
// Also clear TypeScript runner cache
|
|
290
545
|
this.tsRunner.clearCache();
|
|
291
546
|
}
|
|
292
547
|
getCacheStats() {
|
|
293
|
-
const cacheDir =
|
|
548
|
+
const cacheDir = this.getCachePath();
|
|
294
549
|
if (!(0, fs_1.existsSync)(cacheDir)) {
|
|
295
550
|
return {
|
|
296
551
|
size: '0B',
|
|
@@ -298,25 +553,38 @@ class ProjectManager {
|
|
|
298
553
|
lastUpdated: 'Never'
|
|
299
554
|
};
|
|
300
555
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
556
|
+
try {
|
|
557
|
+
const totalSize = this.calculateDirectorySize(cacheDir);
|
|
558
|
+
const fileCount = this.countFiles(cacheDir);
|
|
559
|
+
const stats = (0, fs_1.statSync)(cacheDir);
|
|
560
|
+
return {
|
|
561
|
+
size: this.formatFileSize(totalSize),
|
|
562
|
+
files: fileCount,
|
|
563
|
+
lastUpdated: stats.mtime.toISOString()
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
catch (error) {
|
|
567
|
+
return {
|
|
568
|
+
size: '0B',
|
|
569
|
+
files: 0,
|
|
570
|
+
lastUpdated: 'Error reading cache'
|
|
571
|
+
};
|
|
572
|
+
}
|
|
309
573
|
}
|
|
310
574
|
getCachePath() {
|
|
311
575
|
return (0, path_1.join)(process.cwd(), '.neex', 'cache');
|
|
312
576
|
}
|
|
313
577
|
cleanup(signal) {
|
|
578
|
+
logger_js_1.default.printLine('\nShutting down server...', 'info');
|
|
314
579
|
if (this.server) {
|
|
315
580
|
this.server.kill('SIGTERM');
|
|
316
581
|
}
|
|
317
582
|
if (this.watcher) {
|
|
318
583
|
this.watcher.close();
|
|
319
584
|
}
|
|
585
|
+
if (this.buildWatcher) {
|
|
586
|
+
this.buildWatcher.close();
|
|
587
|
+
}
|
|
320
588
|
}
|
|
321
589
|
}
|
|
322
590
|
exports.ProjectManager = ProjectManager;
|