@julien-lin/universal-pwa-cli 1.3.14 → 1.3.16

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/README.fr.md CHANGED
@@ -17,19 +17,7 @@ Cette commande va :
17
17
  - Générer tous les assets PWA (icônes, manifest, service worker)
18
18
  - Injecter les meta tags dans vos fichiers HTML
19
19
 
20
- Aucune installation globale nécessaire !
21
-
22
- ## Installation
23
-
24
- ```bash
25
- npm install -g @julien-lin/universal-pwa-cli
26
- ```
27
-
28
- Ou avec pnpm :
29
-
30
- ```bash
31
- pnpm add -g @julien-lin/universal-pwa-cli
32
- ```
20
+ Aucune installation globale npx est la méthode recommandée pour exécuter le CLI.
33
21
 
34
22
  ## Utilisation
35
23
 
@@ -50,7 +38,7 @@ pnpm build
50
38
  yarn build
51
39
 
52
40
  # 2. Puis initialiser la PWA (le CLI détectera automatiquement dist/)
53
- universal-pwa init --output-dir dist
41
+ npx @julien-lin/universal-pwa-cli init --output-dir dist
54
42
  ```
55
43
 
56
44
  **Pourquoi ?** Le service worker doit precacher tous vos assets buildés (JS/CSS avec hash). Si vous initialisez avant de builder, le service worker ne connaîtra pas les noms de fichiers hashés.
@@ -62,7 +50,7 @@ Le CLI détecte automatiquement le répertoire `dist/` pour les projets React/Vi
62
50
  Exécutez simplement sans arguments pour lancer les prompts interactifs :
63
51
 
64
52
  ```bash
65
- universal-pwa init
53
+ npx @julien-lin/universal-pwa-cli init
66
54
  ```
67
55
 
68
56
  Le CLI vous guidera à travers :
@@ -76,7 +64,7 @@ Le CLI vous guidera à travers :
76
64
  #### Mode Ligne de Commande
77
65
 
78
66
  ```bash
79
- universal-pwa init [options]
67
+ npx @julien-lin/universal-pwa-cli init [options]
80
68
  ```
81
69
 
82
70
  **Options :**
@@ -98,23 +86,23 @@ universal-pwa init [options]
98
86
  ```bash
99
87
  # Pour un build de production (React/Vite)
100
88
  npm run build
101
- universal-pwa init --output-dir dist --icon-source ./logo.png
89
+ npx @julien-lin/universal-pwa-cli init --output-dir dist --icon-source ./logo.png
102
90
 
103
91
  # Pour le développement ou sites statiques
104
- universal-pwa init \
92
+ npx @julien-lin/universal-pwa-cli init \
105
93
  --name "Mon Application" \
106
94
  --short-name "MonApp" \
107
95
  --icon-source ./logo.png \
108
96
  --theme-color "#2c3e50"
109
97
 
110
98
  # Pour un déploiement sous un sous-chemin
111
- universal-pwa init \
99
+ npx @julien-lin/universal-pwa-cli init \
112
100
  --name "CreativeHub" \
113
101
  --output-dir public \
114
102
  --base-path "/creativehub/"
115
103
 
116
104
  # Pour une PWA basée sur une API
117
- universal-pwa init \
105
+ npx @julien-lin/universal-pwa-cli init \
118
106
  --name "PWA API" \
119
107
  --output-dir dist \
120
108
  --base-path "/api/pwa/"
@@ -151,7 +139,7 @@ Cela garantit:
151
139
 
152
140
  ```bash
153
141
  npm run build
154
- universal-pwa init \
142
+ npx @julien-lin/universal-pwa-cli init \
155
143
  --name "Creative Hub" \
156
144
  --output-dir public \
157
145
  --base-path "/creative-hub/"
@@ -161,7 +149,7 @@ universal-pwa init \
161
149
 
162
150
  ```bash
163
151
  pnpm build
164
- universal-pwa init \
152
+ npx @julien-lin/universal-pwa-cli init \
165
153
  --output-dir .next \
166
154
  --base-path "/dashboard/"
167
155
  ```
@@ -169,7 +157,7 @@ universal-pwa init \
169
157
  **Site Statique sur Hébergement Partagé** - Déployé à `example.com/apps/myapp/`:
170
158
 
171
159
  ```bash
172
- universal-pwa init \
160
+ npx @julien-lin/universal-pwa-cli init \
173
161
  --name "Mon App" \
174
162
  --output-dir dist \
175
163
  --base-path "/apps/myapp/"
@@ -261,7 +249,7 @@ Le script injecté émet des événements personnalisés que vous pouvez écoute
261
249
  Scanne un projet et détecte le framework, l'architecture et les assets.
262
250
 
263
251
  ```bash
264
- universal-pwa scan [options]
252
+ npx @julien-lin/universal-pwa-cli scan [options]
265
253
  ```
266
254
 
267
255
  **Options :**
@@ -271,7 +259,7 @@ universal-pwa scan [options]
271
259
  **Exemple :**
272
260
 
273
261
  ```bash
274
- universal-pwa scan
262
+ npx @julien-lin/universal-pwa-cli scan
275
263
  ```
276
264
 
277
265
  Affiche :
@@ -286,7 +274,7 @@ Affiche :
286
274
  Prévisualise la configuration PWA d'un projet.
287
275
 
288
276
  ```bash
289
- universal-pwa preview [options]
277
+ npx @julien-lin/universal-pwa-cli preview [options]
290
278
  ```
291
279
 
292
280
  **Options :**
@@ -298,12 +286,12 @@ universal-pwa preview [options]
298
286
  **Exemple :**
299
287
 
300
288
  ```bash
301
- universal-pwa preview --port 8080
289
+ npx @julien-lin/universal-pwa-cli preview --port 8080
302
290
  ```
303
291
 
304
292
  ## Fichiers Générés
305
293
 
306
- Après avoir exécuté `universal-pwa init`, les fichiers suivants sont générés :
294
+ Après avoir exécuté `npx @julien-lin/universal-pwa-cli init`, les fichiers suivants sont générés :
307
295
 
308
296
  - `manifest.json` - Fichier manifest PWA
309
297
  - `sw.js` - Service Worker (Workbox)
package/README.md CHANGED
@@ -19,19 +19,7 @@ This command will:
19
19
  - Generate all PWA assets (icons, manifest, service worker)
20
20
  - Inject meta tags into your HTML files
21
21
 
22
- No global installation needed!
23
-
24
- ## Installation
25
-
26
- ```bash
27
- npm install -g @julien-lin/universal-pwa-cli
28
- ```
29
-
30
- Or with pnpm:
31
-
32
- ```bash
33
- pnpm add -g @julien-lin/universal-pwa-cli
34
- ```
22
+ No global installation needed — npx is the recommended way to run the CLI.
35
23
 
36
24
  ## Configuration
37
25
 
@@ -40,7 +28,7 @@ UniversalPWA peut être configuré via un fichier de configuration pour éviter
40
28
  ### Génération automatique
