neex 0.6.87 → 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,43 +7,56 @@ 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"));
12
+ const figures_1 = __importDefault(require("figures"));
10
13
  function addDevCommands(program) {
11
14
  let devManager = null;
12
- // Ultra-fast dev command optimized for speed
15
+ let isShuttingDown = false;
16
+ // Ultra-fast Express dev command optimized for speed
13
17
  program
14
18
  .command('dev [file]')
15
- .description('Start ultra-fast TypeScript development server (like tsx)')
16
- .option('-w, --watch <patterns>', 'Watch patterns (comma-separated)', 'src/**/*')
17
- .option('-i, --ignore <patterns>', 'Ignore patterns (comma-separated)', 'node_modules,dist,build,.git')
18
- .option('-e, --ext <extensions>', 'File extensions to watch', 'ts,tsx,js,jsx')
19
- .option('-d, --delay <ms>', 'Restart delay in milliseconds', parseInt, 100)
20
- .option('--fast', 'Ultra-fast mode (50ms delay)')
19
+ .description('Start TypeScript development server with hot reloading (default: src/index.ts)')
20
+ .option('-w, --watch <patterns>', 'Watch additional patterns (comma-separated)', 'src/**/*')
21
+ .option('-i, --ignore <patterns>', 'Ignore patterns (comma-separated)', 'node_modules,dist,build,.git,coverage')
22
+ .option('-e, --ext <extensions>', 'File extensions to watch (comma-separated)', 'ts,tsx,js,jsx,json')
23
+ .option('-d, --delay <ms>', 'Delay before restart (ms)', parseInt, 800)
24
+ .option('-c, --no-color', 'Disable colored output')
25
+ .option('-q, --quiet', 'Reduce output verbosity')
26
+ .option('-v, --verbose', 'Verbose output')
21
27
  .option('--no-clear', 'Don\'t clear console on restart')
22
- .option('--no-color', 'Disable colored output')
23
- .option('-q, --quiet', 'Minimal output')
24
- .option('-v, --verbose', 'Verbose logging')
25
28
  .option('--inspect', 'Enable Node.js inspector')
26
- .option('--inspect-brk', 'Enable Node.js inspector with breakpoint')
27
- .option('--env <file>', 'Environment file to load', '.env')
28
- .option('--exec <command>', 'Custom command to execute')
29
- .option('--tsconfig <path>', 'TypeScript config file path')
30
- .option('--no-source-maps', 'Disable source map generation')
31
- .option('--transpile-only', 'Skip type checking (faster)')
32
- .option('--node-args <args>', 'Node.js arguments (comma-separated)', '')
29
+ .option('--inspect-brk', 'Enable Node.js inspector with break')
30
+ .option('--env <file>', 'Load environment variables from file', '.env')
31
+ .option('--exec <command>', 'Command to execute instead of built-in TypeScript compilation')
32
+ .option('--tsconfig <path>', 'Path to TypeScript configuration file')
33
+ .option('--no-source-maps', 'Disable source maps')
34
+ .option('--transpile-only', 'Skip type checking for faster compilation')
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')
33
40
  .action(async (file, options) => {
34
41
  try {
35
42
  const targetFile = file || 'src/index.ts';
36
- const delay = options.fast ? 50 : options.delay;
37
43
  if (!options.quiet) {
38
- console.log(''); // Empty line for better visual separation
39
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue('⚡')} ${chalk_1.default.bold('neex dev')} ${chalk_1.default.dim('v1.0.0')} - ${chalk_1.default.cyan(targetFile)}`, 'info');
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');
45
+ if (options.verbose) {
46
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('Features:')}`, 'info');
47
+ logger_manager_js_1.loggerManager.printLine(` ${chalk_1.default.green('✓')} Built-in TypeScript compilation`, 'info');
48
+ logger_manager_js_1.loggerManager.printLine(` ${chalk_1.default.green('✓')} Hot reloading with intelligent caching`, 'info');
49
+ logger_manager_js_1.loggerManager.printLine(` ${chalk_1.default.green('✓')} Source map support`, 'info');
50
+ logger_manager_js_1.loggerManager.printLine(` ${chalk_1.default.green('✓')} Fast transpilation mode`, 'info');
51
+ logger_manager_js_1.loggerManager.printLine(` ${chalk_1.default.green('✓')} Dependency tracking`, 'info');
52
+ }
40
53
  }
41
54
  devManager = new dev_manager_js_1.DevManager({
42
55
  file: targetFile,
43
56
  watch: options.watch.split(',').map((p) => p.trim()),
44
57
  ignore: options.ignore.split(',').map((p) => p.trim()),
45
58
  extensions: options.ext.split(',').map((e) => e.trim()),
46
- delay: delay,
59
+ delay: options.delay,
47
60
  color: options.color,
48
61
  quiet: options.quiet,
49
62
  verbose: options.verbose,
@@ -55,125 +68,64 @@ function addDevCommands(program) {
55
68
  tsConfig: options.tsconfig,
56
69
  sourceMaps: options.sourceMaps,
57
70
  transpileOnly: options.transpileOnly,
58
- 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
59
76
  });
60
77
  await devManager.start();
61
78
  }
62
79
  catch (error) {
63
80
  if (error instanceof Error) {
64
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red('✖')} ${error.message}`, 'error');
81
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Development server error: ${error.message}`, 'error');
65
82
  }
