create-ng-tailwind 3.1.0 → 4.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 +81 -350
- package/README.md +93 -157
- package/lib/cli/index.js +29 -3
- package/lib/cli/interactive.js +26 -1
- package/lib/managers/ProjectManager.js +0 -4
- package/lib/templates/base/components.js +243 -0
- package/lib/templates/base/index.js +207 -0
- package/lib/templates/base/infrastructure.js +314 -0
- package/lib/templates/base/linting.js +359 -0
- package/lib/templates/base/pwa.js +103 -0
- package/lib/templates/base/services.js +362 -0
- package/lib/templates/blog/app.js +250 -0
- package/lib/templates/blog/components.js +360 -0
- package/lib/templates/blog/i18n.js +77 -0
- package/lib/templates/blog/index.js +126 -0
- package/lib/templates/blog/pages.js +554 -0
- package/lib/templates/blog/services.js +390 -0
- package/lib/templates/dashboard/app.js +320 -0
- package/lib/templates/dashboard/charts.js +305 -0
- package/lib/templates/dashboard/components.js +410 -0
- package/lib/templates/dashboard/i18n.js +340 -0
- package/lib/templates/dashboard/index.js +141 -0
- package/lib/templates/dashboard/layout.js +310 -0
- package/lib/templates/dashboard/pages.js +681 -0
- package/lib/templates/ecommerce/app.js +315 -0
- package/lib/templates/ecommerce/components.js +496 -0
- package/lib/templates/ecommerce/i18n.js +389 -0
- package/lib/templates/ecommerce/index.js +152 -0
- package/lib/templates/ecommerce/layout.js +270 -0
- package/lib/templates/ecommerce/pages.js +969 -0
- package/lib/templates/ecommerce/services.js +300 -0
- package/lib/templates/index.js +12 -0
- package/lib/templates/landing/index.js +1117 -0
- package/lib/templates/portfolio/index.js +1160 -0
- package/lib/templates/saas/index.js +1371 -0
- package/lib/templates/starter/app.js +364 -0
- package/lib/templates/starter/i18n.js +856 -0
- package/lib/templates/starter/index.js +52 -4060
- package/lib/templates/starter/layout.js +852 -0
- package/lib/templates/starter/pages.js +1241 -0
- package/package.json +1 -1
- package/lib/templates/starter/features.js +0 -867
- package/lib/utils/ai-config.js +0 -641
- /package/lib/templates/{starter → base}/advanced-features.js +0 -0
- /package/lib/templates/{starter → base}/seo-assets.js +0 -0
- /package/lib/templates/{starter → base}/seo-features.js +0 -0
- /package/lib/templates/{starter → base}/ui-features.js +0 -0
package/lib/utils/ai-config.js
DELETED
|
@@ -1,641 +0,0 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* AI Configuration Generator
|
|
6
|
-
*
|
|
7
|
-
* Strategy:
|
|
8
|
-
* Creates AI tool-specific configuration files only when explicitly selected
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// ==================== CLAUDE CONFIG ====================
|
|
12
|
-
|
|
13
|
-
async function createClaudeConfig(projectPath, projectName) {
|
|
14
|
-
const claudeMd = `# ${projectName} - AI Assistant Guidelines
|
|
15
|
-
|
|
16
|
-
This is an Angular project scaffolded with **create-ng-tailwind**, using **Angular 20+ and Tailwind CSS v4**.
|
|
17
|
-
|
|
18
|
-
## 📁 Project Structure
|
|
19
|
-
|
|
20
|
-
\`\`\`
|
|
21
|
-
src/app/
|
|
22
|
-
├── core/ # Core application logic
|
|
23
|
-
│ ├── services/ # Auth, API, Toast, Modal, Loading, Cache, Storage, i18n
|
|
24
|
-
│ ├── guards/ # Route guards (auth, role-based)
|
|
25
|
-
│ ├── interceptors/ # HTTP interceptors (auth, error, loading, caching)
|
|
26
|
-
│ └── i18n/ # Translation services
|
|
27
|
-
├── shared/ # Reusable shared code
|
|
28
|
-
│ ├── components/ # Button, Card, Spinner, Toast, Modal
|
|
29
|
-
│ ├── pipes/ # TimeAgo, Truncate, FileSize, SafeHtml
|
|
30
|
-
│ ├── directives/ # ClickOutside, Tooltip
|
|
31
|
-
│ └── models/ # TypeScript interfaces and types
|
|
32
|
-
├── features/ # Feature modules
|
|
33
|
-
│ ├── home/
|
|
34
|
-
│ ├── about/
|
|
35
|
-
│ ├── contact/
|
|
36
|
-
│ └── auth/ # Login, Register, Forgot Password
|
|
37
|
-
└── layout/ # Layout components
|
|
38
|
-
├── header/ # Header with language switcher
|
|
39
|
-
├── footer/
|
|
40
|
-
└── auth/ # Auth layout
|
|
41
|
-
\`\`\`
|
|
42
|
-
|
|
43
|
-
## 🛠 Technology Stack
|
|
44
|
-
|
|
45
|
-
- **Framework:** Angular 20+ (Standalone Components)
|
|
46
|
-
- **Styling:** Tailwind CSS v4 (PostCSS)
|
|
47
|
-
- **State:** Angular Signals (reactive primitives)
|
|
48
|
-
- **HTTP:** HttpClient with 4 Interceptors
|
|
49
|
-
- **i18n:** @ngx-translate/core (English & Arabic with RTL)
|
|
50
|
-
- **Forms:** Reactive Forms with validation
|
|
51
|
-
- **Routing:** Angular Router with lazy loading
|
|
52
|
-
- **Icons:** @ng-icons/heroicons
|
|
53
|
-
- **Linting:** ESLint + Prettier + simple-git-hooks
|
|
54
|
-
|
|
55
|
-
## 🎯 Path Aliases (Use These!)
|
|
56
|
-
|
|
57
|
-
- \`@core/*\` → \`src/app/core/*\`
|
|
58
|
-
- \`@shared/*\` → \`src/app/shared/*\`
|
|
59
|
-
- \`@features/*\` → \`src/app/features/*\`
|
|
60
|
-
- \`@environments/*\` → \`src/environments/*\`
|
|
61
|
-
|
|
62
|
-
## ⚡ HTTP Interceptors (Pre-configured)
|
|
63
|
-
|
|
64
|
-
Interceptors run in this order:
|
|
65
|
-
|
|
66
|
-
1. **authInterceptor** → Adds JWT Bearer token to requests
|
|
67
|
-
2. **cachingInterceptor** → Caches GET requests (5min TTL)
|
|
68
|
-
3. **loadingInterceptor** → Shows/hides loading indicator
|
|
69
|
-
4. **errorInterceptor** → Handles errors globally + shows toast
|
|
70
|
-
|
|
71
|
-
## 🧩 Available Services (Use These!)
|
|
72
|
-
|
|
73
|
-
### ToastService
|
|
74
|
-
|
|
75
|
-
\`\`\`typescript
|
|
76
|
-
import { inject } from '@angular/core';
|
|
77
|
-
import { ToastService } from '@core/services/toast.service';
|
|
78
|
-
|
|
79
|
-
const toast = inject(ToastService);
|
|
80
|
-
toast.success('Profile updated!');
|
|
81
|
-
toast.error('Failed to save changes');
|
|
82
|
-
toast.warning('Session expiring soon');
|
|
83
|
-
toast.info('New message received');
|
|
84
|
-
\`\`\`
|
|
85
|
-
|
|
86
|
-
### ModalService
|
|
87
|
-
|
|
88
|
-
\`\`\`typescript
|
|
89
|
-
import { inject } from '@angular/core';
|
|
90
|
-
import { ModalService } from '@core/services/modal.service';
|
|
91
|
-
|
|
92
|
-
const modal = inject(ModalService);
|
|
93
|
-
|
|
94
|
-
// Confirmation dialog
|
|
95
|
-
const confirmed = await modal.confirm(
|
|
96
|
-
'Are you sure you want to delete this item?',
|
|
97
|
-
'Confirm Delete'
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
if (confirmed) {
|
|
101
|
-
// Proceed with deletion
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Alert dialog
|
|
105
|
-
await modal.alert('Your session has expired', 'Session Timeout');
|
|
106
|
-
\`\`\`
|
|
107
|
-
|
|
108
|
-
### ApiService
|
|
109
|
-
|
|
110
|
-
\`\`\`typescript
|
|
111
|
-
import { inject } from '@angular/core';
|
|
112
|
-
import { ApiService } from '@core/services/api.service';
|
|
113
|
-
|
|
114
|
-
const api = inject(ApiService);
|
|
115
|
-
|
|
116
|
-
// GET request
|
|
117
|
-
api.get<User[]>('users').subscribe((users) => {
|
|
118
|
-
console.log(users);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// POST request
|
|
122
|
-
api.post<User>('users', userData).subscribe((newUser) => {
|
|
123
|
-
console.log('Created:', newUser);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// PUT/DELETE also available
|
|
127
|
-
\`\`\`
|
|
128
|
-
|
|
129
|
-
### AuthService
|
|
130
|
-
|
|
131
|
-
\`\`\`typescript
|
|
132
|
-
import { inject } from '@angular/core';
|
|
133
|
-
import { AuthService } from '@core/services/auth.service';
|
|
134
|
-
|
|
135
|
-
const auth = inject(AuthService);
|
|
136
|
-
|
|
137
|
-
// Login
|
|
138
|
-
auth.login({ email, password }).subscribe({
|
|
139
|
-
next: () => router.navigate(['/dashboard']),
|
|
140
|
-
error: (err) => console.error(err),
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// Check auth state (reactive)
|
|
144
|
-
auth.isAuthenticated$.subscribe((isAuth) => {
|
|
145
|
-
console.log('Authenticated:', isAuth);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// Logout
|
|
149
|
-
auth.logout();
|
|
150
|
-
\`\`\`
|
|
151
|
-
|
|
152
|
-
### LoadingService
|
|
153
|
-
|
|
154
|
-
\`\`\`typescript
|
|
155
|
-
import { inject } from '@angular/core';
|
|
156
|
-
import { LoadingService } from '@core/services/loading.service';
|
|
157
|
-
|
|
158
|
-
const loading = inject(LoadingService);
|
|
159
|
-
|
|
160
|
-
// In template (automatic with HTTP interceptor)
|
|
161
|
-
@if (loading.isLoading()) {
|
|
162
|
-
<app-loading-spinner />
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Manual control (rarely needed)
|
|
166
|
-
loading.show();
|
|
167
|
-
// ... do work
|
|
168
|
-
loading.hide();
|
|
169
|
-
\`\`\`
|
|
170
|
-
|
|
171
|
-
## 📝 Coding Patterns
|
|
172
|
-
|
|
173
|
-
### Component Structure (Standalone)
|
|
174
|
-
|
|
175
|
-
\`\`\`typescript
|
|
176
|
-
import { Component, inject, signal } from '@angular/core';
|
|
177
|
-
import { CommonModule } from '@angular/common';
|
|
178
|
-
import { ToastService } from '@core/services/toast.service';
|
|
179
|
-
|
|
180
|
-
@Component({
|
|
181
|
-
selector: 'app-example',
|
|
182
|
-
standalone: true,
|
|
183
|
-
imports: [CommonModule],
|
|
184
|
-
template: \`
|
|
185
|
-
<div class="rounded-lg bg-white p-6 shadow">
|
|
186
|
-
<h2 class="mb-4 text-xl font-bold">{{ title() }}</h2>
|
|
187
|
-
<button
|
|
188
|
-
(click)="handleClick()"
|
|
189
|
-
class="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600"
|
|
190
|
-
>
|
|
191
|
-
Click Me ({{ count() }})
|
|
192
|
-
</button>
|
|
193
|
-
</div>
|
|
194
|
-
\`,
|
|
195
|
-
})
|
|
196
|
-
export class ExampleComponent {
|
|
197
|
-
private toast = inject(ToastService);
|
|
198
|
-
|
|
199
|
-
// Use signals for reactive state
|
|
200
|
-
title = signal('Example Component');
|
|
201
|
-
count = signal(0);
|
|
202
|
-
|
|
203
|
-
handleClick(): void {
|
|
204
|
-
this.count.update((n) => n + 1);
|
|
205
|
-
this.toast.success(\`Clicked \${this.count()} times\`);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
\`\`\`
|
|
209
|
-
|
|
210
|
-
### Service Creation
|
|
211
|
-
|
|
212
|
-
\`\`\`typescript
|
|
213
|
-
import { Injectable, inject, signal } from '@angular/core';
|
|
214
|
-
import { ApiService } from '@core/services/api.service';
|
|
215
|
-
|
|
216
|
-
interface Item {
|
|
217
|
-
id: string;
|
|
218
|
-
name: string;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
@Injectable({ providedIn: 'root' })
|
|
222
|
-
export class ItemService {
|
|
223
|
-
private api = inject(ApiService);
|
|
224
|
-
|
|
225
|
-
// Use signals for reactive state
|
|
226
|
-
items = signal<Item[]>([]);
|
|
227
|
-
loading = signal(false);
|
|
228
|
-
|
|
229
|
-
loadItems(): void {
|
|
230
|
-
this.loading.set(true);
|
|
231
|
-
this.api.get<Item[]>('items').subscribe({
|
|
232
|
-
next: (data) => {
|
|
233
|
-
this.items.set(data);
|
|
234
|
-
this.loading.set(false);
|
|
235
|
-
},
|
|
236
|
-
error: () => this.loading.set(false),
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
\`\`\`
|
|
241
|
-
|
|
242
|
-
### Reactive Forms
|
|
243
|
-
|
|
244
|
-
\`\`\`typescript
|
|
245
|
-
import { Component, inject } from '@angular/core';
|
|
246
|
-
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
247
|
-
|
|
248
|
-
@Component({
|
|
249
|
-
selector: 'app-contact-form',
|
|
250
|
-
standalone: true,
|
|
251
|
-
imports: [ReactiveFormsModule],
|
|
252
|
-
template: \`
|
|
253
|
-
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
|
254
|
-
<input formControlName="email" class="rounded border p-2" />
|
|
255
|
-
@if (form.get('email')?.invalid && form.get('email')?.touched) {
|
|
256
|
-
<span class="text-sm text-red-500">Email is required</span>
|
|
257
|
-
}
|
|
258
|
-
<button type="submit" class="rounded bg-blue-500 px-4 py-2 text-white">Submit</button>
|
|
259
|
-
</form>
|
|
260
|
-
\`,
|
|
261
|
-
})
|
|
262
|
-
export class ContactFormComponent {
|
|
263
|
-
private fb = inject(FormBuilder);
|
|
264
|
-
|
|
265
|
-
form = this.fb.group({
|
|
266
|
-
email: ['', [Validators.required, Validators.email]],
|
|
267
|
-
message: ['', [Validators.required, Validators.minLength(10)]],
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
onSubmit(): void {
|
|
271
|
-
if (this.form.valid) {
|
|
272
|
-
console.log(this.form.value);
|
|
273
|
-
} else {
|
|
274
|
-
Object.keys(this.form.controls).forEach((key) => {
|
|
275
|
-
this.form.get(key)?.markAsTouched();
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
\`\`\`
|
|
281
|
-
|
|
282
|
-
### Routing with Lazy Loading
|
|
283
|
-
|
|
284
|
-
\`\`\`typescript
|
|
285
|
-
import { Routes } from '@angular/router';
|
|
286
|
-
import { authGuard } from '@core/guards/auth.guard';
|
|
287
|
-
|
|
288
|
-
export const routes: Routes = [
|
|
289
|
-
{
|
|
290
|
-
path: '',
|
|
291
|
-
loadComponent: () => import('./features/home/home.component').then((c) => c.HomeComponent),
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
path: 'dashboard',
|
|
295
|
-
loadComponent: () =>
|
|
296
|
-
import('./features/dashboard/dashboard.component').then((c) => c.DashboardComponent),
|
|
297
|
-
canActivate: [authGuard],
|
|
298
|
-
},
|
|
299
|
-
];
|
|
300
|
-
\`\`\`
|
|
301
|
-
|
|
302
|
-
## 🎨 Styling Guidelines
|
|
303
|
-
|
|
304
|
-
**Always use Tailwind CSS classes. Do NOT write custom CSS.**
|
|
305
|
-
|
|
306
|
-
Common patterns:
|
|
307
|
-
|
|
308
|
-
\`\`\`html
|
|
309
|
-
<!-- Layout -->
|
|
310
|
-
<div class="container mx-auto px-4">
|
|
311
|
-
<div class="flex items-center justify-between">
|
|
312
|
-
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
313
|
-
<!-- Spacing -->
|
|
314
|
-
<div class="mt-6 space-y-4 p-4">
|
|
315
|
-
<!-- Colors -->
|
|
316
|
-
<div class="bg-blue-500 text-white">
|
|
317
|
-
<button class="bg-gray-100 text-gray-900 hover:bg-gray-200">
|
|
318
|
-
<!-- Typography -->
|
|
319
|
-
<h1 class="text-3xl font-bold">
|
|
320
|
-
<p class="text-sm text-gray-600">
|
|
321
|
-
<!-- Responsive -->
|
|
322
|
-
</p>
|
|
323
|
-
|
|
324
|
-
<div class="hidden md:block">
|
|
325
|
-
<div class="w-full md:w-1/2 lg:w-1/3"></div>
|
|
326
|
-
</div>
|
|
327
|
-
</h1>
|
|
328
|
-
</button>
|
|
329
|
-
</div>
|
|
330
|
-
</div>
|
|
331
|
-
</div>
|
|
332
|
-
</div>
|
|
333
|
-
</div>
|
|
334
|
-
\`\`\`
|
|
335
|
-
|
|
336
|
-
## 🌍 Internationalization (i18n)
|
|
337
|
-
|
|
338
|
-
Support English and Arabic with RTL:
|
|
339
|
-
|
|
340
|
-
**Add translations to both files:**
|
|
341
|
-
|
|
342
|
-
- \`public/assets/i18n/en.json\`
|
|
343
|
-
- \`public/assets/i18n/ar.json\`
|
|
344
|
-
|
|
345
|
-
**In templates:**
|
|
346
|
-
|
|
347
|
-
\`\`\`html
|
|
348
|
-
<h1>{{ 'welcome.title' | translate }}</h1>
|
|
349
|
-
<button>{{ 'common.save' | translate }}</button>
|
|
350
|
-
\`\`\`
|
|
351
|
-
|
|
352
|
-
**In components:**
|
|
353
|
-
|
|
354
|
-
\`\`\`typescript
|
|
355
|
-
import { inject } from '@angular/core';
|
|
356
|
-
import { TranslateService } from '@ngx-translate/core';
|
|
357
|
-
|
|
358
|
-
const translate = inject(TranslateService);
|
|
359
|
-
const message = translate.instant('error.notFound');
|
|
360
|
-
\`\`\`
|
|
361
|
-
|
|
362
|
-
## ✅ Best Practices
|
|
363
|
-
|
|
364
|
-
1. **Always use standalone components** (no NgModule)
|
|
365
|
-
2. **Use inject() function** (not constructor injection)
|
|
366
|
-
3. **Use signals for reactive state** (not BehaviorSubject unless needed)
|
|
367
|
-
4. **Use existing services** before creating new ones
|
|
368
|
-
5. **Use Tailwind CSS classes** (no custom CSS)
|
|
369
|
-
6. **Use path aliases** (@core/, @shared/, @features/)
|
|
370
|
-
7. **Use lazy loading** for all routes
|
|
371
|
-
8. **Use Reactive Forms** (not template-driven)
|
|
372
|
-
9. **Write unit tests** for services and components
|
|
373
|
-
10. **Support RTL** for Arabic language
|
|
374
|
-
|
|
375
|
-
## ❌ Don't Do This
|
|
376
|
-
|
|
377
|
-
- ❌ Don't use NgModule (use standalone)
|
|
378
|
-
- ❌ Don't use constructor injection (use inject())
|
|
379
|
-
- ❌ Don't write custom CSS (use Tailwind)
|
|
380
|
-
- ❌ Don't use \`any\` type (use proper TypeScript types)
|
|
381
|
-
- ❌ Don't use \`src/assets/\` (use \`public/assets/\`)
|
|
382
|
-
- ❌ Don't use HttpClient directly (use ApiService)
|
|
383
|
-
- ❌ Don't create services that already exist
|
|
384
|
-
- ❌ Don't use template-driven forms
|
|
385
|
-
- ❌ Don't forget to add translations for new text
|
|
386
|
-
|
|
387
|
-
## 📦 Available Pipes
|
|
388
|
-
|
|
389
|
-
- \`truncate:50:true\` - Truncate text to 50 chars with word boundaries
|
|
390
|
-
- \`translate\` - i18n translations (from @ngx-translate)
|
|
391
|
-
|
|
392
|
-
## 📦 Available Directives
|
|
393
|
-
|
|
394
|
-
- \`appClickOutside\` - Detect clicks outside element
|
|
395
|
-
- \`appTooltip="text"\` - Show tooltip on hover
|
|
396
|
-
|
|
397
|
-
## 🧪 Testing
|
|
398
|
-
|
|
399
|
-
Write unit tests using Jasmine/Karma:
|
|
400
|
-
|
|
401
|
-
\`\`\`typescript
|
|
402
|
-
describe('ExampleComponent', () => {
|
|
403
|
-
let component: ExampleComponent;
|
|
404
|
-
let fixture: ComponentFixture<ExampleComponent>;
|
|
405
|
-
|
|
406
|
-
beforeEach(() => {
|
|
407
|
-
TestBed.configureTestingModule({
|
|
408
|
-
imports: [ExampleComponent],
|
|
409
|
-
});
|
|
410
|
-
fixture = TestBed.createComponent(ExampleComponent);
|
|
411
|
-
component = fixture.componentInstance;
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
it('should create', () => {
|
|
415
|
-
expect(component).toBeTruthy();
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
\`\`\`
|
|
419
|
-
|
|
420
|
-
## 📚 Important Notes
|
|
421
|
-
|
|
422
|
-
- Assets go in \`public/assets/\` (not \`src/assets/\`)
|
|
423
|
-
- Tailwind v4 uses CSS imports: \`@import "tailwindcss";\`
|
|
424
|
-
- PostCSS config is in \`.postcssrc.json\`
|
|
425
|
-
- HTTP interceptors run automatically (no manual setup needed)
|
|
426
|
-
- Loading state is automatic for HTTP requests
|
|
427
|
-
- Error handling is automatic (shows toast notifications)
|
|
428
|
-
|
|
429
|
-
---
|
|
430
|
-
|
|
431
|
-
**Remember:** This is a production-ready starter. Use the existing infrastructure before creating new utilities!
|
|
432
|
-
`;
|
|
433
|
-
|
|
434
|
-
// Create .claude directory if it doesn't exist
|
|
435
|
-
const claudeDir = path.join(projectPath, '.claude');
|
|
436
|
-
await fs.ensureDir(claudeDir);
|
|
437
|
-
|
|
438
|
-
// Write CLAUDE.md inside .claude folder (uppercase to match Angular CLI convention)
|
|
439
|
-
await fs.writeFile(path.join(claudeDir, 'CLAUDE.md'), claudeMd);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// ==================== CURSOR-SPECIFIC CONFIG ====================
|
|
443
|
-
|
|
444
|
-
async function createCursorConfig(projectPath, projectName) {
|
|
445
|
-
const cursorRules = `# ${projectName} - Cursor AI Rules
|
|
446
|
-
|
|
447
|
-
## Quick Reference
|
|
448
|
-
|
|
449
|
-
- Angular 20+ standalone components
|
|
450
|
-
- Tailwind CSS v4 (use utility classes only)
|
|
451
|
-
- inject() for DI (not constructors)
|
|
452
|
-
- Signals for state (not BehaviorSubject)
|
|
453
|
-
- Path aliases: @core/, @shared/, @features/, @environments/
|
|
454
|
-
|
|
455
|
-
## Component Pattern
|
|
456
|
-
|
|
457
|
-
\`\`\`typescript
|
|
458
|
-
import { Component, inject, signal } from '@angular/core';
|
|
459
|
-
import { CommonModule } from '@angular/common';
|
|
460
|
-
|
|
461
|
-
@Component({
|
|
462
|
-
selector: 'app-name',
|
|
463
|
-
standalone: true,
|
|
464
|
-
imports: [CommonModule],
|
|
465
|
-
template: \`<div class="p-4">{{ text() }}</div>\`,
|
|
466
|
-
})
|
|
467
|
-
export class NameComponent {
|
|
468
|
-
private service = inject(ServiceName);
|
|
469
|
-
text = signal('Hello');
|
|
470
|
-
}
|
|
471
|
-
\`\`\`
|
|
472
|
-
|
|
473
|
-
## Available Services (Use These!)
|
|
474
|
-
|
|
475
|
-
- **ToastService** - \`toast.success('Message')\`
|
|
476
|
-
- **ModalService** - \`await modal.confirm('Message')\`
|
|
477
|
-
- **ApiService** - \`api.get<Type>('endpoint')\`
|
|
478
|
-
- **AuthService** - \`auth.login(credentials)\`
|
|
479
|
-
- **LoadingService** - \`loading.isLoading()\`
|
|
480
|
-
|
|
481
|
-
## HTTP Interceptors (Automatic)
|
|
482
|
-
|
|
483
|
-
Auth → Cache → Loading → Error
|
|
484
|
-
|
|
485
|
-
## Forms: Use Reactive Forms
|
|
486
|
-
|
|
487
|
-
\`\`\`typescript
|
|
488
|
-
form = inject(FormBuilder).group({
|
|
489
|
-
email: ['', [Validators.required, Validators.email]],
|
|
490
|
-
});
|
|
491
|
-
\`\`\`
|
|
492
|
-
|
|
493
|
-
## Routing: Use Lazy Loading
|
|
494
|
-
|
|
495
|
-
\`\`\`typescript
|
|
496
|
-
{
|
|
497
|
-
path: 'feature',
|
|
498
|
-
loadComponent: () =>
|
|
499
|
-
import('./feature/feature.component').then((c) => c.FeatureComponent),
|
|
500
|
-
}
|
|
501
|
-
\`\`\`
|
|
502
|
-
|
|
503
|
-
## Styling: Tailwind Only
|
|
504
|
-
|
|
505
|
-
Use: \`rounded bg-blue-500 p-4 text-white hover:bg-blue-600\`
|
|
506
|
-
|
|
507
|
-
## i18n: Support English + Arabic
|
|
508
|
-
|
|
509
|
-
Add keys to \`public/assets/i18n/en.json\` and \`ar.json\`
|
|
510
|
-
|
|
511
|
-
## Don't
|
|
512
|
-
|
|
513
|
-
- No NgModule
|
|
514
|
-
- No constructor injection
|
|
515
|
-
- No custom CSS
|
|
516
|
-
- No \`any\` type
|
|
517
|
-
- No \`src/assets/\` (use \`public/assets/\`)
|
|
518
|
-
- No HttpClient directly (use ApiService)
|
|
519
|
-
`;
|
|
520
|
-
|
|
521
|
-
await fs.writeFile(path.join(projectPath, '.cursorrules'), cursorRules);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// ==================== WINDSURF-SPECIFIC CONFIG ====================
|
|
525
|
-
|
|
526
|
-
async function createWindsurfConfig(projectPath, projectName) {
|
|
527
|
-
const windsurfRules = `# ${projectName} - Windsurf AI Rules
|
|
528
|
-
|
|
529
|
-
Angular 20+ | Tailwind CSS v4 | Standalone Components | Signals
|
|
530
|
-
|
|
531
|
-
## Quick Patterns
|
|
532
|
-
|
|
533
|
-
**Component:**
|
|
534
|
-
|
|
535
|
-
\`\`\`typescript
|
|
536
|
-
@Component({
|
|
537
|
-
selector: 'app-name',
|
|
538
|
-
standalone: true,
|
|
539
|
-
imports: [CommonModule],
|
|
540
|
-
template: \`<div class="p-4">{{ state() }}</div>\`,
|
|
541
|
-
})
|
|
542
|
-
export class NameComponent {
|
|
543
|
-
private svc = inject(Service);
|
|
544
|
-
state = signal(value);
|
|
545
|
-
}
|
|
546
|
-
\`\`\`
|
|
547
|
-
|
|
548
|
-
**Show Toast:**
|
|
549
|
-
|
|
550
|
-
\`\`\`typescript
|
|
551
|
-
inject(ToastService).success('Message');
|
|
552
|
-
\`\`\`
|
|
553
|
-
|
|
554
|
-
**Open Modal:**
|
|
555
|
-
|
|
556
|
-
\`\`\`typescript
|
|
557
|
-
await inject(ModalService).confirm('Message');
|
|
558
|
-
\`\`\`
|
|
559
|
-
|
|
560
|
-
**API Call:**
|
|
561
|
-
|
|
562
|
-
\`\`\`typescript
|
|
563
|
-
inject(ApiService).get<Type>('endpoint').subscribe(...);
|
|
564
|
-
\`\`\`
|
|
565
|
-
|
|
566
|
-
**Form:**
|
|
567
|
-
|
|
568
|
-
\`\`\`typescript
|
|
569
|
-
form = inject(FormBuilder).group({
|
|
570
|
-
field: ['', Validators.required],
|
|
571
|
-
});
|
|
572
|
-
\`\`\`
|
|
573
|
-
|
|
574
|
-
## Path Aliases
|
|
575
|
-
|
|
576
|
-
@core/ @shared/ @features/ @environments/
|
|
577
|
-
|
|
578
|
-
## Services Available
|
|
579
|
-
|
|
580
|
-
ToastService, ModalService, ApiService, AuthService, LoadingService, CacheService
|
|
581
|
-
|
|
582
|
-
## HTTP Interceptors (Auto)
|
|
583
|
-
|
|
584
|
-
Auth → Cache → Loading → Error
|
|
585
|
-
|
|
586
|
-
## Styling
|
|
587
|
-
|
|
588
|
-
Tailwind only: rounded bg-blue-500 p-4 text-white
|
|
589
|
-
|
|
590
|
-
## i18n
|
|
591
|
-
|
|
592
|
-
public/assets/i18n/en.json + ar.json
|
|
593
|
-
|
|
594
|
-
## Don't
|
|
595
|
-
|
|
596
|
-
No NgModule, No constructors, No custom CSS, No 'any', No src/assets
|
|
597
|
-
`;
|
|
598
|
-
|
|
599
|
-
await fs.writeFile(path.join(projectPath, '.windsurfrules'), windsurfRules);
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// ==================== MAIN EXPORT ====================
|
|
603
|
-
|
|
604
|
-
/**
|
|
605
|
-
* Create AI configuration files
|
|
606
|
-
* Creates tool-specific configs only when explicitly selected
|
|
607
|
-
*
|
|
608
|
-
* @param {string} projectPath - Full path to the project
|
|
609
|
-
* @param {string} projectName - Name of the project
|
|
610
|
-
* @param {string[]} aiTools - Array of selected AI tools
|
|
611
|
-
*/
|
|
612
|
-
async function createAIConfigs(projectPath, projectName, aiTools) {
|
|
613
|
-
// Skip if user selected "none"
|
|
614
|
-
if (!aiTools || aiTools.length === 0 || (aiTools.length === 1 && aiTools[0] === 'none')) {
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
console.log('\n🤖 Configuring AI tools...');
|
|
619
|
-
|
|
620
|
-
// Create tool-specific configs only if selected
|
|
621
|
-
if (aiTools.includes('claude')) {
|
|
622
|
-
await createClaudeConfig(projectPath, projectName);
|
|
623
|
-
console.log(' ✓ .claude/CLAUDE.md created');
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
if (aiTools.includes('cursor')) {
|
|
627
|
-
await createCursorConfig(projectPath, projectName);
|
|
628
|
-
console.log(' ✓ .cursorrules created');
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
if (aiTools.includes('windsurf')) {
|
|
632
|
-
await createWindsurfConfig(projectPath, projectName);
|
|
633
|
-
console.log(' ✓ .windsurfrules created');
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
console.log('✨ AI configuration complete!\n');
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
module.exports = {
|
|
640
|
-
createAIConfigs,
|
|
641
|
-
};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|