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/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
+ });