66
83
  else {
67
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red('✖')} Unknown error occurred`, 'error');
84
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} An unknown development server error occurred`, 'error');
68
85
  }
69
86
  process.exit(1);
70
87
  }
71
88
  });
72
- // Clean cache command
89
+ // Additional helper commands
73
90
  program
74
91
  .command('dev:clean')
75
- .description('Clean development cache and temp files')
92
+ .description('Clean development server cache and temporary files')
76
93
  .action(() => {
77
- const path = require('path');
78
- const fs = require('fs');
79
- const tempDir = path.join(process.cwd(), '.neex-temp');
80
- const nodeModulesCache = path.join(process.cwd(), 'node_modules/.cache');
81
- let cleaned = false;
82
- if (fs.existsSync(tempDir)) {
83
- fs.rmSync(tempDir, { recursive: true, force: true });
84
- cleaned = true;
85
- }
86
- if (fs.existsSync(nodeModulesCache)) {
87
- try {
88
- fs.rmSync(nodeModulesCache, { recursive: true, force: true });
89
- cleaned = true;
90
- }
91
- catch (error) {
92
- // Ignore cache cleanup errors
93
- }
94
- }
95
- if (cleaned) {
96
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green('✓')} Cache cleaned successfully`, 'info');
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 });
97
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Cleaned development cache`, 'info');
97
98
  }
98
99
  else {
99
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue('ℹ')} No cache to clean`, 'info');
100
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.info)} No cache to clean`, 'info');
100
101
  }
101
102
  });
102
- // TypeScript config check
103
103
  program
104
104
  .command('dev:check')
105
- .description('Check TypeScript configuration')
106
- .option('--tsconfig <path>', 'TypeScript config file path')
105
+ .description('Check TypeScript configuration and dependencies')
106
+ .option('--tsconfig <path>', 'Path to TypeScript configuration file')
107
107
  .action((options) => {
108
- const path = require('path');
109
- const fs = require('fs');
110
108
  const configPath = options.tsconfig || 'tsconfig.json';
111
- if (!fs.existsSync(configPath)) {
112
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red('✖')} TypeScript config not found: ${configPath}`, 'error');
109
+ if (!fs_1.default.existsSync(configPath)) {
110
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} TypeScript config not found: ${configPath}`, 'error');
113
111
  process.exit(1);
114
112
  }
115
113
  try {
116
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
117
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green('✓')} TypeScript config is valid`, 'info');
118
- if (config.compilerOptions && !options.quiet) {
119
- const opts = config.compilerOptions;
120
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('Configuration:')}`, 'info');
121
- logger_manager_js_1.loggerManager.printLine(` Target: ${opts.target || 'ES5'}`, 'info');
122
- logger_manager_js_1.loggerManager.printLine(` Module: ${opts.module || 'CommonJS'}`, 'info');
123
- logger_manager_js_1.loggerManager.printLine(` Strict: ${opts.strict || false}`, 'info');
124
- logger_manager_js_1.loggerManager.printLine(` Source Maps: ${opts.sourceMap || false}`, 'info');
125
- logger_manager_js_1.loggerManager.printLine(` Skip Lib Check: ${opts.skipLibCheck || false}`, 'info');
114
+ const config = JSON.parse(fs_1.default.readFileSync(configPath, 'utf8'));
115
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} TypeScript config is valid`, 'info');
116
+ if (config.compilerOptions) {
117
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('Compiler Options:')}`, 'info');
118
+ logger_manager_js_1.loggerManager.printLine(` Target: ${config.compilerOptions.target || 'default'}`, 'info');
119
+ logger_manager_js_1.loggerManager.printLine(` Module: ${config.compilerOptions.module || 'default'}`, 'info');
120
+ logger_manager_js_1.loggerManager.printLine(` Strict: ${config.compilerOptions.strict || 'false'}`, 'info');
121
+ logger_manager_js_1.loggerManager.printLine(` Source Maps: ${config.compilerOptions.sourceMap || 'false'}`, 'info');
126
122
  }
