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.
- package/dist/src/commands/dev-commands.js +20 -16
- package/dist/src/dev-manager.js +416 -300
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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,
|
|
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
|
|
87
|
-
|
|
88
|
-
|
|
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 (!
|
|
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(
|
|
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');
|
package/dist/src/dev-manager.js
CHANGED
|
@@ -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 -
|
|
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.
|
|
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.
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
170
|
+
// Fall back to defaults
|
|
92
171
|
}
|
|
93
172
|
}
|
|
94
173
|
return defaultOptions;
|
|
95
174
|
}
|
|
96
175
|
loadEnvFile() {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
125
|
-
|
|
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
|
-
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
reportDiagnostics: false
|
|
236
|
-
});
|
|
243
|
+
if (fs_1.default.existsSync(resolvedPath)) {
|
|
244
|
+
dependencies.push(resolvedPath);
|
|
245
|
+
}
|
|
237
246
|
}
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
244
|
-
hash: currentHash
|
|
286
|
+
exports
|
|
245
287
|
};
|
|
246
|
-
this.moduleCache.set(absolutePath,
|
|
288
|
+
this.moduleCache.set(absolutePath, moduleInfo);
|
|
247
289
|
if (this.options.verbose) {
|
|
248
|
-
|
|
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
|
|
293
|
+
return moduleInfo;
|
|
251
294
|
}
|
|
252
295
|
catch (error) {
|
|
253
|
-
logger_manager_js_1.loggerManager.printLine(
|
|
296
|
+
logger_manager_js_1.loggerManager.printLine(`Compilation error in ${filePath}: ${error.message}`, 'error');
|
|
254
297
|
throw error;
|
|
255
298
|
}
|
|
256
299
|
}
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
|
|
263
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
296
|
-
|
|
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:
|
|
396
|
+
return { command: parts[0], args: parts.slice(1) };
|
|
318
397
|
}
|
|
319
|
-
|
|
320
|
-
this.
|
|
321
|
-
//
|
|
322
|
-
|
|
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('\
|
|
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
|
-
|
|
351
|
-
|
|
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
|
-
|
|
361
|
-
|
|
436
|
+
NEEX_DEV_MODE: '1',
|
|
437
|
+
NEEX_HOT_RELOAD: this.options.hotReload ? '1' : '0',
|
|
362
438
|
},
|
|
363
|
-
detached:
|
|
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
|
-
|
|
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(
|
|
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(
|
|
481
|
+
process.stderr.write(output);
|
|
379
482
|
}
|
|
380
483
|
});
|
|
381
484
|
this.process.on('error', (error) => {
|
|
382
|
-
logger_manager_js_1.loggerManager.printLine(
|
|
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
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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(
|
|
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
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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
|
-
|
|
450
|
-
|
|
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(
|
|
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
|
-
//
|
|
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
|
-
'
|
|
465
|
-
'
|
|
466
|
-
'
|
|
467
|
-
'
|
|
468
|
-
'
|
|
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
|
-
|
|
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:
|
|
482
|
-
pollInterval:
|
|
578
|
+
stabilityThreshold: 150,
|
|
579
|
+
pollInterval: 100
|
|
483
580
|
}
|
|
484
581
|
});
|
|
485
582
|
this.watcher.on('change', (filePath) => {
|
|
486
|
-
this.
|
|
583
|
+
this.invalidateModuleCache(filePath);
|
|
487
584
|
if (this.options.verbose) {
|
|
488
|
-
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(
|
|
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('+')}
|
|
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.
|
|
596
|
+
this.invalidateModuleCache(filePath);
|
|
500
597
|
if (this.options.verbose) {
|
|
501
|
-
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red('-')}
|
|
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(
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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.
|
|
548
|
-
logger_manager_js_1.loggerManager.printLine(
|
|
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;
|