41
29
 
42
30
  ```bash
43
- universal-pwa generate-config
31
+ npx @julien-lin/universal-pwa-cli generate-config
44
32
  ```
45
33
 
46
34
  Cette commande génère un fichier `universal-pwa.config.ts` (ou `.js`, `.json`, `.yaml`) basé sur votre projet.
@@ -83,10 +71,10 @@ yarn build
83
71
  # 2. Then initialize PWA
84
72
  # In interactive mode, select "Production" when prompted
85
73
  # The CLI will auto-detect dist/ directory and suggest it
86
- universal-pwa init
74
+ npx @julien-lin/universal-pwa-cli init
87
75
 
88
76
  # Or explicitly specify output directory
89
- universal-pwa init --output-dir dist
77
+ npx @julien-lin/universal-pwa-cli init --output-dir dist
90
78
  ```
91
79
 
92
80
  **Why?** The service worker needs to precache all your built assets (JS/CSS with hashes). If you initialize before building, the service worker won't know about the hashed filenames.
@@ -104,7 +92,7 @@ universal-pwa init --output-dir dist
104
92
  Simply run without arguments to launch interactive prompts:
105
93
 
106
94
  ```bash
107
- universal-pwa init
95
+ npx @julien-lin/universal-pwa-cli init
108
96
  ```
109
97
 
110
98
  The CLI will guide you through a 2-phase workflow:
@@ -128,7 +116,7 @@ All prompts include smart defaults, validation, and contextual suggestions!
128
116
  #### Command Line Mode
129
117
 
130
118
  ```bash
131
- universal-pwa init [options]
119
+ npx @julien-lin/universal-pwa-cli init [options]
132
120
  ```
133
121
 
134
122
  **Options:**
@@ -150,23 +138,23 @@ universal-pwa init [options]
150
138
  ```bash
151
139
  # For production build (React/Vite)
152
140
  npm run build
153
- universal-pwa init --output-dir dist --icon-source ./logo.png
141
+ npx @julien-lin/universal-pwa-cli init --output-dir dist --icon-source ./logo.png
154
142
 
155
143
  # For development or static sites
156
- universal-pwa init \
144
+ npx @julien-lin/universal-pwa-cli init \
157
145
  --name "My Application" \
158
146
  --short-name "MyApp" \
159
147
  --icon-source ./logo.png \
160
148
  --theme-color "#2c3e50"
161
149
 
162
150
  # For deployment under a subpath
163
- universal-pwa init \
151
+ npx @julien-lin/universal-pwa-cli init \
164
152
  --name "CreativeHub" \
165
153
  --output-dir public \
166
154
  --base-path "/creativehub/"
167
155
 
168
156
  # For API-based PWA
169
- universal-pwa init \
157
+ npx @julien-lin/universal-pwa-cli init \
170
158
  --name "PWA API" \
171
159
  --output-dir dist \
172
160
  --base-path "/api/pwa/"
@@ -203,7 +191,7 @@ This ensures:
203
191
 
204
192
  ```bash
205
193
  npm run build
206
- universal-pwa init \
194
+ npx @julien-lin/universal-pwa-cli init \
207
195
  --name "Creative Hub" \
208
196
  --output-dir public \
209
197
  --base-path "/creative-hub/"
@@ -213,7 +201,7 @@ universal-pwa init \
213
201
 
214
202
  ```bash
215
203
  pnpm build
216
- universal-pwa init \
204
+ npx @julien-lin/universal-pwa-cli init \
217
205
  --output-dir .next \
218
206
  --base-path "/dashboard/"
219
207
  ```
@@ -221,7 +209,7 @@ universal-pwa init \
221
209
  **Static Site on Shared Hosting** - Deployed at `example.com/apps/myapp/`:
222
210
 
223
211
  ```bash
224
- universal-pwa init \
212
+ npx @julien-lin/universal-pwa-cli init \
225
213
  --name "My App" \
226
214
  --output-dir dist \
227
215
  --base-path "/apps/myapp/"
@@ -313,7 +301,7 @@ The injected script emits custom events you can listen to:
313
301
  Scan a project and detect framework, architecture, and assets.
314
302
 
315
303
  ```bash
316
- universal-pwa scan [options]
304
+ npx @julien-lin/universal-pwa-cli scan [options]
317
305
  ```
318
306
 
319
307
  **Options:**
@@ -323,7 +311,7 @@ universal-pwa scan [options]
323
311
  **Example:**
324
312
 
325
313
  ```bash
326
- universal-pwa scan
314
+ npx @julien-lin/universal-pwa-cli scan
327
315
  ```
328
316
 
329
317
  Output:
@@ -338,7 +326,7 @@ Output:
338
326
  Preview the PWA configuration of a project.
339
327
 
340
328
  ```bash
341
- universal-pwa preview [options]
329
+ npx @julien-lin/universal-pwa-cli preview [options]
342
330
  ```
343
331
 
344
332
  **Options:**
@@ -350,12 +338,12 @@ universal-pwa preview [options]
350
338
  **Example:**
351
339
 
352
340
  ```bash
353
- universal-pwa preview --port 8080
341
+ npx @julien-lin/universal-pwa-cli preview --port 8080
354
342
  ```
355
343
 
356
344
  ## Generated Files
357
345
 
358
- After running `universal-pwa init`, the following files are generated:
346
+ After running `npx @julien-lin/universal-pwa-cli init`, the following files are generated:
359
347
 
360
348
  - `manifest.json` - PWA manifest file
361
349
  - `sw.js` - Service Worker (Workbox)
package/dist/index.cjs CHANGED
@@ -375,9 +375,9 @@ async function loadProjectConfig(projectPath) {
375
375
  strict: false
376
376
  // Don't throw on validation errors, just warn
377
377
  });
