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.
Files changed (122) hide show
  1. package/README.md +69 -83
  2. package/dist/__tests__/context.test.d.ts +5 -0
  3. package/dist/__tests__/context.test.d.ts.map +1 -0
  4. package/dist/__tests__/context.test.js +86 -0
  5. package/dist/__tests__/context.test.js.map +1 -0
  6. package/dist/__tests__/generator.test.d.ts +5 -0
  7. package/dist/__tests__/generator.test.d.ts.map +1 -0
  8. package/dist/__tests__/generator.test.js +83 -0
  9. package/dist/__tests__/generator.test.js.map +1 -0
  10. package/dist/__tests__/validation.test.d.ts +5 -0
  11. package/dist/__tests__/validation.test.d.ts.map +1 -0
  12. package/dist/__tests__/validation.test.js +81 -0
  13. package/dist/__tests__/validation.test.js.map +1 -0
  14. package/dist/cli/commands/create.d.ts +10 -0
  15. package/dist/cli/commands/create.d.ts.map +1 -0
  16. package/dist/cli/commands/create.js +203 -0
  17. package/dist/cli/commands/create.js.map +1 -0
  18. package/dist/cli/commands/info.d.ts +13 -0
  19. package/dist/cli/commands/info.d.ts.map +1 -0
  20. package/dist/cli/commands/info.js +153 -0
  21. package/dist/cli/commands/info.js.map +1 -0
  22. package/dist/cli/commands/init.d.ts +15 -0
  23. package/dist/cli/commands/init.d.ts.map +1 -0
  24. package/dist/cli/commands/init.js +141 -0
  25. package/dist/cli/commands/init.js.map +1 -0
  26. package/dist/cli/commands/list.d.ts +18 -0
  27. package/dist/cli/commands/list.d.ts.map +1 -0
  28. package/dist/cli/commands/list.js +122 -0
  29. package/dist/cli/commands/list.js.map +1 -0
  30. package/dist/cli/commands/new.d.ts +22 -0
  31. package/dist/cli/commands/new.d.ts.map +1 -0
  32. package/dist/cli/commands/new.js +245 -0
  33. package/dist/cli/commands/new.js.map +1 -0
  34. package/dist/cli/index.d.ts +5 -0
  35. package/dist/cli/index.d.ts.map +1 -0
  36. package/dist/cli/index.js +99 -0
  37. package/dist/cli/index.js.map +1 -0
  38. package/dist/core/config.d.ts +89 -0
  39. package/dist/core/config.d.ts.map +1 -0
  40. package/dist/core/config.js +151 -0
  41. package/dist/core/config.js.map +1 -0
  42. package/dist/core/context.d.ts +47 -0
  43. package/dist/core/context.d.ts.map +1 -0
  44. package/dist/core/context.js +175 -0
  45. package/dist/core/context.js.map +1 -0
  46. package/dist/core/generator.d.ts +44 -0
  47. package/dist/core/generator.d.ts.map +1 -0
  48. package/{src/core/generator.ts → dist/core/generator.js} +394 -484
  49. package/dist/core/generator.js.map +1 -0
  50. package/dist/core/types.d.ts +125 -0
  51. package/dist/core/types.d.ts.map +1 -0
  52. package/dist/core/types.js +22 -0
  53. package/dist/core/types.js.map +1 -0
  54. package/dist/templates/index.d.ts +36 -0
  55. package/dist/templates/index.d.ts.map +1 -0
  56. package/dist/templates/index.js +141 -0
  57. package/dist/templates/index.js.map +1 -0
  58. package/dist/ui/colors.d.ts +40 -0
  59. package/dist/ui/colors.d.ts.map +1 -0
  60. package/dist/ui/colors.js +117 -0
  61. package/dist/ui/colors.js.map +1 -0
  62. package/dist/ui/output.d.ts +69 -0
  63. package/dist/ui/output.d.ts.map +1 -0
  64. package/dist/ui/output.js +199 -0
  65. package/dist/ui/output.js.map +1 -0
  66. package/dist/ui/prompts.d.ts +20 -0
  67. package/dist/ui/prompts.d.ts.map +1 -0
  68. package/dist/ui/prompts.js +118 -0
  69. package/dist/ui/prompts.js.map +1 -0
  70. package/dist/ui/spinners.d.ts +30 -0
  71. package/dist/ui/spinners.d.ts.map +1 -0
  72. package/dist/ui/spinners.js +74 -0
  73. package/dist/ui/spinners.js.map +1 -0
  74. package/dist/ui/types.d.ts +35 -0
  75. package/dist/ui/types.d.ts.map +1 -0
  76. package/dist/ui/types.js +5 -0
  77. package/dist/ui/types.js.map +1 -0
  78. package/dist/utils/filesystem.d.ts +38 -0
  79. package/dist/utils/filesystem.d.ts.map +1 -0
  80. package/dist/utils/filesystem.js +181 -0
  81. package/dist/utils/filesystem.js.map +1 -0
  82. package/dist/utils/logger.d.ts +27 -0
  83. package/dist/utils/logger.d.ts.map +1 -0
  84. package/dist/utils/logger.js +52 -0
  85. package/dist/utils/logger.js.map +1 -0
  86. package/dist/utils/object.d.ts +140 -0
  87. package/dist/utils/object.d.ts.map +1 -0
  88. package/dist/utils/object.js +385 -0
  89. package/dist/utils/object.js.map +1 -0
  90. package/dist/utils/validation.d.ts +35 -0
  91. package/dist/utils/validation.d.ts.map +1 -0
  92. package/dist/utils/validation.js +270 -0
  93. package/dist/utils/validation.js.map +1 -0
  94. package/package.json +8 -19
  95. package/CHANGELOG.md +0 -70
  96. package/CODE_REVIEW_ANALYSIS.md +0 -177
  97. package/CONTRIBUTING.md +0 -132
  98. package/FIXES_IMPLEMENTED.md +0 -546
  99. package/src/__tests__/context.test.ts +0 -133
  100. package/src/__tests__/generator.test.ts +0 -107
  101. package/src/__tests__/validation.test.ts +0 -105
  102. package/src/cli/commands/create.ts +0 -252
  103. package/src/cli/commands/info.ts +0 -178
  104. package/src/cli/commands/init.ts +0 -186
  105. package/src/cli/commands/list.ts +0 -156
  106. package/src/cli/commands/new.ts +0 -316
  107. package/src/cli/index.ts +0 -116
  108. package/src/core/config.ts +0 -172
  109. package/src/core/context.ts +0 -212
  110. package/src/core/types.ts +0 -184
  111. package/src/templates/index.ts +0 -162
  112. package/src/types/gradient-string.d.ts +0 -25
  113. package/src/ui/colors.ts +0 -139
  114. package/src/ui/output.ts +0 -230
  115. package/src/ui/prompts.ts +0 -170
  116. package/src/ui/spinners.ts +0 -95
  117. package/src/ui/types.ts +0 -41
  118. package/src/utils/filesystem.ts +0 -222
  119. package/src/utils/logger.ts +0 -67
  120. package/src/utils/object.ts +0 -456
  121. package/src/utils/validation.ts +0 -345
  122. package/tsconfig.json +0 -25
