neex 0.1.8 → 0.2.5

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/cli.js CHANGED
@@ -3,48 +3,143 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- // src/cli.ts - Main CLI file (refactored)
6
+ // src/cli.ts - Updated version
7
7
  const commander_1 = require("commander");
8
- const index_js_1 = require("./commands/index.js");
8
+ const index_js_1 = require("./index.js");
9
9
  const chalk_1 = __importDefault(require("chalk"));
10
10
  const figures_1 = __importDefault(require("figures"));
11
11
  const { version } = require('../../package.json');
12
12
  function cli() {
13
13
  const program = new commander_1.Command();
14
- // Initialize cleanup handlers
15
- const cleanupHandlers = [];
14
+ let cleanupRunner = null;
16
15
  program
17
16
  .name('neex')
18
- .description('Professional script runner with nodemon and PM2 functionality')
17
+ .description('Professional script runner with beautiful colored output')
19
18
  .version(version);
20
- // Add all command groups
21
- (0, index_js_1.addRunCommands)(program);
22
- (0, index_js_1.addServerCommands)(program);
23
- const devCommands = (0, index_js_1.addDevCommands)(program);
24
- cleanupHandlers.push(devCommands.cleanupDev);
25
- const processCommands = (0, index_js_1.addProcessCommands)(program);
26
- cleanupHandlers.push(processCommands.cleanupProcess);
19
+ // Main command for sequential execution (similar to run-s)
20
+ program
21
+ .command('run <commands...>')
22
+ .alias('s')
23
+ .description('Run commands sequentially')
24
+ .option('-c, --no-color', 'Disable colored output')
25
+ .option('-t, --no-timing', 'Hide timing information')
26
+ .option('-p, --no-prefix', 'Hide command prefix')
27
+ .option('-s, --stop-on-error', 'Stop on first error')
28
+ .option('-o, --no-output', 'Hide command output')
29
+ .option('-m, --minimal', 'Use minimal output format')
30
+ .action(async (commands, options) => {
31
+ try {
32
+ await (0, index_js_1.run)(commands, {
33
+ parallel: false,
34
+ color: options.color,
35
+ showTiming: options.timing,
36
+ prefix: options.prefix,
37
+ stopOnError: options.stopOnError,
38
+ printOutput: options.output,
39
+ minimalOutput: options.minimal,
40
+ registerCleanup: (cleanup) => { cleanupRunner = cleanup; }
41
+ });
42
+ }
43
+ catch (error) {
44
+ if (error instanceof Error) {
45
+ console.error(chalk_1.default.red(`${figures_1.default.cross} Error: ${error.message}`));
46
+ }
47
+ else {
48
+ console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown error occurred`));
49
+ }
50
+ process.exit(1);
51
+ }
52
+ });
53
+ // runx command: parallel execution by default (with alias 'p'), can run sequentially with -q
54
+ program
55
+ .command('runx <commands...>', { isDefault: true })
56
+ .alias('p')
57
+ .description('Run commands in parallel (default) or sequentially with -q. Alias: p')
58
+ .option('-c, --no-color', 'Disable colored output')
59
+ .option('-t, --no-timing', 'Hide timing information')
60
+ .option('-p, --no-prefix', 'Hide command prefix')
61
+ .option('-s, --stop-on-error', 'Stop on first error')
62
+ .option('-o, --no-output', 'Hide command output')
63
+ .option('-m, --minimal', 'Use minimal output format')
64
+ .option('-x, --max-parallel <number>', 'Maximum number of parallel processes', parseInt)
65
+ .option('-q, --sequential', 'Run commands sequentially instead of in parallel')
66
+ .action(async (commands, options) => {
67
+ try {
68
+ await (0, index_js_1.run)(commands, {
69
+ parallel: !options.sequential,
70
+ maxParallel: options.maxParallel,
71
+ color: options.color,
72
+ showTiming: options.timing,
73
+ prefix: options.prefix,
74
+ stopOnError: options.stopOnError,
75
+ printOutput: options.output,
76
+ minimalOutput: options.minimal,
77
+ registerCleanup: (cleanup) => { cleanupRunner = cleanup; }
78
+ });
79
+ }
80
+ catch (error) {
81
+ if (error instanceof Error) {
82
+ console.error(chalk_1.default.red(`${figures_1.default.cross} Error: ${error.message}`));
83
+ }
84
+ else {
85
+ console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown error occurred`));
86
+ }
87
+ process.exit(1);
88
+ }
89
+ });
90
+ // Add a new servers command specifically optimized for running web servers
91
+ program
92
+ .command('servers <commands...>')
93
+ .alias('srv')
94
+ .description('Run multiple servers with optimized output for API, frontend, etc.')
95
+ .option('-c, --no-color', 'Disable colored output')
96
+ .option('-t, --no-timing', 'Hide timing information')
97
+ .option('-p, --no-prefix', 'Hide command prefix')
98
+ .option('-s, --stop-on-error', 'Stop when any server crashes')
99
+ .option('-x, --max-parallel <number>', 'Maximum number of parallel servers', parseInt)
100
+ .option('-g, --group-output', 'Group outputs by server')
101
+ .action(async (commands, options) => {
102
+ try {
103
+ console.log(chalk_1.default.blue(`${figures_1.default.info} Starting servers in parallel mode...`));
104
+ await (0, index_js_1.run)(commands, {
105
+ parallel: true,
106
+ maxParallel: options.maxParallel,
107
+ color: options.color,
108
+ showTiming: options.timing,
109
+ prefix: options.prefix,
110
+ stopOnError: options.stopOnError,
111
+ printOutput: true,
112
+ registerCleanup: (cleanup) => { cleanupRunner = cleanup; },
113
+ groupOutput: options.groupOutput,
114
+ isServerMode: true // Special flag for server mode formatting
115
+ });
116
+ }
117
+ catch (error) {
118
+ if (error instanceof Error) {
119
+ console.error(chalk_1.default.red(`${figures_1.default.cross} Server Error: ${error.message}`));
120
+ }
121
+ else {
122
+ console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown server error occurred`));
123
+ }
124
+ process.exit(1);
125
+ }
126
+ });
27
127
  program.parse(process.argv);
28
128
  // Show help if no commands specified
29
129
  if (program.args.length === 0) {
30
130
  program.help();
31
131
  }
32
132
  // Graceful shutdown handling
33
- const handleSignal = async (signal) => {
133
+ const handleSignal = (signal) => {
34
134
  console.log(`\n${chalk_1.default.yellow(`${figures_1.default.warning} Received ${signal}. Cleaning up...`)}`);
35
- // Run all cleanup handlers
36
- for (const cleanup of cleanupHandlers) {
37
- try {
38
- await cleanup();
39
- }
40
- catch (error) {
41
- console.error(`Cleanup error:`, error);
42
- }
135
+ if (cleanupRunner) {
136
+ cleanupRunner();
43
137
  }
138
+ // Give cleanup a moment, then exit
44
139
  setTimeout(() => process.exit(0), 500);
45
140
  };
46
- process.on('SIGINT', () => handleSignal('SIGINT').catch(err => console.error('SIGINT handler error:', err)));
47
- process.on('SIGTERM', () => handleSignal('SIGTERM').catch(err => console.error('SIGTERM handler error:', err)));
48
- process.on('SIGQUIT', () => handleSignal('SIGQUIT').catch(err => console.error('SIGQUIT handler error:', err)));
141
+ process.on('SIGINT', () => handleSignal('SIGINT')); // Ctrl+C
142
+ process.on('SIGTERM', () => handleSignal('SIGTERM'));
143
+ process.on('SIGQUIT', () => handleSignal('SIGQUIT'));
49
144
  }
50
145
  exports.default = cli;
package/dist/src/index.js CHANGED
@@ -23,9 +23,7 @@ async function run(commands, options) {
23
23
  stopOnError: (_f = options === null || options === void 0 ? void 0 : options.stopOnError) !== null && _f !== void 0 ? _f : false,
24
24
  minimalOutput: (_g = options === null || options === void 0 ? void 0 : options.minimalOutput) !== null && _g !== void 0 ? _g : false,
25
25
  groupOutput: (_h = options === null || options === void 0 ? void 0 : options.groupOutput) !== null && _h !== void 0 ? _h : false,
26
- isServerMode: (_j = options === null || options === void 0 ? void 0 : options.isServerMode) !== null && _j !== void 0 ? _j : false,
27
- retry: options === null || options === void 0 ? void 0 : options.retry,
28
- retryDelay: options === null || options === void 0 ? void 0 : options.retryDelay
26
+ isServerMode: (_j = options === null || options === void 0 ? void 0 : options.isServerMode) !== null && _j !== void 0 ? _j : false
29
27
  };
30
28
  const runner = new runner_1.Runner(runOptions);
31
29
  if (options === null || options === void 0 ? void 0 : options.registerCleanup) {
@@ -7,7 +7,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const figures_1 = __importDefault(require("figures"));
9
9
  const string_width_1 = __importDefault(require("string-width"));
10
- const utils_1 = require("./utils");
11
10
  class Logger {
12
11
  constructor() {
13
12
  this.prefixLength = 0;
@@ -121,9 +120,6 @@ class Logger {
121
120
  // Clear buffer after printing
122
121
  this.outputBuffer.set(command, []);
123
122
  }
124
- clearBuffer(command) {
125
- this.outputBuffer.set(command, []);
126
- }
127
123
  printLine(message, level = 'info') {
128
124
  if (level === 'error') {
129
125
  console.error(chalk_1.default.red(`${figures_1.default.cross} ${message}`));
@@ -140,12 +136,6 @@ class Logger {
140
136
  this.startTimes.set(command, new Date());
141
137
  const prefix = this.formatPrefix(command);
142
138
  const color = this.commandColors.get(command) || chalk_1.default.white;
143
- // Stop any previous spinner for this command (e.g. if retrying)
144
- this.stopSpinner(command);
145
- // Clear the line before printing "Starting..."
146
- if (this.isSpinnerActive) { // Check if any spinner was active to avoid clearing unnecessarily
147
- process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r');
148
- }
149
139
  console.log(`${prefix} ${color('Starting...')}`);
150
140
  // Start spinner for this command
151
141
  this.startSpinner(command);
@@ -207,32 +197,6 @@ class Logger {
207
197
  console.error(`${prefix} ${chalk_1.default.red(error.message)}`);
208
198
  }
209
199
  }
210
- printEnd(result, minimalOutput) {
211
- this.stopSpinner(result.command);
212
- const prefix = this.formatPrefix(result.command); // Corrected to formatPrefix
213
- let durationDisplay = '';
214
- if (result.duration !== null) {
215
- // Ensure result.duration is treated as a number here
216
- durationDisplay = `(${(0, utils_1.formatDuration)(result.duration)})`;
217
- }
218
- const duration = durationDisplay;
219
- if (minimalOutput) {
220
- if (!result.success) {
221
- const status = result.code !== null ? `failed (code ${result.code})` : 'failed';
222
- this.printLine(`${prefix} ${chalk_1.default.red(figures_1.default.cross)} ${result.command} ${status} ${duration}`, 'error');
223
- }
224
- }
225
- else {
226
- if (result.success) {
227
- this.printLine(`${prefix} ${chalk_1.default.green(figures_1.default.tick)} Command "${result.command}" finished successfully ${duration}`, 'info');
228
- }
229
- else {
230
- const errorCode = result.code !== null ? ` (code ${result.code})` : '';
231
- const errorMessage = result.error ? `: ${result.error.message}` : '';
232
- this.printLine(`${prefix} ${chalk_1.default.red(figures_1.default.cross)} Command "${result.command}" failed${errorCode}${errorMessage} ${duration}`, 'error');
233
- }
234
- }
235
- }
236
200
  printSummary(results) {
237
201
  // Stop any remaining spinners
238
202
  this.stopAllSpinners();
@@ -35,7 +35,6 @@ const chalk_1 = __importDefault(require("chalk"));
35
35
  const logger_1 = __importDefault(require("./logger"));
36
36
  const p_map_1 = __importDefault(require("p-map"));
37
37
  const npm_run_path_1 = __importDefault(require("npm-run-path"));
38
- const fs = __importStar(require("fs"));
39
38
  class Runner {
40
39
  constructor(options) {
41
40
  this.activeProcesses = new Map();
@@ -45,41 +44,6 @@ class Runner {
45
44
  this.options = options;
46
45
  this.activeProcesses = new Map();
47
46
  }
48
- async expandWildcardCommands(commands) {
49
- const expandedCommands = [];
50
- let packageJson;
51
- try {
52
- const packageJsonPath = path.join(process.cwd(), 'package.json');
53
- if (fs.existsSync(packageJsonPath)) {
54
- const packageJsonContent = await fsPromises.readFile(packageJsonPath, 'utf-8');
55
- packageJson = JSON.parse(packageJsonContent);
56
- }
57
- }
58
- catch (error) {
59
- logger_1.default.printLine(`Could not read or parse package.json: ${error.message}`, 'warn');
60
- packageJson = { scripts: {} };
61
- }
62
- for (const command of commands) {
63
- if (command.includes('*') && packageJson && packageJson.scripts) {
64
- const pattern = new RegExp(`^${command.replace(/\*/g, '.*')}$`);
65
- let foundMatch = false;
66
- for (const scriptName in packageJson.scripts) {
67
- if (pattern.test(scriptName)) {
68
- expandedCommands.push(scriptName); // Or packageJson.scripts[scriptName] if you want the script value
69
- foundMatch = true;
70
- }
71
- }
72
- if (!foundMatch) {
73
- logger_1.default.printLine(`No scripts found in package.json matching wildcard: ${command}`, 'warn');
74
- expandedCommands.push(command); // Add original command if no match
75
- }
76
- }
77
- else {
78
- expandedCommands.push(command);
79
- }
80
- }
81
- return expandedCommands;
82
- }
83
47
  async resolveScriptAndCwd(scriptNameOrCommand, baseDir) {
84
48
  try {
85
49
  const packageJsonPath = path.join(baseDir, 'package.json');
@@ -139,7 +103,7 @@ class Runner {
139
103
  // Update server info
140
104
  this.serverInfo.set(command, serverInfo);
141
105
  }
142
- async runCommand(originalCommand, currentRetry = 0) {
106
+ async runCommand(originalCommand) {
143
107
  const { executableCommand: command, executionCwd: cwd } = await this.resolveScriptAndCwd(originalCommand, process.cwd());
144
108
  const startTime = new Date();
145
109
  const result = {
@@ -153,8 +117,7 @@ class Runner {
153
117
  if (this.options.printOutput) {
154
118
  logger_1.default.printStart(originalCommand);
155
119
  }
156
- return new Promise(async (resolve) => {
157
- var _a, _b;
120
+ return new Promise((resolve) => {
158
121
  const [cmd, ...args] = command.split(' ');
159
122
  const env = {
160
123
  ...process.env,
@@ -177,93 +140,96 @@ class Runner {
177
140
  startTime: new Date()
178
141
  });
179
142
  }
180
- (_a = proc.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
181
- const output = {
182
- command: originalCommand,
183
- type: 'stdout',
184
- data: data.toString(),
185
- timestamp: new Date()
186
- };
187
- if (this.options.isServerMode)
188
- this.detectServerInfo(originalCommand, data.toString());
189
- if (result.output)
190
- result.output.push(output);
191
- logger_1.default.bufferOutput(output);
192
- if (!this.options.groupOutput && this.options.printOutput)
193
- logger_1.default.printBuffer(originalCommand);
194
- });
195
- (_b = proc.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
196
- const output = {
197
- command: originalCommand,
198
- type: 'stderr',
199
- data: data.toString(),
200
- timestamp: new Date()
201
- };
202
- if (result.output)
203
- result.output.push(output);
204
- logger_1.default.bufferOutput(output);
205
- if (!this.options.groupOutput && this.options.printOutput)
206
- logger_1.default.printBuffer(originalCommand);
207
- });
208
- proc.on('error', async (err) => {
209
- result.endTime = new Date();
210
- result.error = err;
211
- result.success = false;
212
- result.duration = result.endTime.getTime() - startTime.getTime();
143
+ // Capture and display output
144
+ if (this.options.printOutput) {
145
+ proc.stdout.on('data', (data) => {
146
+ const output = {
147
+ command: originalCommand,
148
+ type: 'stdout',
149
+ data: data.toString(),
150
+ timestamp: new Date()
151
+ };
152
+ if (this.options.isServerMode) {
153
+ this.detectServerInfo(originalCommand, data.toString());
154
+ }
155
+ // Store output for logging
156
+ if (result.output)
157
+ result.output.push(output);
158
+ logger_1.default.bufferOutput(output);
159
+ // Print immediately unless we're in group mode
160
+ if (!this.options.groupOutput) {
161
+ logger_1.default.printBuffer(originalCommand);
162
+ }
163
+ });
164
+ proc.stderr.on('data', (data) => {
165
+ const output = {
166
+ command: originalCommand,
167
+ type: 'stderr',
168
+ data: data.toString(),
169
+ timestamp: new Date()
170
+ };
171
+ // Store output for logging
172
+ if (result.output)
173
+ result.output.push(output);
174
+ logger_1.default.bufferOutput(output);
175
+ // Print immediately unless we're in group mode
176
+ if (!this.options.groupOutput) {
177
+ logger_1.default.printBuffer(originalCommand);
178
+ }
179
+ });
180
+ }
181
+ proc.on('close', (code) => {
182
+ const endTime = new Date();
183
+ const duration = endTime.getTime() - startTime.getTime();
184
+ result.endTime = endTime;
185
+ result.duration = duration;
186
+ result.code = code;
187
+ result.success = code === 0;
213
188
  this.activeProcesses.delete(originalCommand);
214
189
  if (this.options.isServerMode) {
215
190
  const serverInfo = this.serverInfo.get(originalCommand);
216
191
  if (serverInfo) {
217
- serverInfo.status = 'error';
192
+ serverInfo.status = code === 0 ? 'stopped' : 'error';
218
193
  this.serverInfo.set(originalCommand, serverInfo);
219
194
  }
220
- logger_1.default.printLine(`Command "${originalCommand}" failed to start: ${err.message}`, 'error');
221
- }
222
- logger_1.default.printBuffer(originalCommand);
223
- if (this.options.printOutput)
224
- logger_1.default.printEnd(result, this.options.minimalOutput);
225
- if (this.options.retry && this.options.retry > 0 && currentRetry < this.options.retry) {
226
- logger_1.default.printLine(`Command "${originalCommand}" failed with error. Retrying (${currentRetry + 1}/${this.options.retry})...`, 'warn');
227
- if (this.options.retryDelay && this.options.retryDelay > 0) {
228
- await new Promise(res => setTimeout(res, this.options.retryDelay));
195
+ // If this is server mode and a server failed, print prominent error
196
+ if (code !== 0) {
197
+ logger_1.default.printLine(`Server ${originalCommand} crashed with code ${code}`, 'error');
229
198
  }
230
- logger_1.default.clearBuffer(originalCommand);
231
- resolve(this.runCommand(originalCommand, currentRetry + 1));
232
199
  }
233
- else {
234
- resolve(result);
200
+ // Print grouped output at the end if enabled
201
+ if (this.options.groupOutput && result.output && result.output.length > 0) {
202
+ logger_1.default.printBuffer(originalCommand);
203
+ }
204
+ if (this.options.printOutput) {
205
+ if (result.success) {
206
+ logger_1.default.printSuccess(result);
207
+ }
208
+ else {
209
+ logger_1.default.printError(result);
210
+ }
235
211
  }
212
+ resolve(result);
236
213
  });
237
- proc.on('close', async (code) => {
238
- result.code = code;
239
- result.success = code === 0;
240
- result.endTime = new Date();
241
- result.duration = result.endTime.getTime() - startTime.getTime();
214
+ proc.on('error', (error) => {
215
+ const endTime = new Date();
216
+ const duration = endTime.getTime() - startTime.getTime();
217
+ result.endTime = endTime;
218
+ result.duration = duration;
219
+ result.error = error;
220
+ result.success = false;
242
221
  this.activeProcesses.delete(originalCommand);
243
222
  if (this.options.isServerMode) {
244
223
  const serverInfo = this.serverInfo.get(originalCommand);
245
224
  if (serverInfo) {
246
- serverInfo.status = code === 0 ? 'stopped' : 'error';
225
+ serverInfo.status = 'error';
247
226
  this.serverInfo.set(originalCommand, serverInfo);
248
227
  }
249
- if (code !== 0) {
250
- logger_1.default.printLine(`Server "${originalCommand}" exited with code ${code}`, 'error');
251
- }
252
228
  }
253
- logger_1.default.printBuffer(originalCommand);
254
- if (this.options.printOutput)
255
- logger_1.default.printEnd(result, this.options.minimalOutput);
256
- if (!result.success && this.options.retry && this.options.retry > 0 && currentRetry < this.options.retry) {
257
- logger_1.default.printLine(`Command "${originalCommand}" failed with code ${code}. Retrying (${currentRetry + 1}/${this.options.retry})...`, 'warn');
258
- if (this.options.retryDelay && this.options.retryDelay > 0) {
259
- await new Promise(res => setTimeout(res, this.options.retryDelay));
260
- }
261
- logger_1.default.clearBuffer(originalCommand);
262
- resolve(this.runCommand(originalCommand, currentRetry + 1));
263
- }
264
- else {
265
- resolve(result);
229
+ if (this.options.printOutput) {
230
+ logger_1.default.printError(result);
266
231
  }
232
+ resolve(result);
267
233
  });
268
234
  });
269
235
  }
@@ -272,6 +238,7 @@ class Runner {
272
238
  for (const cmd of commands) {
273
239
  const result = await this.runCommand(cmd);
274
240
  results.push(result);
241
+ // Stop on error if enabled
275
242
  if (!result.success && this.options.stopOnError) {
276
243
  break;
277
244
  }
@@ -290,19 +257,18 @@ class Runner {
290
257
  });
291
258
  }
292
259
  catch (error) {
260
+ // If pMap stops due to stopOnError
293
261
  if (this.options.isServerMode) {
294
262
  logger_1.default.printLine('One or more servers failed to start. Stopping all servers.', 'error');
295
263
  }
296
264
  return [];
297
265
  }
298
266
  }
299
- async run(initialCommands) {
300
- const commands = await this.expandWildcardCommands(initialCommands);
267
+ async run(commands) {
301
268
  if (commands.length === 0) {
302
- logger_1.default.printLine('No commands to run after wildcard expansion.', 'warn');
303
269
  return [];
304
270
  }
305
- // Initialize logger with the final list of commands
271
+ // Set up logger with commands
306
272
  logger_1.default.setCommands(commands);
307
273
  // Run in parallel or sequential mode
308
274
  if (this.options.parallel) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neex",
3
- "version": "0.1.8",
3
+ "version": "0.2.5",
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",
@@ -12,9 +12,7 @@
12
12
  "start": "node dist/bin/neex.js",
13
13
  "prepublishOnly": "npm run build",
14
14
  "test": "jest",
15
- "dev": "neex w \"ts-node src/server.ts\"",
16
- "w": "neex w \"ts-node src/server.ts\"",
17
- "test:dev": "node ./dist/bin/neex.js px \"echo Starting frontend\" \"echo Starting backend\"",
15
+ "test:dev": "node ./dist/bin/neex.js runx \"echo Starting frontend\" \"echo Starting backend\"",
18
16
  "test:parallel": "node ./dist/src/cli.js parallel \"echo Building frontend\" \"echo Building backend\"",
19
17
  "test:sequence": "node ./dist/src/cli.js run \"echo Step 1\" \"echo Step 2\" \"echo Step 3\""
20
18
  },