378
- const config = result?.config ?? null;
379
- const filePath = result?.filePath ?? null;
380
- const format = result?.format ?? null;
378
+ const config = result.config ?? null;
379
+ const filePath = result.filePath ?? null;
380
+ const format = result.format ?? null;
381
381
  return {
382
382
  config,
383
383
  filePath,
@@ -429,9 +429,14 @@ function mergeConfigWithOptions(config, cliOptions) {
429
429
  merged.skipServiceWorker = generate !== void 0 ? !generate : false;
430
430
  }
431
431
  const injectionConfig = cfg?.injection;
432
- if (injectionConfig && merged.skipInjection === void 0) {
433
- const inject = injectionConfig.inject;
434
- merged.skipInjection = inject !== void 0 ? !inject : false;
432
+ if (injectionConfig) {
433
+ if (merged.skipInjection === void 0) {
434
+ const inject = injectionConfig.inject;
435
+ merged.skipInjection = inject !== void 0 ? !inject : false;
436
+ }
437
+ if (!merged.htmlExtensions && Array.isArray(injectionConfig.extensions)) {
438
+ merged.htmlExtensions = injectionConfig.extensions;
439
+ }
435
440
  }
436
441
  const outputConfig = cfg?.output;
437
442
  if (outputConfig && !merged.outputDir) {
@@ -457,6 +462,9 @@ async function getEffectiveConfig(projectPath, cliOptions) {
457
462
  };
458
463
  }
459
464
 
465
+ // src/commands/init.ts
466
+ var import_universal_pwa_core9 = require("@julien-lin/universal-pwa-core");
467
+
460
468
  // src/utils/ui-utils.ts
461
469
  var import_chalk2 = __toESM(require("chalk"), 1);
462
470
  var import_strip_ansi = __toESM(require("strip-ansi"), 1);
@@ -780,7 +788,8 @@ async function initCommand(options = {}) {
780
788
  skipInjection = false,
781
789
  outputDir,
782
790
  basePath: rawBasePath,
783
- maxHtmlFiles
791
+ maxHtmlFiles,
792
+ htmlExtensions
784
793
  } = mergedOptions;
785
794
  const result = {
786
795
  success: false,
@@ -794,10 +803,18 @@ async function initCommand(options = {}) {
794
803
  };
795
804
  let finalBasePath;
796
805
  try {
797
- const effectiveBasePath = rawBasePath || "/";
806
+ let effectiveBasePath;
807
+ if (rawBasePath) {
808
+ effectiveBasePath = rawBasePath;
809
+ } else {
810
+ const detected = (0, import_universal_pwa_core9.detectBasePath)(result.projectPath);
811
+ effectiveBasePath = detected.basePath != null && detected.confidence >= 0.7 ? detected.basePath : "/";
812
+ }
798
813
  finalBasePath = normalizeBasePath(effectiveBasePath);
799
- if (effectiveBasePath && effectiveBasePath !== "/") {
800
- console.log(import_chalk3.default.gray(` Base path: ${finalBasePath}`));
814
+ if (finalBasePath !== "/") {
815
+ console.log(
816
+ import_chalk3.default.gray(` Base path: ${finalBasePath}${rawBasePath ? "" : " (auto)"}`)
817
+ );
801
818
  }
802
819
  } catch (error) {
803
820
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -845,6 +862,7 @@ async function initCommand(options = {}) {
845
862
  } else {
846
863
  const distDir = (0, import_path2.join)(result.projectPath, "dist");
847
864
  const publicDir = (0, import_path2.join)(result.projectPath, "public");
865
+ const staticDir = (0, import_path2.join)(result.projectPath, "static");
848
866
  if ((result.framework === "react" || result.framework === "nextjs") && (0, import_fs2.existsSync)(distDir)) {
849
867
  finalOutputDir = distDir;
850
868
  console.log(
@@ -852,6 +870,13 @@ async function initCommand(options = {}) {
852
870
  );
853
871
  } else if (result.framework === "wordpress") {
854
872
  finalOutputDir = publicDir;
873
+ } else if ((result.framework === "django" || result.framework === "flask") && (0, import_fs2.existsSync)(staticDir)) {
874
+ finalOutputDir = staticDir;
875
+ console.log(
876
+ import_chalk3.default.gray(
877
+ ` Using static/ directory (${result.framework} detected)`
878
+ )
879
+ );
855
880
  } else if ((0, import_fs2.existsSync)(publicDir)) {
856
881
  finalOutputDir = publicDir;
857
882
  } else if ((0, import_fs2.existsSync)(distDir)) {
@@ -883,6 +908,27 @@ async function initCommand(options = {}) {
883
908
  transaction.backupFile(swRelative);
884
909
  }
885
910
  }
911
+ const factory = (0, import_universal_pwa_core8.getBackendFactory)();
912
+ let backendIntegration = null;
913
+ if (factory && typeof factory === "object" && "detectBackend" in factory) {
914
+ backendIntegration = factory.detectBackend(result.projectPath) ?? null;
915
+ }
916
+ if (!backendIntegration && result.framework && factory && typeof factory === "object" && "getIntegration" in factory) {
917
+ backendIntegration = factory.getIntegration(
918
+ result.framework,
919
+ result.projectPath
920
+ );
921
+ if (backendIntegration && typeof backendIntegration === "object" && "detect" in backendIntegration) {
922
+ const detectionResult = backendIntegration.detect();
923
+ if (detectionResult && typeof detectionResult === "object") {
924
+ const detected = detectionResult.detected ?? false;
925
+ const confidence = detectionResult.confidence ?? "low";
926
+ if (!detected || confidence === "low") {
927
+ backendIntegration = null;
928
+ }
929
+ }
930
+ }
931
+ }
886
932
  console.log(import_chalk3.default.blue("\u{1F4DD} Generating manifest.json..."));
887
933
  const appName = name ?? (result.framework ? `${result.framework} App` : "My PWA");
888
934
  const normalizedShortName = shortName && typeof shortName === "string" && shortName.trim().length > 0 ? shortName.trim() : void 0;
@@ -974,18 +1020,22 @@ async function initCommand(options = {}) {
974
1020
  } catch {
975
1021
  manifestId = appName.toLowerCase().replace(/[^a-z0-9-]/g, "-").substring(0, 20);
976
1022
  }
1023
+ const backendManifestDefaults = backendIntegration && typeof backendIntegration === "object" && "generateManifestVariables" in backendIntegration && typeof backendIntegration.generateManifestVariables === "function" ? (0, import_universal_pwa_core3.mapBackendManifestVarsToOptions)(
1024
+ backendIntegration.generateManifestVariables()
1025
+ ) : {};
977
1026
  let manifestPath;
978
1027
  try {
979
1028
  if (iconPaths.length > 0) {
980
1029
  const manifestWithIconsOptions = {
1030
+ ...backendManifestDefaults,
981
1031
  name: appName,
982
1032
  shortName: finalShortName,
983
1033
  id: manifestId,
984
1034
  startUrl: finalBasePath,
985
1035
  scope: finalBasePath,
986
1036
  display: "standalone",
987
- themeColor: themeColor ?? "#ffffff",
988
- backgroundColor: backgroundColor ?? "#000000",
1037
+ themeColor: themeColor ?? backendManifestDefaults.themeColor ?? "#ffffff",
1038
+ backgroundColor: backgroundColor ?? backendManifestDefaults.backgroundColor ?? "#000000",
989
1039
  icons: iconPaths.map((src) => ({
990
1040
  src,
991
1041
  sizes: src.match(/(\d+)x(\d+)/)?.[0] ?? "192x192",
@@ -1016,14 +1066,15 @@ async function initCommand(options = {}) {
1016
1066
  import_chalk3.default.yellow("\u26A0 Generating manifest with placeholder icon")
1017
1067
  );
1018
1068
  const manifestMinimalOptions = {
1069
+ ...backendManifestDefaults,
1019
1070
  name: appName,
1020
1071
  shortName: finalShortName,
1021
1072
  id: manifestId,
1022
1073
  startUrl: finalBasePath,
1023
1074
  scope: finalBasePath,
1024
1075
  display: "standalone",
1025
- themeColor: themeColor ?? "#ffffff",
1026
- backgroundColor: backgroundColor ?? "#000000",
1076
+ themeColor: themeColor ?? backendManifestDefaults.themeColor ?? "#ffffff",
1077
+ backgroundColor: backgroundColor ?? backendManifestDefaults.backgroundColor ?? "#000000",
1027
1078
  icons: [
1028
1079
  {
1029
1080
  src: "/icon-192x192.png",
@@ -1064,24 +1115,6 @@ async function initCommand(options = {}) {
1064
1115
  if (!skipServiceWorker) {
1065
1116
  console.log(import_chalk3.default.blue("\u2699\uFE0F Generating service worker..."));
1066
1117
  try {
1067
- const factory = (0, import_universal_pwa_core8.getBackendFactory)();
1068
- let backendIntegration = null;
1069
- if (factory && typeof factory === "object" && "detectBackend" in factory) {
1070
- backendIntegration = factory.detectBackend(result.projectPath);
1071
- }
1072
- if (!backendIntegration && result.framework && factory && typeof factory === "object" && "getIntegration" in factory) {
1073
- backendIntegration = factory.getIntegration(result.framework, result.projectPath);
1074
- if (backendIntegration && typeof backendIntegration === "object" && "detect" in backendIntegration) {
1075
- const detectionResult = backendIntegration.detect();
1076
- if (detectionResult && typeof detectionResult === "object") {
1077
- const detected = detectionResult.detected ?? false;
1078
- const confidence = detectionResult.confidence ?? "low";
1079
- if (!detected || confidence === "low") {
1080
- backendIntegration = null;
1081
- }
1082
- }
1083
- }
1084
- }
1085
1118
  let swResult;
1086
1119
  if (backendIntegration && typeof backendIntegration === "object" && "detect" in backendIntegration && "name" in backendIntegration) {
1087
1120
  const detectionResult = backendIntegration.detect();
@@ -1196,16 +1229,33 @@ async function initCommand(options = {}) {
1196
1229
  if (!skipInjection) {
1197
1230
  console.log(import_chalk3.default.blue("\u{1F489} Injecting meta-tags..."));
1198
1231
  try {
1199
- const htmlFiles = await (0, import_glob.glob)("**/*.{html,twig,html.twig,blade.php}", {
1200
- cwd: result.projectPath,
1201
- ignore: [
1202
- "**/node_modules/**",
1203
- "**/.next/**",
1204
- "**/.nuxt/**",
1205
- "**/vendor/**"
1206
- ],
1207
- absolute: true
1208
- });
1232
+ const useWordPressRestricted = result.framework === "wordpress" && !(htmlExtensions && htmlExtensions.length > 0);
1233
+ let htmlFiles;
1234
+ if (useWordPressRestricted) {
1235
+ htmlFiles = await (0, import_glob.glob)([...import_universal_pwa_core9.WORDPRESS_INJECTION_PATTERNS], {
1236
+ cwd: result.projectPath,
1237
+ ignore: [
1238
+ "**/node_modules/**",
1239
+ "**/.next/**",
1240
+ "**/.nuxt/**",
1241
+ "**/vendor/**"
1242
+ ],
1243
+ absolute: true
1244
+ });
1245
+ } else {
1246
+ const injectionExts = htmlExtensions && htmlExtensions.length > 0 ? htmlExtensions : [...import_universal_pwa_core9.DEFAULT_INJECTION_EXTENSIONS];
1247
+ const htmlGlobPattern = injectionExts.length > 0 ? `**/*.{${injectionExts.join(",")}}` : "**/*.html";
1248
+ htmlFiles = await (0, import_glob.glob)(htmlGlobPattern, {
1249
+ cwd: result.projectPath,
1250
+ ignore: [
1251
+ "**/node_modules/**",
1252
+ "**/.next/**",
1253
+ "**/.nuxt/**",
1254
+ "**/vendor/**"
1255
+ ],
1256
+ absolute: true
1257
+ });
1258
+ }
1209
1259
  htmlFiles.sort((a, b) => {
1210
1260
  const aInDist = a.includes("/dist/");
1211
1261
  const bInDist = b.includes("/dist/");
@@ -1228,10 +1278,15 @@ async function initCommand(options = {}) {
1228
1278
  const bladeCount = htmlFiles.filter(
1229
1279
  (f) => f.endsWith(".blade.php")
1230
1280
  ).length;
1281
+ const wpThemeCount = useWordPressRestricted ? htmlFiles.filter(
1282
+ (f) => f.endsWith("header.php") || f.endsWith("footer.php")
1283
+ ).length : 0;
1231
1284
  const fileTypes = [];
1232
1285
  if (htmlCount > 0) fileTypes.push(`${htmlCount} HTML`);
1233
1286
  if (twigCount > 0) fileTypes.push(`${twigCount} Twig`);
1234
1287
  if (bladeCount > 0) fileTypes.push(`${bladeCount} Blade`);
1288
+ if (wpThemeCount > 0)
1289
+ fileTypes.push(`${wpThemeCount} WordPress theme`);
1235
1290
  const typeSummary = fileTypes.length > 0 ? ` (${fileTypes.join(", ")})` : "";
1236
1291
  console.log(
1237
1292
  import_chalk3.default.gray(
@@ -1482,7 +1537,7 @@ async function initCommand(options = {}) {
1482
1537
  var import_chalk4 = __toESM(require("chalk"), 1);
1483
1538
  var import_fs3 = require("fs");
1484
1539
  var import_path3 = require("path");
1485
- var import_universal_pwa_core9 = require("@julien-lin/universal-pwa-core");
1540
+ var import_universal_pwa_core10 = require("@julien-lin/universal-pwa-core");
1486
1541
  async function previewCommand(options = {}) {
1487
1542
  const {
1488
1543
  projectPath = process.cwd(),
@@ -1523,7 +1578,7 @@ async function previewCommand(options = {}) {
1523
1578
  console.log(import_chalk4.default.yellow("\u26A0 Service worker (sw.js) not found"));
1524
1579
  }
1525
1580
  try {
1526
- const httpsCheck = (0, import_universal_pwa_core9.checkProjectHttps)({ projectPath: resolvedPath });
1581
+ const httpsCheck = (0, import_universal_pwa_core10.checkProjectHttps)({ projectPath: resolvedPath });
1527
1582
  if (!httpsCheck.isSecure && !httpsCheck.isLocalhost && httpsCheck.warning) {
1528
1583
  result.warnings.push(httpsCheck.warning);
1529
1584
  console.log(import_chalk4.default.yellow(`\u26A0 ${httpsCheck.warning}`));
@@ -1554,7 +1609,7 @@ var import_fs4 = require("fs");
1554
1609
  var import_path4 = require("path");
1555
1610
  var import_glob2 = require("glob");
1556
1611
  var import_chalk5 = __toESM(require("chalk"), 1);
1557
- var import_universal_pwa_core10 = require("@julien-lin/universal-pwa-core");
1612
+ var import_universal_pwa_core11 = require("@julien-lin/universal-pwa-core");
1558
1613
  async function verifyCommand(options = {}) {
1559
1614
  const {
1560
1615
  projectPath = process.cwd(),
@@ -1587,7 +1642,7 @@ async function verifyCommand(options = {}) {
1587
1642
  absolute: true
1588
1643
  });
1589
1644
  console.log(import_chalk5.default.blue("\u{1F50D} Running comprehensive PWA validation..."));
1590
- const validationResult = await (0, import_universal_pwa_core10.validatePWA)({
1645
+ const validationResult = await (0, import_universal_pwa_core11.validatePWA)({
1591
1646
  projectPath: result.projectPath,
1592
1647
  outputDir: finalOutputDir,
1593
1648
  htmlFiles,
@@ -1753,7 +1808,7 @@ async function verifyCommand(options = {}) {
1753
1808
  }
1754
1809
 
1755
1810
  // src/commands/remove.ts
1756
- var import_universal_pwa_core11 = require("@julien-lin/universal-pwa-core");
1811
+ var import_universal_pwa_core12 = require("@julien-lin/universal-pwa-core");
1757
1812
  var import_chalk6 = __toESM(require("chalk"), 1);
1758
1813
  var import_fs6 = require("fs");
1759
1814
  var import_glob4 = require("glob");
@@ -1777,7 +1832,7 @@ function isPWAScript(content) {
1777
1832
  return lowerContent.includes("serviceworker") || lowerContent.includes("navigator.serviceworker") || lowerContent.includes("register") && lowerContent.includes("sw") || lowerContent.includes("sw.js") || lowerContent.includes("beforeinstallprompt") || lowerContent.includes("window.installpwa") || lowerContent.includes("ispwainstalled") || lowerContent.includes("ispwainstallable") || lowerContent.includes("deferredprompt");
1778
1833
  }
1779
1834
  async function removeMetaTags(htmlContent) {
1780
- const parsed = (0, import_universal_pwa_core11.parseHTML)(htmlContent);
1835
+ const parsed = (0, import_universal_pwa_core12.parseHTML)(htmlContent);
1781
1836
  const removed = [];
1782
1837
  if (!parsed.head) {
1783
1838
  return { html: htmlContent, removed: [] };
@@ -1849,7 +1904,7 @@ async function removeCommand(options = {}) {
1849
1904
  return result;
1850
1905
  }
1851
1906
  console.log(import_chalk6.default.blue("\u{1F50D} Scanning project for PWA files..."));
1852
- const scanResult = await (0, import_universal_pwa_core11.scanProject)({
1907
+ const scanResult = await (0, import_universal_pwa_core12.scanProject)({
1853
1908
  projectPath: result.projectPath,
1854
1909
  includeAssets: false,
1855
1910
  includeArchitecture: false
@@ -1983,7 +2038,7 @@ async function removeCommand(options = {}) {
1983
2038
  }
1984
2039
 
1985
2040
  // src/commands/generate-config.ts
1986
- var import_universal_pwa_core12 = require("@julien-lin/universal-pwa-core");
2041
+ var import_universal_pwa_core13 = require("@julien-lin/universal-pwa-core");
1987
2042
  var import_node_fs = require("fs");
1988
2043
  var import_node_path2 = require("path");
1989
2044
  var import_chalk7 = __toESM(require("chalk"), 1);
@@ -2007,7 +2062,7 @@ async function generateConfigCommand(options = {}) {
2007
2062
  return result;
2008
2063
  }
2009
2064
  console.log(import_chalk7.default.blue("\u{1F50D} Scanning project..."));
2010
- const scanResult = await (0, import_universal_pwa_core12.scanProject)({
2065
+ const scanResult = await (0, import_universal_pwa_core13.scanProject)({
2011
2066
  projectPath: resolvedPath,
2012
2067
  includeAssets: true,
2013
2068
  includeArchitecture: true
@@ -2036,7 +2091,9 @@ async function generateConfigCommand(options = {}) {
2036
2091
  }
2037
2092
  ]);
2038
2093
  if (!overwrite) {
2039
- result.errors.push("Configuration file already exists and overwrite was cancelled");
2094
+ result.errors.push(
2095
+ "Configuration file already exists and overwrite was cancelled"
2096
+ );
2040
2097
  return result;
2041
2098
  }
2042
2099
  }
@@ -2294,7 +2351,7 @@ function formatYAMLConfig(config) {
2294
2351
  }
2295
2352
 
2296
2353
  // src/index.ts
2297
- var import_universal_pwa_core13 = require("@julien-lin/universal-pwa-core");
2354
+ var import_universal_pwa_core14 = require("@julien-lin/universal-pwa-core");
2298
2355
 
2299
2356
  // src/prompts.ts
2300
2357
  var import_fs8 = require("fs");
@@ -2680,7 +2737,7 @@ async function promptInitOptions(projectPath, framework, architecture = null) {
2680
2737
  // package.json
2681
2738
  var package_default = {
2682
2739
  name: "@julien-lin/universal-pwa-cli",
2683
- version: "1.3.14",
2740
+ version: "1.3.16",
2684
2741
  description: "CLI to transform any web project into a PWA",
2685
2742
  keywords: [
2686
2743
  "pwa",
@@ -2777,14 +2834,14 @@ var normalizeConfigFormat = (value) => {
2777
2834
  throw new Error(`Invalid format: ${value ?? "undefined"}`);
2778
2835
  };
2779
2836
  program.name("universal-pwa").description("Transform any web project into a PWA with one click").version(version);
2780
- program.command("init").description("Initialize PWA in your project").option("-p, --project-path <path>", "Project path", process.cwd()).option("-n, --name <name>", "App name").option("-s, --short-name <shortName>", "App short name (max 12 chars)").option("-i, --icon-source <path>", "Source image for icons").option("-t, --theme-color <color>", "Theme color (hex)").option("-b, --background-color <color>", "Background color (hex)").option("--skip-icons", "Skip icon generation").option("--skip-service-worker", "Skip service worker generation").option("--skip-injection", "Skip HTML meta-tag injection").option("-o, --output-dir <dir>", "Output directory", "public").option("--force-scan", "Force scan (bypass cache)").option("--no-cache", "Disable cache").option("--max-html-files <number>", "Maximum number of HTML files to process (default: unlimited)", (value) => Number.parseInt(value, 10)).action(async (options) => {
2837
+ program.command("init").description("Initialize PWA in your project").option("-p, --project-path <path>", "Project path", process.cwd()).option("-n, --name <name>", "App name").option("-s, --short-name <shortName>", "App short name (max 12 chars)").option("-i, --icon-source <path>", "Source image for icons").option("-t, --theme-color <color>", "Theme color (hex)").option("-b, --background-color <color>", "Background color (hex)").option("--skip-icons", "Skip icon generation").option("--skip-service-worker", "Skip service worker generation").option("--skip-injection", "Skip HTML meta-tag injection").option("-o, --output-dir <dir>", "Output directory", "public").option("--force-scan", "Force scan (bypass cache)").option("--no-cache", "Disable cache").option("--max-html-files <number>", "Maximum number of HTML files to process (default: unlimited)", (value) => Number.parseInt(value, 10)).option("--html-extensions <extensions>", "Comma-separated HTML/template extensions (e.g. html,twig,blade.php,j2)", (value) => value ? value.split(",").map((s) => s.trim()).filter(Boolean) : void 0).action(async (options) => {
2781
2838
  try {
2782
2839
  const projectPath = options.projectPath ?? process.cwd();
2783
2840
  const hasOptions = options.name || options.shortName || options.iconSource || options.themeColor || options.backgroundColor || options.skipIcons !== void 0;
2784
2841
  let finalOptions = { ...options };
2785
2842
  if (!hasOptions) {
2786
2843
  console.log(import_chalk9.default.blue("\u{1F50D} Scanning project..."));
2787
- const scanResult = await (0, import_universal_pwa_core13.scanProject)({
2844
+ const scanResult = await (0, import_universal_pwa_core14.scanProject)({
2788
2845
  projectPath,
2789
2846
  includeAssets: false,
2790
2847
  includeArchitecture: false
@@ -2836,7 +2893,8 @@ program.command("init").description("Initialize PWA in your project").option("-p
2836
2893
  outputDir: finalOptions.outputDir,
2837
2894
  forceScan: options.forceScan,
2838
2895
  noCache: options.noCache,
2839
- maxHtmlFiles: options.maxHtmlFiles
2896
+ maxHtmlFiles: options.maxHtmlFiles,
2897
+ htmlExtensions: options.htmlExtensions
2840
2898
  });
2841
2899
  process.exit(result.success ? 0 : 1);
2842
2900
  } catch (error) {
@@ -2860,7 +2918,7 @@ program.command("preview").description("Preview PWA setup").option("-p, --projec
2860
2918
  program.command("scan").description("Scan project and detect framework/architecture").option("-p, --project-path <path>", "Project path", process.cwd()).action(async (options) => {
2861
2919
  try {
2862
2920
  console.log(import_chalk9.default.blue("\u{1F50D} Scanning project..."));
2863
- const result = await (0, import_universal_pwa_core13.scanProject)({
2921
+ const result = await (0, import_universal_pwa_core14.scanProject)({
2864
2922
  projectPath: options.projectPath ?? process.cwd(),
2865
2923
  includeAssets: true,
2866
2924
  includeArchitecture: true
package/dist/index.js CHANGED
@@ -11,7 +11,8 @@ import chalk9 from "chalk";
11
11
  import { scanProject, optimizeProject } from "@julien-lin/universal-pwa-core";
12
12
  import {
13
13
  generateAndWriteManifest,
14
- generateManifestId
14
+ generateManifestId,
15
+ mapBackendManifestVarsToOptions
15
16
  } from "@julien-lin/universal-pwa-core";
16
17
  import { generateIcons } from "@julien-lin/universal-pwa-core";
17
18
  import {
@@ -264,9 +265,9 @@ async function loadProjectConfig(projectPath) {
264
265
  strict: false
265
266
  // Don't throw on validation errors, just warn
266
267
  });
267
- const config = result?.config ?? null;
268
- const filePath = result?.filePath ?? null;
269
- const format = result?.format ?? null;
268
+ const config = result.config ?? null;
269
+ const filePath = result.filePath ?? null;
270
+ const format = result.format ?? null;
270
271
  return {
271
272
  config,
272
273
  filePath,
@@ -318,9 +319,14 @@ function mergeConfigWithOptions(config, cliOptions) {
318
319
  merged.skipServiceWorker = generate !== void 0 ? !generate : false;
319
320
  }
320
321
  const injectionConfig = cfg?.injection;
321
- if (injectionConfig && merged.skipInjection === void 0) {
322
- const inject = injectionConfig.inject;
323
- merged.skipInjection = inject !== void 0 ? !inject : false;
322
+ if (injectionConfig) {
323
+ if (merged.skipInjection === void 0) {
324
+ const inject = injectionConfig.inject;
325
+ merged.skipInjection = inject !== void 0 ? !inject : false;
326
+ }
327
+ if (!merged.htmlExtensions && Array.isArray(injectionConfig.extensions)) {
328
+ merged.htmlExtensions = injectionConfig.extensions;
329
+ }
324
330
  }
325
331
  const outputConfig = cfg?.output;
326
332
  if (outputConfig && !merged.outputDir) {
@@ -346,6 +352,13 @@ async function getEffectiveConfig(projectPath, cliOptions) {
346
352
  };
347
353
  }
348
354
 
355
+ // src/commands/init.ts
356
+ import {
357
+ DEFAULT_INJECTION_EXTENSIONS,
358
+ WORDPRESS_INJECTION_PATTERNS,
359
+ detectBasePath
360
+ } from "@julien-lin/universal-pwa-core";
361
+
349
362
  // src/utils/ui-utils.ts
350
363
  import chalk2 from "chalk";
351
364
  import stripAnsi from "strip-ansi";
@@ -669,7 +682,8 @@ async function initCommand(options = {}) {
669
682
  skipInjection = false,
670
683
  outputDir,
671
684
  basePath: rawBasePath,
672
- maxHtmlFiles
685
+ maxHtmlFiles,
686
+ htmlExtensions
673
687
  } = mergedOptions;
674
688
  const result = {
675
689
  success: false,
@@ -683,10 +697,18 @@ async function initCommand(options = {}) {
683
697
  };
684
698
  let finalBasePath;
685
699
  try {
686
- const effectiveBasePath = rawBasePath || "/";
700
+ let effectiveBasePath;
701
+ if (rawBasePath) {
702
+ effectiveBasePath = rawBasePath;
703
+ } else {
704
+ const detected = detectBasePath(result.projectPath);
705
+ effectiveBasePath = detected.basePath != null && detected.confidence >= 0.7 ? detected.basePath : "/";
706
+ }
687
707
  finalBasePath = normalizeBasePath(effectiveBasePath);
688
- if (effectiveBasePath && effectiveBasePath !== "/") {
689
- console.log(chalk3.gray(` Base path: ${finalBasePath}`));
708
+ if (finalBasePath !== "/") {
709
+ console.log(
710
+ chalk3.gray(` Base path: ${finalBasePath}${rawBasePath ? "" : " (auto)"}`)
711
+ );
690
712
  }
691
713
  } catch (error) {
692
714
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -734,6 +756,7 @@ async function initCommand(options = {}) {
734
756
  } else {
735
757
  const distDir = join2(result.projectPath, "dist");
736
758
  const publicDir = join2(result.projectPath, "public");
759
+ const staticDir = join2(result.projectPath, "static");
737
760
  if ((result.framework === "react" || result.framework === "nextjs") && existsSync2(distDir)) {
738
761
  finalOutputDir = distDir;
739
762
  console.log(
@@ -741,6 +764,13 @@ async function initCommand(options = {}) {
741
764
  );
742
765
  } else if (result.framework === "wordpress") {
743
766
  finalOutputDir = publicDir;
767
+ } else if ((result.framework === "django" || result.framework === "flask") && existsSync2(staticDir)) {
768
+ finalOutputDir = staticDir;
769
+ console.log(
770
+ chalk3.gray(
771
+ ` Using static/ directory (${result.framework} detected)`
772
+ )
773
+ );
744
774
  } else if (existsSync2(publicDir)) {
745
775
  finalOutputDir = publicDir;
746
776
  } else if (existsSync2(distDir)) {
@@ -772,6 +802,27 @@ async function initCommand(options = {}) {
772
802
  transaction.backupFile(swRelative);
773
803
  }
774
804
  }
805
+ const factory = getBackendFactory();
806
+ let backendIntegration = null;
807
+ if (factory && typeof factory === "object" && "detectBackend" in factory) {
808
+ backendIntegration = factory.detectBackend(result.projectPath) ?? null;
809
+ }
810
+ if (!backendIntegration && result.framework && factory && typeof factory === "object" && "getIntegration" in factory) {
811
+ backendIntegration = factory.getIntegration(
812
+ result.framework,
813
+ result.projectPath
814
+ );
815
+ if (backendIntegration && typeof backendIntegration === "object" && "detect" in backendIntegration) {
816
+ const detectionResult = backendIntegration.detect();
817
+ if (detectionResult && typeof detectionResult === "object") {
818
+ const detected = detectionResult.detected ?? false;
819
+ const confidence = detectionResult.confidence ?? "low";
820
+ if (!detected || confidence === "low") {
821
+ backendIntegration = null;
822
+ }
823
+ }
824
+ }
825
+ }
775
826
  console.log(chalk3.blue("\u{1F4DD} Generating manifest.json..."));
776
827
  const appName = name ?? (result.framework ? `${result.framework} App` : "My PWA");
777
828
  const normalizedShortName = shortName && typeof shortName === "string" && shortName.trim().length > 0 ? shortName.trim() : void 0;
@@ -863,18 +914,22 @@ async function initCommand(options = {}) {
863
914
  } catch {
864
915
  manifestId = appName.toLowerCase().replace(/[^a-z0-9-]/g, "-").substring(0, 20);
865
916
  }
917
+ const backendManifestDefaults = backendIntegration && typeof backendIntegration === "object" && "generateManifestVariables" in backendIntegration && typeof backendIntegration.generateManifestVariables === "function" ? mapBackendManifestVarsToOptions(
918
+ backendIntegration.generateManifestVariables()
919
+ ) : {};
866
920
  let manifestPath;
867
921
  try {
868
922
  if (iconPaths.length > 0) {
869
923
  const manifestWithIconsOptions = {
924
+ ...backendManifestDefaults,
870
925
  name: appName,
871
926
  shortName: finalShortName,
872
927
  id: manifestId,
873
928
  startUrl: finalBasePath,
874
929
  scope: finalBasePath,
875
930
  display: "standalone",
876
- themeColor: themeColor ?? "#ffffff",
877
- backgroundColor: backgroundColor ?? "#000000",
931
+ themeColor: themeColor ?? backendManifestDefaults.themeColor ?? "#ffffff",
932
+ backgroundColor: backgroundColor ?? backendManifestDefaults.backgroundColor ?? "#000000",
878
933
  icons: iconPaths.map((src) => ({
879
934
  src,
880
935
  sizes: src.match(/(\d+)x(\d+)/)?.[0] ?? "192x192",
@@ -905,14 +960,15 @@ async function initCommand(options = {}) {
905
960
  chalk3.yellow("\u26A0 Generating manifest with placeholder icon")
906
961
  );
907
962
  const manifestMinimalOptions = {
963
+ ...backendManifestDefaults,
908
964
  name: appName,
909
965
  shortName: finalShortName,
910
966
  id: manifestId,
911
967
  startUrl: finalBasePath,
912
968
  scope: finalBasePath,
913
969
  display: "standalone",
914
- themeColor: themeColor ?? "#ffffff",
915
- backgroundColor: backgroundColor ?? "#000000",
970
+ themeColor: themeColor ?? backendManifestDefaults.themeColor ?? "#ffffff",
971
+ backgroundColor: backgroundColor ?? backendManifestDefaults.backgroundColor ?? "#000000",
916
972
  icons: [
917
973
  {
918
974
  src: "/icon-192x192.png",
@@ -953,24 +1009,6 @@ async function initCommand(options = {}) {
953
1009
  if (!skipServiceWorker) {
954
1010
  console.log(chalk3.blue("\u2699\uFE0F Generating service worker..."));
955
1011
  try {
956
- const factory = getBackendFactory();
957
- let backendIntegration = null;
958
- if (factory && typeof factory === "object" && "detectBackend" in factory) {
959
- backendIntegration = factory.detectBackend(result.projectPath);
960
- }
961
- if (!backendIntegration && result.framework && factory && typeof factory === "object" && "getIntegration" in factory) {
962
- backendIntegration = factory.getIntegration(result.framework, result.projectPath);
963
- if (backendIntegration && typeof backendIntegration === "object" && "detect" in backendIntegration) {
964
- const detectionResult = backendIntegration.detect();
965
- if (detectionResult && typeof detectionResult === "object") {
966
- const detected = detectionResult.detected ?? false;
967
- const confidence = detectionResult.confidence ?? "low";
968
- if (!detected || confidence === "low") {
969
- backendIntegration = null;
970
- }
971
- }
972
- }
973
- }
974
1012
  let swResult;
975
1013
  if (backendIntegration && typeof backendIntegration === "object" && "detect" in backendIntegration && "name" in backendIntegration) {
976
1014
  const detectionResult = backendIntegration.detect();
@@ -1085,16 +1123,33 @@ async function initCommand(options = {}) {
1085
1123
  if (!skipInjection) {
1086
1124
  console.log(chalk3.blue("\u{1F489} Injecting meta-tags..."));
1087
1125
  try {
1088
- const htmlFiles = await glob("**/*.{html,twig,html.twig,blade.php}", {
1089
- cwd: result.projectPath,
1090
- ignore: [
1091
- "**/node_modules/**",
1092
- "**/.next/**",
1093
- "**/.nuxt/**",
1094
- "**/vendor/**"
1095
- ],
1096
- absolute: true
1097
- });
1126
+ const useWordPressRestricted = result.framework === "wordpress" && !(htmlExtensions && htmlExtensions.length > 0);
1127
+ let htmlFiles;
1128
+ if (useWordPressRestricted) {
1129
+ htmlFiles = await glob([...WORDPRESS_INJECTION_PATTERNS], {
1130
+ cwd: result.projectPath,
1131
+ ignore: [
1132
+ "**/node_modules/**",
1133
+ "**/.next/**",
1134
+ "**/.nuxt/**",
1135
+ "**/vendor/**"
1136
+ ],
1137
+ absolute: true
1138
+ });
1139
+ } else {
1140
+ const injectionExts = htmlExtensions && htmlExtensions.length > 0 ? htmlExtensions : [...DEFAULT_INJECTION_EXTENSIONS];
1141
+ const htmlGlobPattern = injectionExts.length > 0 ? `**/*.{${injectionExts.join(",")}}` : "**/*.html";
1142
+ htmlFiles = await glob(htmlGlobPattern, {
1143
+ cwd: result.projectPath,
1144
+ ignore: [
1145
+ "**/node_modules/**",
1146
+ "**/.next/**",
1147
+ "**/.nuxt/**",
1148
+ "**/vendor/**"
1149
+ ],
1150
+ absolute: true
1151
+ });
1152
+ }
1098
1153
  htmlFiles.sort((a, b) => {
1099
1154
  const aInDist = a.includes("/dist/");
1100
1155
  const bInDist = b.includes("/dist/");
@@ -1117,10 +1172,15 @@ async function initCommand(options = {}) {
1117
1172
  const bladeCount = htmlFiles.filter(
1118
1173
  (f) => f.endsWith(".blade.php")
1119
1174
  ).length;
1175
+ const wpThemeCount = useWordPressRestricted ? htmlFiles.filter(
1176
+ (f) => f.endsWith("header.php") || f.endsWith("footer.php")
1177
+ ).length : 0;
1120
1178
  const fileTypes = [];
1121
1179
  if (htmlCount > 0) fileTypes.push(`${htmlCount} HTML`);
1122
1180
  if (twigCount > 0) fileTypes.push(`${twigCount} Twig`);
1123
1181
  if (bladeCount > 0) fileTypes.push(`${bladeCount} Blade`);
1182
+ if (wpThemeCount > 0)
1183
+ fileTypes.push(`${wpThemeCount} WordPress theme`);
1124
1184
  const typeSummary = fileTypes.length > 0 ? ` (${fileTypes.join(", ")})` : "";
1125
1185
  console.log(
1126
1186
  chalk3.gray(
@@ -1924,7 +1984,9 @@ async function generateConfigCommand(options = {}) {
1924
1984
  }
1925
1985
  ]);
1926
1986
  if (!overwrite) {
1927
- result.errors.push("Configuration file already exists and overwrite was cancelled");
1987
+ result.errors.push(
1988
+ "Configuration file already exists and overwrite was cancelled"
1989
+ );
1928
1990
  return result;
1929
1991
  }
1930
1992
  }
@@ -2567,7 +2629,7 @@ async function promptInitOptions(projectPath, framework, architecture = null) {
2567
2629
  // package.json
2568
2630
  var package_default = {
2569
2631
  name: "@julien-lin/universal-pwa-cli",
2570
- version: "1.3.14",
2632
+ version: "1.3.16",
2571
2633
  description: "CLI to transform any web project into a PWA",
2572
2634
  keywords: [
2573
2635
  "pwa",
@@ -2664,7 +2726,7 @@ var normalizeConfigFormat = (value) => {
2664
2726
  throw new Error(`Invalid format: ${value ?? "undefined"}`);
2665
2727
  };
2666
2728
  program.name("universal-pwa").description("Transform any web project into a PWA with one click").version(version);
2667
- program.command("init").description("Initialize PWA in your project").option("-p, --project-path <path>", "Project path", process.cwd()).option("-n, --name <name>", "App name").option("-s, --short-name <shortName>", "App short name (max 12 chars)").option("-i, --icon-source <path>", "Source image for icons").option("-t, --theme-color <color>", "Theme color (hex)").option("-b, --background-color <color>", "Background color (hex)").option("--skip-icons", "Skip icon generation").option("--skip-service-worker", "Skip service worker generation").option("--skip-injection", "Skip HTML meta-tag injection").option("-o, --output-dir <dir>", "Output directory", "public").option("--force-scan", "Force scan (bypass cache)").option("--no-cache", "Disable cache").option("--max-html-files <number>", "Maximum number of HTML files to process (default: unlimited)", (value) => Number.parseInt(value, 10)).action(async (options) => {
2729
+ program.command("init").description("Initialize PWA in your project").option("-p, --project-path <path>", "Project path", process.cwd()).option("-n, --name <name>", "App name").option("-s, --short-name <shortName>", "App short name (max 12 chars)").option("-i, --icon-source <path>", "Source image for icons").option("-t, --theme-color <color>", "Theme color (hex)").option("-b, --background-color <color>", "Background color (hex)").option("--skip-icons", "Skip icon generation").option("--skip-service-worker", "Skip service worker generation").option("--skip-injection", "Skip HTML meta-tag injection").option("-o, --output-dir <dir>", "Output directory", "public").option("--force-scan", "Force scan (bypass cache)").option("--no-cache", "Disable cache").option("--max-html-files <number>", "Maximum number of HTML files to process (default: unlimited)", (value) => Number.parseInt(value, 10)).option("--html-extensions <extensions>", "Comma-separated HTML/template extensions (e.g. html,twig,blade.php,j2)", (value) => value ? value.split(",").map((s) => s.trim()).filter(Boolean) : void 0).action(async (options) => {
2668
2730
  try {
2669
2731
  const projectPath = options.projectPath ?? process.cwd();
2670
2732
  const hasOptions = options.name || options.shortName || options.iconSource || options.themeColor || options.backgroundColor || options.skipIcons !== void 0;
@@ -2723,7 +2785,8 @@ program.command("init").description("Initialize PWA in your project").option("-p
2723
2785
  outputDir: finalOptions.outputDir,
2724
2786
  forceScan: options.forceScan,
2725
2787
  noCache: options.noCache,
2726
- maxHtmlFiles: options.maxHtmlFiles
2788
+ maxHtmlFiles: options.maxHtmlFiles,
2789
+ htmlExtensions: options.htmlExtensions
2727
2790
  });
2728
2791
  process.exit(result.success ? 0 : 1);
2729
2792
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@julien-lin/universal-pwa-cli",
3
- "version": "1.3.14",
3
+ "version": "1.3.16",
4
4
  "description": "CLI to transform any web project into a PWA",
5
5
  "keywords": [
6
6
  "pwa",
@@ -66,7 +66,7 @@
66
66
  "string-width": "^8.1.1",
67
67
  "strip-ansi": "^7.1.2",
68
68
  "zod": "^4.3.6",
69
- "@julien-lin/universal-pwa-core": "^1.3.14"
69
+ "@julien-lin/universal-pwa-core": "^1.3.16"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@vitest/coverage-v8": "^4.0.18",