@@ -1,133 +0,0 @@
1
- /**
2
- * Test suite for context builder
3
- */
4
-
5
- import { test } from 'node:test';
6
- import assert from 'node:assert';
7
- import {
8
- buildDefaultProjectContext,
9
- buildTemplateContext,
10
- validateContext
11
- } from '../core/context.js';
12
- import type { TemplateType } from '../core/types.js';
13
-
14
- test('Context - Build Default Context', async (t) => {
15
- await t.test('should create valid default context', () => {
16
- const context = buildDefaultProjectContext(
17
- 'MyApp',
18
- 'com.example.myapp',
19
- './my-project',
20
- 'kotlin-compose' as TemplateType,
21
- { git: true, readme: true }
22
- );
23
-
24
- assert.strictEqual(context.appName, 'MyApp');
25
- assert.strictEqual(context.packageName, 'com.example.myapp');
26
- assert.strictEqual(context.template, 'kotlin-compose');
27
- assert.strictEqual(context.language, 'kotlin');
28
- assert.strictEqual(context.uiFramework, 'compose');
29
- });
30
-
31
- await t.test('should set default features', () => {
32
- const context = buildDefaultProjectContext(
33
- 'MyApp',
34
- 'com.example.myapp',
35
- './my-project',
36
- 'kotlin-xml' as TemplateType
37
- );
38
-
39
- assert.strictEqual(context.git, true);
40
- assert.strictEqual(context.readme, true);
41
- assert.strictEqual(context.androidX, true);
42
- });
43
- });
44
-
45
- test('Context - Build Template Context', async (t) => {
46
- await t.test('should build full template context', () => {
47
- const baseContext = buildDefaultProjectContext(
48
- 'MyApp',
49
- 'com.example.myapp',
50
- './my-project',
51
- 'kotlin-compose' as TemplateType
52
- );
53
-
54
- const context = buildTemplateContext({
55
- appName: baseContext.appName,
56
- packageName: baseContext.packageName,
57
- projectDirectory: baseContext.projectDirectory,
58
- template: baseContext.template,
59
- uiFramework: baseContext.uiFramework,
60
- language: baseContext.language,
61
- android: baseContext.android,
62
- gradle: baseContext.gradle,
63
- features: baseContext as unknown as Record<string, boolean>
64
- });
65
-
66
- assert.ok(context.appNameCamel);
67
- assert.ok(context.appNamePascal);
68
- assert.ok(context.appNameKebab);
69
- assert.ok(context.appNameSnake);
70
- assert.ok(context.packagePath);
71
- assert.ok(context.year);
72
- assert.ok(context.generatorVersion);
73
- });
74
-
75
- await t.test('should generate correct app name transformations', () => {
76
- const baseContext = buildDefaultProjectContext(
77
- 'MyAwesomeApp',
78
- 'com.example.myapp',
79
- './my-project',
80
- 'kotlin-compose' as TemplateType
81
- );
82
-
83
- const context = buildTemplateContext({
84
- appName: baseContext.appName,
85
- packageName: baseContext.packageName,
86
- projectDirectory: baseContext.projectDirectory,
87
- template: baseContext.template,
88
- uiFramework: baseContext.uiFramework,
89
- language: baseContext.language,
90
- android: baseContext.android,
91
- gradle: baseContext.gradle,
92
- features: baseContext as unknown as Record<string, boolean>
93
- });
94
-
95
- assert.ok(context.appNamePascal.includes('Awesome'));
96
- assert.ok(context.packagePath.includes('/'));
97
- });
98
- });
99
-
100
- test('Context - Validate Context', async (t) => {
101
- await t.test('should accept valid context', () => {
102
- const baseContext = buildDefaultProjectContext(
103
- 'MyApp',
104
- 'com.example.myapp',
105
- './my-project',
106
- 'kotlin-compose' as TemplateType
107
- );
108
-
109
- const result = validateContext(baseContext);
110
- assert.strictEqual(result.valid, true);
111
- });
112
-
113
- await t.test('should reject context with missing appName', () => {
114
- const result = validateContext({
115
- appName: '',
116
- packageName: 'com.example.myapp',
117
- template: 'kotlin-compose' as TemplateType
118
- });
119
-
120
- assert.strictEqual(result.valid, false);
121
- assert.ok(result.errors.length > 0);
122
- });
123
-
124
- await t.test('should reject context with missing packageName', () => {
125
- const result = validateContext({
126
- appName: 'MyApp',
127
- packageName: '',
128
- template: 'kotlin-compose' as TemplateType
129
- });
130
-
131
- assert.strictEqual(result.valid, false);
132
- });
133
- });
@@ -1,107 +0,0 @@
1
- /**
2
- * Test suite for generator utilities
3
- */
4
-
5
- import { test } from 'node:test';
6
- import assert from 'node:assert';
7
- import {
8
- validateContext,
9
- validateProjectDirectory
10
- } from '../core/generator.js';
11
- import type { TemplateContext } from '../core/types.js';
12
-
13
- test('Generator - Validate Context', async (t) => {
14
- await t.test('should accept valid context', () => {
15
- const context: Partial<TemplateContext> = {
16
- appName: 'MyApp',
17
- packageName: 'com.example.myapp',
18
- projectDirectory: './my-project',
19
- template: 'kotlin-compose',
20
- language: 'kotlin',
21
- uiFramework: 'compose',
22
- android: {
23
- minSdk: 31,
24
- targetSdk: 36,
25
- compileSdk: 36
26
- },
27
- gradle: {
28
- agpVersion: '8.7.3',
29
- gradleVersion: '8.14',
30
- kotlinVersion: '2.0.21'
31
- }
32
- };
33
-
34
- const result = validateContext(context);
35
- assert.strictEqual(result.valid, true);
36
- });
37
-
38
- await t.test('should reject context with missing appName', () => {
39
- const context: Partial<TemplateContext> = {
40
- appName: '',
41
- packageName: 'com.example.myapp'
42
- };
43
-
44
- const result = validateContext(context);
45
- assert.strictEqual(result.valid, false);
46
- assert.ok(result.errors.some(e => e.includes('appName')));
47
- });
48
-
49
- await t.test('should reject context with missing packageName', () => {
50
- const context: Partial<TemplateContext> = {
51
- appName: 'MyApp',
52
- packageName: ''
53
- };
54
-
55
- const result = validateContext(context);
56
- assert.strictEqual(result.valid, false);
57
- assert.ok(result.errors.some(e => e.includes('packageName')));
58
- });
59
-
60
- await t.test('should reject context with missing template', () => {
61
- const context: Partial<TemplateContext> = {
62
- appName: 'MyApp',
63
- packageName: 'com.example.myapp',
64
- template: undefined
65
- };
66
-
67
- const result = validateContext(context);
68
- assert.strictEqual(result.valid, false);
69
- assert.ok(result.errors.some(e => e.includes('template')));
70
- });
71
-
72
- await t.test('should reject context with missing language', () => {
73
- const context: Partial<TemplateContext> = {
74
- appName: 'MyApp',
75
- packageName: 'com.example.myapp',
76
- template: 'kotlin-compose',
77
- language: undefined
78
- };
79
-
80
- const result = validateContext(context);
81
- assert.strictEqual(result.valid, false);
82
- assert.ok(result.errors.some(e => e.includes('language')));
83
- });
84
- });
85
-
86
- test('Generator - Validate Project Directory', async (t) => {
87
- await t.test('should validate non-existent directory', async () => {
88
- const result = await validateProjectDirectory(
89
- '/tmp/nonexistent-dir-' + Date.now(),
90
- { overwrite: false }
91
- );
92
-
93
- assert.strictEqual(result.valid, true);
94
- });
95
-
96
- await t.test('should allow non-empty directory with overwrite', async () => {
97
- // Note: This would require actual file system operations
98
- // In a real test, we'd mock the filesystem
99
- const result = await validateProjectDirectory(
100
- '/tmp/test-dir',
101
- { overwrite: true }
102
- );
103
-
104
- // Just assert the result has the expected shape
105
- assert.ok(typeof result.valid === 'boolean');
106
- });
107
- });
@@ -1,105 +0,0 @@
1
- /**
2
- * Test suite for validation utilities
3
- */
4
-
5
- import { test } from 'node:test';
6
- import assert from 'node:assert';
7
- import {
8
- validateAppName,
9
- validatePackageNameInput,
10
- validateDirectoryPath,
11
- camelCase,
12
- pascalCase,
13
- kebabCase,
14
- snakeCase
15
- } from '../utils/validation.js';
16
-
17
- test('Validation - App Name', async (t) => {
18
- await t.test('should accept valid app name', () => {
19
- const result = validateAppName('MyAwesomeApp');
20
- assert.strictEqual(result.valid, true);
21
- });
22
-
23
- await t.test('should reject app name starting with number', () => {
24
- const result = validateAppName('1App');
25
- assert.strictEqual(result.valid, false);
26
- assert.ok(result.errors.length > 0);
27
- });
28
-
29
- await t.test('should reject app name with spaces', () => {
30
- const result = validateAppName('My App');
31
- assert.strictEqual(result.valid, false);
32
- });
33
-
34
- await t.test('should reject empty app name', () => {
35
- const result = validateAppName('');
36
- assert.strictEqual(result.valid, false);
37
- });
38
- });
39
-
40
- test('Validation - Package Name', async (t) => {
41
- await t.test('should accept valid package name', () => {
42
- const result = validatePackageNameInput('com.example.myapp');
43
- assert.strictEqual(result.valid, true);
44
- });
45
-
46
- await t.test('should reject reserved prefix android', () => {
47
- const result = validatePackageNameInput('android.example.app');
48
- assert.strictEqual(result.valid, false);
49
- });
50
-
51
- await t.test('should reject reserved prefix kotlin', () => {
52
- const result = validatePackageNameInput('kotlin.example.app');
53
- assert.strictEqual(result.valid, false);
54
- });
55
-
56
- await t.test('should reject package with single segment', () => {
57
- const result = validatePackageNameInput('myapp');
58
- assert.strictEqual(result.valid, false);
59
- });
60
-
61
- await t.test('should warn on generic com.example prefix', () => {
62
- const result = validatePackageNameInput('com.example.app');
63
- assert.strictEqual(result.valid, true);
64
- assert.ok(result.warnings && result.warnings.length > 0);
65
- });
66
- });
67
-
68
- test('Validation - Directory Path', async (t) => {
69
- await t.test('should accept valid directory path', () => {
70
- const result = validateDirectoryPath('./my-project');
71
- assert.strictEqual(result.valid, true);
72
- });
73
-
74
- await t.test('should reject empty directory path', () => {
75
- const result = validateDirectoryPath('');
76
- assert.strictEqual(result.valid, false);
77
- });
78
-
79
- await t.test('should reject directory traversal', () => {
80
- const result = validateDirectoryPath('../../../etc/passwd');
81
- assert.strictEqual(result.valid, false);
82
- });
83
- });
84
-
85
- test('String Case Transformations', async (t) => {
86
- await t.test('camelCase should transform correctly', () => {
87
- const result = camelCase('my awesome app');
88
- assert.strictEqual(result, 'myAwesomeApp');
89
- });
90
-
91
- await t.test('pascalCase should transform correctly', () => {
92
- const result = pascalCase('my awesome app');
93
- assert.strictEqual(result, 'MyAwesomeApp');
94
- });
95
-
96
- await t.test('kebabCase should transform correctly', () => {
97
- const result = kebabCase('my awesome app');
98
- assert.strictEqual(result, 'my-awesome-app');
99
- });
100
-
101
- await t.test('snakeCase should transform correctly', () => {
102
- const result = snakeCase('my awesome app');
103
- assert.strictEqual(result, 'my_awesome_app');
104
- });
105
- });
@@ -1,252 +0,0 @@
1
- /**
2
- * Clean, minimal Vite-style project creation command
3
- */
4
-
5
- import {
6
- text,
7
- select,
8
- confirm,
9
- isCancel,
10
- cancel,
11
- spinner
12
- } from '@clack/prompts';
13
- import { generateProject } from '../../core/generator.js';
14
- import { buildDefaultProjectContext, buildTemplateContext } from '../../core/context.js';
15
- import { exists, getAbsolutePath } from '../../utils/filesystem.js';
16
- import { getAllTemplates, getTemplateMetadata } from '../../templates/index.js';
17
- import type { TemplateType } from '../../core/types.js';
18
- import pc from 'picocolors';
19
- import { resolve, isAbsolute } from 'path';
20
- import { platform } from 'os';
21
-
22
- /**
23
- * Normalize path for cross-platform compatibility
24
- */
25
- function normalizePath(path: string): string {
26
- if (!path || path.trim() === '') {
27
- return path;
28
- }
29
-
30
- const trimmed = path.trim();
31
-
32
- // On Windows, convert Unix-style paths like /d/... to D:\...
33
- if (platform() === 'win32') {
34
- const unixPathMatch = trimmed.match(/^\/([a-zA-Z])\/(.*)$/);
35
- if (unixPathMatch && unixPathMatch[1] && unixPathMatch[2]) {
36
- return `${unixPathMatch[1].toUpperCase()}:\\${unixPathMatch[2].replace(/\//g, '\\')}`;
37
- }
38
- }
39
-
40
- // Force relative paths to be relative to current directory
41
- if (!isAbsolute(trimmed)) {
42
- return resolve(trimmed);
43
- }
44
-
45
- return trimmed;
46
- }
47
-
48
- /**
49
- * Create a new Android project with clean minimal UI
50
- */
51
- export async function createCommand(options: { force?: boolean } = {}): Promise<void> {
52
- // ============================================
53
- // Step 1: App Name
54
- // ============================================
55
- console.log('');
56
-
57
- let appName: string;
58
- const nameResult = await text({
59
- message: ' ? Project name:',
60
- placeholder: 'MyApp',
61
- defaultValue: 'MyApp',
62
- });
63
-
64
- if (isCancel(nameResult)) {
65
- cancel();
66
- return;
67
- }
68
-
69
- appName = nameResult.trim();
70
- if (!appName || !/^[a-zA-Z]/.test(appName)) {
71
- console.log(pc.red('\n ✘ Project name must start with a letter\n'));
72
- return;
73
- }
74
-
75
- // ============================================
76
- // Step 2: Template Selection
77
- // ============================================
78
- const templates = getAllTemplates();
79
-
80
- const templateOptions = templates.map(t => ({
81
- label: t.name,
82
- value: t.id,
83
- hint: t.language === 'kotlin' ? 'Kotlin' : 'Java'
84
- }));
85
-
86
- const templateResult = await select({
87
- message: ' ? Select template:',
88
- options: templateOptions
89
- });
90
-
91
- if (isCancel(templateResult)) {
92
- cancel();
93
- return;
94
- }
95
-
96
- const selectedTemplate = templateResult as TemplateType;
97
- const selectedMeta = getTemplateMetadata(selectedTemplate);
98
-
99
- // ============================================
100
- // Step 3: Package Name
101
- // ============================================
102
- const defaultPackage = `com.example.${appName.toLowerCase().replace(/[^a-z0-9]/g, '')}`;
103
-
104
- const packageResult = await text({
105
- message: ' ? Package name:',
106
- placeholder: defaultPackage,
107
- defaultValue: defaultPackage,
108
- });
109
-
110
- if (isCancel(packageResult)) {
111
- cancel();
112
- return;
113
- }
114
-
115
- const packageName = packageResult.trim().toLowerCase();
116
- if (!packageName || !/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/.test(packageName)) {
117
- console.log(pc.red('\n ✘ Invalid package name format (e.g., com.example.myapp)\n'));
118
- return;
119
- }
120
-
121
- // ============================================
122
- // Step 4: Directory
123
- // ============================================
124
- const dirResult = await text({
125
- message: ' ? Where to create the project:',
126
- placeholder: 'D:\\Projects\\Android',
127
- defaultValue: 'D:\\Projects\\Android',
128
- });
129
-
130
- if (isCancel(dirResult)) {
131
- cancel();
132
- return;
133
- }
134
-
135
- // Normalize and append app name as project folder
136
- let baseDir = dirResult.trim();
137
-
138
- if (!baseDir) {
139
- console.log(pc.red('\n ✘ Directory path cannot be empty\n'));
140
- return;
141
- }
142
-
143
- baseDir = normalizePath(baseDir);
144
-
145
- // Ensure the directory path ends properly (no trailing slash issues)
146
- let projectDirectory: string;
147
-
148
- // If the path is ./something, append app name
149
- if (baseDir.startsWith('./') || baseDir === '.') {
150
- const baseName = baseDir === '.' ? '' : baseDir.slice(1).replace(/^[\\\/]/, '');
151
- projectDirectory = baseName
152
- ? `${baseName}/${appName.toLowerCase().replace(/[^a-z0-9]/g, '-')}`
153
- : `./${appName.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
154
- } else {
155
- // For absolute paths, append app name + sanitize
156
- projectDirectory = `${baseDir}\\${appName.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
157
- }
158
-
159
- // Check if directory exists
160
- const dirExists = await exists(projectDirectory);
161
- if (dirExists) {
162
- if (!options.force) {
163
- console.log(pc.red(`\n ✘ Directory already exists: ${projectDirectory}`));
164
- console.log(pc.gray(' Use --force to overwrite\n'));
165
- return;
166
- }
167
- }
168
-
169
- // ============================================
170
- // Step 5: Summary & Confirm
171
- // ============================================
172
- console.log('');
173
- console.log(pc.gray('─').repeat(50));
174
- console.log('');
175
- console.log(` ${pc.cyan('Project:')} ${pc.bold(appName)}`);
176
- console.log(` ${pc.cyan('Template:')} ${selectedMeta?.name}`);
177
- console.log(` ${pc.cyan('Package:')} ${packageName}`);
178
- console.log(` ${pc.cyan('Path:')} ${projectDirectory}`);
179
- console.log('');
180
-
181
- const shouldCreate = await confirm({
182
- message: ' Create project?',
183
- initialValue: true
184
- });
185
-
186
- if (isCancel(shouldCreate)) {
187
- cancel();
188
- return;
189
- }
190
-
191
- if (!shouldCreate) {
192
- console.log(pc.gray('\n Cancelled.\n'));
193
- return;
194
- }
195
-
196
- // ============================================
197
- // Generate Project
198
- // ============================================
199
- console.log('');
200
-
201
- const s = spinner();
202
- s.start(' Creating project...');
203
-
204
- // Build context
205
- const baseContext = buildDefaultProjectContext(
206
- appName,
207
- packageName,
208
- projectDirectory,
209
- selectedTemplate,
210
- { git: true, readme: true, androidX: true, kotlinDsl: true }
211
- );
212
-
213
- const context = buildTemplateContext({
214
- appName: baseContext.appName,
215
- packageName: baseContext.packageName,
216
- projectDirectory: baseContext.projectDirectory,
217
- template: baseContext.template,
218
- uiFramework: baseContext.uiFramework,
219
- language: baseContext.language,
220
- android: baseContext.android,
221
- gradle: baseContext.gradle,
222
- features: baseContext as unknown as Record<string, boolean>,
223
- nativeCpp: baseContext.nativeCpp
224
- });
225
-
226
- // Generate the project
227
- const result = await generateProject(context, {
228
- overwrite: options.force ?? false,
229
- dryRun: false,
230
- skipInstall: false,
231
- verbose: false
232
- });
233
-
234
- s.stop(' Done!');
235
-
236
- if (result.success) {
237
- console.log('');
238
- console.log(pc.green(` ✓ Project "${appName}" created`));
239
- console.log(pc.gray('─'.repeat(50)));
240
- console.log('');
241
- console.log(pc.gray(` cd ${projectDirectory}`));
242
- console.log(pc.gray(' ./gradlew assembleDebug'));
243
- console.log(pc.gray(' studio .'));
244
- console.log('');
245
- } else {
246
- console.log(pc.red('\n ✘ Failed to create project'));
247
- result.errors.forEach(err => {
248
- console.log(` ${pc.red('•')} ${err.file ? `${err.file}: ` : ''}${err.message}`);
249
- });
250
- console.log('');
251
- }
252
- }