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.
Files changed (47) hide show
  1. package/CHANGELOG.md +81 -350
  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 +52 -4060
  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,359 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Create VS Code settings and extensions
6
+ */
7
+ async function createVSCodeSettings(config) {
8
+ // Create .vscode directory
9
+ await fs.ensureDir(path.join(config.fullPath, '.vscode'));
10
+
11
+ // VSCode settings for format on save
12
+ const vscodeSettings = {
13
+ 'editor.formatOnSave': true,
14
+ 'editor.defaultFormatter': 'esbenp.prettier-vscode',
15
+ 'editor.codeActionsOnSave': {
16
+ 'source.fixAll.eslint': 'explicit',
17
+ },
18
+ '[typescript]': {
19
+ 'editor.formatOnSave': true,
20
+ 'editor.defaultFormatter': 'esbenp.prettier-vscode',
21
+ },
22
+ '[html]': {
23
+ 'editor.formatOnSave': true,
24
+ 'editor.defaultFormatter': 'esbenp.prettier-vscode',
25
+ },
26
+ '[css]': {
27
+ 'editor.formatOnSave': true,
28
+ 'editor.defaultFormatter': 'esbenp.prettier-vscode',
29
+ },
30
+ '[json]': {
31
+ 'editor.formatOnSave': true,
32
+ 'editor.defaultFormatter': 'esbenp.prettier-vscode',
33
+ },
34
+ '[jsonc]': {
35
+ 'editor.formatOnSave': true,
36
+ 'editor.defaultFormatter': 'esbenp.prettier-vscode',
37
+ },
38
+ 'files.eol': '\n',
39
+ 'files.trimTrailingWhitespace': true,
40
+ 'files.insertFinalNewline': true,
41
+ 'editor.tabSize': 2,
42
+ 'prettier.requireConfig': false,
43
+ 'eslint.validate': ['javascript', 'typescript', 'html'],
44
+ // Suppress CSS linting warnings for Tailwind CSS directives
45
+ 'css.lint.unknownAtRules': 'ignore',
46
+ };
47
+
48
+ // VSCode extensions recommendations
49
+ const vscodeExtensions = {
50
+ recommendations: [
51
+ 'esbenp.prettier-vscode',
52
+ 'dbaeumer.vscode-eslint',
53
+ 'angular.ng-template',
54
+ 'bradlc.vscode-tailwindcss',
55
+ ],
56
+ };
57
+
58
+ // Write settings.json
59
+ await fs.writeFile(
60
+ path.join(config.fullPath, '.vscode/settings.json'),
61
+ JSON.stringify(vscodeSettings, null, 2)
62
+ );
63
+
64
+ // Write extensions.json
65
+ await fs.writeFile(
66
+ path.join(config.fullPath, '.vscode/extensions.json'),
67
+ JSON.stringify(vscodeExtensions, null, 2)
68
+ );
69
+ }
70
+
71
+ /**
72
+ * Update TypeScript config with path mappings
73
+ */
74
+ async function updateTsConfig(config) {
75
+ // Define path mapping configuration
76
+ const pathMappingConfig = ` "baseUrl": "./",
77
+ "paths": {
78
+ "@/*": ["src/*"],
79
+ "@app/*": ["src/app/*"],
80
+ "@core/*": ["src/app/core/*"],
81
+ "@shared/*": ["src/app/shared/*"],
82
+ "@features/*": ["src/app/features/*"],
83
+ "@layout/*": ["src/app/layout/*"],
84
+ "@environments/*": ["src/environments/*"]
85
+ },`;
86
+
87
+ // Helper function to add path mappings to a tsconfig file
88
+ const addPathMappings = (content) => {
89
+ if (content.includes('"compilerOptions": {')) {
90
+ const compilerOptionsStart =
91
+ content.indexOf('"compilerOptions": {') + '"compilerOptions": {'.length;
92
+ const beforeOptions = content.substring(0, compilerOptionsStart);
93
+ const afterOptions = content.substring(compilerOptionsStart);
94
+ return beforeOptions + '\n' + pathMappingConfig + afterOptions;
95
+ }
96
+ return content;
97
+ };
98
+
99
+ // Update tsconfig.json (base config) - CRITICAL for IDE support
100
+ const tsConfigPath = path.join(config.fullPath, 'tsconfig.json');
101
+ let tsConfigContent = await fs.readFile(tsConfigPath, 'utf8');
102
+ tsConfigContent = addPathMappings(tsConfigContent);
103
+ await fs.writeFile(tsConfigPath, tsConfigContent);
104
+
105
+ // Update tsconfig.app.json
106
+ const tsConfigAppPath = path.join(config.fullPath, 'tsconfig.app.json');
107
+ let tsConfigAppContent = await fs.readFile(tsConfigAppPath, 'utf8');
108
+ tsConfigAppContent = addPathMappings(tsConfigAppContent);
109
+ await fs.writeFile(tsConfigAppPath, tsConfigAppContent);
110
+
111
+ // Update tsconfig.spec.json
112
+ const tsConfigSpecPath = path.join(config.fullPath, 'tsconfig.spec.json');
113
+ let tsConfigSpecContent = await fs.readFile(tsConfigSpecPath, 'utf8');
114
+ tsConfigSpecContent = addPathMappings(tsConfigSpecContent);
115
+ await fs.writeFile(tsConfigSpecPath, tsConfigSpecContent);
116
+ }
117
+
118
+ /**
119
+ * Configure angular.json for ESLint
120
+ */
121
+ async function configureAngularJson(config) {
122
+ const angularJsonPath = path.join(config.fullPath, 'angular.json');
123
+ const angularJson = JSON.parse(await fs.readFile(angularJsonPath, 'utf8'));
124
+
125
+ // Add lint architect target
126
+ if (angularJson.projects && angularJson.projects[config.projectName]) {
127
+ angularJson.projects[config.projectName].architect.lint = {
128
+ builder: '@angular-eslint/builder:lint',
129
+ options: {
130
+ lintFilePatterns: ['src/**/*.ts', 'src/**/*.html'],
131
+ },
132
+ };
133
+ }
134
+
135
+ await fs.writeFile(angularJsonPath, JSON.stringify(angularJson, null, 2));
136
+ }
137
+
138
+ /**
139
+ * Install linting packages
140
+ */
141
+ async function installLintingPackages(config) {
142
+ const execa = require('execa');
143
+
144
+ try {
145
+ const packages = [
146
+ '@eslint/js@^9.23.0',
147
+ 'eslint@^9.23.0',
148
+ 'angular-eslint@19.3.0',
149
+ 'typescript-eslint@^8.20.0',
150
+ 'prettier@^3.5.3',
151
+ 'eslint-config-prettier@^10.1.2',
152
+ 'prettier-plugin-tailwindcss@^0.6.11',
153
+ 'simple-git-hooks@^2.11.1',
154
+ ];
155
+
156
+ await execa.command(`npm install ${packages.join(' ')} --save-dev`, {
157
+ cwd: config.fullPath,
158
+ stdio: 'pipe',
159
+ });
160
+ } catch (error) {
161
+ // Silently fail - packages will be installed when user runs npm install
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Setup linting (ESLint, Prettier, simple-git-hooks)
167
+ */
168
+ async function setupLinting(config) {
169
+ // Read current package.json
170
+ const packageJsonPath = path.join(config.fullPath, 'package.json');
171
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
172
+
173
+ // Add icons and PWA to regular dependencies
174
+ if (!packageJson.dependencies) {
175
+ packageJson.dependencies = {};
176
+ }
177
+ packageJson.dependencies['@ng-icons/core'] = '^29.7.0';
178
+ packageJson.dependencies['@ng-icons/heroicons'] = '^29.7.0';
179
+ packageJson.dependencies['@angular/service-worker'] = packageJson.dependencies['@angular/core'];
180
+
181
+ // Add linting dev dependencies
182
+ const lintingDependencies = {
183
+ '@eslint/js': '^9.23.0',
184
+ eslint: '^9.23.0',
185
+ 'angular-eslint': '19.3.0',
186
+ 'typescript-eslint': '^8.20.0',
187
+ prettier: '^3.5.3',
188
+ 'eslint-config-prettier': '^10.1.2',
189
+ 'prettier-plugin-tailwindcss': '^0.6.11',
190
+ 'simple-git-hooks': '^2.11.1',
191
+ };
192
+
193
+ // Add enhanced scripts
194
+ const enhancedScripts = {
195
+ lint: 'ng lint',
196
+ 'lint:fix': 'ng lint --fix',
197
+ format: 'prettier --write "**/*.{ts,html,css,json,md}"',
198
+ 'format:check': 'prettier --check "**/*.{ts,html,css,json,md}"',
199
+ 'format:ts': 'prettier --write "**/*.ts"',
200
+ 'code:check': 'npm run format:check && npm run lint',
201
+ 'code:fix': 'npm run format && npm run lint:fix',
202
+ prepare: 'simple-git-hooks',
203
+ };
204
+
205
+ // Add prettier configuration
206
+ const prettierConfig = {
207
+ printWidth: 100,
208
+ singleQuote: true,
209
+ trailingComma: 'es5',
210
+ tabWidth: 2,
211
+ semi: true,
212
+ arrowParens: 'always',
213
+ endOfLine: 'lf',
214
+ plugins: ['prettier-plugin-tailwindcss'],
215
+ overrides: [
216
+ {
217
+ files: '*.html',
218
+ options: {
219
+ parser: 'angular',
220
+ },
221
+ },
222
+ ],
223
+ };
224
+
225
+ // simple-git-hooks configuration (lightweight pre-commit hooks)
226
+ const simpleGitHooksConfig = {
227
+ 'pre-commit': 'npm run lint',
228
+ };
229
+
230
+ // Update package.json
231
+ packageJson.devDependencies = {
232
+ ...packageJson.devDependencies,
233
+ ...lintingDependencies,
234
+ };
235
+ packageJson.scripts = { ...packageJson.scripts, ...enhancedScripts };
236
+ packageJson.prettier = prettierConfig;
237
+ packageJson['simple-git-hooks'] = simpleGitHooksConfig;
238
+
239
+ // Write updated package.json
240
+ await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
241
+
242
+ // Configure angular.json for ESLint
243
+ await configureAngularJson(config);
244
+
245
+ // Create ESLint configuration (ESLint 9+ flat config)
246
+ const eslintConfig = `// @ts-check
247
+ const eslint = require("@eslint/js");
248
+ const tseslint = require("typescript-eslint");
249
+ const angular = require("angular-eslint");
250
+ const eslintConfigPrettier = require("eslint-config-prettier");
251
+
252
+ module.exports = tseslint.config(
253
+ {
254
+ files: ["**/*.ts"],
255
+ extends: [
256
+ eslint.configs.recommended,
257
+ ...tseslint.configs.recommended,
258
+ ...tseslint.configs.stylistic,
259
+ ...angular.configs.tsRecommended,
260
+ eslintConfigPrettier,
261
+ ],
262
+ processor: angular.processInlineTemplates,
263
+ rules: {
264
+ "@angular-eslint/directive-selector": [
265
+ "error",
266
+ {
267
+ type: "attribute",
268
+ prefix: "app",
269
+ style: "camelCase",
270
+ },
271
+ ],
272
+ "@angular-eslint/component-selector": [
273
+ "error",
274
+ {
275
+ type: "element",
276
+ prefix: "app",
277
+ style: "kebab-case",
278
+ },
279
+ ],
280
+ "@angular-eslint/component-class-suffix": [
281
+ "error",
282
+ {
283
+ "suffixes": ["Component", "App"]
284
+ }
285
+ ],
286
+ "@typescript-eslint/no-unused-vars": [
287
+ "error",
288
+ {
289
+ "argsIgnorePattern": "^_",
290
+ "varsIgnorePattern": "^_"
291
+ }
292
+ ],
293
+ },
294
+ },
295
+ {
296
+ files: ["**/*.html"],
297
+ extends: [
298
+ ...angular.configs.templateRecommended,
299
+ ...angular.configs.templateAccessibility,
300
+ ],
301
+ rules: {},
302
+ }
303
+ );
304
+ `;
305
+
306
+ await fs.writeFile(path.join(config.fullPath, 'eslint.config.js'), eslintConfig);
307
+
308
+ // Create .prettierignore file
309
+ const prettierIgnore = `# Build outputs
310
+ dist/
311
+ coverage/
312
+ node_modules/
313
+
314
+ # Generated files
315
+ *.d.ts
316
+ `;
317
+
318
+ await fs.writeFile(path.join(config.fullPath, '.prettierignore'), prettierIgnore);
319
+
320
+ // Create .prettierrc.json file for better IDE support
321
+ await fs.writeFile(
322
+ path.join(config.fullPath, '.prettierrc.json'),
323
+ JSON.stringify(prettierConfig, null, 2)
324
+ );
325
+
326
+ // Install linting packages if not skipping install
327
+ if (!config.skipInstall) {
328
+ await installLintingPackages(config);
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Format code using Prettier
334
+ */
335
+ async function formatCode(config) {
336
+ const execa = require('execa');
337
+
338
+ try {
339
+ // Run prettier to format all generated files (including config files)
340
+ await execa.command(
341
+ 'npx prettier --write "**/*.{ts,html,css,json}" --ignore-path .gitignore --log-level silent',
342
+ {
343
+ cwd: config.fullPath,
344
+ stdio: 'pipe',
345
+ }
346
+ );
347
+ } catch (error) {
348
+ // If prettier fails, it's not critical - user can run it manually
349
+ }
350
+ }
351
+
352
+ module.exports = {
353
+ createVSCodeSettings,
354
+ updateTsConfig,
355
+ configureAngularJson,
356
+ installLintingPackages,
357
+ setupLinting,
358
+ formatCode,
359
+ };
@@ -0,0 +1,103 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Setup PWA (Progressive Web App) configuration
6
+ */
7
+ async function setupPWA(config) {
8
+ // Create manifest.json
9
+ const manifest = {
10
+ name: config.projectName,
11
+ short_name: config.projectName,
12
+ theme_color: '#3b82f6',
13
+ background_color: '#ffffff',
14
+ display: 'standalone',
15
+ scope: '/',
16
+ start_url: '/',
17
+ description: `${config.projectName} - Built with Angular and Tailwind CSS`,
18
+ icons: [
19
+ {
20
+ src: 'assets/icons/android-chrome-192x192.svg',
21
+ sizes: '192x192',
22
+ type: 'image/svg+xml',
23
+ purpose: 'maskable any',
24
+ },
25
+ {
26
+ src: 'assets/icons/android-chrome-512x512.svg',
27
+ sizes: '512x512',
28
+ type: 'image/svg+xml',
29
+ purpose: 'maskable any',
30
+ },
31
+ ],
32
+ };
33
+
34
+ // Write manifest to public directory (new Angular structure)
35
+ await fs.ensureDir(path.join(config.fullPath, 'public'));
36
+ await fs.writeFile(
37
+ path.join(config.fullPath, 'public/manifest.webmanifest'),
38
+ JSON.stringify(manifest, null, 2)
39
+ );
40
+
41
+ // Create ngsw-config.json (Service Worker configuration)
42
+ const ngswConfig = {
43
+ $schema: './node_modules/@angular/service-worker/config/schema.json',
44
+ index: '/index.html',
45
+ assetGroups: [
46
+ {
47
+ name: 'app',
48
+ installMode: 'prefetch',
49
+ resources: {
50
+ files: ['/favicon.ico', '/index.html', '/manifest.webmanifest', '/*.css', '/*.js'],
51
+ },
52
+ },
53
+ {
54
+ name: 'assets',
55
+ installMode: 'lazy',
56
+ updateMode: 'prefetch',
57
+ resources: {
58
+ files: [
59
+ '/assets/**',
60
+ '/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)',
61
+ ],
62
+ },
63
+ },
64
+ ],
65
+ dataGroups: [
66
+ {
67
+ name: 'api',
68
+ urls: ['https://api.example.com/**'],
69
+ cacheConfig: {
70
+ maxSize: 100,
71
+ maxAge: '1h',
72
+ timeout: '10s',
73
+ strategy: 'freshness',
74
+ },
75
+ },
76
+ ],
77
+ };
78
+
79
+ await fs.writeFile(
80
+ path.join(config.fullPath, 'ngsw-config.json'),
81
+ JSON.stringify(ngswConfig, null, 2)
82
+ );
83
+
84
+ // Update index.html to include PWA assets
85
+ const indexPath = path.join(config.fullPath, 'src/index.html');
86
+ let indexContent = await fs.readFile(indexPath, 'utf8');
87
+
88
+ // Add manifest link and theme color meta tag if not present
89
+ if (!indexContent.includes('manifest.webmanifest')) {
90
+ indexContent = indexContent.replace(
91
+ '</head>',
92
+ ` <link rel="manifest" href="manifest.webmanifest">
93
+ <meta name="theme-color" content="#3b82f6">
94
+ <link rel="apple-touch-icon" href="assets/icons/apple-touch-icon.svg">
95
+ </head>`
96
+ );
97
+ await fs.writeFile(indexPath, indexContent);
98
+ }
99
+ }
100
+
101
+ module.exports = {
102
+ setupPWA,
103
+ };