@julien-lin/universal-pwa-cli 1.1.3 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +193 -33
- package/dist/index.js +192 -32
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -25,10 +25,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
27
|
var import_commander = require("commander");
|
|
28
|
-
var
|
|
29
|
-
var
|
|
28
|
+
var import_chalk4 = __toESM(require("chalk"), 1);
|
|
29
|
+
var import_fs4 = require("fs");
|
|
30
30
|
var import_url = require("url");
|
|
31
|
-
var
|
|
31
|
+
var import_path4 = require("path");
|
|
32
32
|
|
|
33
33
|
// src/commands/init.ts
|
|
34
34
|
var import_universal_pwa_core = require("@julien-lin/universal-pwa-core");
|
|
@@ -96,16 +96,18 @@ async function initCommand(options = {}) {
|
|
|
96
96
|
const finalOutputDir = outputDir ?? (result.framework === "WordPress" ? (0, import_path.join)(result.projectPath, "public") : (0, import_path.join)(result.projectPath, "public"));
|
|
97
97
|
console.log(import_chalk.default.blue("\u{1F4DD} Generating manifest.json..."));
|
|
98
98
|
const appName = name ?? (result.framework ? `${result.framework} App` : "My PWA");
|
|
99
|
+
const normalizedShortName = shortName && typeof shortName === "string" && shortName.trim().length > 0 ? shortName.trim() : void 0;
|
|
99
100
|
let appShortName;
|
|
100
|
-
if (
|
|
101
|
-
appShortName =
|
|
101
|
+
if (normalizedShortName && normalizedShortName.length > 0 && normalizedShortName.length <= 12) {
|
|
102
|
+
appShortName = normalizedShortName;
|
|
102
103
|
} else {
|
|
103
104
|
const fallbackName = appName && appName.length > 0 ? appName : "My PWA";
|
|
104
105
|
appShortName = fallbackName.substring(0, 12);
|
|
105
106
|
}
|
|
106
|
-
if (!appShortName || appShortName.length === 0) {
|
|
107
|
+
if (!appShortName || appShortName.trim().length === 0) {
|
|
107
108
|
appShortName = "PWA";
|
|
108
109
|
}
|
|
110
|
+
appShortName = String(appShortName);
|
|
109
111
|
let iconPaths = [];
|
|
110
112
|
if (!skipIcons && iconSource) {
|
|
111
113
|
const iconSourcePath = (0, import_fs.existsSync)(iconSource) ? iconSource : (0, import_path.join)(result.projectPath, iconSource);
|
|
@@ -135,11 +137,12 @@ async function initCommand(options = {}) {
|
|
|
135
137
|
result.warnings.push(`Icon source not found: ${iconSourcePath}`);
|
|
136
138
|
}
|
|
137
139
|
}
|
|
140
|
+
const finalShortName = appShortName && typeof appShortName === "string" && appShortName.trim().length > 0 ? appShortName.trim().substring(0, 12) : appName && appName.length > 0 ? appName.substring(0, 12) : "PWA";
|
|
138
141
|
let manifestPath;
|
|
139
142
|
if (iconPaths.length > 0) {
|
|
140
143
|
const manifestWithIcons = (0, import_universal_pwa_core2.generateManifest)({
|
|
141
144
|
name: appName,
|
|
142
|
-
shortName:
|
|
145
|
+
shortName: finalShortName,
|
|
143
146
|
startUrl: "/",
|
|
144
147
|
scope: "/",
|
|
145
148
|
display: "standalone",
|
|
@@ -159,7 +162,7 @@ async function initCommand(options = {}) {
|
|
|
159
162
|
console.log(import_chalk.default.yellow("\u26A0 Generating manifest with placeholder icon"));
|
|
160
163
|
const manifestMinimal = (0, import_universal_pwa_core2.generateManifest)({
|
|
161
164
|
name: appName,
|
|
162
|
-
shortName:
|
|
165
|
+
shortName: finalShortName,
|
|
163
166
|
startUrl: "/",
|
|
164
167
|
scope: "/",
|
|
165
168
|
display: "standalone",
|
|
@@ -337,13 +340,147 @@ function previewCommand(options = {}) {
|
|
|
337
340
|
|
|
338
341
|
// src/index.ts
|
|
339
342
|
var import_universal_pwa_core8 = require("@julien-lin/universal-pwa-core");
|
|
343
|
+
|
|
344
|
+
// src/prompts.ts
|
|
345
|
+
var import_inquirer = __toESM(require("inquirer"), 1);
|
|
346
|
+
var import_fs3 = require("fs");
|
|
347
|
+
var import_path3 = require("path");
|
|
348
|
+
var import_chalk3 = __toESM(require("chalk"), 1);
|
|
349
|
+
function detectProjectName(projectPath) {
|
|
350
|
+
try {
|
|
351
|
+
const packageJsonPath2 = (0, import_path3.join)(projectPath, "package.json");
|
|
352
|
+
if ((0, import_fs3.existsSync)(packageJsonPath2)) {
|
|
353
|
+
const packageJsonContent = (0, import_fs3.readFileSync)(packageJsonPath2, "utf-8");
|
|
354
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
355
|
+
return packageJson.name || void 0;
|
|
356
|
+
}
|
|
357
|
+
} catch {
|
|
358
|
+
}
|
|
359
|
+
return void 0;
|
|
360
|
+
}
|
|
361
|
+
function findDefaultIconSource(projectPath) {
|
|
362
|
+
const commonPaths = [
|
|
363
|
+
"public/logo.png",
|
|
364
|
+
"public/icon.png",
|
|
365
|
+
"public/favicon.png",
|
|
366
|
+
"src/assets/logo.png",
|
|
367
|
+
"src/assets/icon.png",
|
|
368
|
+
"assets/logo.png",
|
|
369
|
+
"assets/icon.png",
|
|
370
|
+
"logo.png",
|
|
371
|
+
"icon.png"
|
|
372
|
+
];
|
|
373
|
+
for (const path of commonPaths) {
|
|
374
|
+
const fullPath = (0, import_path3.join)(projectPath, path);
|
|
375
|
+
if ((0, import_fs3.existsSync)(fullPath)) {
|
|
376
|
+
return path;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return void 0;
|
|
380
|
+
}
|
|
381
|
+
async function promptInitOptions(projectPath, framework) {
|
|
382
|
+
const detectedName = detectProjectName(projectPath);
|
|
383
|
+
const defaultIconSource = findDefaultIconSource(projectPath);
|
|
384
|
+
const defaultName = detectedName || (framework ? `${framework} App` : "My PWA");
|
|
385
|
+
const defaultShortName = defaultName.substring(0, 12);
|
|
386
|
+
console.log(import_chalk3.default.blue("\n\u{1F4CB} Configuration PWA\n"));
|
|
387
|
+
const answers = await import_inquirer.default.prompt([
|
|
388
|
+
{
|
|
389
|
+
type: "input",
|
|
390
|
+
name: "name",
|
|
391
|
+
message: "Nom de l'application:",
|
|
392
|
+
default: defaultName,
|
|
393
|
+
validate: (input) => {
|
|
394
|
+
if (!input || input.trim().length === 0) {
|
|
395
|
+
return "Le nom de l'application est requis";
|
|
396
|
+
}
|
|
397
|
+
if (input.length > 50) {
|
|
398
|
+
return "Le nom doit faire moins de 50 caract\xE8res";
|
|
399
|
+
}
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
type: "input",
|
|
405
|
+
name: "shortName",
|
|
406
|
+
message: "Nom court (max 12 caract\xE8res):",
|
|
407
|
+
default: defaultShortName,
|
|
408
|
+
validate: (input) => {
|
|
409
|
+
if (!input || input.trim().length === 0) {
|
|
410
|
+
return "Le nom court est requis";
|
|
411
|
+
}
|
|
412
|
+
if (input.length > 12) {
|
|
413
|
+
return "Le nom court doit faire maximum 12 caract\xE8res";
|
|
414
|
+
}
|
|
415
|
+
return true;
|
|
416
|
+
},
|
|
417
|
+
filter: (input) => input.trim().substring(0, 12)
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
type: "input",
|
|
421
|
+
name: "iconSource",
|
|
422
|
+
message: "Chemin vers l'image source pour les ic\xF4nes:",
|
|
423
|
+
default: defaultIconSource,
|
|
424
|
+
validate: (input) => {
|
|
425
|
+
if (!input || input.trim().length === 0) {
|
|
426
|
+
return true;
|
|
427
|
+
}
|
|
428
|
+
const fullPath = (0, import_fs3.existsSync)(input) ? input : (0, import_path3.join)(projectPath, input);
|
|
429
|
+
if (!(0, import_fs3.existsSync)(fullPath)) {
|
|
430
|
+
return `Le fichier n'existe pas: ${input}`;
|
|
431
|
+
}
|
|
432
|
+
return true;
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
type: "confirm",
|
|
437
|
+
name: "skipIcons",
|
|
438
|
+
message: "G\xE9n\xE9rer les ic\xF4nes PWA?",
|
|
439
|
+
default: true
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
type: "input",
|
|
443
|
+
name: "themeColor",
|
|
444
|
+
message: "Couleur du th\xE8me (hex, ex: #ffffff):",
|
|
445
|
+
default: "#ffffff",
|
|
446
|
+
validate: (input) => {
|
|
447
|
+
if (!input || input.trim().length === 0) {
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
if (!/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(input.trim())) {
|
|
451
|
+
return "Format hex invalide (ex: #ffffff)";
|
|
452
|
+
}
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
type: "input",
|
|
458
|
+
name: "backgroundColor",
|
|
459
|
+
message: "Couleur de fond (hex, ex: #000000):",
|
|
460
|
+
default: "#000000",
|
|
461
|
+
validate: (input) => {
|
|
462
|
+
if (!input || input.trim().length === 0) {
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
if (!/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(input.trim())) {
|
|
466
|
+
return "Format hex invalide (ex: #000000)";
|
|
467
|
+
}
|
|
468
|
+
return true;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
]);
|
|
472
|
+
answers.skipIcons = !answers.skipIcons;
|
|
473
|
+
return answers;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// src/index.ts
|
|
340
477
|
var import_meta = {};
|
|
341
478
|
var __filename = (0, import_url.fileURLToPath)(import_meta.url);
|
|
342
|
-
var __dirname = (0,
|
|
343
|
-
var packageJsonPath = (0,
|
|
479
|
+
var __dirname = (0, import_path4.dirname)(__filename);
|
|
480
|
+
var packageJsonPath = (0, import_path4.join)(__dirname, "../package.json");
|
|
344
481
|
var version = "0.0.0";
|
|
345
482
|
try {
|
|
346
|
-
const packageJsonContent = (0,
|
|
483
|
+
const packageJsonContent = (0, import_fs4.readFileSync)(packageJsonPath, "utf-8");
|
|
347
484
|
const packageJson = JSON.parse(packageJsonContent);
|
|
348
485
|
version = packageJson.version || "0.0.0";
|
|
349
486
|
} catch {
|
|
@@ -353,21 +490,44 @@ var program = new import_commander.Command();
|
|
|
353
490
|
program.name("universal-pwa").description("Transform any web project into a PWA with one click").version(version);
|
|
354
491
|
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").action(async (options) => {
|
|
355
492
|
try {
|
|
493
|
+
const projectPath = options.projectPath ?? process.cwd();
|
|
494
|
+
const hasOptions = options.name || options.shortName || options.iconSource || options.themeColor || options.backgroundColor || options.skipIcons !== void 0;
|
|
495
|
+
let finalOptions = { ...options };
|
|
496
|
+
if (!hasOptions) {
|
|
497
|
+
console.log(import_chalk4.default.blue("\u{1F50D} Scanning project..."));
|
|
498
|
+
const scanResult = await (0, import_universal_pwa_core8.scanProject)({
|
|
499
|
+
projectPath,
|
|
500
|
+
includeAssets: false,
|
|
501
|
+
includeArchitecture: false
|
|
502
|
+
});
|
|
503
|
+
console.log(import_chalk4.default.green(`\u2713 Framework detected: ${scanResult.framework.framework ?? "Unknown"}`));
|
|
504
|
+
console.log(import_chalk4.default.green(`\u2713 Architecture: ${scanResult.architecture.architecture}`));
|
|
505
|
+
const promptAnswers = await promptInitOptions(projectPath, scanResult.framework.framework);
|
|
506
|
+
finalOptions = {
|
|
507
|
+
...options,
|
|
508
|
+
name: promptAnswers.name,
|
|
509
|
+
shortName: promptAnswers.shortName,
|
|
510
|
+
iconSource: promptAnswers.iconSource || void 0,
|
|
511
|
+
themeColor: promptAnswers.themeColor || void 0,
|
|
512
|
+
backgroundColor: promptAnswers.backgroundColor || void 0,
|
|
513
|
+
skipIcons: promptAnswers.skipIcons
|
|
514
|
+
};
|
|
515
|
+
}
|
|
356
516
|
const result = await initCommand({
|
|
357
|
-
projectPath:
|
|
358
|
-
name:
|
|
359
|
-
shortName:
|
|
360
|
-
iconSource:
|
|
361
|
-
themeColor:
|
|
362
|
-
backgroundColor:
|
|
363
|
-
skipIcons:
|
|
364
|
-
skipServiceWorker:
|
|
365
|
-
skipInjection:
|
|
366
|
-
outputDir:
|
|
517
|
+
projectPath: finalOptions.projectPath,
|
|
518
|
+
name: finalOptions.name,
|
|
519
|
+
shortName: finalOptions.shortName,
|
|
520
|
+
iconSource: finalOptions.iconSource,
|
|
521
|
+
themeColor: finalOptions.themeColor,
|
|
522
|
+
backgroundColor: finalOptions.backgroundColor,
|
|
523
|
+
skipIcons: finalOptions.skipIcons,
|
|
524
|
+
skipServiceWorker: finalOptions.skipServiceWorker,
|
|
525
|
+
skipInjection: finalOptions.skipInjection,
|
|
526
|
+
outputDir: finalOptions.outputDir
|
|
367
527
|
});
|
|
368
528
|
process.exit(result.success ? 0 : 1);
|
|
369
529
|
} catch (error) {
|
|
370
|
-
console.error(
|
|
530
|
+
console.error(import_chalk4.default.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
371
531
|
process.exit(1);
|
|
372
532
|
}
|
|
373
533
|
});
|
|
@@ -380,31 +540,31 @@ program.command("preview").description("Preview PWA setup").option("-p, --projec
|
|
|
380
540
|
});
|
|
381
541
|
process.exit(result.success ? 0 : 1);
|
|
382
542
|
} catch (error) {
|
|
383
|
-
console.error(
|
|
543
|
+
console.error(import_chalk4.default.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
384
544
|
process.exit(1);
|
|
385
545
|
}
|
|
386
546
|
});
|
|
387
547
|
program.command("scan").description("Scan project and detect framework/architecture").option("-p, --project-path <path>", "Project path", process.cwd()).action(async (options) => {
|
|
388
548
|
try {
|
|
389
|
-
console.log(
|
|
549
|
+
console.log(import_chalk4.default.blue("\u{1F50D} Scanning project..."));
|
|
390
550
|
const result = await (0, import_universal_pwa_core8.scanProject)({
|
|
391
551
|
projectPath: options.projectPath ?? process.cwd(),
|
|
392
552
|
includeAssets: true,
|
|
393
553
|
includeArchitecture: true
|
|
394
554
|
});
|
|
395
|
-
console.log(
|
|
555
|
+
console.log(import_chalk4.default.green(`
|
|
396
556
|
\u2713 Framework: ${result.framework.framework ?? "Unknown"}`));
|
|
397
|
-
console.log(
|
|
398
|
-
console.log(
|
|
399
|
-
console.log(
|
|
557
|
+
console.log(import_chalk4.default.green(`\u2713 Architecture: ${result.architecture.architecture}`));
|
|
558
|
+
console.log(import_chalk4.default.green(`\u2713 Build Tool: ${result.architecture.buildTool ?? "Unknown"}`));
|
|
559
|
+
console.log(import_chalk4.default.gray(`
|
|
400
560
|
Assets found:`));
|
|
401
|
-
console.log(
|
|
402
|
-
console.log(
|
|
403
|
-
console.log(
|
|
404
|
-
console.log(
|
|
561
|
+
console.log(import_chalk4.default.gray(` - JavaScript: ${result.assets.javascript.length} files`));
|
|
562
|
+
console.log(import_chalk4.default.gray(` - CSS: ${result.assets.css.length} files`));
|
|
563
|
+
console.log(import_chalk4.default.gray(` - Images: ${result.assets.images.length} files`));
|
|
564
|
+
console.log(import_chalk4.default.gray(` - Fonts: ${result.assets.fonts.length} files`));
|
|
405
565
|
process.exit(0);
|
|
406
566
|
} catch (error) {
|
|
407
|
-
console.error(
|
|
567
|
+
console.error(import_chalk4.default.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
408
568
|
process.exit(1);
|
|
409
569
|
}
|
|
410
570
|
});
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
6
|
-
import { readFileSync } from "fs";
|
|
5
|
+
import chalk4 from "chalk";
|
|
6
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
|
-
import { dirname, join as
|
|
8
|
+
import { dirname, join as join4 } from "path";
|
|
9
9
|
|
|
10
10
|
// src/commands/init.ts
|
|
11
11
|
import { scanProject } from "@julien-lin/universal-pwa-core";
|
|
@@ -73,16 +73,18 @@ async function initCommand(options = {}) {
|
|
|
73
73
|
const finalOutputDir = outputDir ?? (result.framework === "WordPress" ? join(result.projectPath, "public") : join(result.projectPath, "public"));
|
|
74
74
|
console.log(chalk.blue("\u{1F4DD} Generating manifest.json..."));
|
|
75
75
|
const appName = name ?? (result.framework ? `${result.framework} App` : "My PWA");
|
|
76
|
+
const normalizedShortName = shortName && typeof shortName === "string" && shortName.trim().length > 0 ? shortName.trim() : void 0;
|
|
76
77
|
let appShortName;
|
|
77
|
-
if (
|
|
78
|
-
appShortName =
|
|
78
|
+
if (normalizedShortName && normalizedShortName.length > 0 && normalizedShortName.length <= 12) {
|
|
79
|
+
appShortName = normalizedShortName;
|
|
79
80
|
} else {
|
|
80
81
|
const fallbackName = appName && appName.length > 0 ? appName : "My PWA";
|
|
81
82
|
appShortName = fallbackName.substring(0, 12);
|
|
82
83
|
}
|
|
83
|
-
if (!appShortName || appShortName.length === 0) {
|
|
84
|
+
if (!appShortName || appShortName.trim().length === 0) {
|
|
84
85
|
appShortName = "PWA";
|
|
85
86
|
}
|
|
87
|
+
appShortName = String(appShortName);
|
|
86
88
|
let iconPaths = [];
|
|
87
89
|
if (!skipIcons && iconSource) {
|
|
88
90
|
const iconSourcePath = existsSync(iconSource) ? iconSource : join(result.projectPath, iconSource);
|
|
@@ -112,11 +114,12 @@ async function initCommand(options = {}) {
|
|
|
112
114
|
result.warnings.push(`Icon source not found: ${iconSourcePath}`);
|
|
113
115
|
}
|
|
114
116
|
}
|
|
117
|
+
const finalShortName = appShortName && typeof appShortName === "string" && appShortName.trim().length > 0 ? appShortName.trim().substring(0, 12) : appName && appName.length > 0 ? appName.substring(0, 12) : "PWA";
|
|
115
118
|
let manifestPath;
|
|
116
119
|
if (iconPaths.length > 0) {
|
|
117
120
|
const manifestWithIcons = generateManifest({
|
|
118
121
|
name: appName,
|
|
119
|
-
shortName:
|
|
122
|
+
shortName: finalShortName,
|
|
120
123
|
startUrl: "/",
|
|
121
124
|
scope: "/",
|
|
122
125
|
display: "standalone",
|
|
@@ -136,7 +139,7 @@ async function initCommand(options = {}) {
|
|
|
136
139
|
console.log(chalk.yellow("\u26A0 Generating manifest with placeholder icon"));
|
|
137
140
|
const manifestMinimal = generateManifest({
|
|
138
141
|
name: appName,
|
|
139
|
-
shortName:
|
|
142
|
+
shortName: finalShortName,
|
|
140
143
|
startUrl: "/",
|
|
141
144
|
scope: "/",
|
|
142
145
|
display: "standalone",
|
|
@@ -314,12 +317,146 @@ function previewCommand(options = {}) {
|
|
|
314
317
|
|
|
315
318
|
// src/index.ts
|
|
316
319
|
import { scanProject as scanProject2 } from "@julien-lin/universal-pwa-core";
|
|
320
|
+
|
|
321
|
+
// src/prompts.ts
|
|
322
|
+
import inquirer from "inquirer";
|
|
323
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
324
|
+
import { join as join3 } from "path";
|
|
325
|
+
import chalk3 from "chalk";
|
|
326
|
+
function detectProjectName(projectPath) {
|
|
327
|
+
try {
|
|
328
|
+
const packageJsonPath2 = join3(projectPath, "package.json");
|
|
329
|
+
if (existsSync3(packageJsonPath2)) {
|
|
330
|
+
const packageJsonContent = readFileSync(packageJsonPath2, "utf-8");
|
|
331
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
332
|
+
return packageJson.name || void 0;
|
|
333
|
+
}
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
return void 0;
|
|
337
|
+
}
|
|
338
|
+
function findDefaultIconSource(projectPath) {
|
|
339
|
+
const commonPaths = [
|
|
340
|
+
"public/logo.png",
|
|
341
|
+
"public/icon.png",
|
|
342
|
+
"public/favicon.png",
|
|
343
|
+
"src/assets/logo.png",
|
|
344
|
+
"src/assets/icon.png",
|
|
345
|
+
"assets/logo.png",
|
|
346
|
+
"assets/icon.png",
|
|
347
|
+
"logo.png",
|
|
348
|
+
"icon.png"
|
|
349
|
+
];
|
|
350
|
+
for (const path of commonPaths) {
|
|
351
|
+
const fullPath = join3(projectPath, path);
|
|
352
|
+
if (existsSync3(fullPath)) {
|
|
353
|
+
return path;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return void 0;
|
|
357
|
+
}
|
|
358
|
+
async function promptInitOptions(projectPath, framework) {
|
|
359
|
+
const detectedName = detectProjectName(projectPath);
|
|
360
|
+
const defaultIconSource = findDefaultIconSource(projectPath);
|
|
361
|
+
const defaultName = detectedName || (framework ? `${framework} App` : "My PWA");
|
|
362
|
+
const defaultShortName = defaultName.substring(0, 12);
|
|
363
|
+
console.log(chalk3.blue("\n\u{1F4CB} Configuration PWA\n"));
|
|
364
|
+
const answers = await inquirer.prompt([
|
|
365
|
+
{
|
|
366
|
+
type: "input",
|
|
367
|
+
name: "name",
|
|
368
|
+
message: "Nom de l'application:",
|
|
369
|
+
default: defaultName,
|
|
370
|
+
validate: (input) => {
|
|
371
|
+
if (!input || input.trim().length === 0) {
|
|
372
|
+
return "Le nom de l'application est requis";
|
|
373
|
+
}
|
|
374
|
+
if (input.length > 50) {
|
|
375
|
+
return "Le nom doit faire moins de 50 caract\xE8res";
|
|
376
|
+
}
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
type: "input",
|
|
382
|
+
name: "shortName",
|
|
383
|
+
message: "Nom court (max 12 caract\xE8res):",
|
|
384
|
+
default: defaultShortName,
|
|
385
|
+
validate: (input) => {
|
|
386
|
+
if (!input || input.trim().length === 0) {
|
|
387
|
+
return "Le nom court est requis";
|
|
388
|
+
}
|
|
389
|
+
if (input.length > 12) {
|
|
390
|
+
return "Le nom court doit faire maximum 12 caract\xE8res";
|
|
391
|
+
}
|
|
392
|
+
return true;
|
|
393
|
+
},
|
|
394
|
+
filter: (input) => input.trim().substring(0, 12)
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
type: "input",
|
|
398
|
+
name: "iconSource",
|
|
399
|
+
message: "Chemin vers l'image source pour les ic\xF4nes:",
|
|
400
|
+
default: defaultIconSource,
|
|
401
|
+
validate: (input) => {
|
|
402
|
+
if (!input || input.trim().length === 0) {
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
const fullPath = existsSync3(input) ? input : join3(projectPath, input);
|
|
406
|
+
if (!existsSync3(fullPath)) {
|
|
407
|
+
return `Le fichier n'existe pas: ${input}`;
|
|
408
|
+
}
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
type: "confirm",
|
|
414
|
+
name: "skipIcons",
|
|
415
|
+
message: "G\xE9n\xE9rer les ic\xF4nes PWA?",
|
|
416
|
+
default: true
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
type: "input",
|
|
420
|
+
name: "themeColor",
|
|
421
|
+
message: "Couleur du th\xE8me (hex, ex: #ffffff):",
|
|
422
|
+
default: "#ffffff",
|
|
423
|
+
validate: (input) => {
|
|
424
|
+
if (!input || input.trim().length === 0) {
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
if (!/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(input.trim())) {
|
|
428
|
+
return "Format hex invalide (ex: #ffffff)";
|
|
429
|
+
}
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
type: "input",
|
|
435
|
+
name: "backgroundColor",
|
|
436
|
+
message: "Couleur de fond (hex, ex: #000000):",
|
|
437
|
+
default: "#000000",
|
|
438
|
+
validate: (input) => {
|
|
439
|
+
if (!input || input.trim().length === 0) {
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
if (!/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(input.trim())) {
|
|
443
|
+
return "Format hex invalide (ex: #000000)";
|
|
444
|
+
}
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
]);
|
|
449
|
+
answers.skipIcons = !answers.skipIcons;
|
|
450
|
+
return answers;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// src/index.ts
|
|
317
454
|
var __filename = fileURLToPath(import.meta.url);
|
|
318
455
|
var __dirname = dirname(__filename);
|
|
319
|
-
var packageJsonPath =
|
|
456
|
+
var packageJsonPath = join4(__dirname, "../package.json");
|
|
320
457
|
var version = "0.0.0";
|
|
321
458
|
try {
|
|
322
|
-
const packageJsonContent =
|
|
459
|
+
const packageJsonContent = readFileSync2(packageJsonPath, "utf-8");
|
|
323
460
|
const packageJson = JSON.parse(packageJsonContent);
|
|
324
461
|
version = packageJson.version || "0.0.0";
|
|
325
462
|
} catch {
|
|
@@ -329,21 +466,44 @@ var program = new Command();
|
|
|
329
466
|
program.name("universal-pwa").description("Transform any web project into a PWA with one click").version(version);
|
|
330
467
|
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").action(async (options) => {
|
|
331
468
|
try {
|
|
469
|
+
const projectPath = options.projectPath ?? process.cwd();
|
|
470
|
+
const hasOptions = options.name || options.shortName || options.iconSource || options.themeColor || options.backgroundColor || options.skipIcons !== void 0;
|
|
471
|
+
let finalOptions = { ...options };
|
|
472
|
+
if (!hasOptions) {
|
|
473
|
+
console.log(chalk4.blue("\u{1F50D} Scanning project..."));
|
|
474
|
+
const scanResult = await scanProject2({
|
|
475
|
+
projectPath,
|
|
476
|
+
includeAssets: false,
|
|
477
|
+
includeArchitecture: false
|
|
478
|
+
});
|
|
479
|
+
console.log(chalk4.green(`\u2713 Framework detected: ${scanResult.framework.framework ?? "Unknown"}`));
|
|
480
|
+
console.log(chalk4.green(`\u2713 Architecture: ${scanResult.architecture.architecture}`));
|
|
481
|
+
const promptAnswers = await promptInitOptions(projectPath, scanResult.framework.framework);
|
|
482
|
+
finalOptions = {
|
|
483
|
+
...options,
|
|
484
|
+
name: promptAnswers.name,
|
|
485
|
+
shortName: promptAnswers.shortName,
|
|
486
|
+
iconSource: promptAnswers.iconSource || void 0,
|
|
487
|
+
themeColor: promptAnswers.themeColor || void 0,
|
|
488
|
+
backgroundColor: promptAnswers.backgroundColor || void 0,
|
|
489
|
+
skipIcons: promptAnswers.skipIcons
|
|
490
|
+
};
|
|
491
|
+
}
|
|
332
492
|
const result = await initCommand({
|
|
333
|
-
projectPath:
|
|
334
|
-
name:
|
|
335
|
-
shortName:
|
|
336
|
-
iconSource:
|
|
337
|
-
themeColor:
|
|
338
|
-
backgroundColor:
|
|
339
|
-
skipIcons:
|
|
340
|
-
skipServiceWorker:
|
|
341
|
-
skipInjection:
|
|
342
|
-
outputDir:
|
|
493
|
+
projectPath: finalOptions.projectPath,
|
|
494
|
+
name: finalOptions.name,
|
|
495
|
+
shortName: finalOptions.shortName,
|
|
496
|
+
iconSource: finalOptions.iconSource,
|
|
497
|
+
themeColor: finalOptions.themeColor,
|
|
498
|
+
backgroundColor: finalOptions.backgroundColor,
|
|
499
|
+
skipIcons: finalOptions.skipIcons,
|
|
500
|
+
skipServiceWorker: finalOptions.skipServiceWorker,
|
|
501
|
+
skipInjection: finalOptions.skipInjection,
|
|
502
|
+
outputDir: finalOptions.outputDir
|
|
343
503
|
});
|
|
344
504
|
process.exit(result.success ? 0 : 1);
|
|
345
505
|
} catch (error) {
|
|
346
|
-
console.error(
|
|
506
|
+
console.error(chalk4.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
347
507
|
process.exit(1);
|
|
348
508
|
}
|
|
349
509
|
});
|
|
@@ -356,31 +516,31 @@ program.command("preview").description("Preview PWA setup").option("-p, --projec
|
|
|
356
516
|
});
|
|
357
517
|
process.exit(result.success ? 0 : 1);
|
|
358
518
|
} catch (error) {
|
|
359
|
-
console.error(
|
|
519
|
+
console.error(chalk4.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
360
520
|
process.exit(1);
|
|
361
521
|
}
|
|
362
522
|
});
|
|
363
523
|
program.command("scan").description("Scan project and detect framework/architecture").option("-p, --project-path <path>", "Project path", process.cwd()).action(async (options) => {
|
|
364
524
|
try {
|
|
365
|
-
console.log(
|
|
525
|
+
console.log(chalk4.blue("\u{1F50D} Scanning project..."));
|
|
366
526
|
const result = await scanProject2({
|
|
367
527
|
projectPath: options.projectPath ?? process.cwd(),
|
|
368
528
|
includeAssets: true,
|
|
369
529
|
includeArchitecture: true
|
|
370
530
|
});
|
|
371
|
-
console.log(
|
|
531
|
+
console.log(chalk4.green(`
|
|
372
532
|
\u2713 Framework: ${result.framework.framework ?? "Unknown"}`));
|
|
373
|
-
console.log(
|
|
374
|
-
console.log(
|
|
375
|
-
console.log(
|
|
533
|
+
console.log(chalk4.green(`\u2713 Architecture: ${result.architecture.architecture}`));
|
|
534
|
+
console.log(chalk4.green(`\u2713 Build Tool: ${result.architecture.buildTool ?? "Unknown"}`));
|
|
535
|
+
console.log(chalk4.gray(`
|
|
376
536
|
Assets found:`));
|
|
377
|
-
console.log(
|
|
378
|
-
console.log(
|
|
379
|
-
console.log(
|
|
380
|
-
console.log(
|
|
537
|
+
console.log(chalk4.gray(` - JavaScript: ${result.assets.javascript.length} files`));
|
|
538
|
+
console.log(chalk4.gray(` - CSS: ${result.assets.css.length} files`));
|
|
539
|
+
console.log(chalk4.gray(` - Images: ${result.assets.images.length} files`));
|
|
540
|
+
console.log(chalk4.gray(` - Fonts: ${result.assets.fonts.length} files`));
|
|
381
541
|
process.exit(0);
|
|
382
542
|
} catch (error) {
|
|
383
|
-
console.error(
|
|
543
|
+
console.error(chalk4.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
384
544
|
process.exit(1);
|
|
385
545
|
}
|
|
386
546
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@julien-lin/universal-pwa-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "CLI pour transformer n'importe quel projet web en PWA",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pwa",
|
|
@@ -42,9 +42,10 @@
|
|
|
42
42
|
"commander": "^12.1.0",
|
|
43
43
|
"fs-extra": "^11.3.3",
|
|
44
44
|
"glob": "^13.0.0",
|
|
45
|
+
"inquirer": "^12.0.0",
|
|
45
46
|
"pino": "^9.14.0",
|
|
46
47
|
"zod": "^4.2.1",
|
|
47
|
-
"@julien-lin/universal-pwa-core": "^1.
|
|
48
|
+
"@julien-lin/universal-pwa-core": "^1.2.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@vitest/coverage-v8": "^2.1.4",
|