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
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Starter App Configuration
|
|
6
|
+
* - Routes (app.routes.ts)
|
|
7
|
+
* - App Config (app.config.ts)
|
|
8
|
+
* - App Component (app.ts)
|
|
9
|
+
* - Global Styles (styles.css with Tailwind v4 theme)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
async function createRouting(config) {
|
|
13
|
+
const routes = `import { Routes } from '@angular/router';
|
|
14
|
+
// import { authGuard } from './core/guards/auth.guard';
|
|
15
|
+
|
|
16
|
+
export const routes: Routes = [
|
|
17
|
+
// Public routes
|
|
18
|
+
{
|
|
19
|
+
path: '',
|
|
20
|
+
loadComponent: () => import('./features/home/home.component').then(c => c.HomeComponent)
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
path: 'about',
|
|
24
|
+
loadComponent: () => import('./features/about/about.component').then(c => c.AboutComponent)
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
path: 'contact',
|
|
28
|
+
loadComponent: () => import('./features/contact/contact.component').then(c => c.ContactComponent)
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
// Auth routes with layout
|
|
32
|
+
{
|
|
33
|
+
path: 'auth',
|
|
34
|
+
loadComponent: () => import('./layout/auth/auth-layout.component').then(c => c.AuthLayoutComponent),
|
|
35
|
+
children: [
|
|
36
|
+
{
|
|
37
|
+
path: '',
|
|
38
|
+
redirectTo: 'login',
|
|
39
|
+
pathMatch: 'full'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
path: 'login',
|
|
43
|
+
loadComponent: () => import('./features/auth/login/login.component').then(c => c.LoginComponent)
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
path: 'register',
|
|
47
|
+
loadComponent: () => import('./features/auth/register/register.component').then(c => c.RegisterComponent)
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
path: 'forgot-password',
|
|
51
|
+
loadComponent: () => import('./features/auth/forgot-password/forgot-password.component').then(c => c.ForgotPasswordComponent)
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
// Example protected route (uncomment authGuard import above when you need authentication)
|
|
57
|
+
// {
|
|
58
|
+
// path: 'dashboard',
|
|
59
|
+
// loadComponent: () => import('./features/dashboard/dashboard.component').then(c => c.DashboardComponent),
|
|
60
|
+
// canActivate: [authGuard]
|
|
61
|
+
// },
|
|
62
|
+
|
|
63
|
+
// Catch-all redirect
|
|
64
|
+
{
|
|
65
|
+
path: '**',
|
|
66
|
+
redirectTo: ''
|
|
67
|
+
}
|
|
68
|
+
];`;
|
|
69
|
+
|
|
70
|
+
await fs.writeFile(path.join(config.fullPath, 'src/app/app.routes.ts'), routes);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function createAppConfig(config) {
|
|
74
|
+
const isSSR = config.ssr || false;
|
|
75
|
+
const isZoneless = config.zoneless || false;
|
|
76
|
+
|
|
77
|
+
const changeDetectionImport = isZoneless
|
|
78
|
+
? 'provideZonelessChangeDetection'
|
|
79
|
+
: 'provideZoneChangeDetection';
|
|
80
|
+
|
|
81
|
+
const coreImports = `import { ApplicationConfig, provideBrowserGlobalErrorListeners, ${changeDetectionImport}, isDevMode, importProvidersFrom } from '@angular/core';
|
|
82
|
+
import { provideRouter } from '@angular/router';
|
|
83
|
+
import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';${
|
|
84
|
+
isSSR
|
|
85
|
+
? "\nimport { provideClientHydration, withEventReplay } from '@angular/platform-browser';"
|
|
86
|
+
: ''
|
|
87
|
+
}
|
|
88
|
+
import { provideServiceWorker } from '@angular/service-worker';
|
|
89
|
+
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
|
|
90
|
+
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
|
91
|
+
import { HttpClient } from '@angular/common/http';
|
|
92
|
+
|
|
93
|
+
import { routes } from './app.routes';
|
|
94
|
+
import { environment } from '@environments/environment';
|
|
95
|
+
import { authInterceptor } from '@core/interceptors/auth.interceptor';
|
|
96
|
+
import { errorInterceptor } from '@core/interceptors/error.interceptor';
|
|
97
|
+
import { loadingInterceptor } from '@core/interceptors/loading.interceptor';
|
|
98
|
+
import { cachingInterceptor } from '@core/interceptors/caching.interceptor';`;
|
|
99
|
+
|
|
100
|
+
const ssrProvider = isSSR ? '\n provideClientHydration(withEventReplay()),' : '';
|
|
101
|
+
const changeDetectionProvider = isZoneless
|
|
102
|
+
? 'provideZonelessChangeDetection()'
|
|
103
|
+
: 'provideZoneChangeDetection({ eventCoalescing: true })';
|
|
104
|
+
|
|
105
|
+
const appConfig = `${coreImports}
|
|
106
|
+
|
|
107
|
+
// Translation loader factory
|
|
108
|
+
export function HttpLoaderFactory(http: HttpClient) {
|
|
109
|
+
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const appConfig: ApplicationConfig = {
|
|
113
|
+
providers: [
|
|
114
|
+
provideBrowserGlobalErrorListeners(),
|
|
115
|
+
${changeDetectionProvider},
|
|
116
|
+
provideRouter(routes),${ssrProvider}
|
|
117
|
+
provideHttpClient(
|
|
118
|
+
withFetch(),
|
|
119
|
+
withInterceptors([
|
|
120
|
+
authInterceptor,
|
|
121
|
+
cachingInterceptor,
|
|
122
|
+
loadingInterceptor,
|
|
123
|
+
errorInterceptor
|
|
124
|
+
])
|
|
125
|
+
),
|
|
126
|
+
// Translation configuration
|
|
127
|
+
importProvidersFrom(
|
|
128
|
+
TranslateModule.forRoot({
|
|
129
|
+
loader: {
|
|
130
|
+
provide: TranslateLoader,
|
|
131
|
+
useFactory: HttpLoaderFactory,
|
|
132
|
+
deps: [HttpClient]
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
),
|
|
136
|
+
provideServiceWorker('ngsw-worker.js', {
|
|
137
|
+
enabled: !isDevMode(),
|
|
138
|
+
registrationStrategy: 'registerWhenStable:30000'
|
|
139
|
+
}),
|
|
140
|
+
// Environment-based providers
|
|
141
|
+
...(environment.enableDevTools && isDevMode() ? [] : []),
|
|
142
|
+
]
|
|
143
|
+
};`;
|
|
144
|
+
|
|
145
|
+
await fs.writeFile(path.join(config.fullPath, 'src/app/app.config.ts'), appConfig);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function createAppComponent(config) {
|
|
149
|
+
const app = `import { Component, OnInit, inject } from '@angular/core';
|
|
150
|
+
import { RouterOutlet } from '@angular/router';
|
|
151
|
+
import { HeaderComponent } from './layout/header/header.component';
|
|
152
|
+
import { FooterComponent } from './layout/footer/footer.component';
|
|
153
|
+
import { ToastComponent } from '@shared/components/toast/toast.component';
|
|
154
|
+
import { ModalComponent } from '@shared/components/modal/modal.component';
|
|
155
|
+
import { TranslationService } from '@core/i18n/translation.service';
|
|
156
|
+
import { SeoService } from '@core/services/seo.service';
|
|
157
|
+
import { createOrganizationSchema, createWebSiteSchema } from '@core/utils/structured-data';
|
|
158
|
+
|
|
159
|
+
@Component({
|
|
160
|
+
selector: 'app-root',
|
|
161
|
+
imports: [RouterOutlet, HeaderComponent, FooterComponent, ToastComponent, ModalComponent],
|
|
162
|
+
template: \`
|
|
163
|
+
<div class="min-h-screen flex flex-col bg-white">
|
|
164
|
+
<app-header />
|
|
165
|
+
|
|
166
|
+
<main class="flex-1">
|
|
167
|
+
<router-outlet />
|
|
168
|
+
</main>
|
|
169
|
+
|
|
170
|
+
<app-footer />
|
|
171
|
+
|
|
172
|
+
<!-- Global Toast Notifications -->
|
|
173
|
+
<app-toast />
|
|
174
|
+
|
|
175
|
+
<!-- Global Modal System -->
|
|
176
|
+
<app-modal />
|
|
177
|
+
</div>
|
|
178
|
+
\`
|
|
179
|
+
})
|
|
180
|
+
export class App implements OnInit {
|
|
181
|
+
private translationService = inject(TranslationService);
|
|
182
|
+
private seo = inject(SeoService);
|
|
183
|
+
|
|
184
|
+
title = '${config.projectName}';
|
|
185
|
+
|
|
186
|
+
ngOnInit(): void {
|
|
187
|
+
const organizationData = createOrganizationSchema({
|
|
188
|
+
name: '${config.projectName}',
|
|
189
|
+
url: 'https://example.com',
|
|
190
|
+
logo: '/assets/images/logo.svg',
|
|
191
|
+
description: 'A modern Angular application built with best practices',
|
|
192
|
+
sameAs: []
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const websiteData = createWebSiteSchema({
|
|
196
|
+
name: '${config.projectName}',
|
|
197
|
+
url: 'https://example.com',
|
|
198
|
+
description: 'A modern Angular application with Tailwind CSS, SEO optimization, and i18n support'
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
this.seo.addStructuredData({
|
|
202
|
+
'@context': 'https://schema.org',
|
|
203
|
+
'@graph': [organizationData, websiteData]
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}`;
|
|
207
|
+
|
|
208
|
+
const appHtml = `<!-- This file is not used in standalone components -->
|
|
209
|
+
<!-- The template is defined inline in app.component.ts -->`;
|
|
210
|
+
|
|
211
|
+
await fs.writeFile(path.join(config.fullPath, 'src/app/app.ts'), app);
|
|
212
|
+
await fs.writeFile(path.join(config.fullPath, 'src/app/app.html'), appHtml);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function createStyles(config) {
|
|
216
|
+
const mainStyles = `@import "tailwindcss";
|
|
217
|
+
|
|
218
|
+
/* ============================================
|
|
219
|
+
TAILWIND V4 THEME CONFIGURATION
|
|
220
|
+
============================================ */
|
|
221
|
+
|
|
222
|
+
@theme {
|
|
223
|
+
/* PRIMARY COLOR SCALE */
|
|
224
|
+
--color-primary-50: #eff6ff;
|
|
225
|
+
--color-primary-100: #dbeafe;
|
|
226
|
+
--color-primary-200: #bfdbfe;
|
|
227
|
+
--color-primary-300: #93c5fd;
|
|
228
|
+
--color-primary-400: #60a5fa;
|
|
229
|
+
--color-primary-500: #3b82f6;
|
|
230
|
+
--color-primary-600: #2563eb;
|
|
231
|
+
--color-primary-700: #1d4ed8;
|
|
232
|
+
--color-primary-800: #1e40af;
|
|
233
|
+
--color-primary-900: #1e3a8a;
|
|
234
|
+
--color-primary-950: #172554;
|
|
235
|
+
|
|
236
|
+
/* SECONDARY COLOR SCALE */
|
|
237
|
+
--color-secondary-50: #ecfeff;
|
|
238
|
+
--color-secondary-100: #cffafe;
|
|
239
|
+
--color-secondary-200: #a5f3fc;
|
|
240
|
+
--color-secondary-300: #67e8f9;
|
|
241
|
+
--color-secondary-400: #22d3ee;
|
|
242
|
+
--color-secondary-500: #06b6d4;
|
|
243
|
+
--color-secondary-600: #0891b2;
|
|
244
|
+
--color-secondary-700: #0e7490;
|
|
245
|
+
--color-secondary-800: #155e75;
|
|
246
|
+
--color-secondary-900: #164e63;
|
|
247
|
+
--color-secondary-950: #083344;
|
|
248
|
+
|
|
249
|
+
/* ACCENT COLOR SCALE */
|
|
250
|
+
--color-accent-50: #faf5ff;
|
|
251
|
+
--color-accent-100: #f3e8ff;
|
|
252
|
+
--color-accent-200: #e9d5ff;
|
|
253
|
+
--color-accent-300: #d8b4fe;
|
|
254
|
+
--color-accent-400: #c084fc;
|
|
255
|
+
--color-accent-500: #a855f7;
|
|
256
|
+
--color-accent-600: #9333ea;
|
|
257
|
+
--color-accent-700: #7e22ce;
|
|
258
|
+
--color-accent-800: #6b21a8;
|
|
259
|
+
--color-accent-900: #581c87;
|
|
260
|
+
--color-accent-950: #3b0764;
|
|
261
|
+
|
|
262
|
+
/* SUCCESS COLOR SCALE */
|
|
263
|
+
--color-success-50: #f0fdf4;
|
|
264
|
+
--color-success-100: #dcfce7;
|
|
265
|
+
--color-success-200: #bbf7d0;
|
|
266
|
+
--color-success-300: #86efac;
|
|
267
|
+
--color-success-400: #4ade80;
|
|
268
|
+
--color-success-500: #22c55e;
|
|
269
|
+
--color-success-600: #16a34a;
|
|
270
|
+
--color-success-700: #15803d;
|
|
271
|
+
--color-success-800: #166534;
|
|
272
|
+
--color-success-900: #14532d;
|
|
273
|
+
--color-success-950: #052e16;
|
|
274
|
+
|
|
275
|
+
/* DANGER COLOR SCALE */
|
|
276
|
+
--color-danger-50: #fef2f2;
|
|
277
|
+
--color-danger-100: #fee2e2;
|
|
278
|
+
--color-danger-200: #fecaca;
|
|
279
|
+
--color-danger-300: #fca5a5;
|
|
280
|
+
--color-danger-400: #f87171;
|
|
281
|
+
--color-danger-500: #ef4444;
|
|
282
|
+
--color-danger-600: #dc2626;
|
|
283
|
+
--color-danger-700: #b91c1c;
|
|
284
|
+
--color-danger-800: #991b1b;
|
|
285
|
+
--color-danger-900: #7f1d1d;
|
|
286
|
+
--color-danger-950: #450a0a;
|
|
287
|
+
|
|
288
|
+
/* WARNING COLOR SCALE */
|
|
289
|
+
--color-warning-50: #fffbeb;
|
|
290
|
+
--color-warning-100: #fef3c7;
|
|
291
|
+
--color-warning-200: #fde68a;
|
|
292
|
+
--color-warning-300: #fcd34d;
|
|
293
|
+
--color-warning-400: #fbbf24;
|
|
294
|
+
--color-warning-500: #f59e0b;
|
|
295
|
+
--color-warning-600: #d97706;
|
|
296
|
+
--color-warning-700: #b45309;
|
|
297
|
+
--color-warning-800: #92400e;
|
|
298
|
+
--color-warning-900: #78350f;
|
|
299
|
+
--color-warning-950: #451a03;
|
|
300
|
+
|
|
301
|
+
/* INFO COLOR SCALE */
|
|
302
|
+
--color-info-50: #eff6ff;
|
|
303
|
+
--color-info-100: #dbeafe;
|
|
304
|
+
--color-info-200: #bfdbfe;
|
|
305
|
+
--color-info-300: #93c5fd;
|
|
306
|
+
--color-info-400: #60a5fa;
|
|
307
|
+
--color-info-500: #3b82f6;
|
|
308
|
+
--color-info-600: #2563eb;
|
|
309
|
+
--color-info-700: #1d4ed8;
|
|
310
|
+
--color-info-800: #1e40af;
|
|
311
|
+
--color-info-900: #1e3a8a;
|
|
312
|
+
--color-info-950: #172554;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/* RTL Support */
|
|
316
|
+
html[dir="rtl"] { direction: rtl; }
|
|
317
|
+
html[dir="ltr"] { direction: ltr; }
|
|
318
|
+
|
|
319
|
+
html[dir="rtl"] .space-x-2 > * + * { margin-right: 0.5rem; margin-left: 0; }
|
|
320
|
+
html[dir="rtl"] .space-x-3 > * + * { margin-right: 0.75rem; margin-left: 0; }
|
|
321
|
+
html[dir="rtl"] .space-x-4 > * + * { margin-right: 1rem; margin-left: 0; }
|
|
322
|
+
html[dir="rtl"] .space-x-8 > * + * { margin-right: 2rem; margin-left: 0; }
|
|
323
|
+
html[dir="rtl"] .text-left { text-align: right; }
|
|
324
|
+
html[dir="rtl"] .text-right { text-align: left; }
|
|
325
|
+
|
|
326
|
+
/* Custom utility classes */
|
|
327
|
+
.container { @apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8; }
|
|
328
|
+
|
|
329
|
+
/* Custom scrollbar */
|
|
330
|
+
::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
331
|
+
::-webkit-scrollbar-track { @apply bg-gray-100; }
|
|
332
|
+
::-webkit-scrollbar-thumb { @apply bg-gray-300 rounded-full; }
|
|
333
|
+
::-webkit-scrollbar-thumb:hover { @apply bg-gray-400; }
|
|
334
|
+
|
|
335
|
+
/* Focus styles */
|
|
336
|
+
.focus-visible { @apply focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2; }
|
|
337
|
+
|
|
338
|
+
/* Animation classes */
|
|
339
|
+
.animate-fade-in { animation: fadeIn 0.6s ease-in-out; }
|
|
340
|
+
.animate-slide-up { animation: slideUp 0.6s ease-out; }
|
|
341
|
+
.animate-blob { animation: blob 7s infinite; }
|
|
342
|
+
.animation-delay-2000 { animation-delay: 2s; }
|
|
343
|
+
.animation-delay-4000 { animation-delay: 4s; }
|
|
344
|
+
.animation-delay-200 { animation-delay: 0.2s; }
|
|
345
|
+
|
|
346
|
+
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
347
|
+
@keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
|
|
348
|
+
@keyframes blob { 0% { transform: translate(0px, 0px) scale(1); } 33% { transform: translate(30px, -50px) scale(1.1); } 66% { transform: translate(-20px, 20px) scale(0.9); } 100% { transform: translate(0px, 0px) scale(1); } }
|
|
349
|
+
|
|
350
|
+
/* Print styles */
|
|
351
|
+
@media print { .no-print { display: none !important; } }
|
|
352
|
+
|
|
353
|
+
/* RTL Transition smoothing */
|
|
354
|
+
* { transition: margin 0.2s ease-in-out; }`;
|
|
355
|
+
|
|
356
|
+
await fs.writeFile(path.join(config.fullPath, 'src/styles.css'), mainStyles);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
module.exports = {
|
|
360
|
+
createRouting,
|
|
361
|
+
createAppConfig,
|
|
362
|
+
createAppComponent,
|
|
363
|
+
createStyles,
|
|
364
|
+
};
|