@rtorcato/js-common 1.8.0 → 2.0.0

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.
@@ -0,0 +1,439 @@
1
+ import { Separator, checkbox, select } from '@inquirer/prompts';
2
+ import chalk from 'chalk';
3
+ import chalkAnimation from 'chalk-animation';
4
+ import { program } from 'commander';
5
+ import figlet from 'figlet';
6
+ import gradient from 'gradient-string';
7
+ import { readFileSync } from 'node:fs';
8
+ import { dirname, resolve } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ // Get current directory
11
+ const currentDir = dirname(fileURLToPath(import.meta.url));
12
+ // Get package version
13
+ const packageJson = JSON.parse(readFileSync(resolve(currentDir, '../../package.json'), 'utf-8'));
14
+ program
15
+ .name('js-common')
16
+ .description(chalk.cyan('✨ CLI utilities from @rtorcato/js-common'))
17
+ .version(packageJson.version);
18
+ // Function categories for list and interactive mode
19
+ const functionCategories = {
20
+ date: {
21
+ name: 'šŸ“… Date & Time',
22
+ functions: [
23
+ { name: 'today', description: "Get today's date (YYYY-MM-DD)" },
24
+ { name: 'now', description: 'Get current timestamp' },
25
+ { name: 'between', description: 'Calculate days between dates' },
26
+ ],
27
+ },
28
+ math: {
29
+ name: 'šŸ”¢ Mathematical',
30
+ functions: [
31
+ { name: 'sum', description: 'Calculate sum of numbers' },
32
+ { name: 'avg', description: 'Calculate average of numbers' },
33
+ { name: 'random', description: 'Generate random number' },
34
+ { name: 'round', description: 'Round to decimal places' },
35
+ { name: 'clamp', description: 'Clamp between min/max' },
36
+ ],
37
+ },
38
+ text: {
39
+ name: 'šŸ“ Text Formatting',
40
+ functions: [
41
+ { name: 'capitalize', description: 'Capitalize first letter' },
42
+ { name: 'title', description: 'Convert to title case' },
43
+ { name: 'pad', description: 'Pad with leading zeros' },
44
+ ],
45
+ },
46
+ file: {
47
+ name: 'šŸ“ File Operations',
48
+ functions: [
49
+ { name: 'exists', description: 'Check if file exists' },
50
+ { name: 'ext', description: 'Get file extension' },
51
+ ],
52
+ },
53
+ security: {
54
+ name: 'šŸ”’ Security',
55
+ functions: [
56
+ { name: 'password', description: 'Check password strength' },
57
+ { name: 'token', description: 'Generate secure token' },
58
+ ],
59
+ },
60
+ validate: {
61
+ name: 'āœ… Validation',
62
+ functions: [{ name: 'url', description: 'Validate URL format' }],
63
+ },
64
+ system: {
65
+ name: 'šŸ’» System Info',
66
+ functions: [
67
+ { name: 'pid', description: 'Get process ID' },
68
+ { name: 'uptime', description: 'Get process uptime' },
69
+ { name: 'ci', description: 'Check CI environment' },
70
+ { name: 'node-version', description: 'Get Node.js version' },
71
+ ],
72
+ },
73
+ };
74
+ // List all functions - add as a separate command
75
+ program
76
+ .command('list')
77
+ .alias('ls')
78
+ .description('šŸ“‹ List all available functions')
79
+ .action(() => {
80
+ console.log(chalk.cyan('\n✨ Available Functions:\n'));
81
+ Object.entries(functionCategories).forEach(([key, category]) => {
82
+ console.log(chalk.bold(category.name));
83
+ console.log(); // Add space after category header
84
+ category.functions.forEach((func) => {
85
+ console.log(chalk.gray(` js-common ${key} ${func.name}`) + chalk.dim(` - ${func.description}`));
86
+ });
87
+ console.log(); // Add space after function list
88
+ });
89
+ console.log(chalk.yellow('šŸ’” Use ') +
90
+ chalk.green('js-common add') +
91
+ chalk.yellow(' to integrate functions into your project'));
92
+ console.log(chalk.yellow('šŸ’” Use ') +
93
+ chalk.green('js-common interactive') +
94
+ chalk.yellow(' for interactive mode'));
95
+ console.log(chalk.yellow('šŸ’” Use ') +
96
+ chalk.green('js-common <command> --help') +
97
+ chalk.yellow(' for detailed help\n'));
98
+ });
99
+ // Also add as an option for compatibility
100
+ program.option('-l, --list', 'List all available functions');
101
+ // Interactive mode
102
+ program
103
+ .command('interactive')
104
+ .alias('i')
105
+ .description('šŸŽ® Interactive mode with guided prompts')
106
+ .action(async () => {
107
+ // Welcome animation
108
+ const title = figlet.textSync('JS Common', { font: 'Small' });
109
+ console.log(gradient.pastel.multiline(title));
110
+ const rainbow = chalkAnimation.rainbow('\n✨ Welcome to JS Common Interactive Mode! ✨\n');
111
+ await new Promise((resolve) => setTimeout(resolve, 1000));
112
+ rainbow.stop();
113
+ try {
114
+ // Main interactive loop
115
+ while (true) {
116
+ const category = await select({
117
+ message: chalk.cyan('What would you like to do?'),
118
+ choices: Object.entries(functionCategories)
119
+ .reduce((acc, [key, cat], index) => {
120
+ acc.push({ name: cat.name, value: key });
121
+ if (index < Object.entries(functionCategories).length - 1) {
122
+ acc.push(new Separator(' '));
123
+ }
124
+ return acc;
125
+ }, [])
126
+ .concat([new Separator(' '), { name: chalk.red('āŒ Exit'), value: 'exit' }]),
127
+ pageSize: 17,
128
+ loop: false,
129
+ });
130
+ // Handle exit selection
131
+ if (category === 'exit') {
132
+ console.log(chalk.yellow('\nšŸ‘‹ Goodbye!\n'));
133
+ return;
134
+ }
135
+ const selectedCategory = functionCategories[category];
136
+ // Function selection loop
137
+ while (true) {
138
+ const functionName = await select({
139
+ message: chalk.cyan(`Choose a ${selectedCategory.name} function:`),
140
+ choices: selectedCategory.functions
141
+ .reduce((acc, func, index) => {
142
+ acc.push({ name: `${func.name} - ${func.description}`, value: func.name });
143
+ if (index < selectedCategory.functions.length - 1) {
144
+ acc.push(new Separator(' '));
145
+ }
146
+ return acc;
147
+ }, [])
148
+ .concat([
149
+ new Separator(' '),
150
+ { name: chalk.gray('ā¬…ļø Back to categories'), value: 'back' },
151
+ { name: chalk.red('āŒ Exit'), value: 'exit' },
152
+ ]),
153
+ pageSize: 12,
154
+ loop: false,
155
+ });
156
+ // Handle navigation options
157
+ if (functionName === 'exit') {
158
+ console.log(chalk.yellow('\nšŸ‘‹ Goodbye!\n'));
159
+ return;
160
+ }
161
+ if (functionName === 'back') {
162
+ break; // Break out of function selection loop, go back to categories
163
+ }
164
+ // Show the command
165
+ console.log(chalk.green(`\nšŸš€ You can run this command:`));
166
+ console.log(chalk.yellow(`js-common ${category} ${functionName} [arguments]`));
167
+ console.log(chalk.gray(`\nFor detailed usage, run: js-common ${category} ${functionName} --help\n`));
168
+ return; // Exit after showing the command
169
+ }
170
+ }
171
+ }
172
+ catch (error) {
173
+ if (error && typeof error === 'object' && 'isTtyError' in error) {
174
+ console.log(chalk.red('\nāŒ Interactive mode requires a TTY terminal'));
175
+ }
176
+ else {
177
+ console.log(chalk.red('\nāŒ Something went wrong in interactive mode'));
178
+ }
179
+ process.exit(1);
180
+ }
181
+ });
182
+ // Add command - help developers integrate functions into their projects
183
+ program
184
+ .command('add')
185
+ .alias('a')
186
+ .description('šŸ”§ Add utility functions to your project')
187
+ .action(async () => {
188
+ // Welcome message
189
+ console.log(gradient.pastel('\nšŸ”§ Function Integration Helper\n'));
190
+ try {
191
+ const category = await select({
192
+ message: chalk.cyan('Which category of utilities do you need?'),
193
+ choices: Object.entries(functionCategories)
194
+ .reduce((acc, [key, cat], index) => {
195
+ acc.push({ name: cat.name, value: key });
196
+ if (index < Object.entries(functionCategories).length - 1) {
197
+ acc.push(new Separator(' '));
198
+ }
199
+ return acc;
200
+ }, [])
201
+ .concat([new Separator(' '), { name: chalk.red('āŒ Exit'), value: 'exit' }]),
202
+ pageSize: 17,
203
+ loop: false,
204
+ });
205
+ // Handle exit selection
206
+ if (category === 'exit') {
207
+ console.log(chalk.yellow('\nšŸ‘‹ Goodbye!\n'));
208
+ return;
209
+ }
210
+ const selectedCategory = functionCategories[category];
211
+ const functions = await checkbox({
212
+ message: chalk.cyan(`Select ${selectedCategory.name} functions to add:`),
213
+ choices: selectedCategory.functions.map((func) => ({
214
+ name: `${func.name} - ${func.description}`,
215
+ value: func.name,
216
+ checked: false,
217
+ })),
218
+ pageSize: 10,
219
+ loop: false,
220
+ });
221
+ if (functions.length === 0) {
222
+ console.log(chalk.yellow('\nšŸ“ No functions selected. Exiting...\n'));
223
+ return;
224
+ }
225
+ console.log(chalk.green('\nšŸ“¦ Import Statement:'));
226
+ console.log(chalk.white(`import { ${functions.join(', ')} } from '@rtorcato/js-common/${category}'`));
227
+ console.log(chalk.green('\nšŸ“ Usage Examples:'));
228
+ functions.forEach((func) => {
229
+ const funcInfo = selectedCategory.functions.find((f) => f.name === func);
230
+ if (funcInfo) {
231
+ console.log(chalk.gray(`\n// ${funcInfo.description}`));
232
+ generateUsageExample(category, func);
233
+ }
234
+ });
235
+ console.log(chalk.green('\nšŸ“š Documentation:'));
236
+ console.log(chalk.blue(`https://github.com/rtorcato/js-common#${category}-utilities`));
237
+ console.log(chalk.yellow('\nšŸ’” Tip: Run ') +
238
+ chalk.green(`js-common ${category} ${functions[0]} --help`) +
239
+ chalk.yellow(' for detailed CLI usage\n'));
240
+ }
241
+ catch (error) {
242
+ if (error && typeof error === 'object' && 'isTtyError' in error) {
243
+ console.log(chalk.red('\nāŒ Interactive mode requires a TTY terminal'));
244
+ }
245
+ else {
246
+ console.log(chalk.red('\nāŒ Something went wrong'));
247
+ }
248
+ process.exit(1);
249
+ }
250
+ });
251
+ // Helper function to generate usage examples
252
+ function generateUsageExample(category, functionName) {
253
+ const examples = {
254
+ date: {
255
+ today: `const today = today(); // "${new Date().toISOString().split('T')[0]}"`,
256
+ now: `const timestamp = now(); // ${Date.now()}`,
257
+ between: `const days = daysBetween('2023-01-01', '2023-12-31'); // 364`,
258
+ },
259
+ math: {
260
+ sum: `const total = sum([1, 2, 3, 4, 5]); // 15`,
261
+ avg: `const average = avg([10, 20, 30]); // 20`,
262
+ random: `const num = randomBetween(1, 100); // 42`,
263
+ round: `const rounded = roundTo(3.14159, 2); // 3.14`,
264
+ clamp: `const clamped = clamp(150, 0, 100); // 100`,
265
+ },
266
+ text: {
267
+ capitalize: `const text = capitalize('hello world'); // "Hello world"`,
268
+ title: `const title = toTitleCase('hello world'); // "Hello World"`,
269
+ pad: `const padded = padWithZeros(42, 5); // "00042"`,
270
+ },
271
+ file: {
272
+ exists: `const exists = await fileExists('package.json'); // true`,
273
+ ext: `const extension = getFileExtension('file.txt'); // "txt"`,
274
+ },
275
+ security: {
276
+ password: `const isStrong = checkPasswordStrength('MyPass123!'); // true`,
277
+ token: `const token = generateSecureToken(32); // "abc123..."`,
278
+ },
279
+ validate: {
280
+ url: `const isValid = isValidUrl('https://example.com'); // true`,
281
+ },
282
+ system: {
283
+ pid: `const processId = getProcessId(); // 12345`,
284
+ uptime: `const uptime = getProcessUptime(); // 123.45`,
285
+ ci: `const isCI = isCiEnvironment(); // false`,
286
+ 'node-version': `const version = getNodeVersion(); // "18.17.0"`,
287
+ },
288
+ };
289
+ const example = examples[category]?.[functionName];
290
+ if (example) {
291
+ console.log(chalk.white(example));
292
+ }
293
+ }
294
+ // Date commands
295
+ const dateCmd = program.command('date').description('šŸ“… Date and time utilities');
296
+ dateCmd
297
+ .command('today')
298
+ .description("Get today's date (YYYY-MM-DD)")
299
+ .action(() => {
300
+ console.log(new Date().toISOString().split('T')[0]);
301
+ });
302
+ dateCmd
303
+ .command('now')
304
+ .description('Get current timestamp')
305
+ .option('-i, --iso', 'ISO format')
306
+ .option('-t, --time', 'Time only')
307
+ .action((options) => {
308
+ if (options.iso) {
309
+ console.log(new Date().toISOString());
310
+ }
311
+ else if (options.time) {
312
+ console.log(new Date().toTimeString());
313
+ }
314
+ else {
315
+ console.log(Date.now());
316
+ }
317
+ });
318
+ dateCmd
319
+ .command('between')
320
+ .description('Calculate days between dates')
321
+ .argument('<date1>', 'First date (YYYY-MM-DD)')
322
+ .argument('<date2>', 'Second date (YYYY-MM-DD)')
323
+ .action((date1, date2) => {
324
+ const d1 = new Date(date1);
325
+ const d2 = new Date(date2);
326
+ const timeDiff = Math.abs(d2.getTime() - d1.getTime());
327
+ const daysDiff = Math.ceil(timeDiff / (1000 * 3600 * 24));
328
+ console.log(`${daysDiff} days`);
329
+ });
330
+ // Math commands
331
+ const mathCmd = program.command('math').description('šŸ”¢ Mathematical utilities');
332
+ mathCmd
333
+ .command('sum')
334
+ .description('Calculate sum of numbers')
335
+ .argument('<numbers...>', 'Numbers to sum')
336
+ .action((numbers) => {
337
+ const nums = numbers.map(Number).filter((n) => !Number.isNaN(n));
338
+ console.log(nums.reduce((a, b) => a + b, 0));
339
+ });
340
+ mathCmd
341
+ .command('avg')
342
+ .description('Calculate average of numbers')
343
+ .argument('<numbers...>', 'Numbers to average')
344
+ .action((numbers) => {
345
+ const nums = numbers.map(Number).filter((n) => !Number.isNaN(n));
346
+ console.log(nums.reduce((a, b) => a + b, 0) / nums.length);
347
+ });
348
+ mathCmd
349
+ .command('random')
350
+ .description('Generate random number')
351
+ .argument('<min>', 'Minimum value')
352
+ .argument('<max>', 'Maximum value')
353
+ .action((min, max) => {
354
+ const minNum = Number(min);
355
+ const maxNum = Number(max);
356
+ console.log(Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum);
357
+ });
358
+ mathCmd
359
+ .command('round')
360
+ .description('Round to decimal places')
361
+ .argument('<number>', 'Number to round')
362
+ .argument('<decimals>', 'Decimal places')
363
+ .action((number, decimals) => {
364
+ const num = Number(number);
365
+ const dec = Number(decimals);
366
+ console.log(Number(num.toFixed(dec)));
367
+ });
368
+ mathCmd
369
+ .command('clamp')
370
+ .description('Clamp between min/max')
371
+ .argument('<number>', 'Number to clamp')
372
+ .argument('<min>', 'Minimum value')
373
+ .argument('<max>', 'Maximum value')
374
+ .action((number, min, max) => {
375
+ const num = Number(number);
376
+ const minVal = Number(min);
377
+ const maxVal = Number(max);
378
+ console.log(Math.min(Math.max(num, minVal), maxVal));
379
+ });
380
+ // Simple implementations for other commands
381
+ const textCmd = program.command('text').description('šŸ“ Text formatting utilities');
382
+ textCmd
383
+ .command('capitalize')
384
+ .description('Capitalize first letter')
385
+ .argument('<text>', 'Text to capitalize')
386
+ .action((text) => {
387
+ console.log(text.charAt(0).toUpperCase() + text.slice(1));
388
+ });
389
+ textCmd
390
+ .command('title')
391
+ .description('Convert to title case')
392
+ .argument('<text>', 'Text to convert')
393
+ .action((text) => {
394
+ console.log(text.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase()));
395
+ });
396
+ // System commands
397
+ const systemCmd = program.command('system').description('šŸ’» System information utilities');
398
+ systemCmd
399
+ .command('pid')
400
+ .description('Get process ID')
401
+ .action(() => {
402
+ console.log(process.pid);
403
+ });
404
+ systemCmd
405
+ .command('uptime')
406
+ .description('Get process uptime')
407
+ .action(() => {
408
+ console.log(`${Math.floor(process.uptime())} seconds`);
409
+ });
410
+ systemCmd
411
+ .command('node-version')
412
+ .description('Get Node.js version')
413
+ .action(() => {
414
+ console.log(process.version);
415
+ });
416
+ // Handle --list option before parsing
417
+ if (process.argv.includes('--list') || process.argv.includes('-l')) {
418
+ console.log(chalk.cyan('\n✨ Available Functions:\n'));
419
+ Object.entries(functionCategories).forEach(([key, category]) => {
420
+ console.log(chalk.bold(category.name));
421
+ console.log(); // Add space after category header
422
+ category.functions.forEach((func) => {
423
+ console.log(chalk.gray(` js-common ${key} ${func.name}`) + chalk.dim(` - ${func.description}`));
424
+ });
425
+ console.log(); // Add space after function list
426
+ });
427
+ console.log(chalk.yellow('šŸ’” Use ') +
428
+ chalk.green('js-common add') +
429
+ chalk.yellow(' to integrate functions into your project'));
430
+ console.log(chalk.yellow('šŸ’” Use ') +
431
+ chalk.green('js-common interactive') +
432
+ chalk.yellow(' for interactive mode'));
433
+ console.log(chalk.yellow('šŸ’” Use ') +
434
+ chalk.green('js-common <command> --help') +
435
+ chalk.yellow(' for detailed help\n'));
436
+ process.exit(0);
437
+ }
438
+ // Parse command line arguments
439
+ program.parse();