neex 0.6.85 → 0.6.88

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.
@@ -7,18 +7,20 @@ exports.addDevCommands = void 0;
7
7
  const dev_manager_js_1 = require("../dev-manager.js");
8
8
  const logger_manager_js_1 = require("../logger-manager.js");
9
9
  const chalk_1 = __importDefault(require("chalk"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const fs_1 = __importDefault(require("fs"));
10
12
  const figures_1 = __importDefault(require("figures"));
11
13
  function addDevCommands(program) {
12
14
  let devManager = null;
13
- // Enhanced dev command with built-in TypeScript compilation
15
+ let isShuttingDown = false;
16
+ // Ultra-fast Express dev command optimized for speed
14
17
  program
15
18
  .command('dev [file]')
16
19
  .description('Start TypeScript development server with hot reloading (default: src/index.ts)')
17
20
  .option('-w, --watch <patterns>', 'Watch additional patterns (comma-separated)', 'src/**/*')
18
21
  .option('-i, --ignore <patterns>', 'Ignore patterns (comma-separated)', 'node_modules,dist,build,.git,coverage')
19
22
  .option('-e, --ext <extensions>', 'File extensions to watch (comma-separated)', 'ts,tsx,js,jsx,json')
20
- .option('-d, --delay <ms>', 'Delay before restart (ms)', parseInt, 500)
21
- .option('--fast', 'Enable fast hot reload (shorter delays)')
23
+ .option('-d, --delay <ms>', 'Delay before restart (ms)', parseInt, 800)
22
24
  .option('-c, --no-color', 'Disable colored output')
23
25
  .option('-q, --quiet', 'Reduce output verbosity')
24
26
  .option('-v, --verbose', 'Verbose output')
@@ -31,11 +33,13 @@ function addDevCommands(program) {
31
33
  .option('--no-source-maps', 'Disable source maps')
32
34
  .option('--transpile-only', 'Skip type checking for faster compilation')
33
35
  .option('--node-args <args>', 'Additional Node.js arguments (comma-separated)', '')
36
+ .option('--port <port>', 'Port to use if available', parseInt)
37
+ .option('--host <host>', 'Host to bind to')
38
+ .option('--hot-reload', 'Enable hot reloading for supported frameworks (e.g., Express)')
39
+ .option('--middleware', 'Inject middleware for features like hot reloading')
34
40
  .action(async (file, options) => {
35
41
  try {
36
42
  const targetFile = file || 'src/index.ts';
37
- // تنظیمات fast mode
38
- const delay = options.fast ? 200 : options.delay;
39
43
  if (!options.quiet) {
40
44
  logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Starting ${chalk_1.default.cyan('neex dev')} for ${chalk_1.default.cyan(targetFile)}`, 'info');
41
45
  if (options.verbose) {
@@ -52,7 +56,7 @@ function addDevCommands(program) {
52
56
  watch: options.watch.split(',').map((p) => p.trim()),
53
57
  ignore: options.ignore.split(',').map((p) => p.trim()),
54
58
  extensions: options.ext.split(',').map((e) => e.trim()),
55
- delay: delay,
59
+ delay: options.delay,
56
60
  color: options.color,
57
61
  quiet: options.quiet,
58
62
  verbose: options.verbose,
@@ -64,7 +68,11 @@ function addDevCommands(program) {
64
68
  tsConfig: options.tsconfig,
65
69
  sourceMaps: options.sourceMaps,
66
70
  transpileOnly: options.transpileOnly,
67
- nodeArgs: options.nodeArgs ? options.nodeArgs.split(',').map((arg) => arg.trim()) : []
71
+ nodeArgs: options.nodeArgs ? options.nodeArgs.split(',').map((arg) => arg.trim()) : [],
72
+ port: options.port,
73
+ host: options.host,
74
+ hotReload: options.hotReload,
75
+ middleware: options.middleware
68
76
  });
69
77
  await devManager.start();
70
78
  }
@@ -83,11 +91,9 @@ function addDevCommands(program) {
83
91
  .command('dev:clean')
84
92
  .description('Clean development server cache and temporary files')
85
93
  .action(() => {
86
- const path = require('path');
87
- const fs = require('fs');
88
- const tempDir = path.join(process.cwd(), '.neex-temp');
89
- if (fs.existsSync(tempDir)) {
90
- fs.rmSync(tempDir, { recursive: true, force: true });
94
+ const tempDir = path_1.default.join(process.cwd(), '.neex');
95
+ if (fs_1.default.existsSync(tempDir)) {
96
+ fs_1.default.rmSync(tempDir, { recursive: true, force: true });
91
97
  logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Cleaned development cache`, 'info');
92
98
  }
93
99
  else {
@@ -99,15 +105,13 @@ function addDevCommands(program) {
99
105
  .description('Check TypeScript configuration and dependencies')
100
106
  .option('--tsconfig <path>', 'Path to TypeScript configuration file')
101
107
  .action((options) => {
102
- const path = require('path');
103
- const fs = require('fs');
104
108
  const configPath = options.tsconfig || 'tsconfig.json';
105
- if (!fs.existsSync(configPath)) {
109
+ if (!fs_1.default.existsSync(configPath)) {
106
110
  logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} TypeScript config not found: ${configPath}`, 'error');
107
111
  process.exit(1);
108
112
  }
109
113
  try {
110
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
114
+ const config = JSON.parse(fs_1.default.readFileSync(configPath, 'utf8'));
111
115
  logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} TypeScript config is valid`, 'info');
112
116
  if (config.compilerOptions) {
113
117
  logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('Compiler Options:')}`, 'info');
@@ -27,16 +27,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.DevManager = void 0;
30
- // src/dev-manager.ts - Enhanced TypeScript development server with improved caching
30
+ // src/dev-manager.ts - Development process manager for Node.js applications
31
31
  const child_process_1 = require("child_process");
32
32
  const chokidar_1 = require("chokidar");
33
33
  const logger_manager_js_1 = require("./logger-manager.js");
34
34
  const chalk_1 = __importDefault(require("chalk"));
35
- const figures_1 = __importDefault(require("figures"));
36
35
  const path_1 = __importDefault(require("path"));
37
36
  const fs_1 = __importDefault(require("fs"));
38
37
  const lodash_1 = require("lodash");
39
38
  const ts = __importStar(require("typescript"));
39
+ const crypto_1 = __importDefault(require("crypto"));
40
+ const net_1 = __importDefault(require("net"));
40
41
  class DevManager {
41
42
  constructor(options) {
42
43
  this.process = null;
@@ -45,10 +46,93 @@ class DevManager {
45
46
  this.restartCount = 0;
46
47
  this.startTime = null;
47
48
  this.moduleCache = new Map();
48
- this.fileWatcher = new Map();
49
+ this.currentTempFile = null;
50
+ this.serverInfo = null;
51
+ this.hotReloadClient = null;
52
+ this.isExpressApp = false;
53
+ this.lastCompileTime = 0;
54
+ this.performanceMetrics = {
55
+ averageRestartTime: 0,
56
+ totalRestarts: 0,
57
+ cacheHits: 0,
58
+ cacheMisses: 0
59
+ };
49
60
  this.options = options;
50
- this.debouncedRestart = (0, lodash_1.debounce)(this.restart.bind(this), options.delay);
61
+ this.tempDir = path_1.default.join(process.cwd(), '.neex');
62
+ this.debouncedRestart = (0, lodash_1.debounce)(this.restart.bind(this), Math.max(options.delay, 100));
51
63
  this.tsCompilerOptions = this.loadTsConfig();
64
+ this.setupTempDir();
65
+ this.detectExpressApp();
66
+ this.setupHotReloadClient();
67
+ }
68
+ setupTempDir() {
69
+ if (fs_1.default.existsSync(this.tempDir)) {
70
+ try {
71
+ fs_1.default.rmSync(this.tempDir, { recursive: true, force: true });
72
+ }
73
+ catch (error) {
74
+ // Ignore cleanup errors
75
+ }
76
+ }
77
+ fs_1.default.mkdirSync(this.tempDir, { recursive: true });
78
+ // Create cache directory
79
+ const cacheDir = path_1.default.join(this.tempDir, 'cache');
80
+ fs_1.default.mkdirSync(cacheDir, { recursive: true });
81
+ }
82
+ detectExpressApp() {
83
+ try {
84
+ const fileContent = fs_1.default.readFileSync(this.options.file, 'utf8');
85
+ this.isExpressApp = /import.*express|require.*express|app\.listen|server\.listen/.test(fileContent);
86
+ if (this.options.verbose) {
87
+ logger_manager_js_1.loggerManager.printLine(`Detected ${this.isExpressApp ? 'Express' : 'Node.js'} application`, 'info');
88
+ }
89
+ }
90
+ catch (error) {
91
+ this.isExpressApp = false;
92
+ }
93
+ }
94
+ setupHotReloadClient() {
95
+ if (!this.options.hotReload || !this.isExpressApp)
96
+ return;
97
+ this.hotReloadClient = `
98
+ // Hot reload client for Express development
99
+ (function() {
100
+ if (typeof window === 'undefined') return;
101
+
102
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
103
+ const wsUrl = protocol + '//' + window.location.host + '/__neex_hot_reload';
104
+
105
+ function connect() {
106
+ const ws = new WebSocket(wsUrl);
107
+
108
+ ws.onopen = function() {
109
+ console.log('🔥 Hot reload connected');
110
+ };
111
+
112
+ ws.onmessage = function(event) {
113
+ const data = JSON.parse(event.data);
114
+
115
+ if (data.type === 'reload') {
116
+ console.log('🔄 Reloading page...');
117
+ window.location.reload();
118
+ } else if (data.type === 'error') {
119
+ console.error('🚨 Build error:', data.message);
120
+ }
121
+ };
122
+
123
+ ws.onclose = function() {
124
+ console.log('🔌 Hot reload disconnected, retrying...');
125
+ setTimeout(connect, 1000);
126
+ };
127
+
128
+ ws.onerror = function(error) {
129
+ console.error('❌ Hot reload error:', error);
130
+ };
131
+ }
132
+
133
+ connect();
134
+ })();
135
+ `;
52
136
  }
53
137
  loadTsConfig() {
54
138
  const configPath = this.options.tsConfig || 'tsconfig.json';
@@ -57,8 +141,6 @@ class DevManager {
57
141
  module: ts.ModuleKind.CommonJS,
58
142
  moduleResolution: ts.ModuleResolutionKind.NodeJs,
59
143
  allowJs: true,
60
- outDir: undefined,
61
- rootDir: undefined,
62
144
  strict: false,
63
145
  esModuleInterop: true,
64
146
  skipLibCheck: true,
@@ -68,408 +150,423 @@ class DevManager {
68
150
  sourceMap: this.options.sourceMaps,
69
151
  inlineSourceMap: false,
70
152
  inlineSources: false,
153
+ removeComments: false,
154
+ preserveConstEnums: false,
155
+ isolatedModules: true,
156
+ incremental: true,
157
+ tsBuildInfoFile: path_1.default.join(this.tempDir, 'tsconfig.tsbuildinfo'),
71
158
  };
72
159
  if (fs_1.default.existsSync(configPath)) {
73
160
  try {
74
161
  const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
75
- if (configFile.error) {
76
- logger_manager_js_1.loggerManager.printLine(`Error reading tsconfig.json: ${configFile.error.messageText}`, 'warn');
77
- return defaultOptions;
78
- }
79
- const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path_1.default.dirname(configPath));
80
- if (parsedConfig.errors.length > 0) {
81
- logger_manager_js_1.loggerManager.printLine(`Error parsing tsconfig.json: ${parsedConfig.errors[0].messageText}`, 'warn');
82
- return defaultOptions;
83
- }
84
- const options = { ...parsedConfig.options, ...defaultOptions };
85
- if (this.options.verbose) {
86
- logger_manager_js_1.loggerManager.printLine(`Loaded TypeScript config from ${configPath}`, 'info');
162
+ if (!configFile.error) {
163
+ const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path_1.default.dirname(configPath));
164
+ if (parsedConfig.errors.length === 0) {
165
+ return { ...defaultOptions, ...parsedConfig.options };
166
+ }
87
167
  }
88
- return options;
89
168
  }
90
169
  catch (error) {
91
- logger_manager_js_1.loggerManager.printLine(`Failed to load tsconfig.json: ${error.message}`, 'warn');
170
+ // Fall back to defaults
92
171
  }
93
172
  }
94
173
  return defaultOptions;
95
174
  }
96
175
  loadEnvFile() {
97
- if (this.options.envFile && fs_1.default.existsSync(this.options.envFile)) {
98
- try {
99
- const envContent = fs_1.default.readFileSync(this.options.envFile, 'utf8');
100
- const lines = envContent.split('\n');
101
- let loadedCount = 0;
102
- for (const line of lines) {
103
- const trimmed = line.trim();
104
- if (trimmed && !trimmed.startsWith('#')) {
105
- const [key, ...values] = trimmed.split('=');
106
- if (key && values.length > 0) {
107
- process.env[key.trim()] = values.join('=').trim().replace(/^["']|["']$/g, '');
108
- loadedCount++;
176
+ const envFiles = [
177
+ this.options.envFile,
178
+ '.env.development',
179
+ '.env.local',
180
+ '.env'
181
+ ].filter(Boolean);
182
+ for (const envFile of envFiles) {
183
+ if (fs_1.default.existsSync(envFile)) {
184
+ try {
185
+ const envContent = fs_1.default.readFileSync(envFile, 'utf8');
186
+ const lines = envContent.split('\n');
187
+ for (const line of lines) {
188
+ const trimmed = line.trim();
189
+ if (trimmed && !trimmed.startsWith('#')) {
190
+ const [key, ...values] = trimmed.split('=');
191
+ if (key && values.length > 0) {
192
+ const value = values.join('=').trim().replace(/^["']|["']$/g, '');
193
+ if (!process.env[key.trim()]) {
194
+ process.env[key.trim()] = value;
195
+ }
196
+ }
109
197
  }
110
198
  }
199
+ if (this.options.verbose) {
200
+ logger_manager_js_1.loggerManager.printLine(`Loaded environment from ${envFile}`, 'info');
201
+ }
202
+ break;
111
203
  }
112
- if (!this.options.quiet && loadedCount > 0) {
113
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim(figures_1.default.info)} Loaded ${loadedCount} env variable${loadedCount > 1 ? 's' : ''} from ${path_1.default.basename(this.options.envFile)}`, 'info');
114
- }
115
- else if (this.options.verbose) {
116
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim(figures_1.default.info)} No env variables found in ${this.options.envFile}`, 'info');
204
+ catch (error) {
205
+ if (this.options.verbose) {
206
+ logger_manager_js_1.loggerManager.printLine(`Failed to load ${envFile}: ${error.message}`, 'warn');
207
+ }
117
208
  }
118
209
  }
119
- catch (error) {
120
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Failed to load ${this.options.envFile}: ${error.message}`, 'warn');
121
- }
122
210
  }
123
211
  }
124
- generateFileHash(content) {
125
- // Simple hash function for content
126
- let hash = 0;
127
- for (let i = 0; i < content.length; i++) {
128
- const char = content.charCodeAt(i);
129
- hash = ((hash << 5) - hash) + char;
130
- hash = hash & hash; // Convert to 32bit integer
131
- }
132
- return hash.toString(16);
212
+ createHash(content) {
213
+ return crypto_1.default.createHash('sha256').update(content).digest('hex').substring(0, 16);
133
214
  }
134
- shouldRecompile(filePath, sourceCode) {
135
- const absolutePath = path_1.default.resolve(filePath);
136
- const cached = this.moduleCache.get(absolutePath);
137
- if (!cached) {
138
- return true;
139
- }
140
- // Check if file was modified
141
- const stat = fs_1.default.statSync(absolutePath);
142
- if (stat.mtimeMs > cached.lastModified) {
143
- return true;
144
- }
145
- // Check if content hash changed
146
- const currentHash = this.generateFileHash(sourceCode);
147
- if (currentHash !== cached.hash) {
148
- return true;
149
- }
150
- return false;
151
- }
152
- compileTypeScript(filePath) {
153
- const absolutePath = path_1.default.resolve(filePath);
154
- try {
155
- const sourceCode = fs_1.default.readFileSync(absolutePath, 'utf8');
156
- const currentHash = this.generateFileHash(sourceCode);
157
- // Check if we need to recompile
158
- if (!this.shouldRecompile(absolutePath, sourceCode)) {
159
- const cached = this.moduleCache.get(absolutePath);
160
- if (this.options.verbose) {
161
- logger_manager_js_1.loggerManager.printLine(`Using cached compilation for ${path_1.default.relative(process.cwd(), filePath)}`, 'info');
162
- }
163
- return cached;
164
- }
165
- if (this.options.verbose) {
166
- logger_manager_js_1.loggerManager.printLine(`Compiling ${path_1.default.relative(process.cwd(), filePath)}`, 'info');
167
- }
168
- const dependencies = new Set();
169
- // Extract import/require dependencies with better regex
170
- const importRegex = /(?:import|require)\s*(?:\([^)]*\)|[^;]+from\s+)['"`]([^'"`]+)['"`]/g;
171
- let match;
172
- while ((match = importRegex.exec(sourceCode)) !== null) {
173
- if (match[1].startsWith('.')) {
174
- let depPath = path_1.default.resolve(path_1.default.dirname(absolutePath), match[1]);
175
- // Handle file extensions
176
- if (!path_1.default.extname(depPath)) {
177
- // Try common extensions
178
- for (const ext of ['.ts', '.tsx', '.js', '.jsx']) {
179
- if (fs_1.default.existsSync(depPath + ext)) {
180
- depPath = depPath + ext;
181
- break;
182
- }
183
- }
184
- // Try index files
185
- if (!fs_1.default.existsSync(depPath)) {
186
- for (const ext of ['.ts', '.tsx', '.js', '.jsx']) {
187
- const indexPath = path_1.default.join(depPath, 'index' + ext);
188
- if (fs_1.default.existsSync(indexPath)) {
189
- depPath = indexPath;
190
- break;
191
- }
192
- }
215
+ extractDependencies(sourceCode, filePath) {
216
+ const dependencies = [];
217
+ const importRegex = /(?:import|require)\s*(?:\([^)]*\)|[^;]+?from\s+)?['"`]([^'"`]+)['"`]/g;
218
+ let match;
219
+ while ((match = importRegex.exec(sourceCode)) !== null) {
220
+ const importPath = match[1];
221
+ if (importPath.startsWith('.')) {
222
+ let resolvedPath = path_1.default.resolve(path_1.default.dirname(filePath), importPath);
223
+ // Try to resolve with extensions
224
+ if (!fs_1.default.existsSync(resolvedPath)) {
225
+ for (const ext of this.options.extensions.map(e => `.${e}`)) {
226
+ const withExt = resolvedPath + ext;
227
+ if (fs_1.default.existsSync(withExt)) {
228
+ resolvedPath = withExt;
229
+ break;
193
230
  }
194
231
  }
195
- if (fs_1.default.existsSync(depPath)) {
196
- dependencies.add(depPath);
197
- }
198
232
  }
199
- }
200
- let result;
201
- if (this.options.transpileOnly) {
202
- result = ts.transpileModule(sourceCode, {
203
- compilerOptions: this.tsCompilerOptions,
204
- fileName: absolutePath,
205
- reportDiagnostics: false
206
- });
207
- }
208
- else {
209
- // Create a program for better error reporting
210
- const program = ts.createProgram([absolutePath], this.tsCompilerOptions);
211
- const sourceFile = program.getSourceFile(absolutePath);
212
- if (!sourceFile) {
213
- throw new Error(`Could not load source file: ${absolutePath}`);
214
- }
215
- // Check for compilation errors
216
- const diagnostics = ts.getPreEmitDiagnostics(program, sourceFile);
217
- if (diagnostics.length > 0) {
218
- const errors = diagnostics.map(diagnostic => {
219
- const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
220
- if (diagnostic.file && diagnostic.start !== undefined) {
221
- const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
222
- return `${path_1.default.relative(process.cwd(), diagnostic.file.fileName)}:${line + 1}:${character + 1} - ${message}`;
233
+ // Try index files
234
+ if (!fs_1.default.existsSync(resolvedPath)) {
235
+ for (const ext of this.options.extensions.map(e => `.${e}`)) {
236
+ const indexPath = path_1.default.join(resolvedPath, 'index' + ext);
237
+ if (fs_1.default.existsSync(indexPath)) {
238
+ resolvedPath = indexPath;
239
+ break;
223
240
  }
224
- return message;
225
- });
226
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} TypeScript compilation errors:\n${errors.join('\n')}`, 'error');
227
- // Don't exit in dev mode, just show errors
228
- if (!this.options.quiet) {
229
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Continuing with compilation errors...`, 'warn');
230
241
  }
231
242
  }
232
- result = ts.transpileModule(sourceCode, {
233
- compilerOptions: this.tsCompilerOptions,
234
- fileName: absolutePath,
235
- reportDiagnostics: false
236
- });
243
+ if (fs_1.default.existsSync(resolvedPath)) {
244
+ dependencies.push(resolvedPath);
245
+ }
237
246
  }
238
- const stat = fs_1.default.statSync(absolutePath);
239
- const compiled = {
247
+ }
248
+ return dependencies;
249
+ }
250
+ extractExports(sourceCode) {
251
+ const exports = [];
252
+ const exportRegex = /export\s+(?:default\s+)?(?:class|function|const|let|var|interface|type|enum)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g;
253
+ let match;
254
+ while ((match = exportRegex.exec(sourceCode)) !== null) {
255
+ exports.push(match[1]);
256
+ }
257
+ return exports;
258
+ }
259
+ compileModule(filePath, forceRecompile = false) {
260
+ const absolutePath = path_1.default.resolve(filePath);
261
+ const compileStart = Date.now();
262
+ try {
263
+ const sourceCode = fs_1.default.readFileSync(absolutePath, 'utf8');
264
+ const hash = this.createHash(sourceCode);
265
+ const cached = this.moduleCache.get(absolutePath);
266
+ // Check if we can use cached version
267
+ if (!forceRecompile && cached && cached.hash === hash) {
268
+ this.performanceMetrics.cacheHits++;
269
+ return cached;
270
+ }
271
+ this.performanceMetrics.cacheMisses++;
272
+ const dependencies = this.extractDependencies(sourceCode, absolutePath);
273
+ const exports = this.extractExports(sourceCode);
274
+ // Fast transpile without type checking for development
275
+ const result = ts.transpileModule(sourceCode, {
276
+ compilerOptions: this.tsCompilerOptions,
277
+ fileName: absolutePath,
278
+ reportDiagnostics: this.options.verbose
279
+ });
280
+ const moduleInfo = {
240
281
  code: result.outputText,
241
282
  map: result.sourceMapText,
283
+ hash,
284
+ timestamp: Date.now(),
242
285
  dependencies,
243
- lastModified: stat.mtimeMs,
244
- hash: currentHash
286
+ exports
245
287
  };
246
- this.moduleCache.set(absolutePath, compiled);
288
+ this.moduleCache.set(absolutePath, moduleInfo);
247
289
  if (this.options.verbose) {
248
- logger_manager_js_1.loggerManager.printLine(`Compiled ${path_1.default.relative(process.cwd(), filePath)} (${dependencies.size} dependencies)`, 'info');
290
+ const compileTime = Date.now() - compileStart;
291
+ logger_manager_js_1.loggerManager.printLine(`Compiled ${path_1.default.relative(process.cwd(), filePath)} (${compileTime}ms)`, 'info');
249
292
  }
250
- return compiled;
293
+ return moduleInfo;
251
294
  }
252
295
  catch (error) {
253
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Compilation error in ${path_1.default.relative(process.cwd(), filePath)}: ${error.message}`, 'error');
296
+ logger_manager_js_1.loggerManager.printLine(`Compilation error in ${filePath}: ${error.message}`, 'error');
254
297
  throw error;
255
298
  }
256
299
  }
257
- createTempFile(compiled, originalPath) {
258
- const tempDir = path_1.default.join(process.cwd(), '.neex-temp');
259
- if (!fs_1.default.existsSync(tempDir)) {
260
- fs_1.default.mkdirSync(tempDir, { recursive: true });
300
+ invalidateModuleCache(filePath) {
301
+ const absolutePath = path_1.default.resolve(filePath);
302
+ const toRemove = new Set();
303
+ // Remove the file itself
304
+ toRemove.add(absolutePath);
305
+ // Remove any modules that depend on this file (recursive)
306
+ const findDependents = (targetPath) => {
307
+ for (const [cachedPath, info] of this.moduleCache.entries()) {
308
+ if (info.dependencies.includes(targetPath) && !toRemove.has(cachedPath)) {
309
+ toRemove.add(cachedPath);
310
+ findDependents(cachedPath);
311
+ }
312
+ }
313
+ };
314
+ findDependents(absolutePath);
315
+ // Remove all affected modules
316
+ for (const pathToRemove of toRemove) {
317
+ this.moduleCache.delete(pathToRemove);
318
+ }
319
+ if (this.options.verbose && toRemove.size > 1) {
320
+ logger_manager_js_1.loggerManager.printLine(`Invalidated ${toRemove.size} modules`, 'info');
321
+ }
322
+ }
323
+ async findFreePort(startPort = 3000) {
324
+ return new Promise((resolve, reject) => {
325
+ const server = net_1.default.createServer();
326
+ server.listen(startPort, () => {
327
+ const port = server.address().port;
328
+ server.close(() => resolve(port));
329
+ });
330
+ server.on('error', () => {
331
+ this.findFreePort(startPort + 1).then(resolve).catch(reject);
332
+ });
333
+ });
334
+ }
335
+ createExecutableFile() {
336
+ const startCompile = Date.now();
337
+ // Always force recompile the main file
338
+ const mainModule = this.compileModule(this.options.file, true);
339
+ // Create a unique temp file
340
+ const timestamp = Date.now();
341
+ const random = Math.random().toString(36).substr(2, 9);
342
+ const tempFile = path_1.default.join(this.tempDir, `server-${timestamp}-${random}.js`);
343
+ let code = mainModule.code;
344
+ // Add hot reload middleware for Express apps
345
+ if (this.isExpressApp && this.options.hotReload) {
346
+ const hotReloadMiddleware = `
347
+ // Hot reload middleware injected by neex
348
+ const originalListen = require('http').Server.prototype.listen;
349
+ require('http').Server.prototype.listen = function(...args) {
350
+ const server = originalListen.apply(this, args);
351
+
352
+ // Setup WebSocket for hot reload
353
+ const WebSocket = require('ws');
354
+ const wss = new WebSocket.Server({ server, path: '/__neex_hot_reload' });
355
+
356
+ global.__neex_hot_reload = (type, data) => {
357
+ wss.clients.forEach(client => {
358
+ if (client.readyState === WebSocket.OPEN) {
359
+ client.send(JSON.stringify({ type, ...data }));
360
+ }
361
+ });
362
+ };
363
+
364
+ return server;
365
+ };
366
+ `;
367
+ code = hotReloadMiddleware + '\n' + code;
261
368
  }
262
- const baseName = path_1.default.basename(originalPath, path_1.default.extname(originalPath));
263
- const tempFile = path_1.default.join(tempDir, `${baseName}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}.js`);
264
- let code = compiled.code;
265
- // Handle source maps
266
- if (compiled.map && this.options.sourceMaps) {
369
+ // Add source map support
370
+ if (mainModule.map && this.options.sourceMaps) {
267
371
  const mapFile = tempFile + '.map';
268
- fs_1.default.writeFileSync(mapFile, compiled.map);
372
+ fs_1.default.writeFileSync(mapFile, mainModule.map);
269
373
  code += `\n//# sourceMappingURL=${path_1.default.basename(mapFile)}`;
270
374
  }
271
375
  fs_1.default.writeFileSync(tempFile, code);
272
- return tempFile;
273
- }
274
- cleanupTempFiles() {
275
- const tempDir = path_1.default.join(process.cwd(), '.neex-temp');
276
- if (fs_1.default.existsSync(tempDir)) {
376
+ // Clean up old temp file
377
+ if (this.currentTempFile && fs_1.default.existsSync(this.currentTempFile)) {
277
378
  try {
278
- const files = fs_1.default.readdirSync(tempDir);
279
- for (const file of files) {
280
- const filePath = path_1.default.join(tempDir, file);
281
- try {
282
- fs_1.default.unlinkSync(filePath);
283
- }
284
- catch (error) {
285
- // Ignore individual file errors
286
- }
379
+ fs_1.default.unlinkSync(this.currentTempFile);
380
+ const mapFile = this.currentTempFile + '.map';
381
+ if (fs_1.default.existsSync(mapFile)) {
382
+ fs_1.default.unlinkSync(mapFile);
287
383
  }
288
- fs_1.default.rmdirSync(tempDir);
289
384
  }
290
385
  catch (error) {
291
386
  // Ignore cleanup errors
292
387
  }
293
388
  }
294
- }
295
- invalidateCache(filePath) {
296
- const absolutePath = path_1.default.resolve(filePath);
297
- // Remove from cache
298
- const removed = this.moduleCache.delete(absolutePath);
299
- if (this.options.verbose && removed) {
300
- logger_manager_js_1.loggerManager.printLine(`Invalidated cache for ${path_1.default.relative(process.cwd(), filePath)}`, 'info');
301
- }
302
- // Remove dependent modules from cache
303
- const dependents = [];
304
- for (const [cachedPath, module] of this.moduleCache.entries()) {
305
- if (module.dependencies.has(absolutePath)) {
306
- this.moduleCache.delete(cachedPath);
307
- dependents.push(cachedPath);
308
- }
309
- }
310
- if (this.options.verbose && dependents.length > 0) {
311
- logger_manager_js_1.loggerManager.printLine(`Invalidated ${dependents.length} dependent modules`, 'info');
312
- }
389
+ this.currentTempFile = tempFile;
390
+ this.lastCompileTime = Date.now() - startCompile;
391
+ return tempFile;
313
392
  }
314
393
  async getExecuteCommand() {
315
394
  if (this.options.execCommand) {
316
395
  const parts = this.options.execCommand.split(' ');
317
- return { command: parts[0], args: [...parts.slice(1)] };
396
+ return { command: parts[0], args: parts.slice(1) };
318
397
  }
319
- // Always recompile the main file to ensure fresh execution
320
- this.invalidateCache(this.options.file);
321
- // Compile TypeScript file
322
- const compiled = this.compileTypeScript(this.options.file);
323
- const tempFile = this.createTempFile(compiled, this.options.file);
324
- const args = [...this.options.nodeArgs, tempFile];
325
- if (this.options.inspect) {
398
+ const executableFile = this.createExecutableFile();
399
+ const args = [...this.options.nodeArgs, executableFile];
400
+ // Add Node.js flags
401
+ if (this.options.inspect)
326
402
  args.unshift('--inspect');
327
- }
328
- if (this.options.inspectBrk) {
403
+ if (this.options.inspectBrk)
329
404
  args.unshift('--inspect-brk');
330
- }
331
- // Enable source map support
332
- if (this.options.sourceMaps) {
405
+ if (this.options.sourceMaps)
333
406
  args.unshift('--enable-source-maps');
334
- }
407
+ // Memory optimization
408
+ args.unshift('--max-old-space-size=4096');
409
+ args.unshift('--optimize-for-size');
335
410
  return { command: 'node', args };
336
411
  }
337
412
  clearConsole() {
338
413
  if (this.options.clearConsole && process.stdout.isTTY) {
339
- process.stdout.write('\x1b[2J\x1b[0f');
414
+ process.stdout.write('\x1Bc');
340
415
  }
341
416
  }
342
417
  async startProcess() {
343
418
  var _a, _b;
344
- if (this.process) {
419
+ if (this.process)
345
420
  return;
346
- }
421
+ const restartStart = Date.now();
347
422
  this.loadEnvFile();
348
423
  try {
349
424
  const { command, args } = await this.getExecuteCommand();
350
- if (this.options.verbose) {
351
- logger_manager_js_1.loggerManager.printLine(`Executing: ${command} ${args.join(' ')}`, 'info');
425
+ // Find free port for Express apps
426
+ if (this.isExpressApp && !process.env.PORT) {
427
+ const port = await this.findFreePort(this.options.port || 3000);
428
+ process.env.PORT = port.toString();
352
429
  }
353
430
  this.process = (0, child_process_1.spawn)(command, args, {
354
431
  stdio: ['ignore', 'pipe', 'pipe'],
355
- shell: false,
356
432
  env: {
357
433
  ...process.env,
358
434
  NODE_ENV: process.env.NODE_ENV || 'development',
359
435
  FORCE_COLOR: this.options.color ? '1' : '0',
360
- TS_NODE_DEV: '1',
361
- NEEX_DEV: '1'
436
+ NEEX_DEV_MODE: '1',
437
+ NEEX_HOT_RELOAD: this.options.hotReload ? '1' : '0',
362
438
  },
363
- detached: true
439
+ detached: false
364
440
  });
365
441
  this.startTime = new Date();
366
442
  this.restartCount++;
443
+ // Update performance metrics
444
+ if (this.restartCount > 1) {
445
+ const restartTime = Date.now() - restartStart;
446
+ this.performanceMetrics.averageRestartTime =
447
+ (this.performanceMetrics.averageRestartTime * (this.restartCount - 2) + restartTime) / (this.restartCount - 1);
448
+ this.performanceMetrics.totalRestarts++;
449
+ }
367
450
  if (!this.options.quiet) {
368
451
  const timestamp = new Date().toLocaleTimeString();
369
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.play)} Started ${chalk_1.default.cyan(path_1.default.relative(process.cwd(), this.options.file))} ${chalk_1.default.dim(`(#${this.restartCount} - ${timestamp})`)}`, 'info');
452
+ const fileRelative = path_1.default.relative(process.cwd(), this.options.file);
453
+ const port = process.env.PORT ? `:${process.env.PORT}` : '';
454
+ const url = this.isExpressApp ? `http://localhost${port}` : '';
455
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green('▶')} ${chalk_1.default.cyan(fileRelative)} ${chalk_1.default.dim(`#${this.restartCount} ${timestamp}`)}`, 'info');
456
+ if (url) {
457
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue('🌐')} ${chalk_1.default.underline(url)}`, 'info');
458
+ }
459
+ if (this.options.verbose) {
460
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('Compile time:')} ${this.lastCompileTime}ms`, 'info');
461
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('Cache hits:')} ${this.performanceMetrics.cacheHits}`, 'info');
462
+ }
370
463
  }
464
+ this.serverInfo = {
465
+ port: parseInt(process.env.PORT || '3000'),
466
+ host: this.options.host || 'localhost',
467
+ pid: this.process.pid,
468
+ startTime: Date.now(),
469
+ restarts: this.restartCount
470
+ };
471
+ // Handle stdout/stderr with better formatting
371
472
  (_a = this.process.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
473
+ const output = data.toString();
372
474
  if (!this.options.quiet) {
373
- process.stdout.write(data);
475
+ process.stdout.write(output);
374
476
  }
375
477
  });
376
478
  (_b = this.process.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
479
+ const output = data.toString();
377
480
  if (!this.options.quiet) {
378
- process.stderr.write(data);
481
+ process.stderr.write(output);
379
482
  }
380
483
  });
381
484
  this.process.on('error', (error) => {
382
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Process error: ${error.message}`, 'error');
485
+ logger_manager_js_1.loggerManager.printLine(`Process error: ${error.message}`, 'error');
486
+ if (this.options.hotReload && global.__neex_hot_reload) {
487
+ global.__neex_hot_reload('error', { message: error.message });
488
+ }
383
489
  });
384
490
  this.process.on('exit', (code, signal) => {
385
491
  if (this.process) {
386
492
  this.process = null;
387
- if (!this.isRestarting) {
388
- if (code !== 0) {
389
- const duration = this.startTime ? Date.now() - this.startTime.getTime() : 0;
390
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Process exited with code ${code} after ${duration}ms`, 'error');
391
- }
493
+ this.serverInfo = null;
494
+ if (!this.isRestarting && code !== 0) {
495
+ const duration = this.startTime ? Date.now() - this.startTime.getTime() : 0;
496
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red('✖')} Process exited with code ${code} (${duration}ms)`, 'error');
392
497
  }
393
498
  }
394
499
  });
395
500
  }
396
501
  catch (error) {
397
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Failed to start process: ${error.message}`, 'error');
502
+ logger_manager_js_1.loggerManager.printLine(`Failed to start: ${error.message}`, 'error');
398
503
  throw error;
399
504
  }
400
505
  }
