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/cli/commands/new.ts
DELETED
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* New project creation command with interactive prompts
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
select,
|
|
7
|
-
text,
|
|
8
|
-
multiselect,
|
|
9
|
-
confirm,
|
|
10
|
-
isCancel,
|
|
11
|
-
cancel
|
|
12
|
-
} from '@clack/prompts';
|
|
13
|
-
import {
|
|
14
|
-
printWelcome,
|
|
15
|
-
printSuccess,
|
|
16
|
-
printError,
|
|
17
|
-
printSection,
|
|
18
|
-
printKeyValue,
|
|
19
|
-
printGoodbye,
|
|
20
|
-
printSteps
|
|
21
|
-
} from '../../ui/output.js';
|
|
22
|
-
import { gradientTeen } from '../../ui/colors.js';
|
|
23
|
-
import { validateAppName, validatePackageStructure, validateDirectoryPath } from '../../utils/validation.js';
|
|
24
|
-
import { generateProject } from '../../core/generator.js';
|
|
25
|
-
import { buildDefaultProjectContext, buildTemplateContext } from '../../core/context.js';
|
|
26
|
-
import { exists, getCurrentWorkingDirectory } from '../../utils/filesystem.js';
|
|
27
|
-
import { getTemplateMetadata, getAllTemplates } from '../../templates/index.js';
|
|
28
|
-
import pc from 'picocolors';
|
|
29
|
-
import type { TemplateType } from '../../core/types.js';
|
|
30
|
-
|
|
31
|
-
interface NewCommandOptions {
|
|
32
|
-
name?: string;
|
|
33
|
-
template?: string;
|
|
34
|
-
packageName?: string;
|
|
35
|
-
directory?: string;
|
|
36
|
-
minSdk?: number;
|
|
37
|
-
targetSdk?: number;
|
|
38
|
-
force?: boolean;
|
|
39
|
-
skipInstall?: boolean;
|
|
40
|
-
git?: boolean;
|
|
41
|
-
kotlin?: boolean;
|
|
42
|
-
verbose?: boolean;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Create a new Android project
|
|
47
|
-
*/
|
|
48
|
-
export async function createNewCommand(
|
|
49
|
-
name?: string,
|
|
50
|
-
options: NewCommandOptions = {}
|
|
51
|
-
): Promise<void> {
|
|
52
|
-
try {
|
|
53
|
-
// Print welcome banner
|
|
54
|
-
printWelcome();
|
|
55
|
-
|
|
56
|
-
// Get all available templates
|
|
57
|
-
const templates = getAllTemplates();
|
|
58
|
-
|
|
59
|
-
// Step 1: Select template
|
|
60
|
-
let selectedTemplate: string;
|
|
61
|
-
if (options.template) {
|
|
62
|
-
const meta = getTemplateMetadata(options.template as TemplateType);
|
|
63
|
-
if (!meta) {
|
|
64
|
-
printError(`Unknown template: ${options.template}`);
|
|
65
|
-
console.log('Available templates:');
|
|
66
|
-
templates.forEach(t => console.log(` - ${t.id}`));
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
selectedTemplate = options.template;
|
|
70
|
-
} else {
|
|
71
|
-
const templateOptions = templates.map(t => ({
|
|
72
|
-
label: `${t.name}`,
|
|
73
|
-
value: t.id,
|
|
74
|
-
hint: t.keywords.slice(0, 2).join(', ')
|
|
75
|
-
}));
|
|
76
|
-
|
|
77
|
-
const templateResult = await select({
|
|
78
|
-
message: '? Select a project template',
|
|
79
|
-
options: templateOptions
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
if (isCancel(templateResult)) {
|
|
83
|
-
cancel();
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
selectedTemplate = templateResult as string;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
printSection(`Template: ${selectedTemplate}`);
|
|
91
|
-
|
|
92
|
-
// Step 2: Get app name
|
|
93
|
-
let appName: string;
|
|
94
|
-
if (name) {
|
|
95
|
-
const validation = validateAppName(name);
|
|
96
|
-
if (!validation.valid) {
|
|
97
|
-
printError('Invalid app name', validation.errors.join(', '));
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
appName = validation.normalized ?? name;
|
|
101
|
-
} else {
|
|
102
|
-
const nameResult = await text({
|
|
103
|
-
message: '? What is the app name?',
|
|
104
|
-
placeholder: 'MyAwesomeApp',
|
|
105
|
-
defaultValue: 'MyAwesomeApp',
|
|
106
|
-
validate: (value: string) => {
|
|
107
|
-
const result = validateAppName(value);
|
|
108
|
-
if (!result.valid) {
|
|
109
|
-
return result.errors[0];
|
|
110
|
-
}
|
|
111
|
-
return undefined;
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
if (isCancel(nameResult)) {
|
|
116
|
-
cancel();
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const validation = validateAppName(nameResult as string);
|
|
121
|
-
if (!validation.valid) {
|
|
122
|
-
printError('Invalid app name', validation.errors.join(', '));
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
appName = validation.normalized ?? (nameResult as string);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Step 3: Get package name
|
|
129
|
-
let packageName: string;
|
|
130
|
-
if (options.packageName) {
|
|
131
|
-
const validation = validatePackageStructure(options.packageName);
|
|
132
|
-
if (!validation.valid) {
|
|
133
|
-
printError('Invalid package name', validation.errors.join(', '));
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
packageName = options.packageName;
|
|
137
|
-
} else {
|
|
138
|
-
// Generate default package name from app name
|
|
139
|
-
const defaultPackage = `com.example.${appName.toLowerCase().replace(/[^a-z0-9]/g, '')}`;
|
|
140
|
-
|
|
141
|
-
const packageResult = await text({
|
|
142
|
-
message: '? What is the package name?',
|
|
143
|
-
placeholder: 'com.example.myapp',
|
|
144
|
-
defaultValue: defaultPackage,
|
|
145
|
-
validate: (value: string) => {
|
|
146
|
-
const result = validatePackageStructure(value);
|
|
147
|
-
if (!result.valid) {
|
|
148
|
-
return result.errors[0];
|
|
149
|
-
}
|
|
150
|
-
return undefined;
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
if (isCancel(packageResult)) {
|
|
155
|
-
cancel();
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const validation = validatePackageStructure(packageResult as string);
|
|
160
|
-
if (!validation.valid) {
|
|
161
|
-
printError('Invalid package name', validation.errors.join(', '));
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
packageName = packageResult as string;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Step 4: Get project directory
|
|
168
|
-
let projectDirectory: string;
|
|
169
|
-
if (options.directory) {
|
|
170
|
-
const validation = validateDirectoryPath(options.directory);
|
|
171
|
-
if (!validation.valid) {
|
|
172
|
-
printError('Invalid directory', validation.errors.join(', '));
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
projectDirectory = validation.normalized ?? options.directory;
|
|
176
|
-
} else {
|
|
177
|
-
const defaultDir = `./${appName.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
|
|
178
|
-
const dirResult = await text({
|
|
179
|
-
message: '? In which directory should the project be created?',
|
|
180
|
-
placeholder: defaultDir,
|
|
181
|
-
defaultValue: defaultDir,
|
|
182
|
-
validate: (value: string) => {
|
|
183
|
-
const result = validateDirectoryPath(value);
|
|
184
|
-
if (!result.valid) {
|
|
185
|
-
return result.errors[0];
|
|
186
|
-
}
|
|
187
|
-
return undefined;
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
if (isCancel(dirResult)) {
|
|
192
|
-
cancel();
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const validation = validateDirectoryPath(dirResult as string);
|
|
197
|
-
if (!validation.valid) {
|
|
198
|
-
printError('Invalid directory', validation.errors.join(', '));
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
projectDirectory = validation.normalized ?? (dirResult as string);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Step 5: Optional features
|
|
205
|
-
const featureOptions = [
|
|
206
|
-
{ label: 'Git repository', value: 'git', hint: 'Initialize with git' },
|
|
207
|
-
{ label: 'README.md', value: 'readme', hint: 'Generate readme' },
|
|
208
|
-
{ label: 'AndroidX libraries', value: 'androidX', hint: 'Use AndroidX (recommended)', selected: true },
|
|
209
|
-
{ label: 'Kotlin DSL', value: 'kotlinDsl', hint: 'Use Kotlin DSL for Gradle', selected: true }
|
|
210
|
-
];
|
|
211
|
-
|
|
212
|
-
const featuresResult = await multiselect({
|
|
213
|
-
message: '? Select additional features (optional)',
|
|
214
|
-
options: featureOptions,
|
|
215
|
-
required: false
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
if (isCancel(featuresResult)) {
|
|
219
|
-
cancel();
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const selectedFeatures = featuresResult as string[];
|
|
224
|
-
const features = {
|
|
225
|
-
git: selectedFeatures.includes('git') || options.git === true,
|
|
226
|
-
readme: selectedFeatures.includes('readme'),
|
|
227
|
-
androidX: selectedFeatures.includes('androidX'),
|
|
228
|
-
kotlinDsl: selectedFeatures.includes('kotlinDsl')
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
// Build context
|
|
232
|
-
const baseContext = buildDefaultProjectContext(
|
|
233
|
-
appName,
|
|
234
|
-
packageName,
|
|
235
|
-
projectDirectory,
|
|
236
|
-
selectedTemplate as TemplateType,
|
|
237
|
-
features,
|
|
238
|
-
options.minSdk,
|
|
239
|
-
options.targetSdk
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
const context = buildTemplateContext({
|
|
243
|
-
appName: baseContext.appName,
|
|
244
|
-
packageName: baseContext.packageName,
|
|
245
|
-
projectDirectory: baseContext.projectDirectory,
|
|
246
|
-
template: baseContext.template,
|
|
247
|
-
uiFramework: baseContext.uiFramework,
|
|
248
|
-
language: baseContext.language,
|
|
249
|
-
android: baseContext.android,
|
|
250
|
-
gradle: baseContext.gradle,
|
|
251
|
-
features: baseContext as unknown as Record<string, boolean>,
|
|
252
|
-
nativeCpp: baseContext.nativeCpp
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
// Check if directory exists
|
|
256
|
-
const dirExists = await exists(projectDirectory);
|
|
257
|
-
if (dirExists && !options.force) {
|
|
258
|
-
printError(
|
|
259
|
-
`Directory "${projectDirectory}" already exists`,
|
|
260
|
-
'Use --force to overwrite existing files'
|
|
261
|
-
);
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Generate project
|
|
266
|
-
console.log('');
|
|
267
|
-
printSection('Generating Project');
|
|
268
|
-
|
|
269
|
-
const result = await generateProject(context, {
|
|
270
|
-
overwrite: options.force,
|
|
271
|
-
dryRun: false,
|
|
272
|
-
skipInstall: options.skipInstall,
|
|
273
|
-
verbose: options.verbose ?? false
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
if (result.success) {
|
|
277
|
-
printSuccess('Project generated successfully!');
|
|
278
|
-
printKeyValue([
|
|
279
|
-
{ key: 'Project', value: appName },
|
|
280
|
-
{ key: 'Package', value: packageName },
|
|
281
|
-
{ key: 'Template', value: selectedTemplate },
|
|
282
|
-
{ key: 'Location', value: projectDirectory },
|
|
283
|
-
{ key: 'Files created', value: result.generatedFiles.length.toString() },
|
|
284
|
-
{ key: 'Duration', value: `${(result.duration / 1000).toFixed(1)}s` }
|
|
285
|
-
]);
|
|
286
|
-
|
|
287
|
-
console.log('');
|
|
288
|
-
printSteps([
|
|
289
|
-
`cd ${projectDirectory}`,
|
|
290
|
-
'Open in Android Studio: studio .',
|
|
291
|
-
'Or build from command line: ./gradlew assembleDebug'
|
|
292
|
-
]);
|
|
293
|
-
|
|
294
|
-
printGoodbye(true);
|
|
295
|
-
} else {
|
|
296
|
-
printError('Project generation failed');
|
|
297
|
-
if (result.errors.length > 0) {
|
|
298
|
-
console.log('');
|
|
299
|
-
result.errors.forEach(err => {
|
|
300
|
-
console.log(` ${pc.red('•')} ${err.file ? `${err.file}: ` : ''}${err.message}`);
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
printGoodbye(false);
|
|
304
|
-
}
|
|
305
|
-
} catch (error) {
|
|
306
|
-
printError('An unexpected error occurred', (error as Error).message);
|
|
307
|
-
if (process.env.DEBUG || process.env.VERBOSE) {
|
|
308
|
-
console.error(error);
|
|
309
|
-
}
|
|
310
|
-
printGoodbye(false);
|
|
311
|
-
process.exit(1);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Export for use in CLI
|
|
316
|
-
export type { NewCommandOptions };
|
package/src/cli/index.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Command registration and main CLI entry point
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { cac } from 'cac';
|
|
6
|
-
import pc from 'picocolors';
|
|
7
|
-
import { createCommand } from './commands/create.js';
|
|
8
|
-
import { createNewCommand } from './commands/new.js';
|
|
9
|
-
import { createInitCommand } from './commands/init.js';
|
|
10
|
-
import { createInfoCommand } from './commands/info.js';
|
|
11
|
-
import { createListCommand } from './commands/list.js';
|
|
12
|
-
|
|
13
|
-
const pkg = { version: '1.0.0' };
|
|
14
|
-
|
|
15
|
-
const cli = cac('andrud');
|
|
16
|
-
|
|
17
|
-
cli.version(pkg.version);
|
|
18
|
-
|
|
19
|
-
// Clean minimal help
|
|
20
|
-
function printHelp(): void {
|
|
21
|
-
console.log('');
|
|
22
|
-
console.log(` ${pc.cyan('andrud')} - Android Project Scaffolding`);
|
|
23
|
-
console.log('');
|
|
24
|
-
console.log(` ${pc.gray('andrud create')} Create a new project`);
|
|
25
|
-
console.log(` ${pc.gray('andrud list')} Show available templates`);
|
|
26
|
-
console.log(` ${pc.gray('andrud info <template>')} View template details`);
|
|
27
|
-
console.log('');
|
|
28
|
-
console.log(` ${pc.gray('andrud create MyApp')} Create with name`);
|
|
29
|
-
console.log('');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Commands
|
|
33
|
-
cli
|
|
34
|
-
.command('create', 'Create a new Android project')
|
|
35
|
-
.alias('c')
|
|
36
|
-
.option('-f, --force', 'Overwrite existing files')
|
|
37
|
-
.example('andrud create')
|
|
38
|
-
.example('andrud create --force')
|
|
39
|
-
.action(createCommand);
|
|
40
|
-
|
|
41
|
-
cli
|
|
42
|
-
.command('new [name]', 'Create a new project')
|
|
43
|
-
.alias('n')
|
|
44
|
-
.option('-t, --template <type>', 'Template type')
|
|
45
|
-
.option('-p, --package <name>', 'Package name')
|
|
46
|
-
.option('-f, --force', 'Overwrite')
|
|
47
|
-
.example('andrud new MyApp -t kotlin-compose')
|
|
48
|
-
.action(createNewCommand);
|
|
49
|
-
|
|
50
|
-
cli
|
|
51
|
-
.command('init', 'Initialize in current directory')
|
|
52
|
-
.alias('i')
|
|
53
|
-
.option('-f, --force', 'Overwrite')
|
|
54
|
-
.example('andrud init')
|
|
55
|
-
.action(createInitCommand);
|
|
56
|
-
|
|
57
|
-
cli
|
|
58
|
-
.command('info [template]', 'Show template info')
|
|
59
|
-
.example('andrud info kotlin-compose')
|
|
60
|
-
.action(createInfoCommand);
|
|
61
|
-
|
|
62
|
-
cli
|
|
63
|
-
.command('list', 'List all templates')
|
|
64
|
-
.alias('ls')
|
|
65
|
-
.option('--json', 'Output as JSON')
|
|
66
|
-
.action(createListCommand);
|
|
67
|
-
|
|
68
|
-
cli.on('command:!', () => {
|
|
69
|
-
console.error(pc.red(` Unknown command. See ${pc.cyan('--help')}\n`));
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
process.on('uncaughtException', (error) => {
|
|
73
|
-
console.error(pc.red(` ${error.message}\n`));
|
|
74
|
-
process.exit(1);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
process.on('unhandledRejection', (reason) => {
|
|
78
|
-
console.error(pc.red(` ${reason}\n`));
|
|
79
|
-
process.exit(1);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
export function runCli(): void {
|
|
83
|
-
try {
|
|
84
|
-
const args = process.argv.slice(2);
|
|
85
|
-
|
|
86
|
-
// Show help
|
|
87
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
88
|
-
printHelp();
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Show version
|
|
93
|
-
if (args.includes('--version') || args.includes('-V')) {
|
|
94
|
-
console.log(pkg.version);
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Quick help on no args
|
|
99
|
-
if (args.length === 0) {
|
|
100
|
-
console.log('');
|
|
101
|
-
console.log(` ${pc.cyan('andrud')} - Create Android projects`);
|
|
102
|
-
console.log('');
|
|
103
|
-
console.log(` ${pc.gray('andrud create')} Start interactive setup`);
|
|
104
|
-
console.log(` ${pc.gray('andrud --help')} Show all commands`);
|
|
105
|
-
console.log('');
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
cli.parse(process.argv, { run: true });
|
|
110
|
-
} catch (error) {
|
|
111
|
-
console.error(pc.red(` ${(error as Error).message}\n`));
|
|
112
|
-
process.exit(1);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Note: runCli() is called from bin/andrud.js
|
package/src/core/config.ts
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration constants for Android and Gradle versions
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Default Android SDK configuration
|
|
7
|
-
*/
|
|
8
|
-
export const ANDROID_SDK_DEFAULTS = {
|
|
9
|
-
MIN_SDK: 24,
|
|
10
|
-
TARGET_SDK: 36,
|
|
11
|
-
COMPILE_SDK: 36,
|
|
12
|
-
BUILD_TOOLS_VERSION: '36.0.0'
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Gradle-related versions
|
|
17
|
-
*/
|
|
18
|
-
export const GRADLE_VERSIONS = {
|
|
19
|
-
AGP: '8.7.3',
|
|
20
|
-
GRADLE: '8.14',
|
|
21
|
-
KOTLIN: '2.0.21',
|
|
22
|
-
COMPOSE_COMPILER: '1.5.14',
|
|
23
|
-
COMPOSE_BOM: '2024.09.03',
|
|
24
|
-
NDK: '28.2.13676358'
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* AndroidX library versions
|
|
29
|
-
*/
|
|
30
|
-
export const ANDROIDX_VERSIONS = {
|
|
31
|
-
CORE_KTX: '1.15.0',
|
|
32
|
-
APP_COMPAT: '1.7.0',
|
|
33
|
-
MATERIAL: '1.12.0',
|
|
34
|
-
LIFECYCLE: '2.8.7',
|
|
35
|
-
ACTIVITY: '1.9.3',
|
|
36
|
-
CONSTRAINT_LAYOUT: '2.2.0',
|
|
37
|
-
RECYCLER_VIEW: '1.3.2',
|
|
38
|
-
CARD_VIEW: '1.0.0',
|
|
39
|
-
SWIPE_REFRESH: '1.1.0',
|
|
40
|
-
NAVIGATION: '2.8.4',
|
|
41
|
-
ROOM: '2.6.1',
|
|
42
|
-
WORK: '2.10.0',
|
|
43
|
-
HILT: '2.52'
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Compose versions
|
|
48
|
-
*/
|
|
49
|
-
export const COMPOSE_VERSIONS = {
|
|
50
|
-
BOM: '2025.01.00',
|
|
51
|
-
UI: '1.3.0',
|
|
52
|
-
UI_GRAPHICS: '1.3.0',
|
|
53
|
-
UI_TOOLING: '1.3.0',
|
|
54
|
-
MATERIAL3: '1.3.0',
|
|
55
|
-
MATERIAL_ICONS: '1.3.0',
|
|
56
|
-
FOUNDATION: '1.3.0',
|
|
57
|
-
ACTIVITY: '1.9.3',
|
|
58
|
-
LIFECYCLE: '2.8.7',
|
|
59
|
-
NAVIGATION: '2.8.4'
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Default Gradle properties for Android projects
|
|
64
|
-
*/
|
|
65
|
-
export function getDefaultGradleProperties(): Record<string, string | number | boolean> {
|
|
66
|
-
return {
|
|
67
|
-
'org.gradle.jvmargs': '-Xmx2048m -Dfile.encoding=UTF-8',
|
|
68
|
-
'android.useAndroidX': true,
|
|
69
|
-
'android.enableJetifier': true,
|
|
70
|
-
'kotlin.code.style': 'official',
|
|
71
|
-
'org.gradle.parallel': true,
|
|
72
|
-
'org.gradle.caching': true,
|
|
73
|
-
'org.gradle.configureondemand': true,
|
|
74
|
-
'android.nonTransitiveRClass': true,
|
|
75
|
-
'android.defaults.buildfeatures.buildconfig': true
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Get recommended minimum SDK based on year
|
|
81
|
-
*/
|
|
82
|
-
export function getRecommendedMinSdk(): number {
|
|
83
|
-
return 24; // Android 7.0 Nougat
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Get target SDK for a given year
|
|
88
|
-
*/
|
|
89
|
-
export function getTargetSdkForYear(year: number): number {
|
|
90
|
-
// By default, target the latest stable SDK
|
|
91
|
-
if (year >= 2024) {
|
|
92
|
-
return 35; // Android 15
|
|
93
|
-
} else if (year >= 2023) {
|
|
94
|
-
return 34; // Android 14
|
|
95
|
-
} else if (year >= 2022) {
|
|
96
|
-
return 33; // Android 13
|
|
97
|
-
} else {
|
|
98
|
-
return 32; // Android 12L
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Template-specific configurations
|
|
104
|
-
*/
|
|
105
|
-
export interface TemplateConfig {
|
|
106
|
-
language: 'kotlin' | 'java';
|
|
107
|
-
uiFramework: 'xml' | 'compose' | 'none';
|
|
108
|
-
minSdk: number;
|
|
109
|
-
targetSdk: number;
|
|
110
|
-
compileSdk: number;
|
|
111
|
-
kotlinVersion: string;
|
|
112
|
-
agpVersion: string;
|
|
113
|
-
gradleVersion: string;
|
|
114
|
-
composeEnabled: boolean;
|
|
115
|
-
ndkVersion?: string;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export const TEMPLATE_CONFIGS: Record<string, TemplateConfig> = {
|
|
119
|
-
'kotlin-compose': {
|
|
120
|
-
language: 'kotlin',
|
|
121
|
-
uiFramework: 'compose',
|
|
122
|
-
minSdk: 31,
|
|
123
|
-
targetSdk: 36,
|
|
124
|
-
compileSdk: 36,
|
|
125
|
-
kotlinVersion: GRADLE_VERSIONS.KOTLIN,
|
|
126
|
-
agpVersion: GRADLE_VERSIONS.AGP,
|
|
127
|
-
gradleVersion: GRADLE_VERSIONS.GRADLE,
|
|
128
|
-
composeEnabled: true
|
|
129
|
-
},
|
|
130
|
-
'kotlin-xml': {
|
|
131
|
-
language: 'kotlin',
|
|
132
|
-
uiFramework: 'xml',
|
|
133
|
-
minSdk: 31,
|
|
134
|
-
targetSdk: 36,
|
|
135
|
-
compileSdk: 36,
|
|
136
|
-
kotlinVersion: GRADLE_VERSIONS.KOTLIN,
|
|
137
|
-
agpVersion: GRADLE_VERSIONS.AGP,
|
|
138
|
-
gradleVersion: GRADLE_VERSIONS.GRADLE,
|
|
139
|
-
composeEnabled: false
|
|
140
|
-
},
|
|
141
|
-
'java-xml': {
|
|
142
|
-
language: 'java',
|
|
143
|
-
uiFramework: 'xml',
|
|
144
|
-
minSdk: 31,
|
|
145
|
-
targetSdk: 36,
|
|
146
|
-
compileSdk: 36,
|
|
147
|
-
kotlinVersion: '', // Not used for Java
|
|
148
|
-
agpVersion: GRADLE_VERSIONS.AGP,
|
|
149
|
-
gradleVersion: GRADLE_VERSIONS.GRADLE,
|
|
150
|
-
composeEnabled: false
|
|
151
|
-
},
|
|
152
|
-
'native-cpp': {
|
|
153
|
-
language: 'kotlin',
|
|
154
|
-
uiFramework: 'xml',
|
|
155
|
-
minSdk: 31,
|
|
156
|
-
targetSdk: 36,
|
|
157
|
-
compileSdk: 36,
|
|
158
|
-
kotlinVersion: GRADLE_VERSIONS.KOTLIN,
|
|
159
|
-
agpVersion: GRADLE_VERSIONS.AGP,
|
|
160
|
-
gradleVersion: GRADLE_VERSIONS.GRADLE,
|
|
161
|
-
composeEnabled: false,
|
|
162
|
-
ndkVersion: GRADLE_VERSIONS.NDK
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Get template configuration
|
|
168
|
-
*/
|
|
169
|
-
export function getTemplateConfig(template: string): TemplateConfig {
|
|
170
|
-
const config = TEMPLATE_CONFIGS[template];
|
|
171
|
-
return config ?? TEMPLATE_CONFIGS['kotlin-xml']!;
|
|
172
|
-
}
|