andrud 1.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.
- package/CHANGELOG.md +70 -0
- package/CODE_REVIEW_ANALYSIS.md +177 -0
- package/CONTRIBUTING.md +132 -0
- package/FIXES_IMPLEMENTED.md +546 -0
- package/LICENSE +21 -0
- package/README.md +310 -0
- package/bin/andrud.js +24 -0
- package/package.json +80 -0
- package/src/__tests__/context.test.ts +133 -0
- package/src/__tests__/generator.test.ts +107 -0
- package/src/__tests__/validation.test.ts +105 -0
- package/src/cli/commands/create.ts +252 -0
- package/src/cli/commands/info.ts +178 -0
- package/src/cli/commands/init.ts +186 -0
- package/src/cli/commands/list.ts +156 -0
- package/src/cli/commands/new.ts +316 -0
- package/src/cli/index.ts +116 -0
- package/src/core/config.ts +172 -0
- package/src/core/context.ts +212 -0
- package/src/core/generator.ts +1350 -0
- package/src/core/types.ts +184 -0
- package/src/templates/index.ts +162 -0
- package/src/types/gradient-string.d.ts +25 -0
- package/src/ui/colors.ts +139 -0
- package/src/ui/output.ts +230 -0
- package/src/ui/prompts.ts +170 -0
- package/src/ui/spinners.ts +95 -0
- package/src/ui/types.ts +41 -0
- package/src/utils/filesystem.ts +222 -0
- package/src/utils/logger.ts +67 -0
- package/src/utils/object.ts +456 -0
- package/src/utils/validation.ts +345 -0
- package/tsconfig.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# andrud
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://img.shields.io/npm/v/@andrud/cli?style=for-the-badge&color=%2300C9FF&labelColor=%23222" alt="npm version">
|
|
5
|
+
<img src="https://img.shields.io/npm/dm/@andrud/cli?style=for-the-badge&color=%2392FE9D&labelColor=%23222" alt="npm downloads">
|
|
6
|
+
<img src="https://img.shields.io/github/license/MurShidM01/andrud?style=for-the-badge&color=%23ff6b6b&labelColor=%23222" alt="license">
|
|
7
|
+
<img src="https://img.shields.io/github/stars/MurShidM01/andrud?style=for-the-badge&color=%23ffd93d&labelColor=%23222" alt="stars">
|
|
8
|
+
<img src="https://img.shields.io/github/forks/MurShidM01/andrud?style=for-the-badge&color=%236bcb77&labelColor=%23222" alt="forks">
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<img src="https://img.shields.io/badge/Node.js-18+-green?style=for-the-badge&logo=node.js&logoColor=white" alt="node">
|
|
13
|
+
<img src="https://img.shields.io/badge/TypeScript-5.4-blue?style=for-the-badge&logo=typescript&logoColor=white" alt="typescript">
|
|
14
|
+
<img src="https://img.shields.io/badge/Android-Studio-3DDC84?style=for-the-badge&logo=android&logoColor=white" alt="android">
|
|
15
|
+
<img src="https://img.shields.io/badge/Kotlin-7F52FF?style=for-the-badge&logo=kotlin&logoColor=white" alt="kotlin">
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<h1 align="center">⚡ Modern Android Project Scaffolding CLI</h1>
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
<strong>andrud</strong> is a blazing-fast, interactive CLI tool for generating production-ready Android project structures in seconds. Built for developers who value speed, consistency, and modern Android development practices.
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<p align="center">
|
|
25
|
+
<img src="https://i.imgur.com/example.gif" width="600" alt="andrud demo">
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## ✨ Features
|
|
31
|
+
|
|
32
|
+
| Feature | Description |
|
|
33
|
+
|---------|-------------|
|
|
34
|
+
| 🚀 **Blazing Fast** | Generate complete Android projects in under 5 seconds |
|
|
35
|
+
| 🎨 **Multiple Templates** | Kotlin, Java, Jetpack Compose, Native C++/NDK support |
|
|
36
|
+
| 💎 **Interactive CLI** | Beautiful prompts powered by @clack/prompts |
|
|
37
|
+
| 📦 **Production Ready** | Industry-standard Gradle configuration with latest versions |
|
|
38
|
+
| 🎯 **TypeScript** | Fully typed codebase for reliability |
|
|
39
|
+
| 🔧 **Customizable** | Configure SDK versions, features, and more |
|
|
40
|
+
| 📱 **Modern Android** | Android 15 (SDK 35/36) with Jetpack libraries |
|
|
41
|
+
| 🌙 **Beautiful UI** | Colorful terminal output with gradients |
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 📋 Templates
|
|
46
|
+
|
|
47
|
+
| Template | Language | UI Framework | Use Case |
|
|
48
|
+
|----------|----------|--------------|----------|
|
|
49
|
+
| `kotlin-xml` | Kotlin | XML Views | Traditional Android development |
|
|
50
|
+
| `kotlin-compose` | Kotlin | Jetpack Compose | Modern declarative UI |
|
|
51
|
+
| `java-xml` | Java | XML Views | Java projects & legacy codebases |
|
|
52
|
+
| `native-cpp` | Kotlin + C++ | XML Views | High-performance & game development |
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 🚀 Quick Start
|
|
57
|
+
|
|
58
|
+
### Installation
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Install globally via npm
|
|
62
|
+
npm install -g @andrud/cli
|
|
63
|
+
|
|
64
|
+
# Verify installation
|
|
65
|
+
andrud --version
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Create a New Project
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Interactive mode (recommended)
|
|
72
|
+
andrud create
|
|
73
|
+
|
|
74
|
+
# With all options specified
|
|
75
|
+
andrud new MyApp -t kotlin-compose -p com.example.myapp -d ./projects
|
|
76
|
+
|
|
77
|
+
# Quick create with defaults
|
|
78
|
+
andrud new MyApp
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Available Commands
|
|
82
|
+
|
|
83
|
+
| Command | Description |
|
|
84
|
+
|---------|-------------|
|
|
85
|
+
| `andrud create` | Interactive project creation |
|
|
86
|
+
| `andrud new <name>` | Create project with name |
|
|
87
|
+
| `andrud list` | Show all available templates |
|
|
88
|
+
| `andrud info <template>` | View template details |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 📖 Usage Examples
|
|
93
|
+
|
|
94
|
+
### Interactive Project Creation
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
$ andrud create
|
|
98
|
+
|
|
99
|
+
? Enter your app name: MyAwesomeApp
|
|
100
|
+
? Enter package name: com.example.myawesomeapp
|
|
101
|
+
? Select template: kotlin-compose
|
|
102
|
+
? Project directory: ./projects
|
|
103
|
+
|
|
104
|
+
⚡ Generating project structure...
|
|
105
|
+
✅ MyAwesomeApp created successfully!
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Create with Options
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Kotlin with Jetpack Compose
|
|
112
|
+
andrud new MyComposeApp -t kotlin-compose -p com.mydomain.app
|
|
113
|
+
|
|
114
|
+
# Kotlin with XML Views
|
|
115
|
+
andrud new MyKotlinApp -t kotlin-xml -p com.mydomain.app
|
|
116
|
+
|
|
117
|
+
# Java with XML Views
|
|
118
|
+
andrud new MyJavaApp -t java-xml -p com.mydomain.app
|
|
119
|
+
|
|
120
|
+
# Native C++ with NDK
|
|
121
|
+
andrud new MyNativeApp -t native-cpp -p com.mydomain.app
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### List Templates
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
$ andrud list
|
|
128
|
+
|
|
129
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
130
|
+
║ Android Project Templates ║
|
|
131
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
132
|
+
|
|
133
|
+
1. Kotlin with XML Layouts
|
|
134
|
+
Traditional Android Views with Kotlin...
|
|
135
|
+
|
|
136
|
+
2. Kotlin with Jetpack Compose
|
|
137
|
+
Modern declarative UI...
|
|
138
|
+
|
|
139
|
+
3. Java with XML Layouts
|
|
140
|
+
Traditional Android Views with Java...
|
|
141
|
+
|
|
142
|
+
4. Kotlin with Native C++/NDK
|
|
143
|
+
Native C++ development with JNI...
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 🏗️ Project Structure
|
|
149
|
+
|
|
150
|
+
Generated projects include:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
MyApp/
|
|
154
|
+
├── app/
|
|
155
|
+
│ ├── src/main/
|
|
156
|
+
│ │ ├── java/ # Java/Kotlin source files
|
|
157
|
+
│ │ ├── res/ # Resources (layouts, values, drawables)
|
|
158
|
+
│ │ ├── AndroidManifest.xml
|
|
159
|
+
│ │ └── ...
|
|
160
|
+
│ ├── build.gradle.kts # App-level build config
|
|
161
|
+
│ └── proguard-rules.pro
|
|
162
|
+
├── gradle/wrapper/ # Gradle wrapper files
|
|
163
|
+
├── build.gradle.kts # Root build config
|
|
164
|
+
├── settings.gradle.kts # Project settings
|
|
165
|
+
├── gradle.properties # Gradle properties
|
|
166
|
+
├── .gitignore
|
|
167
|
+
└── README.md
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## ⚙️ Configuration
|
|
173
|
+
|
|
174
|
+
### SDK Versions
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Custom SDK versions
|
|
178
|
+
andrud new MyApp --min-sdk 24 --target-sdk 35
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Features
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
# Disable specific features
|
|
185
|
+
andrud new MyApp --no-git --no-readme
|
|
186
|
+
|
|
187
|
+
# Skip dependency installation
|
|
188
|
+
andrud new MyApp --skip-install
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## 🛠️ Development
|
|
194
|
+
|
|
195
|
+
### Setup
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
# Clone the repository
|
|
199
|
+
git clone https://github.com/MurShidM01/andrud.git
|
|
200
|
+
cd andrud
|
|
201
|
+
|
|
202
|
+
# Install dependencies
|
|
203
|
+
npm install
|
|
204
|
+
|
|
205
|
+
# Build the project
|
|
206
|
+
npm run build
|
|
207
|
+
|
|
208
|
+
# Run in development mode
|
|
209
|
+
npm run dev -- create
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Available Scripts
|
|
213
|
+
|
|
214
|
+
| Script | Description |
|
|
215
|
+
|--------|-------------|
|
|
216
|
+
| `npm run build` | Build TypeScript to JavaScript |
|
|
217
|
+
| `npm run dev` | Build and run in development |
|
|
218
|
+
| `npm run link` | Link package globally for testing |
|
|
219
|
+
| `npm test` | Run tests |
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## 📦 What's Included
|
|
224
|
+
|
|
225
|
+
### Dependencies
|
|
226
|
+
- **@clack/prompts** - Beautiful interactive prompts
|
|
227
|
+
- **cac** - Lightweight CLI argument parser
|
|
228
|
+
- **fs-extra** - Enhanced file system operations
|
|
229
|
+
- **ora** - Elegant terminal spinners
|
|
230
|
+
- **picocolors** - Terminal colors
|
|
231
|
+
- **gradient-string** - Gradient text effects
|
|
232
|
+
- **update-notifier** - Check for updates
|
|
233
|
+
|
|
234
|
+
### Dev Dependencies
|
|
235
|
+
- **TypeScript 5.4** - Type safety
|
|
236
|
+
- **@types/node** - Node.js type definitions
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 🎯 Tech Stack
|
|
241
|
+
|
|
242
|
+
<p align="center">
|
|
243
|
+
<img src="https://skillicons.dev/icons?i=nodejs,typescript,androidstudio,kotlin,gradle,npm" alt="Tech Stack">
|
|
244
|
+
</p>
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 📊 Statistics
|
|
249
|
+
|
|
250
|
+
<p align="center">
|
|
251
|
+
<img src="https://img.shields.io/github/repo-size/MurShidM01/andrud?style=for-the-badge" alt="repo size">
|
|
252
|
+
<img src="https://img.shields.io/github/languages/code-size/MurShidM01/andrud?style=for-the-badge" alt="code size">
|
|
253
|
+
<img src="https://img.shields.io/github/languages/count/MurShidM01/andrud?style=for-the-badge" alt="languages">
|
|
254
|
+
<img src="https://img.shields.io/tokei/lines/github/MurShidM01/andrud?style=for-the-badge" alt="lines of code">
|
|
255
|
+
</p>
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## 🤝 Contributing
|
|
260
|
+
|
|
261
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
262
|
+
|
|
263
|
+
1. Fork the repository
|
|
264
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
265
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
266
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
267
|
+
5. Open a Pull Request
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 📝 License
|
|
272
|
+
|
|
273
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## 🙏 Acknowledgments
|
|
278
|
+
|
|
279
|
+
- [Android Open Source Project](https://source.android.com/)
|
|
280
|
+
- [Jetpack Compose](https://developer.android.com/compose)
|
|
281
|
+
- [Kotlin](https://kotlinlang.org/)
|
|
282
|
+
- [Gradle](https://gradle.org/)
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## 📮 Contact
|
|
287
|
+
|
|
288
|
+
<p align="center">
|
|
289
|
+
<a href="https://github.com/MurShidM01/andrud/issues">
|
|
290
|
+
<img src="https://img.shields.io/badge/Issues-Open-green?style=for-the-badge&logo=github" alt="issues">
|
|
291
|
+
</a>
|
|
292
|
+
<a href="https://github.com/MurShidM01/andrud/discussions">
|
|
293
|
+
<img src="https://img.shields.io/badge/Discussions-Q&A-blue?style=for-the-badge&logo=github" alt="discussions">
|
|
294
|
+
</a>
|
|
295
|
+
</p>
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
<p align="center">
|
|
300
|
+
<strong>Made with ❤️ by <a href="https://github.com/MurShidM01">MurShidM01</a></strong>
|
|
301
|
+
</p>
|
|
302
|
+
|
|
303
|
+
<p align="center">
|
|
304
|
+
<img src="https://img.shields.io/badge/JavaScript-ES2022-yellow?style=for-the-badge&logo=javascript&logoColor=black" alt="js">
|
|
305
|
+
<img src="https://img.shields.io/badge/PowerShell-7+-blue?style=for-the-badge&logo=powershell" alt="powershell">
|
|
306
|
+
</p>
|
|
307
|
+
|
|
308
|
+
<p align="center">
|
|
309
|
+
<substar>Star ⭐ this repo if you find it useful!</substar>
|
|
310
|
+
</p>
|
package/bin/andrud.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @andrud/cli - Android Project Scaffolding CLI
|
|
5
|
+
*
|
|
6
|
+
* Entry point for the andrud CLI tool.
|
|
7
|
+
* This file is used when running via `node bin/andrud.js`
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Run the CLI
|
|
11
|
+
import('../dist/cli/index.js').then((module) => {
|
|
12
|
+
if (module.runCli) {
|
|
13
|
+
Promise.resolve(module.runCli()).catch((error) => {
|
|
14
|
+
console.error('CLI execution failed:', error);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
});
|
|
17
|
+
} else {
|
|
18
|
+
console.error('Failed to load CLI: runCli function not found');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
}).catch((error) => {
|
|
22
|
+
console.error('Failed to start CLI:', error);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "andrud",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Modern Android project scaffolding CLI tool - Generate production-ready Android projects in seconds",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"andrud": "./bin/andrud.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/cli/index.js",
|
|
10
|
+
"types": "./dist/cli/index.d.ts",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"android",
|
|
13
|
+
"cli",
|
|
14
|
+
"scaffolding",
|
|
15
|
+
"generator",
|
|
16
|
+
"kotlin",
|
|
17
|
+
"java",
|
|
18
|
+
"jetpack-compose",
|
|
19
|
+
"ndk",
|
|
20
|
+
"native",
|
|
21
|
+
"project-template",
|
|
22
|
+
"android-studio",
|
|
23
|
+
"mobile-development"
|
|
24
|
+
],
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/cli/index.d.ts",
|
|
28
|
+
"import": "./dist/cli/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./core": {
|
|
31
|
+
"types": "./dist/core/index.d.ts",
|
|
32
|
+
"import": "./dist/core/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./ui": {
|
|
35
|
+
"types": "./dist/ui/index.d.ts",
|
|
36
|
+
"import": "./dist/ui/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./utils": {
|
|
39
|
+
"types": "./dist/utils/index.d.ts",
|
|
40
|
+
"import": "./dist/utils/index.js"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc -b",
|
|
45
|
+
"build:all": "tsc -b",
|
|
46
|
+
"dev": "npm run build && node bin/andrud.js",
|
|
47
|
+
"link": "npm run build && npm link",
|
|
48
|
+
"test": "node --test"
|
|
49
|
+
},
|
|
50
|
+
"author": "MurShidM01",
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/MurShidM01/andrud.git"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://github.com/MurShidM01/andrud#readme",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/MurShidM01/andrud/issues"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@clack/prompts": "^0.7.0",
|
|
62
|
+
"cac": "^6.7.14",
|
|
63
|
+
"ejs": "^3.1.10",
|
|
64
|
+
"fs-extra": "^11.2.0",
|
|
65
|
+
"gradient-string": "^2.0.2",
|
|
66
|
+
"ora": "^8.0.1",
|
|
67
|
+
"picocolors": "^1.0.1",
|
|
68
|
+
"update-notifier": "^7.0.0",
|
|
69
|
+
"validate-npm-package-name": "^4.0.0"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@types/ejs": "^3.1.5",
|
|
73
|
+
"@types/fs-extra": "^11.0.4",
|
|
74
|
+
"@types/node": "^20.12.12",
|
|
75
|
+
"typescript": "^5.4.5"
|
|
76
|
+
},
|
|
77
|
+
"engines": {
|
|
78
|
+
"node": ">=18.0.0"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
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
|
+
});
|