127
123
  }
128
124
  catch (error) {
129
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red('✖')} Invalid TypeScript config: ${error.message}`, 'error');
125
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Invalid TypeScript config: ${error.message}`, 'error');
130
126
  process.exit(1);
131
127
  }
132
128
  });
133
- // Performance info command
134
- program
135
- .command('dev:info')
136
- .description('Show development server information')
137
- .action(() => {
138
- const path = require('path');
139
- const fs = require('fs');
140
- const os = require('os');
141
- console.log('');
142
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue('⚡')} ${chalk_1.default.bold('neex dev')} - Development Server Info`, 'info');
143
- console.log('');
144
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('System:')}`, 'info');
145
- logger_manager_js_1.loggerManager.printLine(` Platform: ${os.platform()} ${os.arch()}`, 'info');
146
- logger_manager_js_1.loggerManager.printLine(` Node.js: ${process.version}`, 'info');
147
- logger_manager_js_1.loggerManager.printLine(` Memory: ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB used`, 'info');
148
- console.log('');
149
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('Features:')}`, 'info');
150
- logger_manager_js_1.loggerManager.printLine(` ${chalk_1.default.green('✓')} Ultra-fast TypeScript compilation`, 'info');
151
- logger_manager_js_1.loggerManager.printLine(` ${chalk_1.default.green('✓')} Intelligent module caching`, 'info');
152
- logger_manager_js_1.loggerManager.printLine(` ${chalk_1.default.green('✓')} Hot reload with dependency tracking`, 'info');
153
- logger_manager_js_1.loggerManager.printLine(` ${chalk_1.default.green('✓')} Source map support`, 'info');
154
- logger_manager_js_1.loggerManager.printLine(` ${chalk_1.default.green('✓')} Memory-optimized processing`, 'info');
155
- const tsConfigExists = fs.existsSync('tsconfig.json');
156
- const packageJsonExists = fs.existsSync('package.json');
157
- console.log('');
158
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.dim('Project:')}`, 'info');
159
- logger_manager_js_1.loggerManager.printLine(` TypeScript Config: ${tsConfigExists ? chalk_1.default.green('✓') : chalk_1.default.red('✖')}`, 'info');
160
- logger_manager_js_1.loggerManager.printLine(` Package.json: ${packageJsonExists ? chalk_1.default.green('✓') : chalk_1.default.red('✖')}`, 'info');
161
- if (packageJsonExists) {
162
- try {
163
- const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
164
- if (pkg.name) {
165
- logger_manager_js_1.loggerManager.printLine(` Name: ${pkg.name}`, 'info');
166
- }
167
- if (pkg.version) {
168
- logger_manager_js_1.loggerManager.printLine(` Version: ${pkg.version}`, 'info');
169
- }
170
- }
171
- catch (error) {
172
- // Ignore package.json parsing errors
173
- }
174
- }
175
- console.log('');
176
- });
177
129
  // Cleanup function