401
506
  async stopProcess() {
402
- if (!this.process) {
507
+ if (!this.process)
403
508
  return;
404
- }
405
509
  return new Promise((resolve) => {
406
- if (!this.process) {
407
- resolve();
408
- return;
409
- }
410
510
  const proc = this.process;
411
511
  this.process = null;
512
+ this.serverInfo = null;
412
513
  const cleanup = () => {
413
- if (!this.options.quiet) {
414
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.square)} Process stopped`, 'info');
415
- }
416
- this.cleanupTempFiles();
417
514
  resolve();
418
515
  };
419
516
  proc.on('exit', cleanup);
420
517
  proc.on('error', cleanup);
421
518
  try {
422
- if (proc.pid) {
423
- // Kill process group
424
- process.kill(-proc.pid, 'SIGTERM');
425
- // Fallback after timeout
426
- setTimeout(() => {
427
- if (proc.pid && !proc.killed) {
428
- try {
429
- process.kill(-proc.pid, 'SIGKILL');
430
- }
431
- catch (e) {
432
- // Ignore
433
- }
434
- }
435
- }, 2000);
436
- }
519
+ proc.kill('SIGTERM');
520
+ // Force kill after timeout
521
+ setTimeout(() => {
522
+ if (!proc.killed) {
523
+ proc.kill('SIGKILL');
524
+ }
525
+ cleanup();
526
+ }, 2000);
437
527
  }
438
528
  catch (error) {
439
- // Process might already be dead
440
529
  cleanup();
441
530
  }
442
531
  });
443
532
  }
444
533
  async restart() {
445
- if (this.isRestarting) {
534
+ if (this.isRestarting)
446
535
  return;
447
- }
448
536
  this.isRestarting = true;
449
- if (this.options.clearConsole) {
450
- this.clearConsole();
451
- }
537
+ // Clear console for better UX
538
+ this.clearConsole();
452
539
  if (!this.options.quiet) {
453
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.arrowRight)} Restarting due to changes...`, 'info');
540
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow('⟳')} Restarting server...`, 'info');
541
+ }
542
+ // Notify hot reload clients
543
+ if (this.options.hotReload && global.__neex_hot_reload) {
544
+ global.__neex_hot_reload('reload', {});
454
545
  }
546
+ // Stop current process
455
547
  await this.stopProcess();
456
- // Small delay to ensure cleanup
457
- await new Promise(resolve => setTimeout(resolve, 50));
548
+ // Start new process
458
549
  await this.startProcess();
459
550
  this.isRestarting = false;
460
551
  }
461
552
  setupWatcher() {
462
553
  const watchPatterns = this.options.watch;
554
+ // Optimized ignore patterns for Express projects
463
555
  const ignored = [
464
- '**/node_modules/**',
465
- '**/.git/**',
466
- '**/dist/**',
467
- '**/build/**',
468
- '**/.neex-temp/**',
556
+ 'node_modules/**',
557
+ '.git/**',
558
+ 'dist/**',
559
+ 'build/**',
560
+ '.neex/**',
561
+ 'coverage/**',
562
+ 'logs/**',
469
563
  '**/*.log',
470
564
  '**/*.d.ts',
471
565
  '**/*.map',
472
- ...this.options.ignore.map(pattern => `**/${pattern}/**`)
566
+ '**/*.tsbuildinfo',
567
+ '**/package-lock.json',
568
+ '**/yarn.lock',
569
+ ...this.options.ignore
473
570
  ];
