@abuhannaa/create-apptemplate 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 +165 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +725 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# @abuhannaa/create-apptemplate
|
|
2
|
+
|
|
3
|
+
Create fullstack applications with .NET/Spring/NestJS backends and Vue (Vuetify/PrimeVue) frontends.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Using npm
|
|
9
|
+
npm create @abuhannaa/apptemplate@latest
|
|
10
|
+
|
|
11
|
+
# Using npx
|
|
12
|
+
npx @abuhannaa/create-apptemplate my-app
|
|
13
|
+
|
|
14
|
+
# Using pnpm
|
|
15
|
+
pnpm create @abuhannaa/apptemplate
|
|
16
|
+
|
|
17
|
+
# Using yarn
|
|
18
|
+
yarn create @abuhannaa/apptemplate
|
|
19
|
+
|
|
20
|
+
# Using bun
|
|
21
|
+
bun create @abuhannaa/apptemplate
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
- **Multiple Backend Options**
|
|
27
|
+
- .NET 8 (Clean Architecture, CQRS, Entity Framework)
|
|
28
|
+
- Spring Boot 3 (Clean Architecture, Java 21)
|
|
29
|
+
- NestJS (Clean Architecture, TypeScript)
|
|
30
|
+
|
|
31
|
+
- **Multiple Frontend Options**
|
|
32
|
+
- Vuetify (Material Design 3)
|
|
33
|
+
- PrimeVue (Aura Theme)
|
|
34
|
+
|
|
35
|
+
- **Project Types**
|
|
36
|
+
- Fullstack (Backend + Frontend + Docker)
|
|
37
|
+
- Backend only (API service)
|
|
38
|
+
- Frontend only (SPA)
|
|
39
|
+
|
|
40
|
+
- **Production Ready**
|
|
41
|
+
- Docker & Docker Compose setup
|
|
42
|
+
- CI/CD with GitHub Actions
|
|
43
|
+
- Environment configuration
|
|
44
|
+
- JWT Authentication
|
|
45
|
+
- Real-time notifications (SignalR/WebSocket)
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
### Interactive Mode (Recommended)
|
|
50
|
+
|
|
51
|
+
Simply run the command without arguments:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm create @abuhannaa/apptemplate@latest
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The wizard will guide you through:
|
|
58
|
+
|
|
59
|
+
1. **Project location** - Where to create your project
|
|
60
|
+
2. **Project type** - Fullstack, Backend only, or Frontend only
|
|
61
|
+
3. **Backend framework** - .NET 8, Spring Boot 3, or NestJS
|
|
62
|
+
4. **UI library** - Vuetify or PrimeVue
|
|
63
|
+
5. **Project name** - For namespaces (e.g., MyCompany.MyApp)
|
|
64
|
+
6. **Install dependencies** - Whether to install now
|
|
65
|
+
|
|
66
|
+
### Non-Interactive Mode
|
|
67
|
+
|
|
68
|
+
Specify all options via command line:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Fullstack project with .NET backend and Vuetify frontend
|
|
72
|
+
npx @abuhannaa/create-apptemplate my-app -b dotnet -u vuetify -n "MyCompany.MyApp" -i
|
|
73
|
+
|
|
74
|
+
# Backend-only project with Spring Boot
|
|
75
|
+
npx @abuhannaa/create-apptemplate my-api -t backend -b spring -n "MyCompany.MyApi"
|
|
76
|
+
|
|
77
|
+
# Frontend-only project with PrimeVue
|
|
78
|
+
npx @abuhannaa/create-apptemplate my-spa -t frontend -u primevue
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Options
|
|
82
|
+
|
|
83
|
+
| Option | Alias | Description | Values |
|
|
84
|
+
|--------|-------|-------------|--------|
|
|
85
|
+
| `--type` | `-t` | Project type | `fullstack`, `backend`, `frontend` |
|
|
86
|
+
| `--backend` | `-b` | Backend framework | `dotnet`, `spring`, `nestjs` |
|
|
87
|
+
| `--ui` | `-u` | UI library | `vuetify`, `primevue` |
|
|
88
|
+
| `--name` | `-n` | Project name (namespaces) | `Company.Project` format |
|
|
89
|
+
| `--install` | `-i` | Install dependencies | - |
|
|
90
|
+
| `--help` | `-h` | Show help | - |
|
|
91
|
+
| `--version` | `-v` | Show version | - |
|
|
92
|
+
|
|
93
|
+
## After Creation
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Navigate to project
|
|
97
|
+
cd my-app
|
|
98
|
+
|
|
99
|
+
# Setup environment
|
|
100
|
+
cp .env.example .env
|
|
101
|
+
|
|
102
|
+
# Start with Docker (recommended)
|
|
103
|
+
docker compose up -d --build
|
|
104
|
+
|
|
105
|
+
# Or run manually:
|
|
106
|
+
# Backend (.NET)
|
|
107
|
+
cd backend-dotnet/src/Presentation/App.Template.WebAPI && dotnet run
|
|
108
|
+
|
|
109
|
+
# Frontend
|
|
110
|
+
cd frontend-vuetify && npm run dev
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Access Points
|
|
114
|
+
|
|
115
|
+
- **Frontend**: http://localhost
|
|
116
|
+
- **Backend API**: http://localhost:5100
|
|
117
|
+
- **Swagger UI**: http://localhost:5100/swagger
|
|
118
|
+
|
|
119
|
+
### Default Login
|
|
120
|
+
|
|
121
|
+
- **Username**: `admin`
|
|
122
|
+
- **Password**: `Admin@123`
|
|
123
|
+
|
|
124
|
+
## Project Structure
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
my-app/
|
|
128
|
+
├── backend-dotnet/ # .NET 8 API (if selected)
|
|
129
|
+
│ ├── src/
|
|
130
|
+
│ │ ├── Core/ # Domain & Application layers
|
|
131
|
+
│ │ ├── Infrastructure/ # EF Core, Services
|
|
132
|
+
│ │ └── Presentation/ # Controllers, Middleware
|
|
133
|
+
│ └── tests/
|
|
134
|
+
├── frontend-vuetify/ # Vue 3 + Vuetify (if selected)
|
|
135
|
+
│ ├── src/
|
|
136
|
+
│ │ ├── components/ # Vue components
|
|
137
|
+
│ │ ├── pages/ # File-based routing
|
|
138
|
+
│ │ ├── stores/ # Pinia state
|
|
139
|
+
│ │ └── services/ # API services
|
|
140
|
+
│ └── ...
|
|
141
|
+
├── docker/ # Docker configurations
|
|
142
|
+
├── scripts/ # Utility scripts
|
|
143
|
+
├── Dockerfile # Unified container build
|
|
144
|
+
├── docker-compose.yml # Development setup
|
|
145
|
+
└── .env.example # Environment template
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Requirements
|
|
149
|
+
|
|
150
|
+
- **Node.js** 18.0.0 or higher
|
|
151
|
+
- **Docker** (recommended for running the app)
|
|
152
|
+
- **.NET 8 SDK** (for .NET backend development)
|
|
153
|
+
- **Java 21** (for Spring backend development)
|
|
154
|
+
|
|
155
|
+
## Documentation
|
|
156
|
+
|
|
157
|
+
For detailed documentation, see:
|
|
158
|
+
|
|
159
|
+
- [Backend Guide](https://github.com/abuhanna/app-template/blob/main/backend-dotnet/README.md)
|
|
160
|
+
- [Frontend Guide](https://github.com/abuhanna/app-template/blob/main/frontend-vuetify/README.md)
|
|
161
|
+
- [Deployment Guide](https://github.com/abuhanna/app-template/blob/main/DEPLOYMENT.md)
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { intro, outro, isCancel as isCancel2 } from "@clack/prompts";
|
|
5
|
+
import pc3 from "picocolors";
|
|
6
|
+
|
|
7
|
+
// src/cli.ts
|
|
8
|
+
var validProjectTypes = ["fullstack", "backend", "frontend"];
|
|
9
|
+
var validBackends = ["dotnet", "spring", "nestjs"];
|
|
10
|
+
var validUILibraries = ["vuetify", "primevue"];
|
|
11
|
+
function parseArgs() {
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
const result = {};
|
|
14
|
+
let i = 0;
|
|
15
|
+
while (i < args.length) {
|
|
16
|
+
const arg = args[i];
|
|
17
|
+
if (arg === "-h" || arg === "--help") {
|
|
18
|
+
result.help = true;
|
|
19
|
+
i++;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (arg === "-v" || arg === "--version") {
|
|
23
|
+
result.version = true;
|
|
24
|
+
i++;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (arg === "-i" || arg === "--install") {
|
|
28
|
+
result.install = true;
|
|
29
|
+
i++;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (arg === "-t" || arg === "--type") {
|
|
33
|
+
const value = args[++i];
|
|
34
|
+
if (isValidProjectType(value)) {
|
|
35
|
+
result.type = value;
|
|
36
|
+
} else {
|
|
37
|
+
console.warn(`Warning: Invalid project type "${value}". Valid options: ${validProjectTypes.join(", ")}`);
|
|
38
|
+
}
|
|
39
|
+
i++;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (arg === "-b" || arg === "--backend") {
|
|
43
|
+
const value = args[++i];
|
|
44
|
+
if (isValidBackend(value)) {
|
|
45
|
+
result.backend = value;
|
|
46
|
+
} else {
|
|
47
|
+
console.warn(`Warning: Invalid backend "${value}". Valid options: ${validBackends.join(", ")}`);
|
|
48
|
+
}
|
|
49
|
+
i++;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (arg === "-u" || arg === "--ui") {
|
|
53
|
+
const value = args[++i];
|
|
54
|
+
if (isValidUI(value)) {
|
|
55
|
+
result.ui = value;
|
|
56
|
+
} else {
|
|
57
|
+
console.warn(`Warning: Invalid UI library "${value}". Valid options: ${validUILibraries.join(", ")}`);
|
|
58
|
+
}
|
|
59
|
+
i++;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (arg === "-n" || arg === "--name") {
|
|
63
|
+
const value = args[++i];
|
|
64
|
+
if (isValidProjectName(value)) {
|
|
65
|
+
result.projectName = value;
|
|
66
|
+
} else {
|
|
67
|
+
console.warn(`Warning: Project name should be in "Company.Project" format`);
|
|
68
|
+
}
|
|
69
|
+
i++;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (!arg.startsWith("-") && !result.projectPath) {
|
|
73
|
+
result.projectPath = arg;
|
|
74
|
+
}
|
|
75
|
+
i++;
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
function isValidProjectType(value) {
|
|
80
|
+
return value !== void 0 && validProjectTypes.includes(value);
|
|
81
|
+
}
|
|
82
|
+
function isValidBackend(value) {
|
|
83
|
+
return value !== void 0 && validBackends.includes(value);
|
|
84
|
+
}
|
|
85
|
+
function isValidUI(value) {
|
|
86
|
+
return value !== void 0 && validUILibraries.includes(value);
|
|
87
|
+
}
|
|
88
|
+
function isValidProjectName(value) {
|
|
89
|
+
if (!value) return false;
|
|
90
|
+
const pattern = /^[A-Za-z][A-Za-z0-9]*(\.[A-Za-z][A-Za-z0-9]*)+$/;
|
|
91
|
+
return pattern.test(value);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/prompts.ts
|
|
95
|
+
import * as p from "@clack/prompts";
|
|
96
|
+
import pc from "picocolors";
|
|
97
|
+
import path from "path";
|
|
98
|
+
import fs from "fs";
|
|
99
|
+
async function runInteractivePrompts(cliArgs) {
|
|
100
|
+
let projectPath = cliArgs.projectPath;
|
|
101
|
+
if (!projectPath) {
|
|
102
|
+
const result = await p.text({
|
|
103
|
+
message: "Where should we create your project?",
|
|
104
|
+
placeholder: "./my-app",
|
|
105
|
+
defaultValue: "./my-app",
|
|
106
|
+
validate: (value) => {
|
|
107
|
+
if (!value) return "Please enter a directory path";
|
|
108
|
+
const resolvedPath = path.resolve(value);
|
|
109
|
+
if (fs.existsSync(resolvedPath) && fs.readdirSync(resolvedPath).length > 0) {
|
|
110
|
+
return "Directory exists and is not empty";
|
|
111
|
+
}
|
|
112
|
+
return void 0;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
if (p.isCancel(result)) return result;
|
|
116
|
+
projectPath = result;
|
|
117
|
+
}
|
|
118
|
+
let projectType = cliArgs.type;
|
|
119
|
+
if (!projectType) {
|
|
120
|
+
const result = await p.select({
|
|
121
|
+
message: "What type of project would you like to create?",
|
|
122
|
+
options: [
|
|
123
|
+
{
|
|
124
|
+
value: "fullstack",
|
|
125
|
+
label: "Fullstack",
|
|
126
|
+
hint: "Backend + Frontend + Docker"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
value: "backend",
|
|
130
|
+
label: "Backend only",
|
|
131
|
+
hint: "API service or microservice"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
value: "frontend",
|
|
135
|
+
label: "Frontend only",
|
|
136
|
+
hint: "SPA with external API"
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
});
|
|
140
|
+
if (p.isCancel(result)) return result;
|
|
141
|
+
projectType = result;
|
|
142
|
+
}
|
|
143
|
+
let backend = cliArgs.backend || "dotnet";
|
|
144
|
+
if (projectType !== "frontend" && !cliArgs.backend) {
|
|
145
|
+
const result = await p.select({
|
|
146
|
+
message: "Which backend framework would you like to use?",
|
|
147
|
+
options: [
|
|
148
|
+
{
|
|
149
|
+
value: "dotnet",
|
|
150
|
+
label: ".NET 8",
|
|
151
|
+
hint: "Clean Architecture, CQRS, Entity Framework"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
value: "spring",
|
|
155
|
+
label: "Spring Boot 3",
|
|
156
|
+
hint: "Clean Architecture, Java 21"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
value: "nestjs",
|
|
160
|
+
label: "NestJS",
|
|
161
|
+
hint: "Clean Architecture, TypeScript"
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
});
|
|
165
|
+
if (p.isCancel(result)) return result;
|
|
166
|
+
backend = result;
|
|
167
|
+
}
|
|
168
|
+
let ui = cliArgs.ui || "vuetify";
|
|
169
|
+
if (projectType !== "backend" && !cliArgs.ui) {
|
|
170
|
+
const result = await p.select({
|
|
171
|
+
message: "Which UI library would you like to use?",
|
|
172
|
+
options: [
|
|
173
|
+
{
|
|
174
|
+
value: "vuetify",
|
|
175
|
+
label: "Vuetify",
|
|
176
|
+
hint: "Material Design 3, 80+ components"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
value: "primevue",
|
|
180
|
+
label: "PrimeVue",
|
|
181
|
+
hint: "Aura theme, 90+ components"
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
});
|
|
185
|
+
if (p.isCancel(result)) return result;
|
|
186
|
+
ui = result;
|
|
187
|
+
}
|
|
188
|
+
let projectName = cliArgs.projectName;
|
|
189
|
+
if (!projectName) {
|
|
190
|
+
const dirName = path.basename(path.resolve(projectPath));
|
|
191
|
+
const defaultName = `MyCompany.${toPascalCase(dirName)}`;
|
|
192
|
+
const result = await p.text({
|
|
193
|
+
message: "Project name (for namespaces)",
|
|
194
|
+
placeholder: defaultName,
|
|
195
|
+
defaultValue: defaultName,
|
|
196
|
+
validate: (value) => {
|
|
197
|
+
if (!value) return "Please enter a project name";
|
|
198
|
+
const pattern = /^[A-Za-z][A-Za-z0-9]*(\.[A-Za-z][A-Za-z0-9]*)+$/;
|
|
199
|
+
if (!pattern.test(value)) {
|
|
200
|
+
return 'Name must be in "Company.Project" format (e.g., MyCompany.MyApp)';
|
|
201
|
+
}
|
|
202
|
+
return void 0;
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
if (p.isCancel(result)) return result;
|
|
206
|
+
projectName = result;
|
|
207
|
+
}
|
|
208
|
+
let installDeps = cliArgs.install ?? false;
|
|
209
|
+
if (!cliArgs.install) {
|
|
210
|
+
const result = await p.confirm({
|
|
211
|
+
message: "Install dependencies after creation?",
|
|
212
|
+
initialValue: true
|
|
213
|
+
});
|
|
214
|
+
if (p.isCancel(result)) return result;
|
|
215
|
+
installDeps = result;
|
|
216
|
+
}
|
|
217
|
+
console.log();
|
|
218
|
+
p.note(
|
|
219
|
+
[
|
|
220
|
+
`${pc.cyan("Project path:")} ${projectPath}`,
|
|
221
|
+
`${pc.cyan("Project type:")} ${projectType}`,
|
|
222
|
+
projectType !== "frontend" ? `${pc.cyan("Backend:")} ${getBackendLabel(backend)}` : null,
|
|
223
|
+
projectType !== "backend" ? `${pc.cyan("UI Library:")} ${getUILabel(ui)}` : null,
|
|
224
|
+
`${pc.cyan("Project name:")} ${projectName}`,
|
|
225
|
+
`${pc.cyan("Install deps:")} ${installDeps ? "Yes" : "No"}`
|
|
226
|
+
].filter(Boolean).join("\n"),
|
|
227
|
+
"Configuration"
|
|
228
|
+
);
|
|
229
|
+
const shouldContinue = await p.confirm({
|
|
230
|
+
message: "Create project with these settings?",
|
|
231
|
+
initialValue: true
|
|
232
|
+
});
|
|
233
|
+
if (p.isCancel(shouldContinue) || !shouldContinue) {
|
|
234
|
+
return p.isCancel(shouldContinue) ? shouldContinue : /* @__PURE__ */ Symbol("cancelled");
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
projectPath,
|
|
238
|
+
projectType,
|
|
239
|
+
backend,
|
|
240
|
+
ui,
|
|
241
|
+
projectName,
|
|
242
|
+
installDeps
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function toPascalCase(str) {
|
|
246
|
+
return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^(.)/, (c) => c.toUpperCase());
|
|
247
|
+
}
|
|
248
|
+
function getBackendLabel(backend) {
|
|
249
|
+
const labels = {
|
|
250
|
+
dotnet: ".NET 8",
|
|
251
|
+
spring: "Spring Boot 3",
|
|
252
|
+
nestjs: "NestJS"
|
|
253
|
+
};
|
|
254
|
+
return labels[backend];
|
|
255
|
+
}
|
|
256
|
+
function getUILabel(ui) {
|
|
257
|
+
const labels = {
|
|
258
|
+
vuetify: "Vuetify (Material Design)",
|
|
259
|
+
primevue: "PrimeVue (Aura Theme)"
|
|
260
|
+
};
|
|
261
|
+
return labels[ui];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/generator.ts
|
|
265
|
+
import * as p2 from "@clack/prompts";
|
|
266
|
+
import pc2 from "picocolors";
|
|
267
|
+
import path5 from "path";
|
|
268
|
+
import fs5 from "fs";
|
|
269
|
+
|
|
270
|
+
// src/utils/download.ts
|
|
271
|
+
import degit from "degit";
|
|
272
|
+
import path2 from "path";
|
|
273
|
+
import fs2 from "fs";
|
|
274
|
+
async function downloadTemplate(repo, folder, destPath) {
|
|
275
|
+
const source = `${repo}/${folder}`;
|
|
276
|
+
const emitter = degit(source, {
|
|
277
|
+
cache: false,
|
|
278
|
+
force: true,
|
|
279
|
+
verbose: false
|
|
280
|
+
});
|
|
281
|
+
await emitter.clone(destPath);
|
|
282
|
+
}
|
|
283
|
+
async function copyRootFiles(repo, destPath, config) {
|
|
284
|
+
const commonFiles = [
|
|
285
|
+
".env.example",
|
|
286
|
+
".gitignore",
|
|
287
|
+
"README.md",
|
|
288
|
+
"CLAUDE.md"
|
|
289
|
+
];
|
|
290
|
+
const fullstackFiles = [
|
|
291
|
+
"Dockerfile",
|
|
292
|
+
"docker-compose.yml",
|
|
293
|
+
"docker-compose.staging.yml",
|
|
294
|
+
"docker-compose.production.yml",
|
|
295
|
+
"Makefile"
|
|
296
|
+
];
|
|
297
|
+
const backendOnlyFiles = [
|
|
298
|
+
"docker/Dockerfile.backend",
|
|
299
|
+
"docker-compose.backend.yml"
|
|
300
|
+
];
|
|
301
|
+
const frontendOnlyFiles = [
|
|
302
|
+
"docker/Dockerfile.frontend",
|
|
303
|
+
"docker/nginx/frontend-only.conf",
|
|
304
|
+
"docker-compose.frontend.yml"
|
|
305
|
+
];
|
|
306
|
+
let filesToDownload = [...commonFiles];
|
|
307
|
+
switch (config.projectType) {
|
|
308
|
+
case "fullstack":
|
|
309
|
+
filesToDownload = [...filesToDownload, ...fullstackFiles];
|
|
310
|
+
break;
|
|
311
|
+
case "backend":
|
|
312
|
+
filesToDownload = [...filesToDownload, ...backendOnlyFiles];
|
|
313
|
+
break;
|
|
314
|
+
case "frontend":
|
|
315
|
+
filesToDownload = [...filesToDownload, ...frontendOnlyFiles];
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
const dockerFolder = "docker";
|
|
319
|
+
const dockerDest = path2.join(destPath, dockerFolder);
|
|
320
|
+
try {
|
|
321
|
+
await downloadTemplate(repo, dockerFolder, dockerDest);
|
|
322
|
+
} catch {
|
|
323
|
+
}
|
|
324
|
+
const scriptsFolder = "scripts";
|
|
325
|
+
const scriptsDest = path2.join(destPath, scriptsFolder);
|
|
326
|
+
try {
|
|
327
|
+
await downloadTemplate(repo, scriptsFolder, scriptsDest);
|
|
328
|
+
} catch {
|
|
329
|
+
}
|
|
330
|
+
const tempDir = path2.join(destPath, ".temp-download");
|
|
331
|
+
try {
|
|
332
|
+
const emitter = degit(repo, {
|
|
333
|
+
cache: false,
|
|
334
|
+
force: true,
|
|
335
|
+
verbose: false
|
|
336
|
+
});
|
|
337
|
+
await emitter.clone(tempDir);
|
|
338
|
+
for (const file of filesToDownload) {
|
|
339
|
+
const srcFile = path2.join(tempDir, file);
|
|
340
|
+
const destFile = path2.join(destPath, file);
|
|
341
|
+
if (fs2.existsSync(srcFile)) {
|
|
342
|
+
const parentDir = path2.dirname(destFile);
|
|
343
|
+
if (!fs2.existsSync(parentDir)) {
|
|
344
|
+
fs2.mkdirSync(parentDir, { recursive: true });
|
|
345
|
+
}
|
|
346
|
+
fs2.copyFileSync(srcFile, destFile);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} finally {
|
|
350
|
+
if (fs2.existsSync(tempDir)) {
|
|
351
|
+
fs2.rmSync(tempDir, { recursive: true, force: true });
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// src/utils/rename.ts
|
|
357
|
+
import path3 from "path";
|
|
358
|
+
import fs3 from "fs";
|
|
359
|
+
async function renameProject(projectPath, config) {
|
|
360
|
+
const newDotName = config.projectName;
|
|
361
|
+
const newNamespace = config.projectName.replace(/\./g, "");
|
|
362
|
+
if (config.projectType !== "frontend" && config.backend === "dotnet") {
|
|
363
|
+
await renameDotNetProject(projectPath, config, newDotName, newNamespace);
|
|
364
|
+
}
|
|
365
|
+
if (config.projectType !== "frontend" && config.backend === "spring") {
|
|
366
|
+
await renameSpringProject(projectPath, config, newDotName);
|
|
367
|
+
}
|
|
368
|
+
if (config.projectType !== "frontend" && config.backend === "nestjs") {
|
|
369
|
+
await renameNestJSProject(projectPath, config, newDotName);
|
|
370
|
+
}
|
|
371
|
+
await updateCommonFiles(projectPath, config, newDotName, newNamespace);
|
|
372
|
+
}
|
|
373
|
+
async function renameDotNetProject(projectPath, _config, newDotName, newNamespace) {
|
|
374
|
+
const backendDir = path3.join(projectPath, "backend-dotnet");
|
|
375
|
+
if (!fs3.existsSync(backendDir)) return;
|
|
376
|
+
const folderMappings = [
|
|
377
|
+
["src/Core/App.Template.Domain", `src/Core/${newDotName}.Domain`],
|
|
378
|
+
["src/Core/App.Template.Application", `src/Core/${newDotName}.Application`],
|
|
379
|
+
["src/Infrastructure/App.Template.Infrastructure", `src/Infrastructure/${newDotName}.Infrastructure`],
|
|
380
|
+
["src/Presentation/App.Template.WebAPI", `src/Presentation/${newDotName}.WebAPI`],
|
|
381
|
+
["tests/App.Template.Domain.Tests", `tests/${newDotName}.Domain.Tests`],
|
|
382
|
+
["tests/App.Template.Application.Tests", `tests/${newDotName}.Application.Tests`]
|
|
383
|
+
];
|
|
384
|
+
for (const [oldFolder, newFolder] of folderMappings) {
|
|
385
|
+
const oldPath = path3.join(backendDir, oldFolder);
|
|
386
|
+
const newPath = path3.join(backendDir, newFolder);
|
|
387
|
+
if (fs3.existsSync(oldPath)) {
|
|
388
|
+
fs3.renameSync(oldPath, newPath);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
await renameFilesWithPattern(backendDir, /App\.Template\.(.*)\.csproj$/, (match) => {
|
|
392
|
+
return `${newDotName}.${match[1]}.csproj`;
|
|
393
|
+
});
|
|
394
|
+
const oldSlnPath = path3.join(backendDir, "App.Template.sln");
|
|
395
|
+
const newSlnPath = path3.join(backendDir, `${newDotName}.sln`);
|
|
396
|
+
if (fs3.existsSync(oldSlnPath)) {
|
|
397
|
+
fs3.renameSync(oldSlnPath, newSlnPath);
|
|
398
|
+
}
|
|
399
|
+
const extensions = [".cs", ".csproj", ".sln", ".json"];
|
|
400
|
+
await updateFileContents(backendDir, extensions, (content) => {
|
|
401
|
+
return content.replace(/App\.Template/g, newDotName).replace(/AppTemplate/g, newNamespace);
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
async function renameSpringProject(projectPath, _config, newDotName) {
|
|
405
|
+
const backendDir = path3.join(projectPath, "backend-spring");
|
|
406
|
+
if (!fs3.existsSync(backendDir)) return;
|
|
407
|
+
const packageName = newDotName.toLowerCase().replace(/\./g, ".");
|
|
408
|
+
const artifactId = newDotName.toLowerCase().replace(/\./g, "-");
|
|
409
|
+
const pomPath = path3.join(backendDir, "pom.xml");
|
|
410
|
+
if (fs3.existsSync(pomPath)) {
|
|
411
|
+
let content = fs3.readFileSync(pomPath, "utf-8");
|
|
412
|
+
content = content.replace(/<groupId>com\.apptemplate<\/groupId>/g, `<groupId>${packageName}</groupId>`).replace(/<artifactId>apptemplate<\/artifactId>/g, `<artifactId>${artifactId}</artifactId>`).replace(/com\.apptemplate/g, packageName);
|
|
413
|
+
fs3.writeFileSync(pomPath, content);
|
|
414
|
+
}
|
|
415
|
+
await updateFileContents(backendDir, [".java"], (content) => {
|
|
416
|
+
return content.replace(/com\.apptemplate/g, packageName);
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
async function renameNestJSProject(projectPath, _config, newDotName) {
|
|
420
|
+
const backendDir = path3.join(projectPath, "backend-nestjs");
|
|
421
|
+
if (!fs3.existsSync(backendDir)) return;
|
|
422
|
+
const packageName = newDotName.toLowerCase().replace(/\./g, "-");
|
|
423
|
+
const packageJsonPath = path3.join(backendDir, "package.json");
|
|
424
|
+
if (fs3.existsSync(packageJsonPath)) {
|
|
425
|
+
let content = fs3.readFileSync(packageJsonPath, "utf-8");
|
|
426
|
+
const pkg = JSON.parse(content);
|
|
427
|
+
pkg.name = packageName;
|
|
428
|
+
fs3.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2));
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
async function updateCommonFiles(projectPath, _config, newDotName, newNamespace) {
|
|
432
|
+
const filesToUpdate = [
|
|
433
|
+
"Dockerfile",
|
|
434
|
+
"docker-compose.yml",
|
|
435
|
+
"docker-compose.staging.yml",
|
|
436
|
+
"docker-compose.production.yml",
|
|
437
|
+
"docker-compose.backend.yml",
|
|
438
|
+
"docker-compose.frontend.yml",
|
|
439
|
+
"Makefile",
|
|
440
|
+
"CLAUDE.md",
|
|
441
|
+
"README.md"
|
|
442
|
+
];
|
|
443
|
+
for (const file of filesToUpdate) {
|
|
444
|
+
const filePath = path3.join(projectPath, file);
|
|
445
|
+
if (fs3.existsSync(filePath)) {
|
|
446
|
+
let content = fs3.readFileSync(filePath, "utf-8");
|
|
447
|
+
content = content.replace(/App\.Template/g, newDotName).replace(/AppTemplate/g, newNamespace).replace(/apptemplate/gi, newNamespace.toLowerCase());
|
|
448
|
+
fs3.writeFileSync(filePath, content);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
async function renameFilesWithPattern(dir, pattern, replacer) {
|
|
453
|
+
const files = getAllFiles(dir);
|
|
454
|
+
for (const file of files) {
|
|
455
|
+
const fileName = path3.basename(file);
|
|
456
|
+
const match = fileName.match(pattern);
|
|
457
|
+
if (match) {
|
|
458
|
+
const newFileName = replacer(match);
|
|
459
|
+
const newPath = path3.join(path3.dirname(file), newFileName);
|
|
460
|
+
fs3.renameSync(file, newPath);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
async function updateFileContents(dir, extensions, updater) {
|
|
465
|
+
const files = getAllFiles(dir);
|
|
466
|
+
for (const file of files) {
|
|
467
|
+
const ext = path3.extname(file);
|
|
468
|
+
if (extensions.includes(ext)) {
|
|
469
|
+
let content = fs3.readFileSync(file, "utf-8");
|
|
470
|
+
const updatedContent = updater(content);
|
|
471
|
+
if (content !== updatedContent) {
|
|
472
|
+
fs3.writeFileSync(file, updatedContent);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
function getAllFiles(dir) {
|
|
478
|
+
const files = [];
|
|
479
|
+
if (!fs3.existsSync(dir)) return files;
|
|
480
|
+
const entries = fs3.readdirSync(dir, { withFileTypes: true });
|
|
481
|
+
for (const entry of entries) {
|
|
482
|
+
const fullPath = path3.join(dir, entry.name);
|
|
483
|
+
if (entry.isDirectory()) {
|
|
484
|
+
if (!["node_modules", ".git", "bin", "obj", "dist", "build"].includes(entry.name)) {
|
|
485
|
+
files.push(...getAllFiles(fullPath));
|
|
486
|
+
}
|
|
487
|
+
} else {
|
|
488
|
+
files.push(fullPath);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return files;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// src/utils/package-manager.ts
|
|
495
|
+
import spawn from "cross-spawn";
|
|
496
|
+
import path4 from "path";
|
|
497
|
+
import fs4 from "fs";
|
|
498
|
+
function detectPackageManager() {
|
|
499
|
+
const cwd = process.cwd();
|
|
500
|
+
if (fs4.existsSync(path4.join(cwd, "bun.lockb"))) return "bun";
|
|
501
|
+
if (fs4.existsSync(path4.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
502
|
+
if (fs4.existsSync(path4.join(cwd, "yarn.lock"))) return "yarn";
|
|
503
|
+
if (fs4.existsSync(path4.join(cwd, "package-lock.json"))) return "npm";
|
|
504
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
505
|
+
if (userAgent) {
|
|
506
|
+
if (userAgent.includes("bun")) return "bun";
|
|
507
|
+
if (userAgent.includes("pnpm")) return "pnpm";
|
|
508
|
+
if (userAgent.includes("yarn")) return "yarn";
|
|
509
|
+
}
|
|
510
|
+
return "npm";
|
|
511
|
+
}
|
|
512
|
+
async function installDependencies(projectPath, config) {
|
|
513
|
+
const pm = detectPackageManager();
|
|
514
|
+
if (config.projectType !== "backend") {
|
|
515
|
+
const frontendDir = path4.join(projectPath, `frontend-${config.ui}`);
|
|
516
|
+
if (fs4.existsSync(frontendDir)) {
|
|
517
|
+
await runInstallCommand(frontendDir, pm);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (config.projectType !== "frontend") {
|
|
521
|
+
const backendDir = path4.join(projectPath, `backend-${config.backend}`);
|
|
522
|
+
if (fs4.existsSync(backendDir)) {
|
|
523
|
+
switch (config.backend) {
|
|
524
|
+
case "dotnet":
|
|
525
|
+
await runCommand("dotnet", ["restore"], backendDir);
|
|
526
|
+
break;
|
|
527
|
+
case "spring":
|
|
528
|
+
const mvnwPath = path4.join(backendDir, process.platform === "win32" ? "mvnw.cmd" : "mvnw");
|
|
529
|
+
if (fs4.existsSync(mvnwPath)) {
|
|
530
|
+
await runCommand(mvnwPath, ["install", "-DskipTests"], backendDir);
|
|
531
|
+
} else {
|
|
532
|
+
await runCommand("mvn", ["install", "-DskipTests"], backendDir);
|
|
533
|
+
}
|
|
534
|
+
break;
|
|
535
|
+
case "nestjs":
|
|
536
|
+
await runInstallCommand(backendDir, pm);
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
async function runInstallCommand(cwd, pm) {
|
|
543
|
+
const installCommands = {
|
|
544
|
+
npm: ["install"],
|
|
545
|
+
yarn: ["install"],
|
|
546
|
+
pnpm: ["install"],
|
|
547
|
+
bun: ["install"]
|
|
548
|
+
};
|
|
549
|
+
await runCommand(pm, installCommands[pm], cwd);
|
|
550
|
+
}
|
|
551
|
+
function runCommand(command, args, cwd) {
|
|
552
|
+
return new Promise((resolve, reject) => {
|
|
553
|
+
const child = spawn(command, args, {
|
|
554
|
+
cwd,
|
|
555
|
+
stdio: "pipe",
|
|
556
|
+
shell: process.platform === "win32"
|
|
557
|
+
});
|
|
558
|
+
let stderr = "";
|
|
559
|
+
child.stderr?.on("data", (data) => {
|
|
560
|
+
stderr += data.toString();
|
|
561
|
+
});
|
|
562
|
+
child.on("close", (code) => {
|
|
563
|
+
if (code === 0) {
|
|
564
|
+
resolve();
|
|
565
|
+
} else {
|
|
566
|
+
reject(new Error(`Command "${command} ${args.join(" ")}" failed with code ${code}: ${stderr}`));
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
child.on("error", (error) => {
|
|
570
|
+
reject(error);
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// src/generator.ts
|
|
576
|
+
var REPO = "abuhanna/app-template";
|
|
577
|
+
async function generateProject(config) {
|
|
578
|
+
const absolutePath = path5.resolve(config.projectPath);
|
|
579
|
+
if (!fs5.existsSync(absolutePath)) {
|
|
580
|
+
fs5.mkdirSync(absolutePath, { recursive: true });
|
|
581
|
+
}
|
|
582
|
+
const spinner2 = p2.spinner();
|
|
583
|
+
spinner2.start("Downloading templates...");
|
|
584
|
+
try {
|
|
585
|
+
if (config.projectType !== "frontend") {
|
|
586
|
+
const backendFolder = `backend-${config.backend}`;
|
|
587
|
+
await downloadTemplate(REPO, backendFolder, path5.join(absolutePath, backendFolder));
|
|
588
|
+
spinner2.message(`Downloaded ${backendFolder}`);
|
|
589
|
+
}
|
|
590
|
+
if (config.projectType !== "backend") {
|
|
591
|
+
const frontendFolder = `frontend-${config.ui}`;
|
|
592
|
+
await downloadTemplate(REPO, frontendFolder, path5.join(absolutePath, frontendFolder));
|
|
593
|
+
spinner2.message(`Downloaded ${frontendFolder}`);
|
|
594
|
+
}
|
|
595
|
+
await copyRootFiles(REPO, absolutePath, config);
|
|
596
|
+
spinner2.message("Downloaded configuration files");
|
|
597
|
+
spinner2.stop("Templates downloaded");
|
|
598
|
+
} catch (error) {
|
|
599
|
+
spinner2.stop("Download failed");
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
if (config.projectName !== "App.Template") {
|
|
603
|
+
spinner2.start("Renaming project...");
|
|
604
|
+
try {
|
|
605
|
+
await renameProject(absolutePath, config);
|
|
606
|
+
spinner2.stop("Project renamed");
|
|
607
|
+
} catch (error) {
|
|
608
|
+
spinner2.stop("Rename failed");
|
|
609
|
+
throw error;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
if (config.installDeps) {
|
|
613
|
+
spinner2.start("Installing dependencies...");
|
|
614
|
+
try {
|
|
615
|
+
await installDependencies(absolutePath, config);
|
|
616
|
+
spinner2.stop("Dependencies installed");
|
|
617
|
+
} catch (error) {
|
|
618
|
+
spinner2.stop("Installation failed");
|
|
619
|
+
console.log(pc2.yellow(" Warning: Some dependencies may not have been installed"));
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
const envExamplePath = path5.join(absolutePath, ".env.example");
|
|
623
|
+
const envPath = path5.join(absolutePath, ".env");
|
|
624
|
+
if (fs5.existsSync(envExamplePath) && !fs5.existsSync(envPath)) {
|
|
625
|
+
fs5.copyFileSync(envExamplePath, envPath);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// src/index.ts
|
|
630
|
+
async function main() {
|
|
631
|
+
console.log();
|
|
632
|
+
intro(pc3.bgCyan(pc3.black(" Create AppTemplate ")));
|
|
633
|
+
try {
|
|
634
|
+
const cliArgs = parseArgs();
|
|
635
|
+
if (cliArgs.help) {
|
|
636
|
+
showHelp();
|
|
637
|
+
process.exit(0);
|
|
638
|
+
}
|
|
639
|
+
if (cliArgs.version) {
|
|
640
|
+
console.log("create-apptemplate v1.0.0");
|
|
641
|
+
process.exit(0);
|
|
642
|
+
}
|
|
643
|
+
let config;
|
|
644
|
+
if (cliArgs.projectPath && cliArgs.backend && cliArgs.projectName) {
|
|
645
|
+
config = {
|
|
646
|
+
projectPath: cliArgs.projectPath,
|
|
647
|
+
projectType: cliArgs.type || "fullstack",
|
|
648
|
+
backend: cliArgs.backend,
|
|
649
|
+
ui: cliArgs.ui || "vuetify",
|
|
650
|
+
projectName: cliArgs.projectName,
|
|
651
|
+
installDeps: cliArgs.install || false
|
|
652
|
+
};
|
|
653
|
+
} else {
|
|
654
|
+
const result = await runInteractivePrompts(cliArgs);
|
|
655
|
+
if (isCancel2(result)) {
|
|
656
|
+
outro(pc3.yellow("Operation cancelled"));
|
|
657
|
+
process.exit(0);
|
|
658
|
+
}
|
|
659
|
+
config = result;
|
|
660
|
+
}
|
|
661
|
+
await generateProject(config);
|
|
662
|
+
console.log();
|
|
663
|
+
outro(pc3.green("\u2713 Done! Your project is ready."));
|
|
664
|
+
console.log();
|
|
665
|
+
console.log(pc3.cyan("Next steps:"));
|
|
666
|
+
console.log(` ${pc3.gray("$")} cd ${config.projectPath}`);
|
|
667
|
+
console.log(` ${pc3.gray("$")} cp .env.example .env`);
|
|
668
|
+
if (!config.installDeps) {
|
|
669
|
+
if (config.projectType !== "frontend") {
|
|
670
|
+
console.log(` ${pc3.gray("$")} cd backend-${config.backend} && dotnet restore`);
|
|
671
|
+
}
|
|
672
|
+
if (config.projectType !== "backend") {
|
|
673
|
+
console.log(` ${pc3.gray("$")} cd frontend-${config.ui} && npm install`);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
console.log(` ${pc3.gray("$")} docker compose up -d --build`);
|
|
677
|
+
console.log();
|
|
678
|
+
console.log(pc3.gray("Access points:"));
|
|
679
|
+
console.log(` Frontend: ${pc3.cyan("http://localhost")}`);
|
|
680
|
+
console.log(` Backend: ${pc3.cyan("http://localhost:5100")}`);
|
|
681
|
+
console.log(` Swagger: ${pc3.cyan("http://localhost:5100/swagger")}`);
|
|
682
|
+
console.log();
|
|
683
|
+
console.log(pc3.gray("Default login:"));
|
|
684
|
+
console.log(` Username: ${pc3.cyan("admin")}`);
|
|
685
|
+
console.log(` Password: ${pc3.cyan("Admin@123")}`);
|
|
686
|
+
console.log();
|
|
687
|
+
} catch (error) {
|
|
688
|
+
if (error instanceof Error) {
|
|
689
|
+
console.error(pc3.red(`Error: ${error.message}`));
|
|
690
|
+
} else {
|
|
691
|
+
console.error(pc3.red("An unexpected error occurred"));
|
|
692
|
+
}
|
|
693
|
+
process.exit(1);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function showHelp() {
|
|
697
|
+
console.log(`
|
|
698
|
+
${pc3.bold("Usage:")}
|
|
699
|
+
${pc3.cyan("npm create apptemplate@latest")} ${pc3.gray("[project-directory]")} ${pc3.gray("[options]")}
|
|
700
|
+
|
|
701
|
+
${pc3.bold("Options:")}
|
|
702
|
+
${pc3.yellow("-t, --type")} Project type: fullstack, backend, frontend ${pc3.gray("(default: fullstack)")}
|
|
703
|
+
${pc3.yellow("-b, --backend")} Backend framework: dotnet, spring, nestjs
|
|
704
|
+
${pc3.yellow("-u, --ui")} UI library: vuetify, primevue ${pc3.gray("(default: vuetify)")}
|
|
705
|
+
${pc3.yellow("-n, --name")} Project name (Company.Project format)
|
|
706
|
+
${pc3.yellow("-i, --install")} Install dependencies after creation
|
|
707
|
+
${pc3.yellow("-h, --help")} Show this help message
|
|
708
|
+
${pc3.yellow("-v, --version")} Show version number
|
|
709
|
+
|
|
710
|
+
${pc3.bold("Examples:")}
|
|
711
|
+
${pc3.gray("# Interactive mode")}
|
|
712
|
+
npm create apptemplate@latest
|
|
713
|
+
|
|
714
|
+
${pc3.gray("# Create fullstack project with .NET backend")}
|
|
715
|
+
npm create apptemplate@latest my-app -b dotnet -n "MyCompany.MyApp" -i
|
|
716
|
+
|
|
717
|
+
${pc3.gray("# Create backend-only project with Spring Boot")}
|
|
718
|
+
npm create apptemplate@latest my-api -t backend -b spring -n "MyCompany.MyApi"
|
|
719
|
+
|
|
720
|
+
${pc3.gray("# Create frontend-only project with PrimeVue")}
|
|
721
|
+
npm create apptemplate@latest my-spa -t frontend -u primevue
|
|
722
|
+
`);
|
|
723
|
+
}
|
|
724
|
+
main();
|
|
725
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/cli.ts","../src/prompts.ts","../src/generator.ts","../src/utils/download.ts","../src/utils/rename.ts","../src/utils/package-manager.ts"],"sourcesContent":["import { intro, outro, isCancel } from '@clack/prompts';\r\nimport pc from 'picocolors';\r\nimport { parseArgs } from './cli.js';\r\nimport { runInteractivePrompts } from './prompts.js';\r\nimport { generateProject } from './generator.js';\r\nimport type { ProjectConfig } from './types.js';\r\n\r\nasync function main(): Promise<void> {\r\n console.log();\r\n intro(pc.bgCyan(pc.black(' Create AppTemplate ')));\r\n\r\n try {\r\n // Parse CLI arguments\r\n const cliArgs = parseArgs();\r\n\r\n // Show help if requested\r\n if (cliArgs.help) {\r\n showHelp();\r\n process.exit(0);\r\n }\r\n\r\n // Show version if requested\r\n if (cliArgs.version) {\r\n console.log('create-apptemplate v1.0.0');\r\n process.exit(0);\r\n }\r\n\r\n // Get project configuration (interactive or from CLI args)\r\n let config: ProjectConfig;\r\n\r\n if (cliArgs.projectPath && cliArgs.backend && cliArgs.projectName) {\r\n // Non-interactive mode - all required options provided\r\n config = {\r\n projectPath: cliArgs.projectPath,\r\n projectType: cliArgs.type || 'fullstack',\r\n backend: cliArgs.backend,\r\n ui: cliArgs.ui || 'vuetify',\r\n projectName: cliArgs.projectName,\r\n installDeps: cliArgs.install || false,\r\n };\r\n } else {\r\n // Interactive mode\r\n const result = await runInteractivePrompts(cliArgs);\r\n if (isCancel(result)) {\r\n outro(pc.yellow('Operation cancelled'));\r\n process.exit(0);\r\n }\r\n config = result;\r\n }\r\n\r\n // Generate the project\r\n await generateProject(config);\r\n\r\n // Success message\r\n console.log();\r\n outro(pc.green('✓ Done! Your project is ready.'));\r\n\r\n console.log();\r\n console.log(pc.cyan('Next steps:'));\r\n console.log(` ${pc.gray('$')} cd ${config.projectPath}`);\r\n console.log(` ${pc.gray('$')} cp .env.example .env`);\r\n if (!config.installDeps) {\r\n if (config.projectType !== 'frontend') {\r\n console.log(` ${pc.gray('$')} cd backend-${config.backend} && dotnet restore`);\r\n }\r\n if (config.projectType !== 'backend') {\r\n console.log(` ${pc.gray('$')} cd frontend-${config.ui} && npm install`);\r\n }\r\n }\r\n console.log(` ${pc.gray('$')} docker compose up -d --build`);\r\n console.log();\r\n console.log(pc.gray('Access points:'));\r\n console.log(` Frontend: ${pc.cyan('http://localhost')}`);\r\n console.log(` Backend: ${pc.cyan('http://localhost:5100')}`);\r\n console.log(` Swagger: ${pc.cyan('http://localhost:5100/swagger')}`);\r\n console.log();\r\n console.log(pc.gray('Default login:'));\r\n console.log(` Username: ${pc.cyan('admin')}`);\r\n console.log(` Password: ${pc.cyan('Admin@123')}`);\r\n console.log();\r\n } catch (error) {\r\n if (error instanceof Error) {\r\n console.error(pc.red(`Error: ${error.message}`));\r\n } else {\r\n console.error(pc.red('An unexpected error occurred'));\r\n }\r\n process.exit(1);\r\n }\r\n}\r\n\r\nfunction showHelp(): void {\r\n console.log(`\r\n${pc.bold('Usage:')}\r\n ${pc.cyan('npm create apptemplate@latest')} ${pc.gray('[project-directory]')} ${pc.gray('[options]')}\r\n\r\n${pc.bold('Options:')}\r\n ${pc.yellow('-t, --type')} Project type: fullstack, backend, frontend ${pc.gray('(default: fullstack)')}\r\n ${pc.yellow('-b, --backend')} Backend framework: dotnet, spring, nestjs\r\n ${pc.yellow('-u, --ui')} UI library: vuetify, primevue ${pc.gray('(default: vuetify)')}\r\n ${pc.yellow('-n, --name')} Project name (Company.Project format)\r\n ${pc.yellow('-i, --install')} Install dependencies after creation\r\n ${pc.yellow('-h, --help')} Show this help message\r\n ${pc.yellow('-v, --version')} Show version number\r\n\r\n${pc.bold('Examples:')}\r\n ${pc.gray('# Interactive mode')}\r\n npm create apptemplate@latest\r\n\r\n ${pc.gray('# Create fullstack project with .NET backend')}\r\n npm create apptemplate@latest my-app -b dotnet -n \"MyCompany.MyApp\" -i\r\n\r\n ${pc.gray('# Create backend-only project with Spring Boot')}\r\n npm create apptemplate@latest my-api -t backend -b spring -n \"MyCompany.MyApi\"\r\n\r\n ${pc.gray('# Create frontend-only project with PrimeVue')}\r\n npm create apptemplate@latest my-spa -t frontend -u primevue\r\n`);\r\n}\r\n\r\nmain();\r\n","import type { CLIArgs, ProjectType, BackendFramework, UILibrary } from './types.js';\r\n\r\nconst validProjectTypes: ProjectType[] = ['fullstack', 'backend', 'frontend'];\r\nconst validBackends: BackendFramework[] = ['dotnet', 'spring', 'nestjs'];\r\nconst validUILibraries: UILibrary[] = ['vuetify', 'primevue'];\r\n\r\nexport function parseArgs(): CLIArgs {\r\n const args = process.argv.slice(2);\r\n const result: CLIArgs = {};\r\n\r\n let i = 0;\r\n while (i < args.length) {\r\n const arg = args[i];\r\n\r\n // Handle flags\r\n if (arg === '-h' || arg === '--help') {\r\n result.help = true;\r\n i++;\r\n continue;\r\n }\r\n\r\n if (arg === '-v' || arg === '--version') {\r\n result.version = true;\r\n i++;\r\n continue;\r\n }\r\n\r\n if (arg === '-i' || arg === '--install') {\r\n result.install = true;\r\n i++;\r\n continue;\r\n }\r\n\r\n // Handle options with values\r\n if (arg === '-t' || arg === '--type') {\r\n const value = args[++i];\r\n if (isValidProjectType(value)) {\r\n result.type = value;\r\n } else {\r\n console.warn(`Warning: Invalid project type \"${value}\". Valid options: ${validProjectTypes.join(', ')}`);\r\n }\r\n i++;\r\n continue;\r\n }\r\n\r\n if (arg === '-b' || arg === '--backend') {\r\n const value = args[++i];\r\n if (isValidBackend(value)) {\r\n result.backend = value;\r\n } else {\r\n console.warn(`Warning: Invalid backend \"${value}\". Valid options: ${validBackends.join(', ')}`);\r\n }\r\n i++;\r\n continue;\r\n }\r\n\r\n if (arg === '-u' || arg === '--ui') {\r\n const value = args[++i];\r\n if (isValidUI(value)) {\r\n result.ui = value;\r\n } else {\r\n console.warn(`Warning: Invalid UI library \"${value}\". Valid options: ${validUILibraries.join(', ')}`);\r\n }\r\n i++;\r\n continue;\r\n }\r\n\r\n if (arg === '-n' || arg === '--name') {\r\n const value = args[++i];\r\n if (isValidProjectName(value)) {\r\n result.projectName = value;\r\n } else {\r\n console.warn(`Warning: Project name should be in \"Company.Project\" format`);\r\n }\r\n i++;\r\n continue;\r\n }\r\n\r\n // If not a flag, treat as project path (first positional argument)\r\n if (!arg.startsWith('-') && !result.projectPath) {\r\n result.projectPath = arg;\r\n }\r\n\r\n i++;\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction isValidProjectType(value: string | undefined): value is ProjectType {\r\n return value !== undefined && validProjectTypes.includes(value as ProjectType);\r\n}\r\n\r\nfunction isValidBackend(value: string | undefined): value is BackendFramework {\r\n return value !== undefined && validBackends.includes(value as BackendFramework);\r\n}\r\n\r\nfunction isValidUI(value: string | undefined): value is UILibrary {\r\n return value !== undefined && validUILibraries.includes(value as UILibrary);\r\n}\r\n\r\nfunction isValidProjectName(value: string | undefined): boolean {\r\n if (!value) return false;\r\n // Validate Company.Project format\r\n const pattern = /^[A-Za-z][A-Za-z0-9]*(\\.[A-Za-z][A-Za-z0-9]*)+$/;\r\n return pattern.test(value);\r\n}\r\n","import * as p from '@clack/prompts';\r\nimport pc from 'picocolors';\r\nimport path from 'path';\r\nimport fs from 'fs';\r\nimport type { CLIArgs, ProjectConfig, ProjectType, BackendFramework, UILibrary } from './types.js';\r\n\r\nexport async function runInteractivePrompts(cliArgs: CLIArgs): Promise<ProjectConfig | symbol> {\r\n // Project path\r\n let projectPath = cliArgs.projectPath;\r\n if (!projectPath) {\r\n const result = await p.text({\r\n message: 'Where should we create your project?',\r\n placeholder: './my-app',\r\n defaultValue: './my-app',\r\n validate: (value) => {\r\n if (!value) return 'Please enter a directory path';\r\n const resolvedPath = path.resolve(value);\r\n if (fs.existsSync(resolvedPath) && fs.readdirSync(resolvedPath).length > 0) {\r\n return 'Directory exists and is not empty';\r\n }\r\n return undefined;\r\n },\r\n });\r\n if (p.isCancel(result)) return result;\r\n projectPath = result;\r\n }\r\n\r\n // Project type\r\n let projectType = cliArgs.type;\r\n if (!projectType) {\r\n const result = await p.select({\r\n message: 'What type of project would you like to create?',\r\n options: [\r\n {\r\n value: 'fullstack' as ProjectType,\r\n label: 'Fullstack',\r\n hint: 'Backend + Frontend + Docker',\r\n },\r\n {\r\n value: 'backend' as ProjectType,\r\n label: 'Backend only',\r\n hint: 'API service or microservice',\r\n },\r\n {\r\n value: 'frontend' as ProjectType,\r\n label: 'Frontend only',\r\n hint: 'SPA with external API',\r\n },\r\n ],\r\n });\r\n if (p.isCancel(result)) return result;\r\n projectType = result;\r\n }\r\n\r\n // Backend framework (skip for frontend-only)\r\n let backend: BackendFramework = cliArgs.backend || 'dotnet';\r\n if (projectType !== 'frontend' && !cliArgs.backend) {\r\n const result = await p.select({\r\n message: 'Which backend framework would you like to use?',\r\n options: [\r\n {\r\n value: 'dotnet' as BackendFramework,\r\n label: '.NET 8',\r\n hint: 'Clean Architecture, CQRS, Entity Framework',\r\n },\r\n {\r\n value: 'spring' as BackendFramework,\r\n label: 'Spring Boot 3',\r\n hint: 'Clean Architecture, Java 21',\r\n },\r\n {\r\n value: 'nestjs' as BackendFramework,\r\n label: 'NestJS',\r\n hint: 'Clean Architecture, TypeScript',\r\n },\r\n ],\r\n });\r\n if (p.isCancel(result)) return result;\r\n backend = result;\r\n }\r\n\r\n // UI library (skip for backend-only)\r\n let ui: UILibrary = cliArgs.ui || 'vuetify';\r\n if (projectType !== 'backend' && !cliArgs.ui) {\r\n const result = await p.select({\r\n message: 'Which UI library would you like to use?',\r\n options: [\r\n {\r\n value: 'vuetify' as UILibrary,\r\n label: 'Vuetify',\r\n hint: 'Material Design 3, 80+ components',\r\n },\r\n {\r\n value: 'primevue' as UILibrary,\r\n label: 'PrimeVue',\r\n hint: 'Aura theme, 90+ components',\r\n },\r\n ],\r\n });\r\n if (p.isCancel(result)) return result;\r\n ui = result;\r\n }\r\n\r\n // Project name (for namespaces)\r\n let projectName = cliArgs.projectName;\r\n if (!projectName) {\r\n // Generate default name from project path\r\n const dirName = path.basename(path.resolve(projectPath));\r\n const defaultName = `MyCompany.${toPascalCase(dirName)}`;\r\n\r\n const result = await p.text({\r\n message: 'Project name (for namespaces)',\r\n placeholder: defaultName,\r\n defaultValue: defaultName,\r\n validate: (value) => {\r\n if (!value) return 'Please enter a project name';\r\n const pattern = /^[A-Za-z][A-Za-z0-9]*(\\.[A-Za-z][A-Za-z0-9]*)+$/;\r\n if (!pattern.test(value)) {\r\n return 'Name must be in \"Company.Project\" format (e.g., MyCompany.MyApp)';\r\n }\r\n return undefined;\r\n },\r\n });\r\n if (p.isCancel(result)) return result;\r\n projectName = result;\r\n }\r\n\r\n // Install dependencies\r\n let installDeps = cliArgs.install ?? false;\r\n if (!cliArgs.install) {\r\n const result = await p.confirm({\r\n message: 'Install dependencies after creation?',\r\n initialValue: true,\r\n });\r\n if (p.isCancel(result)) return result;\r\n installDeps = result;\r\n }\r\n\r\n // Show summary\r\n console.log();\r\n p.note(\r\n [\r\n `${pc.cyan('Project path:')} ${projectPath}`,\r\n `${pc.cyan('Project type:')} ${projectType}`,\r\n projectType !== 'frontend' ? `${pc.cyan('Backend:')} ${getBackendLabel(backend)}` : null,\r\n projectType !== 'backend' ? `${pc.cyan('UI Library:')} ${getUILabel(ui)}` : null,\r\n `${pc.cyan('Project name:')} ${projectName}`,\r\n `${pc.cyan('Install deps:')} ${installDeps ? 'Yes' : 'No'}`,\r\n ]\r\n .filter(Boolean)\r\n .join('\\n'),\r\n 'Configuration'\r\n );\r\n\r\n const shouldContinue = await p.confirm({\r\n message: 'Create project with these settings?',\r\n initialValue: true,\r\n });\r\n if (p.isCancel(shouldContinue) || !shouldContinue) {\r\n return p.isCancel(shouldContinue) ? shouldContinue : Symbol('cancelled');\r\n }\r\n\r\n return {\r\n projectPath,\r\n projectType,\r\n backend,\r\n ui,\r\n projectName,\r\n installDeps,\r\n };\r\n}\r\n\r\nfunction toPascalCase(str: string): string {\r\n return str\r\n .replace(/[-_\\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))\r\n .replace(/^(.)/, (c) => c.toUpperCase());\r\n}\r\n\r\nfunction getBackendLabel(backend: BackendFramework): string {\r\n const labels: Record<BackendFramework, string> = {\r\n dotnet: '.NET 8',\r\n spring: 'Spring Boot 3',\r\n nestjs: 'NestJS',\r\n };\r\n return labels[backend];\r\n}\r\n\r\nfunction getUILabel(ui: UILibrary): string {\r\n const labels: Record<UILibrary, string> = {\r\n vuetify: 'Vuetify (Material Design)',\r\n primevue: 'PrimeVue (Aura Theme)',\r\n };\r\n return labels[ui];\r\n}\r\n","import * as p from '@clack/prompts';\r\nimport pc from 'picocolors';\r\nimport path from 'path';\r\nimport fs from 'fs';\r\nimport type { ProjectConfig } from './types.js';\r\nimport { downloadTemplate, copyRootFiles } from './utils/download.js';\r\nimport { renameProject } from './utils/rename.js';\r\nimport { installDependencies } from './utils/package-manager.js';\r\n\r\n// GitHub repository for templates\r\nconst REPO = 'abuhanna/app-template';\r\n\r\nexport async function generateProject(config: ProjectConfig): Promise<void> {\r\n const absolutePath = path.resolve(config.projectPath);\r\n\r\n // Create project directory\r\n if (!fs.existsSync(absolutePath)) {\r\n fs.mkdirSync(absolutePath, { recursive: true });\r\n }\r\n\r\n const spinner = p.spinner();\r\n\r\n // Step 1: Download templates\r\n spinner.start('Downloading templates...');\r\n\r\n try {\r\n // Download backend (if not frontend-only)\r\n if (config.projectType !== 'frontend') {\r\n const backendFolder = `backend-${config.backend}`;\r\n await downloadTemplate(REPO, backendFolder, path.join(absolutePath, backendFolder));\r\n spinner.message(`Downloaded ${backendFolder}`);\r\n }\r\n\r\n // Download frontend (if not backend-only)\r\n if (config.projectType !== 'backend') {\r\n const frontendFolder = `frontend-${config.ui}`;\r\n await downloadTemplate(REPO, frontendFolder, path.join(absolutePath, frontendFolder));\r\n spinner.message(`Downloaded ${frontendFolder}`);\r\n }\r\n\r\n // Download common files (docker, scripts, etc.)\r\n await copyRootFiles(REPO, absolutePath, config);\r\n spinner.message('Downloaded configuration files');\r\n\r\n spinner.stop('Templates downloaded');\r\n } catch (error) {\r\n spinner.stop('Download failed');\r\n throw error;\r\n }\r\n\r\n // Step 2: Rename project\r\n if (config.projectName !== 'App.Template') {\r\n spinner.start('Renaming project...');\r\n\r\n try {\r\n await renameProject(absolutePath, config);\r\n spinner.stop('Project renamed');\r\n } catch (error) {\r\n spinner.stop('Rename failed');\r\n throw error;\r\n }\r\n }\r\n\r\n // Step 3: Install dependencies (if requested)\r\n if (config.installDeps) {\r\n spinner.start('Installing dependencies...');\r\n\r\n try {\r\n await installDependencies(absolutePath, config);\r\n spinner.stop('Dependencies installed');\r\n } catch (error) {\r\n spinner.stop('Installation failed');\r\n // Don't throw - just warn\r\n console.log(pc.yellow(' Warning: Some dependencies may not have been installed'));\r\n }\r\n }\r\n\r\n // Step 4: Create .env file from example\r\n const envExamplePath = path.join(absolutePath, '.env.example');\r\n const envPath = path.join(absolutePath, '.env');\r\n if (fs.existsSync(envExamplePath) && !fs.existsSync(envPath)) {\r\n fs.copyFileSync(envExamplePath, envPath);\r\n }\r\n}\r\n","import degit from 'degit';\r\nimport path from 'path';\r\nimport fs from 'fs';\r\nimport type { ProjectConfig } from '../types.js';\r\n\r\n/**\r\n * Download a specific folder from the GitHub repository\r\n */\r\nexport async function downloadTemplate(repo: string, folder: string, destPath: string): Promise<void> {\r\n const source = `${repo}/${folder}`;\r\n const emitter = degit(source, {\r\n cache: false,\r\n force: true,\r\n verbose: false,\r\n });\r\n\r\n await emitter.clone(destPath);\r\n}\r\n\r\n/**\r\n * Download root configuration files based on project type\r\n */\r\nexport async function copyRootFiles(repo: string, destPath: string, config: ProjectConfig): Promise<void> {\r\n // Files to download based on project type\r\n const commonFiles = [\r\n '.env.example',\r\n '.gitignore',\r\n 'README.md',\r\n 'CLAUDE.md',\r\n ];\r\n\r\n const fullstackFiles = [\r\n 'Dockerfile',\r\n 'docker-compose.yml',\r\n 'docker-compose.staging.yml',\r\n 'docker-compose.production.yml',\r\n 'Makefile',\r\n ];\r\n\r\n const backendOnlyFiles = [\r\n 'docker/Dockerfile.backend',\r\n 'docker-compose.backend.yml',\r\n ];\r\n\r\n const frontendOnlyFiles = [\r\n 'docker/Dockerfile.frontend',\r\n 'docker/nginx/frontend-only.conf',\r\n 'docker-compose.frontend.yml',\r\n ];\r\n\r\n // Determine which files to download\r\n let filesToDownload = [...commonFiles];\r\n\r\n switch (config.projectType) {\r\n case 'fullstack':\r\n filesToDownload = [...filesToDownload, ...fullstackFiles];\r\n break;\r\n case 'backend':\r\n filesToDownload = [...filesToDownload, ...backendOnlyFiles];\r\n break;\r\n case 'frontend':\r\n filesToDownload = [...filesToDownload, ...frontendOnlyFiles];\r\n break;\r\n }\r\n\r\n // Download docker folder for all project types\r\n const dockerFolder = 'docker';\r\n const dockerDest = path.join(destPath, dockerFolder);\r\n\r\n try {\r\n await downloadTemplate(repo, dockerFolder, dockerDest);\r\n } catch {\r\n // Docker folder might not exist for all project types, ignore error\r\n }\r\n\r\n // Download scripts folder\r\n const scriptsFolder = 'scripts';\r\n const scriptsDest = path.join(destPath, scriptsFolder);\r\n\r\n try {\r\n await downloadTemplate(repo, scriptsFolder, scriptsDest);\r\n } catch {\r\n // Scripts folder might not exist, ignore error\r\n }\r\n\r\n // Download individual root files\r\n // Note: degit doesn't support individual file downloads, so we download the whole repo\r\n // and then filter the files we need. This is a workaround.\r\n\r\n // Create a temporary directory for the full repo download\r\n const tempDir = path.join(destPath, '.temp-download');\r\n\r\n try {\r\n const emitter = degit(repo, {\r\n cache: false,\r\n force: true,\r\n verbose: false,\r\n });\r\n\r\n await emitter.clone(tempDir);\r\n\r\n // Copy only the files we need\r\n for (const file of filesToDownload) {\r\n const srcFile = path.join(tempDir, file);\r\n const destFile = path.join(destPath, file);\r\n\r\n if (fs.existsSync(srcFile)) {\r\n // Ensure parent directory exists\r\n const parentDir = path.dirname(destFile);\r\n if (!fs.existsSync(parentDir)) {\r\n fs.mkdirSync(parentDir, { recursive: true });\r\n }\r\n fs.copyFileSync(srcFile, destFile);\r\n }\r\n }\r\n } finally {\r\n // Clean up temp directory\r\n if (fs.existsSync(tempDir)) {\r\n fs.rmSync(tempDir, { recursive: true, force: true });\r\n }\r\n }\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs';\r\nimport type { ProjectConfig } from '../types.js';\r\n\r\n/**\r\n * Rename project files and update namespaces\r\n */\r\nexport async function renameProject(projectPath: string, config: ProjectConfig): Promise<void> {\r\n const newDotName = config.projectName;\r\n const newNamespace = config.projectName.replace(/\\./g, '');\r\n\r\n // Rename backend project files (for .NET)\r\n if (config.projectType !== 'frontend' && config.backend === 'dotnet') {\r\n await renameDotNetProject(projectPath, config, newDotName, newNamespace);\r\n }\r\n\r\n // Rename backend project files (for Spring)\r\n if (config.projectType !== 'frontend' && config.backend === 'spring') {\r\n await renameSpringProject(projectPath, config, newDotName);\r\n }\r\n\r\n // Rename backend project files (for NestJS)\r\n if (config.projectType !== 'frontend' && config.backend === 'nestjs') {\r\n await renameNestJSProject(projectPath, config, newDotName);\r\n }\r\n\r\n // Update common files\r\n await updateCommonFiles(projectPath, config, newDotName, newNamespace);\r\n}\r\n\r\n/**\r\n * Rename .NET project structure\r\n */\r\nasync function renameDotNetProject(\r\n projectPath: string,\r\n _config: ProjectConfig,\r\n newDotName: string,\r\n newNamespace: string\r\n): Promise<void> {\r\n const backendDir = path.join(projectPath, 'backend-dotnet');\r\n\r\n if (!fs.existsSync(backendDir)) return;\r\n\r\n // Define folder mappings\r\n const folderMappings = [\r\n ['src/Core/App.Template.Domain', `src/Core/${newDotName}.Domain`],\r\n ['src/Core/App.Template.Application', `src/Core/${newDotName}.Application`],\r\n ['src/Infrastructure/App.Template.Infrastructure', `src/Infrastructure/${newDotName}.Infrastructure`],\r\n ['src/Presentation/App.Template.WebAPI', `src/Presentation/${newDotName}.WebAPI`],\r\n ['tests/App.Template.Domain.Tests', `tests/${newDotName}.Domain.Tests`],\r\n ['tests/App.Template.Application.Tests', `tests/${newDotName}.Application.Tests`],\r\n ];\r\n\r\n // Rename folders\r\n for (const [oldFolder, newFolder] of folderMappings) {\r\n const oldPath = path.join(backendDir, oldFolder);\r\n const newPath = path.join(backendDir, newFolder);\r\n\r\n if (fs.existsSync(oldPath)) {\r\n fs.renameSync(oldPath, newPath);\r\n }\r\n }\r\n\r\n // Rename .csproj files\r\n await renameFilesWithPattern(backendDir, /App\\.Template\\.(.*)\\.csproj$/, (match) => {\r\n return `${newDotName}.${match[1]}.csproj`;\r\n });\r\n\r\n // Rename solution file\r\n const oldSlnPath = path.join(backendDir, 'App.Template.sln');\r\n const newSlnPath = path.join(backendDir, `${newDotName}.sln`);\r\n if (fs.existsSync(oldSlnPath)) {\r\n fs.renameSync(oldSlnPath, newSlnPath);\r\n }\r\n\r\n // Update file contents in all relevant files\r\n const extensions = ['.cs', '.csproj', '.sln', '.json'];\r\n await updateFileContents(backendDir, extensions, (content) => {\r\n return content\r\n .replace(/App\\.Template/g, newDotName)\r\n .replace(/AppTemplate/g, newNamespace);\r\n });\r\n}\r\n\r\n/**\r\n * Rename Spring Boot project structure\r\n */\r\nasync function renameSpringProject(\r\n projectPath: string,\r\n _config: ProjectConfig,\r\n newDotName: string\r\n): Promise<void> {\r\n const backendDir = path.join(projectPath, 'backend-spring');\r\n\r\n if (!fs.existsSync(backendDir)) return;\r\n\r\n // Convert project name to Java package format\r\n const packageName = newDotName.toLowerCase().replace(/\\./g, '.');\r\n const artifactId = newDotName.toLowerCase().replace(/\\./g, '-');\r\n\r\n // Update pom.xml\r\n const pomPath = path.join(backendDir, 'pom.xml');\r\n if (fs.existsSync(pomPath)) {\r\n let content = fs.readFileSync(pomPath, 'utf-8');\r\n content = content\r\n .replace(/<groupId>com\\.apptemplate<\\/groupId>/g, `<groupId>${packageName}<\\/groupId>`)\r\n .replace(/<artifactId>apptemplate<\\/artifactId>/g, `<artifactId>${artifactId}<\\/artifactId>`)\r\n .replace(/com\\.apptemplate/g, packageName);\r\n fs.writeFileSync(pomPath, content);\r\n }\r\n\r\n // Update Java files\r\n await updateFileContents(backendDir, ['.java'], (content) => {\r\n return content.replace(/com\\.apptemplate/g, packageName);\r\n });\r\n}\r\n\r\n/**\r\n * Rename NestJS project structure\r\n */\r\nasync function renameNestJSProject(\r\n projectPath: string,\r\n _config: ProjectConfig,\r\n newDotName: string\r\n): Promise<void> {\r\n const backendDir = path.join(projectPath, 'backend-nestjs');\r\n\r\n if (!fs.existsSync(backendDir)) return;\r\n\r\n // Convert project name to package name format\r\n const packageName = newDotName.toLowerCase().replace(/\\./g, '-');\r\n\r\n // Update package.json\r\n const packageJsonPath = path.join(backendDir, 'package.json');\r\n if (fs.existsSync(packageJsonPath)) {\r\n let content = fs.readFileSync(packageJsonPath, 'utf-8');\r\n const pkg = JSON.parse(content);\r\n pkg.name = packageName;\r\n fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2));\r\n }\r\n}\r\n\r\n/**\r\n * Update common files (Dockerfile, docker-compose, etc.)\r\n */\r\nasync function updateCommonFiles(\r\n projectPath: string,\r\n _config: ProjectConfig,\r\n newDotName: string,\r\n newNamespace: string\r\n): Promise<void> {\r\n const filesToUpdate = [\r\n 'Dockerfile',\r\n 'docker-compose.yml',\r\n 'docker-compose.staging.yml',\r\n 'docker-compose.production.yml',\r\n 'docker-compose.backend.yml',\r\n 'docker-compose.frontend.yml',\r\n 'Makefile',\r\n 'CLAUDE.md',\r\n 'README.md',\r\n ];\r\n\r\n for (const file of filesToUpdate) {\r\n const filePath = path.join(projectPath, file);\r\n if (fs.existsSync(filePath)) {\r\n let content = fs.readFileSync(filePath, 'utf-8');\r\n content = content\r\n .replace(/App\\.Template/g, newDotName)\r\n .replace(/AppTemplate/g, newNamespace)\r\n .replace(/apptemplate/gi, newNamespace.toLowerCase());\r\n fs.writeFileSync(filePath, content);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Rename files matching a pattern in a directory\r\n */\r\nasync function renameFilesWithPattern(\r\n dir: string,\r\n pattern: RegExp,\r\n replacer: (match: RegExpMatchArray) => string\r\n): Promise<void> {\r\n const files = getAllFiles(dir);\r\n\r\n for (const file of files) {\r\n const fileName = path.basename(file);\r\n const match = fileName.match(pattern);\r\n\r\n if (match) {\r\n const newFileName = replacer(match);\r\n const newPath = path.join(path.dirname(file), newFileName);\r\n fs.renameSync(file, newPath);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Update file contents in a directory\r\n */\r\nasync function updateFileContents(\r\n dir: string,\r\n extensions: string[],\r\n updater: (content: string) => string\r\n): Promise<void> {\r\n const files = getAllFiles(dir);\r\n\r\n for (const file of files) {\r\n const ext = path.extname(file);\r\n if (extensions.includes(ext)) {\r\n let content = fs.readFileSync(file, 'utf-8');\r\n const updatedContent = updater(content);\r\n if (content !== updatedContent) {\r\n fs.writeFileSync(file, updatedContent);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Get all files in a directory recursively\r\n */\r\nfunction getAllFiles(dir: string): string[] {\r\n const files: string[] = [];\r\n\r\n if (!fs.existsSync(dir)) return files;\r\n\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n\r\n if (entry.isDirectory()) {\r\n // Skip node_modules and other common directories\r\n if (!['node_modules', '.git', 'bin', 'obj', 'dist', 'build'].includes(entry.name)) {\r\n files.push(...getAllFiles(fullPath));\r\n }\r\n } else {\r\n files.push(fullPath);\r\n }\r\n }\r\n\r\n return files;\r\n}\r\n","import spawn from 'cross-spawn';\r\nimport path from 'path';\r\nimport fs from 'fs';\r\nimport type { ProjectConfig } from '../types.js';\r\n\r\ntype PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';\r\n\r\n/**\r\n * Detect which package manager is being used\r\n */\r\nexport function detectPackageManager(): PackageManager {\r\n // Check for lockfiles in current directory\r\n const cwd = process.cwd();\r\n\r\n if (fs.existsSync(path.join(cwd, 'bun.lockb'))) return 'bun';\r\n if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) return 'pnpm';\r\n if (fs.existsSync(path.join(cwd, 'yarn.lock'))) return 'yarn';\r\n if (fs.existsSync(path.join(cwd, 'package-lock.json'))) return 'npm';\r\n\r\n // Check npm_config_user_agent environment variable\r\n const userAgent = process.env.npm_config_user_agent;\r\n if (userAgent) {\r\n if (userAgent.includes('bun')) return 'bun';\r\n if (userAgent.includes('pnpm')) return 'pnpm';\r\n if (userAgent.includes('yarn')) return 'yarn';\r\n }\r\n\r\n // Default to npm\r\n return 'npm';\r\n}\r\n\r\n/**\r\n * Install dependencies for the project\r\n */\r\nexport async function installDependencies(projectPath: string, config: ProjectConfig): Promise<void> {\r\n const pm = detectPackageManager();\r\n\r\n // Install frontend dependencies\r\n if (config.projectType !== 'backend') {\r\n const frontendDir = path.join(projectPath, `frontend-${config.ui}`);\r\n if (fs.existsSync(frontendDir)) {\r\n await runInstallCommand(frontendDir, pm);\r\n }\r\n }\r\n\r\n // Install/restore backend dependencies\r\n if (config.projectType !== 'frontend') {\r\n const backendDir = path.join(projectPath, `backend-${config.backend}`);\r\n\r\n if (fs.existsSync(backendDir)) {\r\n switch (config.backend) {\r\n case 'dotnet':\r\n await runCommand('dotnet', ['restore'], backendDir);\r\n break;\r\n case 'spring':\r\n // Maven wrapper should be included\r\n const mvnwPath = path.join(backendDir, process.platform === 'win32' ? 'mvnw.cmd' : 'mvnw');\r\n if (fs.existsSync(mvnwPath)) {\r\n await runCommand(mvnwPath, ['install', '-DskipTests'], backendDir);\r\n } else {\r\n await runCommand('mvn', ['install', '-DskipTests'], backendDir);\r\n }\r\n break;\r\n case 'nestjs':\r\n await runInstallCommand(backendDir, pm);\r\n break;\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Run package manager install command\r\n */\r\nasync function runInstallCommand(cwd: string, pm: PackageManager): Promise<void> {\r\n const installCommands: Record<PackageManager, string[]> = {\r\n npm: ['install'],\r\n yarn: ['install'],\r\n pnpm: ['install'],\r\n bun: ['install'],\r\n };\r\n\r\n await runCommand(pm, installCommands[pm], cwd);\r\n}\r\n\r\n/**\r\n * Run a command in a directory\r\n */\r\nfunction runCommand(command: string, args: string[], cwd: string): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n const child = spawn(command, args, {\r\n cwd,\r\n stdio: 'pipe',\r\n shell: process.platform === 'win32',\r\n });\r\n\r\n let stderr = '';\r\n\r\n child.stderr?.on('data', (data) => {\r\n stderr += data.toString();\r\n });\r\n\r\n child.on('close', (code) => {\r\n if (code === 0) {\r\n resolve();\r\n } else {\r\n reject(new Error(`Command \"${command} ${args.join(' ')}\" failed with code ${code}: ${stderr}`));\r\n }\r\n });\r\n\r\n child.on('error', (error) => {\r\n reject(error);\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * Check if a command exists\r\n */\r\nexport function commandExists(command: string): boolean {\r\n try {\r\n const result = spawn.sync(command, ['--version'], {\r\n stdio: 'pipe',\r\n shell: process.platform === 'win32',\r\n });\r\n return result.status === 0;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n"],"mappings":"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","names":["isCancel","pc","p","pc","path","fs","path","fs","path","fs","path","fs","path","fs","spinner","pc","pc","isCancel"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@abuhannaa/create-apptemplate",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create fullstack apps with .NET/Spring/NestJS + Vue (Vuetify/PrimeVue)",
|
|
5
|
+
"author": "abuhanna",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"bin": {
|
|
10
|
+
"create-apptemplate": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18.0.0"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"dev": "tsup --watch",
|
|
24
|
+
"prepublishOnly": "npm run build",
|
|
25
|
+
"typecheck": "tsc --noEmit"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"create",
|
|
29
|
+
"apptemplate",
|
|
30
|
+
"vue",
|
|
31
|
+
"vuetify",
|
|
32
|
+
"primevue",
|
|
33
|
+
"dotnet",
|
|
34
|
+
"spring",
|
|
35
|
+
"nestjs",
|
|
36
|
+
"fullstack",
|
|
37
|
+
"scaffolding",
|
|
38
|
+
"template",
|
|
39
|
+
"cli"
|
|
40
|
+
],
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/abuhanna/app-template.git"
|
|
44
|
+
},
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/abuhanna/app-template/issues"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/abuhanna/app-template#readme",
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@clack/prompts": "^0.9.1",
|
|
51
|
+
"cross-spawn": "^7.0.6",
|
|
52
|
+
"degit": "^2.8.4",
|
|
53
|
+
"picocolors": "^1.1.1"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/cross-spawn": "^6.0.6",
|
|
57
|
+
"@types/degit": "^2.8.6",
|
|
58
|
+
"@types/node": "^22.10.5",
|
|
59
|
+
"tsup": "^8.3.5",
|
|
60
|
+
"typescript": "^5.7.3"
|
|
61
|
+
}
|
|
62
|
+
}
|