178
130
  const cleanupDev = () => {
179
131
  if (devManager) {
@@ -181,27 +133,20 @@ function addDevCommands(program) {
181
133
  devManager = null;
182
134
  }
183
135
  };
184
- // Enhanced signal handling
185
- const handleSignal = (signal) => {
136
+ // Handle process termination
137
+ process.on('SIGINT', () => {
186
138
  if (devManager) {
187
- console.log(''); // New line for better formatting
188
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow('⏹')} Received ${signal}, shutting down...`, 'info');
139
+ logger_manager_js_1.loggerManager.printLine(`\n${chalk_1.default.yellow(figures_1.default.warning)} Received SIGINT, shutting down gracefully...`, 'info');
189
140
  cleanupDev();
190
141
  process.exit(0);
191
142
  }
192
- };
193
- process.on('SIGINT', () => handleSignal('SIGINT'));
194
- process.on('SIGTERM', () => handleSignal('SIGTERM'));
195
- // Handle uncaught exceptions
196
- process.on('uncaughtException', (error) => {
197
- console.error(chalk_1.default.red('Uncaught Exception:'), error);
198
- cleanupDev();
199
- process.exit(1);
200
143
  });
201
- process.on('unhandledRejection', (reason, promise) => {
202
- console.error(chalk_1.default.red('Unhandled Rejection at:'), promise, 'reason:', reason);
203
- cleanupDev();
204
- process.exit(1);
144
+ process.on('SIGTERM', () => {
145
+ if (devManager) {
146
+ logger_manager_js_1.loggerManager.printLine(`\n${chalk_1.default.yellow(figures_1.default.warning)} Received SIGTERM, shutting down gracefully...`, 'info');
147
+ cleanupDev();
148
+ process.exit(0);
149
+ }
205
150
  });
206
151
  return { cleanupDev };
207
152
  }
@@ -27,7 +27,7 @@ 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 - Ultra-fast TypeScript development server like tsx
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");
@@ -37,6 +37,7 @@ const fs_1 = __importDefault(require("fs"));
37
37
  const lodash_1 = require("lodash");
38
38
  const ts = __importStar(require("typescript"));
39
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;
@@ -46,11 +47,23 @@ class DevManager {
46
47
  this.startTime = null;
47
48
  this.moduleCache = new Map();
48
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.tempDir = path_1.default.join(process.cwd(), '.neex-temp');
61
+ this.tempDir = path_1.default.join(process.cwd(), '.neex');
51
62
  this.debouncedRestart = (0, lodash_1.debounce)(this.restart.bind(this), Math.max(options.delay, 100));
52
63
  this.tsCompilerOptions = this.loadTsConfig();
53
64
  this.setupTempDir();
65
+ this.detectExpressApp();
66
+ this.setupHotReloadClient();
54
67
  }
55
68
  setupTempDir() {
56
69
  if (fs_1.default.existsSync(this.tempDir)) {
@@ -62,6 +75,64 @@ class DevManager {
62
75
  }
63
76
  }
64
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
+ `;
65
136
  }
66
137
  loadTsConfig() {
67
138
  const configPath = this.options.tsConfig || 'tsconfig.json';
@@ -81,7 +152,9 @@ class DevManager {
81
152
  inlineSources: false,
82
153
  removeComments: false,
83
154
  preserveConstEnums: false,
84
- isolatedModules: true, // For faster compilation
155
+ isolatedModules: true,
156
+ incremental: true,
157
+ tsBuildInfoFile: path_1.default.join(this.tempDir, 'tsconfig.tsbuildinfo'),
85
158
  };
86
159
  if (fs_1.default.existsSync(configPath)) {
87
160
  try {
@@ -100,29 +173,44 @@ class DevManager {
100
173
  return defaultOptions;
101
174
  }
102
175
  loadEnvFile() {
103
- if (this.options.envFile && fs_1.default.existsSync(this.options.envFile)) {
104
- try {
105
- const envContent = fs_1.default.readFileSync(this.options.envFile, 'utf8');
106
- const lines = envContent.split('\n');
107
- for (const line of lines) {
108
- const trimmed = line.trim();
109
- if (trimmed && !trimmed.startsWith('#')) {
110
- const [key, ...values] = trimmed.split('=');
111
- if (key && values.length > 0) {
112
- process.env[key.trim()] = values.join('=').trim().replace(/^["']|["']$/g, '');
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
+ }
113
197
  }
114
198
  }
199
+ if (this.options.verbose) {
200
+ logger_manager_js_1.loggerManager.printLine(`Loaded environment from ${envFile}`, 'info');
201
+ }
202
+ break;
115
203
  }
116
- }
117
- catch (error) {
118
- if (this.options.verbose) {
119
- logger_manager_js_1.loggerManager.printLine(`Failed to load ${this.options.envFile}: ${error.message}`, 'warn');
204
+ catch (error) {
205
+ if (this.options.verbose) {
206
+ logger_manager_js_1.loggerManager.printLine(`Failed to load ${envFile}: ${error.message}`, 'warn');
207
+ }
120
208
  }
121
209
  }
122
210
  }
123
211
  }
124
212
  createHash(content) {
125
- return crypto_1.default.createHash('md5').update(content).digest('hex');
213
+ return crypto_1.default.createHash('sha256').update(content).digest('hex').substring(0, 16);
126
214
  }
127
215
  extractDependencies(sourceCode, filePath) {
128
216
  const dependencies = [];
@@ -134,7 +222,7 @@ class DevManager {
134
222
  let resolvedPath = path_1.default.resolve(path_1.default.dirname(filePath), importPath);
135
223
  // Try to resolve with extensions
136
224
  if (!fs_1.default.existsSync(resolvedPath)) {
137
- for (const ext of ['.ts', '.tsx', '.js', '.jsx']) {
225
+ for (const ext of this.options.extensions.map(e => `.${e}`)) {
138
226
  const withExt = resolvedPath + ext;
139
227
  if (fs_1.default.existsSync(withExt)) {
140
228
  resolvedPath = withExt;
@@ -144,7 +232,7 @@ class DevManager {
144
232
  }
145
233
  // Try index files
146
234
  if (!fs_1.default.existsSync(resolvedPath)) {
147
- for (const ext of ['.ts', '.tsx', '.js', '.jsx']) {
235
+ for (const ext of this.options.extensions.map(e => `.${e}`)) {
148
236
  const indexPath = path_1.default.join(resolvedPath, 'index' + ext);
149
237
  if (fs_1.default.existsSync(indexPath)) {
150
238
  resolvedPath = indexPath;
@@ -159,67 +247,125 @@ class DevManager {
159
247
  }
160
248
  return dependencies;
161
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
+ }
162
259
  compileModule(filePath, forceRecompile = false) {
163
260
  const absolutePath = path_1.default.resolve(filePath);
261
+ const compileStart = Date.now();
164
262
  try {
165
263
  const sourceCode = fs_1.default.readFileSync(absolutePath, 'utf8');
166
264
  const hash = this.createHash(sourceCode);
167
265
  const cached = this.moduleCache.get(absolutePath);
168
266
  // Check if we can use cached version
169
267
  if (!forceRecompile && cached && cached.hash === hash) {
268
+ this.performanceMetrics.cacheHits++;
170
269
  return cached;
171
270
  }
271
+ this.performanceMetrics.cacheMisses++;
172
272
  const dependencies = this.extractDependencies(sourceCode, absolutePath);
273
+ const exports = this.extractExports(sourceCode);
173
274
  // Fast transpile without type checking for development
174
275
  const result = ts.transpileModule(sourceCode, {
175
276
  compilerOptions: this.tsCompilerOptions,
176
277
  fileName: absolutePath,
177
- reportDiagnostics: false // Skip diagnostics for speed
278
+ reportDiagnostics: this.options.verbose
178
279
  });
179
280
  const moduleInfo = {
180
281
  code: result.outputText,
181
282
  map: result.sourceMapText,
182
283
  hash,
183
284
  timestamp: Date.now(),
184
- dependencies
285
+ dependencies,
286
+ exports
185
287
  };
186
288
  this.moduleCache.set(absolutePath, moduleInfo);
187
289
  if (this.options.verbose) {
188
- logger_manager_js_1.loggerManager.printLine(`Compiled ${path_1.default.relative(process.cwd(), filePath)}`, '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');
189
292
  }
190
293
  return moduleInfo;
191
294
  }
192
295
  catch (error) {
193
- logger_manager_js_1.loggerManager.printLine(`Compilation error: ${error.message}`, 'error');
296
+ logger_manager_js_1.loggerManager.printLine(`Compilation error in ${filePath}: ${error.message}`, 'error');
194
297
  throw error;
195
298
  }
196
299
  }
197
300
  invalidateModuleCache(filePath) {
198
301
  const absolutePath = path_1.default.resolve(filePath);
302
+ const toRemove = new Set();
199
303
  // Remove the file itself
200
- this.moduleCache.delete(absolutePath);
201
- // Remove any modules that depend on this file
202
- const toRemove = [];
203
- for (const [cachedPath, info] of this.moduleCache.entries()) {
204
- if (info.dependencies.includes(absolutePath)) {
205
- toRemove.push(cachedPath);
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
+ }
206
312
  }
207
- }
313
+ };
314
+ findDependents(absolutePath);
315
+ // Remove all affected modules
208
316
  for (const pathToRemove of toRemove) {
209
317
  this.moduleCache.delete(pathToRemove);
210
318
  }
211
- if (this.options.verbose && toRemove.length > 0) {
212
- logger_manager_js_1.loggerManager.printLine(`Invalidated ${toRemove.length + 1} modules`, 'info');
319
+ if (this.options.verbose && toRemove.size > 1) {
320
+ logger_manager_js_1.loggerManager.printLine(`Invalidated ${toRemove.size} modules`, 'info');
213
321
  }
214
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
+ }
215
335
  createExecutableFile() {
336
+ const startCompile = Date.now();
216
337
  // Always force recompile the main file
217
338
  const mainModule = this.compileModule(this.options.file, true);
218
339
  // Create a unique temp file
219
340
  const timestamp = Date.now();
220
341
  const random = Math.random().toString(36).substr(2, 9);
221
- const tempFile = path_1.default.join(this.tempDir, `main-${timestamp}-${random}.js`);
342
+ const tempFile = path_1.default.join(this.tempDir, `server-${timestamp}-${random}.js`);
222
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;
368
+ }
223
369
  // Add source map support
224
370
  if (mainModule.map && this.options.sourceMaps) {
225
371
  const mapFile = tempFile + '.map';
@@ -241,6 +387,7 @@ class DevManager {
241
387
  }
242
388
  }
243
389
  this.currentTempFile = tempFile;
390
+ this.lastCompileTime = Date.now() - startCompile;
244
391
  return tempFile;
245
392
  }
246
393
  async getExecuteCommand() {
@@ -257,50 +404,93 @@ class DevManager {
257
404
  args.unshift('--inspect-brk');
258
405
  if (this.options.sourceMaps)
259
406
  args.unshift('--enable-source-maps');
407
+ // Memory optimization
408
+ args.unshift('--max-old-space-size=4096');
409
+ args.unshift('--optimize-for-size');
260
410
  return { command: 'node', args };
261
411
  }
262
412
  clearConsole() {
263
413
  if (this.options.clearConsole && process.stdout.isTTY) {
264
- process.stdout.write('\x1Bc'); // Clear screen and scrollback
414
+ process.stdout.write('\x1Bc');
265
415
  }
266
416
  }
267
417
  async startProcess() {
268
418
  var _a, _b;
269
419
  if (this.process)
270
420
  return;
421
+ const restartStart = Date.now();
271
422
  this.loadEnvFile();
272
423
  try {
273
424
  const { command, args } = await this.getExecuteCommand();
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();
429
+ }
274
430
  this.process = (0, child_process_1.spawn)(command, args, {
275
431
  stdio: ['ignore', 'pipe', 'pipe'],
276
432
  env: {
277
433
  ...process.env,
278
434
  NODE_ENV: process.env.NODE_ENV || 'development',
279
435
  FORCE_COLOR: this.options.color ? '1' : '0',
280
- NODE_OPTIONS: '--max-old-space-size=4096', // Prevent memory issues
436
+ NEEX_DEV_MODE: '1',
437
+ NEEX_HOT_RELOAD: this.options.hotReload ? '1' : '0',
281
438
  },
282
- detached: false // Keep attached for better cleanup
439
+ detached: false
283
440
  });
284
441
  this.startTime = new Date();
285
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
+ }
286
450
  if (!this.options.quiet) {
287
451
  const timestamp = new Date().toLocaleTimeString();
288
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}` : '';
289
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
+ }
290
463
  }
291
- // Handle stdout/stderr
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
292
472
  (_a = this.process.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
293
- process.stdout.write(data);
473
+ const output = data.toString();
474
+ if (!this.options.quiet) {
475
+ process.stdout.write(output);
476
+ }
294
477
  });
295
478
  (_b = this.process.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
296
- process.stderr.write(data);
479
+ const output = data.toString();
480
+ if (!this.options.quiet) {
481
+ process.stderr.write(output);
482
+ }
297
483
  });
298
484
  this.process.on('error', (error) => {
299
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
+ }
300
489
  });
301
490
  this.process.on('exit', (code, signal) => {
302
491
  if (this.process) {
303
492
  this.process = null;
493
+ this.serverInfo = null;
304
494
  if (!this.isRestarting && code !== 0) {
305
495
  const duration = this.startTime ? Date.now() - this.startTime.getTime() : 0;
306
496
  logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red('✖')} Process exited with code ${code} (${duration}ms)`, 'error');
@@ -319,6 +509,7 @@ class DevManager {
319
509
  return new Promise((resolve) => {
320
510
  const proc = this.process;
321
511
  this.process = null;
512
+ this.serverInfo = null;
322
513
  const cleanup = () => {
323
514
  resolve();
324
515
  };
@@ -331,7 +522,8 @@ class DevManager {
331
522
  if (!proc.killed) {
332
523
  proc.kill('SIGKILL');
333
524
  }
334
- }, 1000);
525
+ cleanup();
526
+ }, 2000);
335
527
  }
336
528
  catch (error) {
337
529
  cleanup();
@@ -342,10 +534,14 @@ class DevManager {
342
534
  if (this.isRestarting)
343
535
  return;
344
536
  this.isRestarting = true;
345
- // Clear console immediately for better UX
537
+ // Clear console for better UX
346
538
  this.clearConsole();
347
539
  if (!this.options.quiet) {
348
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow('⟳')} Restarting...`, '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', {});
349
545
  }
350
546
  // Stop current process
351
547
  await this.stopProcess();
@@ -355,17 +551,21 @@ class DevManager {
355
551
  }
356
552
  setupWatcher() {
357
553
  const watchPatterns = this.options.watch;
358
- // Optimized ignore patterns
554
+ // Optimized ignore patterns for Express projects
359
555
  const ignored = [
360
556
  'node_modules/**',
361
557
  '.git/**',
362
558
  'dist/**',
363
559
  'build/**',
364
- '.neex-temp/**',
560
+ '.neex/**',
561
+ 'coverage/**',
562
+ 'logs/**',
365
563
  '**/*.log',
366
564
  '**/*.d.ts',
367
565
  '**/*.map',
368
566
  '**/*.tsbuildinfo',
567
+ '**/package-lock.json',
568
+ '**/yarn.lock',
369
569
  ...this.options.ignore
370
570
  ];
371
571
  this.watcher = (0, chokidar_1.watch)(watchPatterns, {
@@ -373,10 +573,10 @@ class DevManager {
373
573
  ignoreInitial: true,
374
574
  followSymlinks: false,
375
575
  usePolling: false,
376
- atomic: 50,
576
+ atomic: 100,
377
577
  awaitWriteFinish: {
378
- stabilityThreshold: 100,
379
- pollInterval: 50
578
+ stabilityThreshold: 150,
579
+ pollInterval: 100
380
580
  }
381
581
  });
382
582
  this.watcher.on('change', (filePath) => {
@@ -408,24 +608,28 @@ class DevManager {
408
608
  throw new Error(`Target file not found: ${this.options.file}`);
409
609
  }
410
610
  const ext = path_1.default.extname(this.options.file);
411
- if (!['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
611
+ if (!this.options.extensions.map(e => `.${e}`).includes(ext)) {
412
612
  throw new Error(`Unsupported file extension: ${ext}`);
413
613
  }
414
614
  // Clear any existing cache
415
615
  this.moduleCache.clear();
416
616
  this.setupTempDir();
417
617
  if (!this.options.quiet) {
418
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue('')} Starting dev server...`, 'info');
618
+ const appType = this.isExpressApp ? 'Express' : 'Node.js';
619
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue('⚡')} Starting ${appType} development server...`, 'info');
419
620
  }
420
621
  this.setupWatcher();
421
622
  await this.startProcess();
422
623
  if (!this.options.quiet) {
423
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
+ }
424
628
  }
425
629
  }
426
630
  async stop() {
427
631
  if (!this.options.quiet) {
428
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow('⏹')} Stopping dev server...`, 'info');
632
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow('⏹')} Stopping development server...`, 'info');
429
633
  }
430
634
  if (this.watcher) {
431
635
  await this.watcher.close();
@@ -442,6 +646,23 @@ class DevManager {
442
646
  }
443
647
  }
444
648
  this.moduleCache.clear();
649
+ if (this.options.verbose) {
650
+ logger_manager_js_1.loggerManager.printLine('Development server stopped', 'info');
651
+ }
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();
445
666
  }
446
667
  }
447
668
  exports.DevManager = DevManager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neex",
3
- "version": "0.6.87",
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",