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.
@@ -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) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
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 ${this.tsRunner['compiler']} compiler...`, 'info');
61
- await this.startServer(entryPath, options);
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
- const compiler = options.compiler === 'auto' ? this.tsRunner['compiler'] : options.compiler;
74
- const buildCommand = this.getBuildCommand(compiler, options);
75
- const build = (0, child_process_1.spawn)('sh', ['-c', buildCommand], {
76
- stdio: 'inherit'
77
- });
78
- build.on('close', (code) => {
79
- if (code === 0) {
80
- const buildTime = Date.now() - startTime;
81
- logger_js_1.default.printLine(`Build completed successfully in ${buildTime}ms`, 'info');
82
- logger_js_1.default.printLine(`Output: ${options.outDir}`, 'info');
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({ outDir: 'dist', minify: false, sourcemap: false, watch: false, compiler: 'auto', target: 'ES2022' });
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 findAvailablePort(startPort = 8000) {
110
- return new Promise((resolve, reject) => {
109
+ async isPortAvailable(port) {
110
+ return new Promise((resolve) => {
111
111
  const server = net.createServer();
112
112
  server.unref();
113
- const tryPort = (port) => {
114
- if (port > 8003) {
115
- server.close();
116
- logger_js_1.default.printLine('All default ports (8000-8003) are in use.', 'error');
117
- logger_js_1.default.printLine('Please specify a custom port in your .env file:', 'info');
118
- logger_js_1.default.printLine('PORT=your_custom_port', 'info');
119
- process.exit(1);
120
- }
121
- server.once('error', (err) => {
122
- if (err.code === 'EADDRINUSE') {
123
- tryPort(port + 1);
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
- else {
126
- reject(err);
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
- tryPort(startPort);
158
+ askForPort();
137
159
  });
138
160
  }
139
- async startServer(entryPath, options) {
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, 100));
209
+ await new Promise(resolve => setTimeout(resolve, 200));
143
210
  }
144
211
  try {
145
212
  const startTime = Date.now();
146
- let executablePath = entryPath;
147
- if (entryPath.endsWith('.ts')) {
148
- executablePath = await this.tsRunner.compile(entryPath);
149
- const compileTime = Date.now() - startTime;
150
- logger_js_1.default.printLine(`Compiled in ${compileTime}ms`, 'info');
151
- }
152
- else {
153
- logger_js_1.default.printLine('Using JavaScript file directly', 'info');
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.port || process.env.PORT || '8000';
161
- const port = await this.findAvailablePort(parseInt(requestedPort || '8000'));
162
- this.server = (0, child_process_1.spawn)('node', [...nodeArgs, executablePath], {
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
- logger_js_1.default.printLine(`Server started on port ${port}`, 'info');
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
- this.startServer((0, path_1.resolve)(process.cwd(), options.entry), options).then(() => {
219
- isRestarting = false;
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
- return `npx esbuild src/**/*.ts --outdir=${options.outDir} --platform=node --target=node18 --format=cjs${options.sourcemap ? ' --sourcemap' : ''}${options.minify ? ' --minify' : ''}`;
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.port || process.env.PORT || '8000';
259
- const port = await this.findAvailablePort(parseInt(requestedPort));
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(`Production server started on port ${port}`, 'info');
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 = (0, path_1.join)(process.cwd(), '.neex', 'cache');
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
- const size = (0, child_process_2.execSync)(`du -sh ${cacheDir}`, { encoding: 'utf8' }).trim().split('\t')[0];
302
- const files = (0, child_process_2.execSync)(`find ${cacheDir} -type f | wc -l`, { encoding: 'utf8' }).trim();
303
- const lastUpdated = (0, child_process_2.execSync)(`stat -f "%Sm" ${cacheDir}`, { encoding: 'utf8' }).trim();
304
- return {
305
- size,
306
- files: parseInt(files),
307
- lastUpdated
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;