474
571
  this.watcher = (0, chokidar_1.watch)(watchPatterns, {
475
572
  ignored,
@@ -478,75 +575,94 @@ class DevManager {
478
575
  usePolling: false,
479
576
  atomic: 100,
480
577
  awaitWriteFinish: {
481
- stabilityThreshold: 50,
482
- pollInterval: 25
578
+ stabilityThreshold: 150,
579
+ pollInterval: 100
483
580
  }
484
581
  });
485
582
  this.watcher.on('change', (filePath) => {
486
- this.invalidateCache(filePath);
583
+ this.invalidateModuleCache(filePath);
487
584
  if (this.options.verbose) {
488
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} File changed: ${path_1.default.relative(process.cwd(), filePath)}`, 'info');
585
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue('●')} ${path_1.default.relative(process.cwd(), filePath)}`, 'info');
489
586
  }
490
587
  this.debouncedRestart();
491
588
  });
492
589
  this.watcher.on('add', (filePath) => {
493
590
  if (this.options.verbose) {
494
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green('+')} File added: ${path_1.default.relative(process.cwd(), filePath)}`, 'info');
591
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green('+')} ${path_1.default.relative(process.cwd(), filePath)}`, 'info');
495
592
  }
496
593
  this.debouncedRestart();
497
594
  });
