create-ng-tailwind 3.0.1 → 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.
Files changed (47) hide show
  1. package/CHANGELOG.md +81 -344
  2. package/README.md +93 -157
  3. package/lib/cli/index.js +29 -3
  4. package/lib/cli/interactive.js +26 -1
  5. package/lib/managers/ProjectManager.js +0 -4
  6. package/lib/templates/base/components.js +243 -0
  7. package/lib/templates/base/index.js +207 -0
  8. package/lib/templates/base/infrastructure.js +314 -0
  9. package/lib/templates/base/linting.js +359 -0
  10. package/lib/templates/base/pwa.js +103 -0
  11. package/lib/templates/base/services.js +362 -0
  12. package/lib/templates/blog/app.js +250 -0
  13. package/lib/templates/blog/components.js +360 -0
  14. package/lib/templates/blog/i18n.js +77 -0
  15. package/lib/templates/blog/index.js +126 -0
  16. package/lib/templates/blog/pages.js +554 -0
  17. package/lib/templates/blog/services.js +390 -0
  18. package/lib/templates/dashboard/app.js +320 -0
  19. package/lib/templates/dashboard/charts.js +305 -0
  20. package/lib/templates/dashboard/components.js +410 -0
  21. package/lib/templates/dashboard/i18n.js +340 -0
  22. package/lib/templates/dashboard/index.js +141 -0
  23. package/lib/templates/dashboard/layout.js +310 -0
  24. package/lib/templates/dashboard/pages.js +681 -0
  25. package/lib/templates/ecommerce/app.js +315 -0
  26. package/lib/templates/ecommerce/components.js +496 -0
  27. package/lib/templates/ecommerce/i18n.js +389 -0
  28. package/lib/templates/ecommerce/index.js +152 -0
  29. package/lib/templates/ecommerce/layout.js +270 -0
  30. package/lib/templates/ecommerce/pages.js +969 -0
  31. package/lib/templates/ecommerce/services.js +300 -0
  32. package/lib/templates/index.js +12 -0
  33. package/lib/templates/landing/index.js +1117 -0
  34. package/lib/templates/portfolio/index.js +1160 -0
  35. package/lib/templates/saas/index.js +1371 -0
  36. package/lib/templates/starter/app.js +364 -0
  37. package/lib/templates/starter/i18n.js +856 -0
  38. package/lib/templates/starter/index.js +53 -4055
  39. package/lib/templates/starter/layout.js +852 -0
  40. package/lib/templates/starter/pages.js +1241 -0
  41. package/package.json +1 -1
  42. package/lib/templates/starter/features.js +0 -867
  43. package/lib/utils/ai-config.js +0 -641
  44. /package/lib/templates/{starter → base}/advanced-features.js +0 -0
  45. /package/lib/templates/{starter → base}/seo-assets.js +0 -0
  46. /package/lib/templates/{starter → base}/seo-features.js +0 -0
  47. /package/lib/templates/{starter → base}/ui-features.js +0 -0
