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,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
|
+
};
|