498
595
  this.watcher.on('unlink', (filePath) => {
499
- this.invalidateCache(filePath);
596
+ this.invalidateModuleCache(filePath);
500
597
  if (this.options.verbose) {
501
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red('-')} File removed: ${path_1.default.relative(process.cwd(), filePath)}`, 'info');
598
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red('-')} ${path_1.default.relative(process.cwd(), filePath)}`, 'info');
502
599
  }
503
600
  this.debouncedRestart();
504
601
  });
505
602
  this.watcher.on('error', (error) => {
506
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Watcher error: ${error.message}`, 'error');
603
+ logger_manager_js_1.loggerManager.printLine(`Watcher error: ${error.message}`, 'error');
507
604
  });
508
- if (this.options.verbose) {
509
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim(figures_1.default.info)} Watching: ${watchPatterns.join(', ')}`, 'info');
510
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim(figures_1.default.info)} Ignoring: ${ignored.join(', ')}`, 'info');
511
- }
512
605
  }
513
606
  async start() {
514
- // Check if target file exists
515
607
  if (!fs_1.default.existsSync(this.options.file)) {
516
608
  throw new Error(`Target file not found: ${this.options.file}`);
517
609
  }
518
- // Validate TypeScript file
519
610
  const ext = path_1.default.extname(this.options.file);
520
- if (!['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
611
+ if (!this.options.extensions.map(e => `.${e}`).includes(ext)) {
521
612
  throw new Error(`Unsupported file extension: ${ext}`);
522
613
  }
523
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Starting TypeScript development server...`, 'info');
524
- // Show configuration in verbose mode
525
- if (this.options.verbose) {
526
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('Target file:')} ${this.options.file}`, 'info');
527
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('Watch patterns:')} ${this.options.watch.join(', ')}`, 'info');
528
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('Restart delay:')} ${this.options.delay}ms`, 'info');
529
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('Transpile only:')} ${this.options.transpileOnly}`, 'info');
530
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('Source maps:')} ${this.options.sourceMaps}`, 'info');
531
- }
532
614
  // Clear any existing cache
533
615
  this.moduleCache.clear();
616
+ this.setupTempDir();
617
+ if (!this.options.quiet) {
618
+ const appType = this.isExpressApp ? 'Express' : 'Node.js';
619
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue('⚡')} Starting ${appType} development server...`, 'info');
620
+ }
534
621
  this.setupWatcher();