@@ -0,0 +1,389 @@
1
+ const fs = require("fs-extra");
2
+ const path = require("path");
3
+
4
+ /**
5
+ * Ecommerce i18n (Internationalization)
6
+ * - TranslationService
7
+ * - English translations
8
+ * - Arabic translations (RTL)
9
+ */
10
+
11
+ async function createI18n(config) {
12
+ // Translation Service
13
+ const translationService = `import { Injectable, inject, signal, effect, PLATFORM_ID } from '@angular/core';
14
+ import { isPlatformBrowser } from '@angular/common';
15
+ import { TranslateService } from '@ngx-translate/core';
16
+ import { StorageService } from '@core/services/storage.service';
17
+
18
+ export type SupportedLanguage = 'en' | 'ar';
19
+
20
+ export interface LanguageOption {
21
+ code: SupportedLanguage;
22
+ name: string;
23
+ nativeName: string;
24
+ dir: 'ltr' | 'rtl';
25
+ flag: string;
26
+ }
27
+
28
+ @Injectable({
29
+ providedIn: 'root'
30
+ })
31
+ export class TranslationService {
32
+ private translateService = inject(TranslateService);
33
+ private storage = inject(StorageService);
34
+ private platformId = inject(PLATFORM_ID);
35
+ private isBrowser = isPlatformBrowser(this.platformId);
36
+
37
+ public currentLang = signal<SupportedLanguage>('en');
38
+ public textDirection = signal<'ltr' | 'rtl'>('ltr');
39
+
40
+ public readonly languages: LanguageOption[] = [
41
+ { code: 'en', name: 'English', nativeName: 'English', dir: 'ltr', flag: '🇺🇸' },
42
+ { code: 'ar', name: 'Arabic', nativeName: 'العربية', dir: 'rtl', flag: '🇸🇦' }
43
+ ];
44
+
45
+ constructor() {
46
+ this.translateService.setDefaultLang('en');
47
+ const savedLang = this.getSavedLanguage();
48
+ const browserLang = this.translateService.getBrowserLang() as SupportedLanguage;
49
+ const initialLang = savedLang || (this.isSupportedLanguage(browserLang) ? browserLang : 'en');
50
+ this.setLanguage(initialLang);
51
+
52
+ if (this.isBrowser) {
53
+ effect(() => {
54
+ const dir = this.textDirection();
55
+ document.documentElement.setAttribute('dir', dir);
56
+ document.documentElement.setAttribute('lang', this.currentLang());
57
+ });
58
+ }
59
+ }
60
+
61
+ setLanguage(lang: SupportedLanguage): void {
62
+ if (!this.isSupportedLanguage(lang)) {
63
+ lang = 'en';
64
+ }
65
+ this.translateService.use(lang);
66
+ this.currentLang.set(lang);
67
+ const languageOption = this.languages.find(l => l.code === lang);
68
+ if (languageOption) {
69
+ this.textDirection.set(languageOption.dir);
70
+ }
71
+ this.saveLanguage(lang);
72
+ }
73
+
74
+ getCurrentLanguage(): SupportedLanguage {
75
+ return this.currentLang();
76
+ }
77
+
78
+ getLanguageOption(code: SupportedLanguage): LanguageOption | undefined {
79
+ return this.languages.find(lang => lang.code === code);
80
+ }
81
+
82
+ isSupportedLanguage(lang: string): lang is SupportedLanguage {
83
+ return ['en', 'ar'].includes(lang);
84
+ }
85
+
86
+ toggleLanguage(): void {
87
+ const newLang: SupportedLanguage = this.currentLang() === 'en' ? 'ar' : 'en';
88
+ this.setLanguage(newLang);
89
+ }
90
+
91
+ instant(key: string, params?: object): string {
92
+ return this.translateService.instant(key, params);
93
+ }
94
+
95
+ get(key: string | string[], params?: object) {
96
+ return this.translateService.get(key, params);
97
+ }
98
+
99
+ private saveLanguage(lang: SupportedLanguage): void {
100
+ this.storage.setItem('preferred_language', lang);
101
+ }
102
+
103
+ private getSavedLanguage(): SupportedLanguage | null {
104
+ const saved = this.storage.getItem('preferred_language');
105
+ return saved && this.isSupportedLanguage(saved) ? saved : null;
106
+ }
107
+ }`;
108
+
109
+ await fs.writeFile(
110
+ path.join(config.fullPath, "src/app/core/i18n/translation.service.ts"),
111
+ translationService
112
+ );
113
+
114
+ // English translations
115
+ const enTranslations = {
116
+ nav: {
117
+ home: "Home",
118
+ shop: "Shop",
119
+ cart: "Cart",
120
+ wishlist: "Wishlist",
121
+ account: "Account",
122
+ },
123
+ home: {
124
+ heroTitle: "Discover Your Style",
125
+ heroSubtitle:
126
+ "Shop the latest trends with free shipping on orders over $100. Quality products, unbeatable prices.",
127
+ shopNow: "Shop Now",
128
+ viewDeals: "View Deals",
129
+ saleBadge: "Flash Sale - Up to 50% Off!",
130
+ upTo: "Up to",
131
+ newCollection: "New Collection",
132
+ justArrived: "Just Arrived",
133
+ happyCustomers: "Happy Customers",
134
+ products: "Products",
135
+ rating: "Rating",
136
+ featuredProducts: "Featured Products",
137
+ featuredSubtitle: "Handpicked just for you",
138
+ viewAll: "View All",
139
+ seeMore: "See More",
140
+ shopByCategory: "Shop by Category",
141
+ categorySubtitle: "Find what you need in our curated collections",
142
+ newArrivals: "New Arrivals",
143
+ newArrivalsSubtitle: "Fresh styles just dropped",
144
+ freeShipping: "Free Shipping",
145
+ freeShippingDesc: "On orders over $100",
146
+ securePayment: "Secure Payment",
147
+ securePaymentDesc: "100% secure checkout",
148
+ easyReturns: "Easy Returns",
149
+ easyReturnsDesc: "30-day return policy",
150
+ support: "24/7 Support",
151
+ supportDesc: "Dedicated support",
152
+ limitedOffer: "Limited Time Offer",
153
+ promoTitle: "Summer Sale is Here!",
154
+ promoSubtitle: "Get up to 50% off on selected items. Don't miss out!",
155
+ exclusive: "Exclusive Offer",
156
+ newsletterTitle: "Get 15% Off Your First Order",
157
+ newsletterSubtitle: "Subscribe to our newsletter and be the first to know about new arrivals, sales, and exclusive deals.",
158
+ emailPlaceholder: "Enter your email",
159
+ subscribe: "Subscribe",
160
+ privacyNote: "By subscribing, you agree to our Privacy Policy. Unsubscribe anytime.",
161
+ testimonials: "What Our Customers Say",
162
+ testimonialsSubtitle: "Real reviews from real customers",
163
+ },
164
+ shop: {
165
+ title: "All Products",
166
+ subtitle: "Browse our collection of premium products",
167
+ filters: "Filters",
168
+ clearAll: "Clear All",
169
+ category: "Category",
170
+ allCategories: "All Categories",
171
+ priceRange: "Price Range",
172
+ min: "Min",
173
+ max: "Max",
174
+ sortBy: "Sort By",
175
+ newest: "Newest",
176
+ priceLowHigh: "Price: Low to High",
177
+ priceHighLow: "Price: High to Low",
178
+ topRated: "Top Rated",
179
+ productsFound: "products found",
180
+ },
181
+ product: {
182
+ addToCart: "Add to Cart",
183
+ features: "Features",
184
+ inStock: "In Stock",
185
+ outOfStock: "Out of Stock",
186
+ relatedProducts: "You May Also Like",
187
+ },
188
+ cart: {
189
+ title: "Shopping Cart",
190
+ orderSummary: "Order Summary",
191
+ subtotal: "Subtotal",
192
+ shipping: "Shipping",
193
+ tax: "Tax",
194
+ total: "Total",
195
+ free: "Free",
196
+ checkout: "Proceed to Checkout",
197
+ continueShopping: "Continue Shopping",
198
+ empty: "Your cart is empty",
199
+ emptyDesc: "Looks like you haven't added anything to your cart yet.",
200
+ startShopping: "Start Shopping",
201
+ qty: "Qty",
202
+ },
203
+ checkout: {
204
+ title: "Checkout",
205
+ contact: "Contact Information",
206
+ email: "Email",
207
+ phone: "Phone",
208
+ shipping: "Shipping Address",
209
+ firstName: "First Name",
210
+ lastName: "Last Name",
211
+ address: "Address",
212
+ city: "City",
213
+ zip: "ZIP Code",
214
+ payment: "Payment Method",
215
+ cardNumber: "Card Number",
216
+ expiry: "Expiry Date",
217
+ cvv: "CVV",
218
+ placeOrder: "Place Order",
219
+ secureNote: "Your payment information is secure and encrypted",
220
+ orderPlaced: "Order Placed Successfully!",
221
+ orderNumber: "Order Number",
222
+ confirmationEmail:
223
+ "A confirmation email has been sent to your email address.",
224
+ backToHome: "Back to Home",
225
+ },
226
+ wishlist: {
227
+ title: "My Wishlist",
228
+ empty: "Your wishlist is empty",
229
+ emptyDesc: "Save items you love by clicking the heart icon.",
230
+ browseProducts: "Browse Products",
231
+ },
232
+ footer: {
233
+ description:
234
+ "Your one-stop destination for quality products at great prices.",
235
+ newsletter: "Subscribe to our newsletter",
236
+ emailPlaceholder: "Enter your email",
237
+ quickLinks: "Quick Links",
238
+ support: "Support",
239
+ faq: "FAQ",
240
+ shipping: "Shipping Info",
241
+ returns: "Returns",
242
+ contact: "Contact Us",
243
+ rights: "All rights reserved.",
244
+ },
245
+ };
246
+
247
+ await fs.writeFile(
248
+ path.join(config.fullPath, "public/assets/i18n/en.json"),
249
+ JSON.stringify(enTranslations, null, 2)
250
+ );
251
+
252
+ // Arabic translations
253
+ const arTranslations = {
254
+ nav: {
255
+ home: "الرئيسية",
256
+ shop: "المتجر",
257
+ cart: "السلة",
258
+ wishlist: "المفضلة",
259
+ account: "الحساب",
260
+ },
261
+ home: {
262
+ heroTitle: "اكتشف أسلوبك",
263
+ heroSubtitle:
264
+ "تسوق أحدث الصيحات مع شحن مجاني للطلبات فوق 100$. منتجات عالية الجودة بأسعار لا تقبل المنافسة.",
265
+ shopNow: "تسوق الآن",
266
+ viewDeals: "عرض العروض",
267
+ saleBadge: "تخفيضات - خصم يصل إلى 50%!",
268
+ upTo: "يصل إلى",
269
+ newCollection: "مجموعة جديدة",
270
+ justArrived: "وصلت حديثاً",
271
+ happyCustomers: "عميل سعيد",
272
+ products: "منتج",
273
+ rating: "التقييم",
274
+ featuredProducts: "منتجات مميزة",
275
+ featuredSubtitle: "مختارة خصيصاً لك",
276
+ viewAll: "عرض الكل",
277
+ seeMore: "عرض المزيد",
278
+ shopByCategory: "تسوق حسب الفئة",
279
+ categorySubtitle: "اعثر على ما تحتاجه في مجموعاتنا المختارة",
280
+ newArrivals: "وصل حديثاً",
281
+ newArrivalsSubtitle: "أحدث الصيحات وصلت للتو",
282
+ freeShipping: "شحن مجاني",
283
+ freeShippingDesc: "للطلبات فوق 100$",
284
+ securePayment: "دفع آمن",
285
+ securePaymentDesc: "دفع مشفر 100%",
286
+ easyReturns: "إرجاع سهل",
287
+ easyReturnsDesc: "سياسة إرجاع 30 يوم",
288
+ support: "دعم على مدار الساعة",
289
+ supportDesc: "دعم مخصص",
290
+ limitedOffer: "عرض محدود",
291
+ promoTitle: "تخفيضات الصيف هنا!",
292
+ promoSubtitle: "احصل على خصم يصل إلى 50% على منتجات مختارة. لا تفوت الفرصة!",
293
+ exclusive: "عرض حصري",
294
+ newsletterTitle: "احصل على خصم 15% على طلبك الأول",
295
+ newsletterSubtitle: "اشترك في نشرتنا البريدية وكن أول من يعرف عن المنتجات الجديدة والعروض الحصرية.",
296
+ emailPlaceholder: "أدخل بريدك الإلكتروني",
297
+ subscribe: "اشترك",
298
+ privacyNote: "بالاشتراك، أنت توافق على سياسة الخصوصية. يمكنك إلغاء الاشتراك في أي وقت.",
299
+ testimonials: "ماذا يقول عملاؤنا",
300
+ testimonialsSubtitle: "تقييمات حقيقية من عملاء حقيقيين",
301
+ },
302
+ shop: {
303
+ title: "جميع المنتجات",
304
+ subtitle: "تصفح مجموعتنا من المنتجات المميزة",
305
+ filters: "الفلاتر",
306
+ clearAll: "مسح الكل",
307
+ category: "الفئة",
308
+ allCategories: "جميع الفئات",
309
+ priceRange: "نطاق السعر",
310
+ min: "الحد الأدنى",
311
+ max: "الحد الأقصى",
312
+ sortBy: "ترتيب حسب",
313
+ newest: "الأحدث",
314
+ priceLowHigh: "السعر: من الأقل للأعلى",
315
+ priceHighLow: "السعر: من الأعلى للأقل",
316
+ topRated: "الأعلى تقييماً",
317
+ productsFound: "منتج",
318
+ },
319
+ product: {
320
+ addToCart: "أضف للسلة",
321
+ features: "المميزات",
322
+ inStock: "متوفر",
323
+ outOfStock: "غير متوفر",
324
+ relatedProducts: "قد يعجبك أيضاً",
325
+ },
326
+ cart: {
327
+ title: "سلة التسوق",
328
+ orderSummary: "ملخص الطلب",
329
+ subtotal: "المجموع الفرعي",
330
+ shipping: "الشحن",
331
+ tax: "الضريبة",
332
+ total: "الإجمالي",
333
+ free: "مجاني",
334
+ checkout: "متابعة الدفع",
335
+ continueShopping: "متابعة التسوق",
336
+ empty: "سلتك فارغة",
337
+ emptyDesc: "يبدو أنك لم تضف أي منتج إلى سلتك بعد.",
338
+ startShopping: "ابدأ التسوق",
339
+ qty: "الكمية",
340
+ },
341
+ checkout: {
342
+ title: "إتمام الطلب",
343
+ contact: "معلومات التواصل",
344
+ email: "البريد الإلكتروني",
345
+ phone: "الهاتف",
346
+ shipping: "عنوان الشحن",
347
+ firstName: "الاسم الأول",
348
+ lastName: "اسم العائلة",
349
+ address: "العنوان",
350
+ city: "المدينة",
351
+ zip: "الرمز البريدي",
352
+ payment: "طريقة الدفع",
353
+ cardNumber: "رقم البطاقة",
354
+ expiry: "تاريخ الانتهاء",
355
+ cvv: "رمز CVV",
356
+ placeOrder: "تأكيد الطلب",
357
+ secureNote: "معلومات الدفع الخاصة بك آمنة ومشفرة",
358
+ orderPlaced: "تم تأكيد طلبك بنجاح!",
359
+ orderNumber: "رقم الطلب",
360
+ confirmationEmail: "تم إرسال رسالة تأكيد إلى بريدك الإلكتروني.",
361
+ backToHome: "العودة للرئيسية",
362
+ },
363
+ wishlist: {
364
+ title: "قائمة المفضلة",
365
+ empty: "قائمة المفضلة فارغة",
366
+ emptyDesc: "احفظ المنتجات المفضلة بالضغط على أيقونة القلب.",
367
+ browseProducts: "تصفح المنتجات",
368
+ },
369
+ footer: {
370
+ description: "وجهتك الأولى لمنتجات عالية الجودة بأسعار رائعة.",
371
+ newsletter: "اشترك في نشرتنا البريدية",
372
+ emailPlaceholder: "أدخل بريدك الإلكتروني",
373
+ quickLinks: "روابط سريعة",
374
+ support: "الدعم",
375
+ faq: "الأسئلة الشائعة",
376
+ shipping: "معلومات الشحن",
377
+ returns: "الإرجاع",
378
+ contact: "اتصل بنا",
379
+ rights: "جميع الحقوق محفوظة.",
380
+ },
381
+ };
382
+
383
+ await fs.writeFile(
384
+ path.join(config.fullPath, "public/assets/i18n/ar.json"),
385
+ JSON.stringify(arTranslations, null, 2)
386
+ );
387
+ }
388
+
389
+ module.exports = { createI18n };
@@ -0,0 +1,152 @@
1
+ const fs = require("fs-extra");
2
+ const path = require("path");
3
+ const base = require("../base");
4
+
5
+ // Import modular components
6
+ const { createComponents } = require("./components");
7
+ const { createLayout } = require("./layout");
8
+ const { createServices } = require("./services");
9
+ const { createPages } = require("./pages");
10
+ const { createI18n } = require("./i18n");
11
+ const {
12
+ createRouting,
13
+ createAppConfig,
14
+ createAppComponent,
15
+ createStyles,
16
+ installI18nPackages,
17
+ } = require("./app");
18
+
19
+ /**
20
+ * Ecommerce Template
21
+ * Extends base template with ecommerce UI:
22
+ * - Product listings with filters
23
+ * - Product detail pages
24
+ * - Shopping cart
25
+ * - Checkout flow
26
+ * - Header with cart icon
27
+ * - Footer with links
28
+ *
29
+ * File Structure:
30
+ * - components.js: Shared components (Rating, ProductCard, CartItem, etc.)
31
+ * - layout.js: Header and Footer components
32
+ * - services.js: ProductService, CartService, WishlistService
33
+ * - pages.js: Home, Products, ProductDetail, Cart, Checkout, Wishlist
34
+ * - i18n.js: TranslationService and translation files (EN/AR)
35
+ * - app.js: Routes, AppConfig, AppComponent, Styles
36
+ */
37
+ const ecommerce = {
38
+ info: {
39
+ name: "Ecommerce",
40
+ description: "Online store with products, cart, and checkout",
41
+ features: [
42
+ ...base.info.features,
43
+ "Product listing with grid/list views",
44
+ "Product filtering and sorting",
45
+ "Product detail page with gallery",
46
+ "Shopping cart with quantity controls",
47
+ "Checkout flow with form validation",
48
+ "Header with navigation and cart icon",
49
+ "Footer with links and newsletter",
50
+ "i18n translation support (English & Arabic) with RTL",
51
+ "Responsive mobile-first design",
52
+ "Product search functionality",
53
+ "Category navigation",
54
+ "Wishlist functionality",
55
+ ],
56
+ },
57
+
58
+ async apply(config, spinner) {
59
+ this.spinner = spinner;
60
+ const chalk = require("chalk");
61
+
62
+ const completeStep = (message) => {
63
+ if (spinner) {
64
+ spinner.stop();
65
+ console.log(chalk.green(" ✔") + chalk.white(" " + message));
66
+ spinner.start();
67
+ }
68
+ };
69
+
70
+ // Step 1: Apply base template (infrastructure)
71
+ if (spinner) spinner.update("Setting up base infrastructure...");
72
+ await base.apply(config, null);
73
+
74
+ // Step 2: Create ecommerce-specific directories
75
+ if (spinner) spinner.update("Setting up ecommerce UI...");
76
+ await this.createEcommerceDirectories(config);
77
+
78
+ // Step 3: Create shared ecommerce components
79
+ await createComponents(config);
80
+
81
+ // Step 4: Create layout (header + footer)
82
+ await createLayout(config);
83
+
84
+ // Step 5: Create ecommerce pages
85
+ await createPages(config);
86
+
87
+ // Step 6: Create services (cart, products)
88
+ await createServices(config);
89
+
90
+ // Step 7: Create i18n translations
91
+ await createI18n(config);
92
+
93
+ // Step 8: Create routing, app config, app component, styles
94
+ await createRouting(config);
95
+ await createAppConfig(config);
96
+ await createAppComponent(config);
97
+ await createStyles(config);
98
+
99
+ // Step 9: Install i18n packages
100
+ await installI18nPackages(config);
101
+
102
+ // Step 10: Format code
103
+ await base.formatCode(config);
104
+
105
+ if (spinner) spinner.stop();
106
+
107
+ // Show summary
108
+ console.log("");
109
+ completeStep("Angular 20+ project created");
110
+ completeStep("Tailwind CSS v4 configured");
111
+ completeStep("Ecommerce layout with header/footer");
112
+ completeStep("Product listing with filters");
113
+ completeStep("Shopping cart functionality");
114
+ completeStep("Checkout flow");
115
+ completeStep("i18n translation support (English & Arabic)");
116
+ completeStep("RTL/LTR language direction support");
117
+ completeStep("HTTP interceptors (Auth, Error, Loading, Cache)");
118
+ completeStep("Core services (Cart, Products, + base services)");
119
+ completeStep("ESLint + Prettier + simple-git-hooks");
120
+ console.log("");
121
+ },
122
+
123
+ async createEcommerceDirectories(config) {
124
+ const directories = [
125
+ "src/app/layout/header",
126
+ "src/app/layout/footer",
127
+ "src/app/shared/components/product-card",
128
+ "src/app/shared/components/product-grid",
129
+ "src/app/shared/components/cart-item",
130
+ "src/app/shared/components/quantity-selector",
131
+ "src/app/shared/components/price-display",
132
+ "src/app/shared/components/rating",
133
+ "src/app/shared/components/product-filters",
134
+ "src/app/features/home",
135
+ "src/app/features/products",
136
+ "src/app/features/product-detail",
137
+ "src/app/features/cart",
138
+ "src/app/features/checkout",
139
+ "src/app/features/wishlist",
140
+ "src/app/core/services",
141
+ "src/app/core/i18n",
142
+ "public/assets/i18n",
143
+ "public/assets/images/products",
144
+ ];
145
+
146
+ for (const dir of directories) {
147
+ await fs.ensureDir(path.join(config.fullPath, dir));
148
+ }
149
+ },
150
+ };
151
+
152
+ module.exports = ecommerce;