@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 +16 -28
- package/README.md +18 -30
- package/dist/index.cjs +116 -58
- package/dist/index.js +110 -47
- package/package.json +2 -2
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
|
|
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
|
|
379
|
-
const filePath = result
|
|
380
|
-
const format = result
|
|
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
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
|
|
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 (
|
|
800
|
-
console.log(
|
|
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
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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(
|
|
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
|
|
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.
|
|
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,
|
|
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,
|
|
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
|
|
268
|
-
const filePath = result
|
|
269
|
-
const format = result
|
|
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
|
|
322
|
-
|
|
323
|
-
|
|
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
|
-
|
|
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 (
|
|
689
|
-
console.log(
|
|
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
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
69
|
+
"@julien-lin/universal-pwa-core": "^1.3.16"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@vitest/coverage-v8": "^4.0.18",
|