535
622
  await this.startProcess();
536
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} TypeScript development server started. Watching for changes...`, 'info');
623
+ if (!this.options.quiet) {
624
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green('✓')} Watching for changes...`, 'info');
625
+ if (this.options.hotReload) {
626
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue('🔥')} Hot reload enabled`, 'info');
627
+ }
628
+ }
537
629
  }
538
630
  async stop() {
539
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Stopping development server...`, 'info');
631
+ if (!this.options.quiet) {
632
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow('⏹')} Stopping development server...`, 'info');
633
+ }
540
634
  if (this.watcher) {
541
635
  await this.watcher.close();
542
636
  this.watcher = null;
543
637
  }
544
638
  await this.stopProcess();
545
- // Clear cache on stop
639
+ // Cleanup temp files
640
+ if (fs_1.default.existsSync(this.tempDir)) {
641
+ try {
642
+ fs_1.default.rmSync(this.tempDir, { recursive: true, force: true });
643
+ }
644
+ catch (error) {
645
+ // Ignore cleanup errors
646
+ }
647
+ }
546
648
  this.moduleCache.clear();
547
- if (this.restartCount > 0) {
548
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Development server stopped after ${this.restartCount} restart(s)`, 'info');
649
+ if (this.options.verbose) {
650
+ logger_manager_js_1.loggerManager.printLine('Development server stopped', 'info');
549
651
  }
550
652
  }
653
+ getServerInfo() {
654
+ return this.serverInfo;
655
+ }
656
+ getPerformanceMetrics() {
657
+ return {
658
+ ...this.performanceMetrics,
659
+ modulesCached: this.moduleCache.size,
660
+ lastCompileTime: this.lastCompileTime
661
+ };
662
+ }
663
+ async forceRestart() {
664
+ this.moduleCache.clear();
665
+ await this.restart();
666
+ }
551
667
  }
552
668
  exports.DevManager = DevManager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neex",
3
- "version": "0.6.85",
3
+ "version": "0.6.88",
4
4
  "description": "The Modern Build System for Polyrepo-in-Monorepo Architecture",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",