andrud 1.0.0 → 1.0.2
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/README.md +69 -83
- package/dist/__tests__/context.test.d.ts +5 -0
- package/dist/__tests__/context.test.d.ts.map +1 -0
- package/dist/__tests__/context.test.js +86 -0
- package/dist/__tests__/context.test.js.map +1 -0
- package/dist/__tests__/generator.test.d.ts +5 -0
- package/dist/__tests__/generator.test.d.ts.map +1 -0
- package/dist/__tests__/generator.test.js +83 -0
- package/dist/__tests__/generator.test.js.map +1 -0
- package/dist/__tests__/validation.test.d.ts +5 -0
- package/dist/__tests__/validation.test.d.ts.map +1 -0
- package/dist/__tests__/validation.test.js +81 -0
- package/dist/__tests__/validation.test.js.map +1 -0
- package/dist/cli/commands/create.d.ts +10 -0
- package/dist/cli/commands/create.d.ts.map +1 -0
- package/dist/cli/commands/create.js +203 -0
- package/dist/cli/commands/create.js.map +1 -0
- package/dist/cli/commands/info.d.ts +13 -0
- package/dist/cli/commands/info.d.ts.map +1 -0
- package/dist/cli/commands/info.js +153 -0
- package/dist/cli/commands/info.js.map +1 -0
- package/dist/cli/commands/init.d.ts +15 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +141 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/list.d.ts +18 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +122 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/new.d.ts +22 -0
- package/dist/cli/commands/new.d.ts.map +1 -0
- package/dist/cli/commands/new.js +245 -0
- package/dist/cli/commands/new.js.map +1 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +99 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/config.d.ts +89 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +151 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/context.d.ts +47 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +175 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/generator.d.ts +44 -0
- package/dist/core/generator.d.ts.map +1 -0
- package/{src/core/generator.ts → dist/core/generator.js} +394 -484
- package/dist/core/generator.js.map +1 -0
- package/dist/core/types.d.ts +125 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +22 -0
- package/dist/core/types.js.map +1 -0
- package/dist/templates/index.d.ts +36 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +141 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/ui/colors.d.ts +40 -0
- package/dist/ui/colors.d.ts.map +1 -0
- package/dist/ui/colors.js +117 -0
- package/dist/ui/colors.js.map +1 -0
- package/dist/ui/output.d.ts +69 -0
- package/dist/ui/output.d.ts.map +1 -0
- package/dist/ui/output.js +199 -0
- package/dist/ui/output.js.map +1 -0
- package/dist/ui/prompts.d.ts +20 -0
- package/dist/ui/prompts.d.ts.map +1 -0
- package/dist/ui/prompts.js +118 -0
- package/dist/ui/prompts.js.map +1 -0
- package/dist/ui/spinners.d.ts +30 -0
- package/dist/ui/spinners.d.ts.map +1 -0
- package/dist/ui/spinners.js +74 -0
- package/dist/ui/spinners.js.map +1 -0
- package/dist/ui/types.d.ts +35 -0
- package/dist/ui/types.d.ts.map +1 -0
- package/dist/ui/types.js +5 -0
- package/dist/ui/types.js.map +1 -0
- package/dist/utils/filesystem.d.ts +38 -0
- package/dist/utils/filesystem.d.ts.map +1 -0
- package/dist/utils/filesystem.js +181 -0
- package/dist/utils/filesystem.js.map +1 -0
- package/dist/utils/logger.d.ts +27 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +52 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/object.d.ts +140 -0
- package/dist/utils/object.d.ts.map +1 -0
- package/dist/utils/object.js +385 -0
- package/dist/utils/object.js.map +1 -0
- package/dist/utils/validation.d.ts +35 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +270 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +8 -19
- package/CHANGELOG.md +0 -70
- package/CODE_REVIEW_ANALYSIS.md +0 -177
- package/CONTRIBUTING.md +0 -132
- package/FIXES_IMPLEMENTED.md +0 -546
- package/src/__tests__/context.test.ts +0 -133
- package/src/__tests__/generator.test.ts +0 -107
- package/src/__tests__/validation.test.ts +0 -105
- package/src/cli/commands/create.ts +0 -252
- package/src/cli/commands/info.ts +0 -178
- package/src/cli/commands/init.ts +0 -186
- package/src/cli/commands/list.ts +0 -156
- package/src/cli/commands/new.ts +0 -316
- package/src/cli/index.ts +0 -116
- package/src/core/config.ts +0 -172
- package/src/core/context.ts +0 -212
- package/src/core/types.ts +0 -184
- package/src/templates/index.ts +0 -162
- package/src/types/gradient-string.d.ts +0 -25
- package/src/ui/colors.ts +0 -139
- package/src/ui/output.ts +0 -230
- package/src/ui/prompts.ts +0 -170
- package/src/ui/spinners.ts +0 -95
- package/src/ui/types.ts +0 -41
- package/src/utils/filesystem.ts +0 -222
- package/src/utils/logger.ts +0 -67
- package/src/utils/object.ts +0 -456
- package/src/utils/validation.ts +0 -345
- package/tsconfig.json +0 -25
package/src/ui/colors.ts
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Color and text formatting utilities using picocolors and gradient-string
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import pc from 'picocolors';
|
|
6
|
-
import gradient from 'gradient-string';
|
|
7
|
-
|
|
8
|
-
// Style function to apply color based on type
|
|
9
|
-
export function style(text: string, type: 'primary' | 'success' | 'warning' | 'error' | 'info' | 'muted'): string {
|
|
10
|
-
switch (type) {
|
|
11
|
-
case 'primary':
|
|
12
|
-
return pc.cyan(text);
|
|
13
|
-
case 'success':
|
|
14
|
-
return pc.green(text);
|
|
15
|
-
case 'warning':
|
|
16
|
-
return pc.yellow(text);
|
|
17
|
-
case 'error':
|
|
18
|
-
return pc.red(text);
|
|
19
|
-
case 'info':
|
|
20
|
-
return pc.blue(text);
|
|
21
|
-
case 'muted':
|
|
22
|
-
return pc.gray(text);
|
|
23
|
-
default:
|
|
24
|
-
return text;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Text formatting functions
|
|
29
|
-
export const bold = (text: string): string => pc.bold(text);
|
|
30
|
-
export const dim = (text: string): string => pc.dim(text);
|
|
31
|
-
export const underline = (text: string): string => pc.underline(text);
|
|
32
|
-
export const inverse = (text: string): string => pc.inverse(text);
|
|
33
|
-
export const italic = (text: string): string => pc.italic(text);
|
|
34
|
-
|
|
35
|
-
// Color functions
|
|
36
|
-
export const primary = (text: string): string => pc.cyan(text);
|
|
37
|
-
export const success = (text: string): string => pc.green(text);
|
|
38
|
-
export const warning = (text: string): string => pc.yellow(text);
|
|
39
|
-
export const error = (text: string): string => pc.red(text);
|
|
40
|
-
export const info = (text: string): string => pc.blue(text);
|
|
41
|
-
export const muted = (text: string): string => pc.gray(text);
|
|
42
|
-
|
|
43
|
-
// Aliases for backward compatibility
|
|
44
|
-
export const cyan = (text: string): string => pc.cyan(text);
|
|
45
|
-
export const green = (text: string): string => pc.green(text);
|
|
46
|
-
export const yellow = (text: string): string => pc.yellow(text);
|
|
47
|
-
export const red = (text: string): string => pc.red(text);
|
|
48
|
-
export const blue = (text: string): string => pc.blue(text);
|
|
49
|
-
export const gray = (text: string): string => pc.gray(text);
|
|
50
|
-
|
|
51
|
-
// Gradient functions using gradient-string
|
|
52
|
-
export function gradientTeen(text: string): string {
|
|
53
|
-
const g = gradient('#00C9FF', '#92FE9D');
|
|
54
|
-
return g(text);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function gradientRainbow(text: string): string {
|
|
58
|
-
return gradient.rainbow(text);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function gradientCool(text: string): string {
|
|
62
|
-
const g = gradient('#667eea', '#764ba2');
|
|
63
|
-
return g(text);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function gradientPassion(text: string): string {
|
|
67
|
-
const g = gradient('#ee0979', '#ff6a00');
|
|
68
|
-
return g(text);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Section formatting
|
|
72
|
-
export const header = (text: string): string => {
|
|
73
|
-
return bold(pc.cyan(pc.bold(`\n${text}\n`)));
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
export const section = (text: string): string => {
|
|
77
|
-
return bold(pc.white(text));
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export const subsection = (text: string): string => {
|
|
81
|
-
return pc.dim(text);
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
// Symbol functions for visual elements
|
|
85
|
-
export const checkmark = (text?: string): string => {
|
|
86
|
-
const symbol = pc.green('✓');
|
|
87
|
-
return text ? `${symbol} ${text}` : symbol;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
export const crossmark = (text?: string): string => {
|
|
91
|
-
const symbol = pc.red('✗');
|
|
92
|
-
return text ? `${symbol} ${text}` : symbol;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
// Arrow and bullet functions
|
|
96
|
-
export const bullet = (text: string): string => {
|
|
97
|
-
return `${pc.gray('•')} ${text}`;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
export const numbered = (num: number, text: string): string => {
|
|
101
|
-
return `${pc.gray(`${num}.`)} ${text}`;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
export const arrow = (text: string): string => {
|
|
105
|
-
return `${pc.gray('→')} ${text}`;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
export const rightArrow = (from: string, to: string): string => {
|
|
109
|
-
return `${from} ${pc.gray('→')} ${to}`;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// Progress bar
|
|
113
|
-
export function progressBar(current: number, total: number, width: number = 20): string {
|
|
114
|
-
const percentage = Math.min(Math.max(current / total, 0), 1);
|
|
115
|
-
const filled = Math.round(percentage * width);
|
|
116
|
-
const empty = width - filled;
|
|
117
|
-
const filledBar = pc.green('█'.repeat(filled));
|
|
118
|
-
const emptyBar = pc.gray('░'.repeat(empty));
|
|
119
|
-
const percentText = pc.cyan(`${Math.round(percentage * 100)}%`);
|
|
120
|
-
return `${filledBar}${emptyBar} ${percentText}`;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Separator line
|
|
124
|
-
export function printSeparator(char: string = '─', length: number = 60): string {
|
|
125
|
-
return pc.gray(char.repeat(length));
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Terminal control functions
|
|
129
|
-
export function clearLine(): void {
|
|
130
|
-
process.stdout.write('\r\x1B[K');
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export function cursorHide(): void {
|
|
134
|
-
process.stdout.write('\x1B[?25l');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function cursorShow(): void {
|
|
138
|
-
process.stdout.write('\x1B[?25h');
|
|
139
|
-
}
|
package/src/ui/output.ts
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Output formatting utilities
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import pc from 'picocolors';
|
|
6
|
-
import { style, bold, dim, muted, primary, success, error, warning, info, section, printSeparator } from './colors.js';
|
|
7
|
-
import gradient from 'gradient-string';
|
|
8
|
-
|
|
9
|
-
export interface Logger {
|
|
10
|
-
log: (message: string, ...args: unknown[]) => void;
|
|
11
|
-
info: (message: string, ...args: unknown[]) => void;
|
|
12
|
-
success: (message: string, ...args: unknown[]) => void;
|
|
13
|
-
warn: (message: string, ...args: unknown[]) => void;
|
|
14
|
-
error: (message: string, ...args: unknown[]) => void;
|
|
15
|
-
debug: (message: string, ...args: unknown[]) => void;
|
|
16
|
-
verbose: (message: string, ...args: unknown[]) => void;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const logger: Logger = {
|
|
20
|
-
log: (message: string, ...args: unknown[]) => {
|
|
21
|
-
console.log(message, ...args);
|
|
22
|
-
},
|
|
23
|
-
info: (message: string, ...args: unknown[]) => {
|
|
24
|
-
console.log(pc.blue('INFO: ') + message, ...args);
|
|
25
|
-
},
|
|
26
|
-
success: (message: string, ...args: unknown[]) => {
|
|
27
|
-
console.log(pc.green('SUCCESS: ') + message, ...args);
|
|
28
|
-
},
|
|
29
|
-
warn: (message: string, ...args: unknown[]) => {
|
|
30
|
-
console.log(pc.yellow('WARN: ') + message, ...args);
|
|
31
|
-
},
|
|
32
|
-
error: (message: string, ...args: unknown[]) => {
|
|
33
|
-
console.error(pc.red('ERROR: ') + message, ...args);
|
|
34
|
-
},
|
|
35
|
-
debug: (message: string, ...args: unknown[]) => {
|
|
36
|
-
if (process.env.DEBUG || process.env.VERBOSE) {
|
|
37
|
-
console.log(pc.gray('DEBUG: ') + message, ...args);
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
verbose: (message: string, ...args: unknown[]) => {
|
|
41
|
-
if (process.env.VERBOSE) {
|
|
42
|
-
console.log(pc.gray('VERBOSE: ') + message, ...args);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export { logger };
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Creates a gradient from cyan to green (singleton pattern to prevent memory leak)
|
|
51
|
-
*/
|
|
52
|
-
const teenGradient = gradient('#00C9FF', '#92FE9D');
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Prints the welcome banner
|
|
56
|
-
*/
|
|
57
|
-
export function printWelcome(): void {
|
|
58
|
-
console.log('');
|
|
59
|
-
console.log(pc.cyan(`
|
|
60
|
-
╔═══════════════════════════════════════════════════════════╗
|
|
61
|
-
║ ║
|
|
62
|
-
║ ██╗ ██╗ █████╗ ██████╗ ███████╗███╗ ██╗ ║
|
|
63
|
-
║ ██║ ██║██╔══██╗██╔══██╗ ██╔════╝████╗ ██║ ║
|
|
64
|
-
║ ██║ █╗ ██║███████║██████╔╝ █████╗ ██╔██╗ ██║ ║
|
|
65
|
-
║ ██║███╗██║██╔══██║██╔══██╗ ██╔══╝ ██║╚██╗██║ ║
|
|
66
|
-
║ ╚███╔███╔╝██║ ██║██║ ██║ ███████╗██║ ╚████║ ║
|
|
67
|
-
║ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ║
|
|
68
|
-
║ ║
|
|
69
|
-
║ Android Project Generator v1.0.0 ║
|
|
70
|
-
║ Modern Android Development CLI ║
|
|
71
|
-
║ ║
|
|
72
|
-
╚═══════════════════════════════════════════════════════════╝
|
|
73
|
-
`));
|
|
74
|
-
console.log('');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Prints the goodbye message
|
|
79
|
-
*/
|
|
80
|
-
export function printGoodbye(success: boolean = true): void {
|
|
81
|
-
// Uses module-level teenGradient constant
|
|
82
|
-
if (success) {
|
|
83
|
-
console.log('');
|
|
84
|
-
console.log(teenGradient(`
|
|
85
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
86
|
-
|
|
87
|
-
Thanks for using andrud! Happy coding! 🚀
|
|
88
|
-
|
|
89
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
90
|
-
`));
|
|
91
|
-
} else {
|
|
92
|
-
console.log('');
|
|
93
|
-
console.log(pc.red(`
|
|
94
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
95
|
-
|
|
96
|
-
Operation failed. Please check the error above.
|
|
97
|
-
|
|
98
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
99
|
-
`));
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Prints a success message
|
|
105
|
-
*/
|
|
106
|
-
export function printSuccess(message: string, details?: string): void {
|
|
107
|
-
console.log(pc.green(' ✓ ') + pc.white(message));
|
|
108
|
-
if (details) {
|
|
109
|
-
console.log(pc.gray(` ${details}`));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Prints an error message
|
|
115
|
-
*/
|
|
116
|
-
export function printError(message: string, details?: string): void {
|
|
117
|
-
console.error(pc.red(' ✗ ') + pc.white(message));
|
|
118
|
-
if (details) {
|
|
119
|
-
console.error(pc.gray(` ${details}`));
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Prints a section header
|
|
125
|
-
*/
|
|
126
|
-
export function printSection(text: string): void {
|
|
127
|
-
console.log('');
|
|
128
|
-
console.log(section(text));
|
|
129
|
-
console.log(dim(printSeparator('─', 50)));
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Prints key-value pairs
|
|
134
|
-
*/
|
|
135
|
-
export function printKeyValue(items: Array<{ key: string; value: string }>): void {
|
|
136
|
-
const maxKeyLength = Math.max(...items.map(item => item.key.length));
|
|
137
|
-
items.forEach(item => {
|
|
138
|
-
const padding = ' '.repeat(maxKeyLength - item.key.length);
|
|
139
|
-
console.log(` ${pc.cyan(item.key)}${padding} ${item.value}`);
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Prints a separator line
|
|
145
|
-
*/
|
|
146
|
-
export function printSeparatorLine(char: string = '─', length: number = 60): void {
|
|
147
|
-
console.log(dim(char.repeat(length)));
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Prints a banner with text
|
|
152
|
-
*/
|
|
153
|
-
export function printBanner(text: string, color: 'primary' | 'success' | 'warning' | 'error' = 'primary'): void {
|
|
154
|
-
const padding = 4;
|
|
155
|
-
const line = ' '.repeat(padding);
|
|
156
|
-
const border = '━'.repeat(text.length + padding * 2);
|
|
157
|
-
|
|
158
|
-
console.log('');
|
|
159
|
-
console.log(pc.cyan(' ' + border));
|
|
160
|
-
console.log(pc.cyan(' ║') + ' '.repeat(padding) + text + ' '.repeat(padding) + pc.cyan('║'));
|
|
161
|
-
console.log(pc.cyan(' ' + border));
|
|
162
|
-
console.log('');
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Prints an ASCII box with content
|
|
167
|
-
*/
|
|
168
|
-
export function printAsciiBox(lines: string[], options: { border?: string; padding?: number } = {}): void {
|
|
169
|
-
const border = options.border ?? '─';
|
|
170
|
-
const padding = options.padding ?? 2;
|
|
171
|
-
const maxLength = Math.max(...lines.map(l => l.length));
|
|
172
|
-
const width = maxLength + padding * 2;
|
|
173
|
-
|
|
174
|
-
console.log(pc.gray(' ┌' + border.repeat(width) + '┐'));
|
|
175
|
-
|
|
176
|
-
if (lines.length === 0) {
|
|
177
|
-
console.log(pc.gray(' │' + ' '.repeat(width) + '│'));
|
|
178
|
-
} else {
|
|
179
|
-
lines.forEach(line => {
|
|
180
|
-
const padded = line + ' '.repeat(maxLength - line.length);
|
|
181
|
-
console.log(pc.gray(' │') + ' '.repeat(padding) + padded + ' '.repeat(padding) + pc.gray('│'));
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
console.log(pc.gray(' └' + border.repeat(width) + '┘'));
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Prints a table with columns
|
|
190
|
-
*/
|
|
191
|
-
export function printTable<T>(
|
|
192
|
-
columns: Array<{ header: string; accessor: (row: T) => string; width?: number }>,
|
|
193
|
-
rows: T[]
|
|
194
|
-
): void {
|
|
195
|
-
const colWidths = columns.map(col => {
|
|
196
|
-
const headerWidth = col.header.length;
|
|
197
|
-
const dataWidths = rows.map(row => col.accessor(row).length);
|
|
198
|
-
return col.width ?? Math.max(headerWidth, ...dataWidths);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
// Print header
|
|
202
|
-
const headerRow = columns.map((_col, i) => {
|
|
203
|
-
const width = colWidths[i] ?? 10;
|
|
204
|
-
return _col.header.substring(0, width).padEnd(width);
|
|
205
|
-
});
|
|
206
|
-
console.log(bold(headerRow.join(' ')));
|
|
207
|
-
console.log(pc.gray(colWidths.map(w => '─'.repeat(w)).join(' ')));
|
|
208
|
-
|
|
209
|
-
// Print rows
|
|
210
|
-
rows.forEach(row => {
|
|
211
|
-
const dataRow = columns.map((col, i) => {
|
|
212
|
-
const width = colWidths[i] ?? 10;
|
|
213
|
-
const value = col.accessor(row);
|
|
214
|
-
return value.substring(0, width).padEnd(width);
|
|
215
|
-
});
|
|
216
|
-
console.log(dataRow.join(' '));
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Prints numbered steps
|
|
222
|
-
*/
|
|
223
|
-
export function printSteps(steps: string[]): void {
|
|
224
|
-
console.log('');
|
|
225
|
-
steps.forEach((step, index) => {
|
|
226
|
-
const num = pc.cyan(`${index + 1}.`);
|
|
227
|
-
console.log(` ${num} ${step}`);
|
|
228
|
-
});
|
|
229
|
-
console.log('');
|
|
230
|
-
}
|
package/src/ui/prompts.ts
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wrapper around @clack/prompts with proper TypeScript types
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
select,
|
|
7
|
-
text,
|
|
8
|
-
multiselect,
|
|
9
|
-
confirm,
|
|
10
|
-
isCancel,
|
|
11
|
-
cancel,
|
|
12
|
-
type SelectOptions,
|
|
13
|
-
type TextOptions,
|
|
14
|
-
type MultiSelectOptions,
|
|
15
|
-
type ConfirmOptions
|
|
16
|
-
} from '@clack/prompts';
|
|
17
|
-
|
|
18
|
-
// Re-export core functions
|
|
19
|
-
export {
|
|
20
|
-
select,
|
|
21
|
-
text,
|
|
22
|
-
multiselect,
|
|
23
|
-
confirm,
|
|
24
|
-
isCancel,
|
|
25
|
-
cancel,
|
|
26
|
-
type SelectOptions,
|
|
27
|
-
type TextOptions,
|
|
28
|
-
type MultiSelectOptions,
|
|
29
|
-
type ConfirmOptions
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// Helper function to ask for app name
|
|
33
|
-
export async function askAppName(defaultValue?: string): Promise<string> {
|
|
34
|
-
const value = await text({
|
|
35
|
-
message: '? What is the app name?',
|
|
36
|
-
placeholder: 'MyAwesomeApp',
|
|
37
|
-
defaultValue: defaultValue ?? 'MyAwesomeApp',
|
|
38
|
-
validate: (value: string) => {
|
|
39
|
-
if (!value || value.trim().length === 0) {
|
|
40
|
-
return 'App name cannot be empty';
|
|
41
|
-
}
|
|
42
|
-
if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(value)) {
|
|
43
|
-
return 'App name must start with a letter and contain only letters, numbers, and underscores';
|
|
44
|
-
}
|
|
45
|
-
return undefined;
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
if (isCancel(value)) {
|
|
50
|
-
cancel();
|
|
51
|
-
process.exit(0);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return value as string;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Helper function to ask for package name
|
|
58
|
-
export async function askPackageName(defaultValue?: string): Promise<string> {
|
|
59
|
-
const value = await text({
|
|
60
|
-
message: '? What is the package name?',
|
|
61
|
-
placeholder: 'com.example.myapp',
|
|
62
|
-
defaultValue: defaultValue ?? 'com.example.myapp',
|
|
63
|
-
validate: (value: string) => {
|
|
64
|
-
if (!value || value.trim().length === 0) {
|
|
65
|
-
return 'Package name cannot be empty';
|
|
66
|
-
}
|
|
67
|
-
if (!/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/.test(value)) {
|
|
68
|
-
return 'Package name must be a valid domain structure (e.g., com.example.myapp)';
|
|
69
|
-
}
|
|
70
|
-
return undefined;
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
if (isCancel(value)) {
|
|
75
|
-
cancel();
|
|
76
|
-
process.exit(0);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return value as string;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Helper function to ask for project directory
|
|
83
|
-
export async function askDirectory(defaultValue?: string): Promise<string> {
|
|
84
|
-
const value = await text({
|
|
85
|
-
message: '? In which directory should the project be created?',
|
|
86
|
-
placeholder: './my-app',
|
|
87
|
-
defaultValue: defaultValue ?? './my-app',
|
|
88
|
-
validate: (value: string) => {
|
|
89
|
-
if (!value || value.trim().length === 0) {
|
|
90
|
-
return 'Directory path cannot be empty';
|
|
91
|
-
}
|
|
92
|
-
if (value.includes('..')) {
|
|
93
|
-
return 'Directory path cannot contain ".." to prevent directory traversal';
|
|
94
|
-
}
|
|
95
|
-
return undefined;
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
if (isCancel(value)) {
|
|
100
|
-
cancel();
|
|
101
|
-
process.exit(0);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return value as string;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Template selection helper
|
|
108
|
-
export async function selectTemplate<T extends string>(
|
|
109
|
-
templates: Array<{ label: string; value: T; hint?: string }>
|
|
110
|
-
): Promise<T> {
|
|
111
|
-
const options = templates.map(t => ({
|
|
112
|
-
label: t.label + (t.hint ? ` ${t.hint}` : ''),
|
|
113
|
-
value: t.value
|
|
114
|
-
}));
|
|
115
|
-
|
|
116
|
-
const value = await select({
|
|
117
|
-
message: '? Select a project template',
|
|
118
|
-
options
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
if (isCancel(value)) {
|
|
122
|
-
cancel();
|
|
123
|
-
process.exit(0);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return value as T;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Multi-select helper
|
|
130
|
-
export async function askMultiSelect<T>(
|
|
131
|
-
options: Array<{ label: string; value: T; hint?: string }>,
|
|
132
|
-
message: string = '? Select options',
|
|
133
|
-
min?: number
|
|
134
|
-
): Promise<T[]> {
|
|
135
|
-
const formattedOptions = options.map(o => ({
|
|
136
|
-
label: o.label + (o.hint ? ` (${o.hint})` : ''),
|
|
137
|
-
value: o.value
|
|
138
|
-
}));
|
|
139
|
-
|
|
140
|
-
const value = await multiselect({
|
|
141
|
-
message,
|
|
142
|
-
options: formattedOptions,
|
|
143
|
-
...(min !== undefined ? { min } : {})
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
if (isCancel(value)) {
|
|
147
|
-
cancel();
|
|
148
|
-
process.exit(0);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return value as T[];
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Confirmation helper
|
|
155
|
-
export async function askConfirmation(
|
|
156
|
-
message: string,
|
|
157
|
-
initialValue: boolean = false
|
|
158
|
-
): Promise<boolean> {
|
|
159
|
-
const value = await confirm({
|
|
160
|
-
message: `? ${message}`,
|
|
161
|
-
initialValue
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
if (isCancel(value)) {
|
|
165
|
-
cancel();
|
|
166
|
-
process.exit(0);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return value as boolean;
|
|
170
|
-
}
|
package/src/ui/spinners.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Spinner utilities using ora
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import ora, { type Ora } from 'ora';
|
|
6
|
-
|
|
7
|
-
export class AsyncSpinner {
|
|
8
|
-
private spinner: Ora | null = null;
|
|
9
|
-
private startTime: number = 0;
|
|
10
|
-
|
|
11
|
-
start(text?: string): void {
|
|
12
|
-
this.startTime = Date.now();
|
|
13
|
-
this.spinner = ora({
|
|
14
|
-
text: text ?? 'Loading...',
|
|
15
|
-
spinner: 'dots'
|
|
16
|
-
}).start();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
update(text: string): void {
|
|
20
|
-
if (this.spinner) {
|
|
21
|
-
this.spinner.text = text;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
succeed(text?: string): void {
|
|
26
|
-
if (this.spinner) {
|
|
27
|
-
const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
|
|
28
|
-
this.spinner.succeed(text ? `${text} (${elapsed}s)` : undefined);
|
|
29
|
-
this.spinner = null;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
fail(text?: string): void {
|
|
34
|
-
if (this.spinner) {
|
|
35
|
-
const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
|
|
36
|
-
this.spinner.fail(text ? `${text} (${elapsed}s)` : undefined);
|
|
37
|
-
this.spinner = null;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
warn(text?: string): void {
|
|
42
|
-
if (this.spinner) {
|
|
43
|
-
this.spinner.warn(text);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
stop(): void {
|
|
48
|
-
if (this.spinner) {
|
|
49
|
-
this.spinner.stop();
|
|
50
|
-
this.spinner = null;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
isSpinning(): boolean {
|
|
55
|
-
return this.spinner?.isSpinning ?? false;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface WithSpinnerOptions {
|
|
60
|
-
text?: string;
|
|
61
|
-
successText?: string;
|
|
62
|
-
failText?: string;
|
|
63
|
-
warnText?: string;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Executes an async function with a spinner
|
|
68
|
-
*/
|
|
69
|
-
export async function withSpinner<T>(
|
|
70
|
-
text: string,
|
|
71
|
-
fn: () => Promise<T>,
|
|
72
|
-
options?: WithSpinnerOptions
|
|
73
|
-
): Promise<T> {
|
|
74
|
-
const spinner = new AsyncSpinner();
|
|
75
|
-
spinner.start(text);
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
const result = await fn();
|
|
79
|
-
spinner.succeed(options?.successText);
|
|
80
|
-
return result;
|
|
81
|
-
} catch (error) {
|
|
82
|
-
spinner.fail(options?.failText ?? (error instanceof Error ? error.message : 'An error occurred'));
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Creates a simple loading indicator
|
|
89
|
-
*/
|
|
90
|
-
export function createSpinner(text?: string): Ora {
|
|
91
|
-
return ora({
|
|
92
|
-
text,
|
|
93
|
-
spinner: 'dots'
|
|
94
|
-
});
|
|
95
|
-
}
|
package/src/ui/types.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type definitions for UI components and utilities
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export type StyleType = 'primary' | 'success' | 'warning' | 'error' | 'info' | 'muted';
|
|
6
|
-
|
|
7
|
-
export interface SpinnerOptions {
|
|
8
|
-
text?: string;
|
|
9
|
-
color?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface TableColumn<T> {
|
|
13
|
-
header: string;
|
|
14
|
-
accessor: (row: T) => string;
|
|
15
|
-
width?: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface ConfirmationOptions {
|
|
19
|
-
message: string;
|
|
20
|
-
initialValue?: boolean;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface SelectOption<T> {
|
|
24
|
-
label: string;
|
|
25
|
-
value: T;
|
|
26
|
-
hint?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface TextInputOptions {
|
|
30
|
-
message: string;
|
|
31
|
-
placeholder?: string;
|
|
32
|
-
defaultValue?: string;
|
|
33
|
-
validate?: (value: string) => string | null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface MultiSelectOptions<T> {
|
|
37
|
-
message: string;
|
|
38
|
-
options: SelectOption<T>[];
|
|
39
|
-
min?: number;
|
|
40
|
-
max?: number;
|
|
41
|
-
}
|