@julien-lin/universal-pwa-cli 1.2.5 → 1.2.7
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 +213 -33
- package/dist/index.js +212 -32
- package/package.json +2 -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_chalk5 = __toESM(require("chalk"), 1);
|
|
29
|
+
var import_fs5 = require("fs");
|
|
30
30
|
var import_url = require("url");
|
|
31
|
-
var
|
|
31
|
+
var import_path5 = require("path");
|
|
32
32
|
|
|
33
33
|
// src/commands/init.ts
|
|
34
34
|
var import_universal_pwa_core = require("@julien-lin/universal-pwa-core");
|
|
@@ -213,21 +213,46 @@ async function initCommand(options = {}) {
|
|
|
213
213
|
try {
|
|
214
214
|
const htmlFiles = await (0, import_glob.glob)("**/*.html", {
|
|
215
215
|
cwd: result.projectPath,
|
|
216
|
-
ignore: ["**/node_modules/**", "
|
|
216
|
+
ignore: ["**/node_modules/**", "**/.next/**", "**/.nuxt/**"],
|
|
217
217
|
absolute: true
|
|
218
218
|
});
|
|
219
|
+
htmlFiles.sort((a, b) => {
|
|
220
|
+
const aInDist = a.includes("/dist/");
|
|
221
|
+
const bInDist = b.includes("/dist/");
|
|
222
|
+
const aInPublic = a.includes("/public/");
|
|
223
|
+
const bInPublic = b.includes("/public/");
|
|
224
|
+
if (aInDist && !bInDist) return -1;
|
|
225
|
+
if (!aInDist && bInDist) return 1;
|
|
226
|
+
if (aInPublic && !bInPublic) return -1;
|
|
227
|
+
if (!aInPublic && bInPublic) return 1;
|
|
228
|
+
return 0;
|
|
229
|
+
});
|
|
219
230
|
let injectedCount = 0;
|
|
220
231
|
for (const htmlFile of htmlFiles.slice(0, 10)) {
|
|
221
232
|
try {
|
|
222
233
|
const normalizePathForInjection = (fullPath, basePath, outputDir2, fallback) => {
|
|
223
234
|
if (!fullPath) return fallback;
|
|
224
235
|
try {
|
|
236
|
+
const htmlInDist = htmlFile.includes("/dist/");
|
|
237
|
+
const swInDist = fullPath.includes("/dist/");
|
|
238
|
+
if (htmlInDist && swInDist) {
|
|
239
|
+
const distIndex = fullPath.indexOf("/dist/");
|
|
240
|
+
if (distIndex !== -1) {
|
|
241
|
+
const distPath = fullPath.substring(distIndex + 6);
|
|
242
|
+
return distPath.startsWith("/") ? distPath : `/${distPath}`;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
225
245
|
const rel = relativePath(fullPath, basePath);
|
|
226
246
|
let normalized = rel.startsWith("/") ? rel : `/${rel}`;
|
|
227
247
|
const outputDirName = outputDir2.replace(basePath, "").replace(/^\/+|\/+$/g, "");
|
|
228
248
|
if (outputDirName && normalized.startsWith(`/${outputDirName}/`)) {
|
|
229
249
|
normalized = normalized.replace(`/${outputDirName}/`, "/");
|
|
230
250
|
}
|
|
251
|
+
if (normalized.includes("/dist/")) {
|
|
252
|
+
const distIndex = normalized.indexOf("/dist/");
|
|
253
|
+
normalized = normalized.substring(distIndex + 6);
|
|
254
|
+
normalized = normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
255
|
+
}
|
|
231
256
|
return normalized;
|
|
232
257
|
} catch {
|
|
233
258
|
return fallback;
|
|
@@ -347,19 +372,161 @@ function previewCommand(options = {}) {
|
|
|
347
372
|
}
|
|
348
373
|
}
|
|
349
374
|
|
|
375
|
+
// src/commands/verify.ts
|
|
376
|
+
var import_fs3 = require("fs");
|
|
377
|
+
var import_path3 = require("path");
|
|
378
|
+
var import_chalk3 = __toESM(require("chalk"), 1);
|
|
379
|
+
function verifyCommand(options = {}) {
|
|
380
|
+
const {
|
|
381
|
+
projectPath = process.cwd(),
|
|
382
|
+
outputDir,
|
|
383
|
+
checkDocker = true
|
|
384
|
+
} = options;
|
|
385
|
+
const result = {
|
|
386
|
+
success: false,
|
|
387
|
+
projectPath: (0, import_path3.resolve)(projectPath),
|
|
388
|
+
outputDir: "",
|
|
389
|
+
filesFound: [],
|
|
390
|
+
filesMissing: [],
|
|
391
|
+
warnings: [],
|
|
392
|
+
errors: [],
|
|
393
|
+
dockerfileFound: false,
|
|
394
|
+
dockerfileNeedsUpdate: false,
|
|
395
|
+
dockerfileSuggestions: []
|
|
396
|
+
};
|
|
397
|
+
try {
|
|
398
|
+
if (!(0, import_fs3.existsSync)(result.projectPath)) {
|
|
399
|
+
result.errors.push(`Project path does not exist: ${result.projectPath}`);
|
|
400
|
+
return result;
|
|
401
|
+
}
|
|
402
|
+
console.log(import_chalk3.default.blue("\u{1F50D} Verifying PWA setup..."));
|
|
403
|
+
const finalOutputDir = outputDir ?? (0, import_path3.join)(result.projectPath, "public");
|
|
404
|
+
result.outputDir = finalOutputDir;
|
|
405
|
+
const requiredFiles = [
|
|
406
|
+
"sw.js",
|
|
407
|
+
"manifest.json",
|
|
408
|
+
"icon-192x192.png",
|
|
409
|
+
"icon-512x512.png",
|
|
410
|
+
"apple-touch-icon.png"
|
|
411
|
+
];
|
|
412
|
+
const recommendedFiles = [
|
|
413
|
+
"icon-72x72.png",
|
|
414
|
+
"icon-96x96.png",
|
|
415
|
+
"icon-128x128.png",
|
|
416
|
+
"icon-144x144.png",
|
|
417
|
+
"icon-152x152.png",
|
|
418
|
+
"icon-384x384.png"
|
|
419
|
+
];
|
|
420
|
+
console.log(import_chalk3.default.blue("\u{1F4CB} Checking required PWA files..."));
|
|
421
|
+
for (const file of requiredFiles) {
|
|
422
|
+
const filePath = (0, import_path3.join)(finalOutputDir, file);
|
|
423
|
+
if ((0, import_fs3.existsSync)(filePath)) {
|
|
424
|
+
result.filesFound.push(file);
|
|
425
|
+
console.log(import_chalk3.default.green(` \u2713 ${file}`));
|
|
426
|
+
} else {
|
|
427
|
+
result.filesMissing.push(file);
|
|
428
|
+
result.errors.push(`Missing required file: ${file}`);
|
|
429
|
+
console.log(import_chalk3.default.red(` \u2717 ${file} (MISSING)`));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
console.log(import_chalk3.default.blue("\u{1F4CB} Checking recommended PWA files..."));
|
|
433
|
+
for (const file of recommendedFiles) {
|
|
434
|
+
const filePath = (0, import_path3.join)(finalOutputDir, file);
|
|
435
|
+
if ((0, import_fs3.existsSync)(filePath)) {
|
|
436
|
+
result.filesFound.push(file);
|
|
437
|
+
console.log(import_chalk3.default.green(` \u2713 ${file}`));
|
|
438
|
+
} else {
|
|
439
|
+
result.filesMissing.push(file);
|
|
440
|
+
result.warnings.push(`Missing recommended file: ${file}`);
|
|
441
|
+
console.log(import_chalk3.default.yellow(` \u26A0 ${file} (recommended)`));
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (checkDocker) {
|
|
445
|
+
const dockerfilePath = (0, import_path3.join)(result.projectPath, "Dockerfile");
|
|
446
|
+
const dockerfileExists = (0, import_fs3.existsSync)(dockerfilePath);
|
|
447
|
+
result.dockerfileFound = dockerfileExists;
|
|
448
|
+
if (dockerfileExists) {
|
|
449
|
+
console.log(import_chalk3.default.blue("\u{1F433} Checking Dockerfile..."));
|
|
450
|
+
const dockerfileContent = (0, import_fs3.readFileSync)(dockerfilePath, "utf-8");
|
|
451
|
+
const pwaFilesInDockerfile = [
|
|
452
|
+
"sw.js",
|
|
453
|
+
"manifest.json",
|
|
454
|
+
"icon-",
|
|
455
|
+
"apple-touch-icon.png"
|
|
456
|
+
];
|
|
457
|
+
let needsUpdate = false;
|
|
458
|
+
for (const file of pwaFilesInDockerfile) {
|
|
459
|
+
if (!dockerfileContent.includes(file)) {
|
|
460
|
+
needsUpdate = true;
|
|
461
|
+
result.dockerfileNeedsUpdate = true;
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (needsUpdate) {
|
|
466
|
+
result.warnings.push("Dockerfile may not copy all PWA files");
|
|
467
|
+
console.log(import_chalk3.default.yellow(" \u26A0 Dockerfile may need updates to copy PWA files"));
|
|
468
|
+
if (!dockerfileContent.includes("sw.js")) {
|
|
469
|
+
result.dockerfileSuggestions.push("COPY sw.js /usr/share/nginx/html/");
|
|
470
|
+
}
|
|
471
|
+
if (!dockerfileContent.includes("manifest.json")) {
|
|
472
|
+
result.dockerfileSuggestions.push("COPY manifest.json /usr/share/nginx/html/");
|
|
473
|
+
}
|
|
474
|
+
if (!dockerfileContent.includes("icon-")) {
|
|
475
|
+
result.dockerfileSuggestions.push("COPY icon-*.png /usr/share/nginx/html/");
|
|
476
|
+
}
|
|
477
|
+
if (!dockerfileContent.includes("apple-touch-icon.png")) {
|
|
478
|
+
result.dockerfileSuggestions.push("COPY apple-touch-icon.png /usr/share/nginx/html/");
|
|
479
|
+
}
|
|
480
|
+
if (result.dockerfileSuggestions.length > 0) {
|
|
481
|
+
console.log(import_chalk3.default.yellow("\n Suggested Dockerfile additions:"));
|
|
482
|
+
result.dockerfileSuggestions.forEach((suggestion) => {
|
|
483
|
+
console.log(import_chalk3.default.gray(` ${suggestion}`));
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
} else {
|
|
487
|
+
console.log(import_chalk3.default.green(" \u2713 Dockerfile appears to copy PWA files"));
|
|
488
|
+
}
|
|
489
|
+
} else {
|
|
490
|
+
console.log(import_chalk3.default.gray(" \u2139 No Dockerfile found (skipping Docker checks)"));
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
console.log(import_chalk3.default.blue("\u{1F4C4} Checking HTML files..."));
|
|
494
|
+
result.success = result.filesMissing.length === 0 && result.errors.length === 0;
|
|
495
|
+
console.log(import_chalk3.default.blue("\n\u{1F4CA} Summary:"));
|
|
496
|
+
console.log(import_chalk3.default.gray(` Files found: ${result.filesFound.length}`));
|
|
497
|
+
console.log(import_chalk3.default.gray(` Files missing: ${result.filesMissing.length}`));
|
|
498
|
+
console.log(import_chalk3.default.gray(` Warnings: ${result.warnings.length}`));
|
|
499
|
+
console.log(import_chalk3.default.gray(` Errors: ${result.errors.length}`));
|
|
500
|
+
if (result.success) {
|
|
501
|
+
console.log(import_chalk3.default.green("\n\u2705 PWA setup is complete!"));
|
|
502
|
+
} else {
|
|
503
|
+
console.log(import_chalk3.default.red("\n\u274C PWA setup has issues that need to be fixed."));
|
|
504
|
+
if (result.dockerfileNeedsUpdate) {
|
|
505
|
+
console.log(import_chalk3.default.yellow("\n\u{1F4A1} Tip: Update your Dockerfile to copy PWA files."));
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return result;
|
|
509
|
+
} catch (error) {
|
|
510
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
511
|
+
result.errors.push(`Verification failed: ${errorMessage}`);
|
|
512
|
+
console.log(import_chalk3.default.red(`\u2717 Verification failed: ${errorMessage}`));
|
|
513
|
+
return result;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
350
517
|
// src/index.ts
|
|
351
518
|
var import_universal_pwa_core8 = require("@julien-lin/universal-pwa-core");
|
|
352
519
|
|
|
353
520
|
// src/prompts.ts
|
|
354
521
|
var import_inquirer = __toESM(require("inquirer"), 1);
|
|
355
|
-
var
|
|
356
|
-
var
|
|
357
|
-
var
|
|
522
|
+
var import_fs4 = require("fs");
|
|
523
|
+
var import_path4 = require("path");
|
|
524
|
+
var import_chalk4 = __toESM(require("chalk"), 1);
|
|
358
525
|
function detectProjectName(projectPath) {
|
|
359
526
|
try {
|
|
360
|
-
const packageJsonPath2 = (0,
|
|
361
|
-
if ((0,
|
|
362
|
-
const packageJsonContent = (0,
|
|
527
|
+
const packageJsonPath2 = (0, import_path4.join)(projectPath, "package.json");
|
|
528
|
+
if ((0, import_fs4.existsSync)(packageJsonPath2)) {
|
|
529
|
+
const packageJsonContent = (0, import_fs4.readFileSync)(packageJsonPath2, "utf-8");
|
|
363
530
|
const packageJson = JSON.parse(packageJsonContent);
|
|
364
531
|
return packageJson.name || void 0;
|
|
365
532
|
}
|
|
@@ -380,8 +547,8 @@ function findDefaultIconSource(projectPath) {
|
|
|
380
547
|
"icon.png"
|
|
381
548
|
];
|
|
382
549
|
for (const path of commonPaths) {
|
|
383
|
-
const fullPath = (0,
|
|
384
|
-
if ((0,
|
|
550
|
+
const fullPath = (0, import_path4.join)(projectPath, path);
|
|
551
|
+
if ((0, import_fs4.existsSync)(fullPath)) {
|
|
385
552
|
return path;
|
|
386
553
|
}
|
|
387
554
|
}
|
|
@@ -392,7 +559,7 @@ async function promptInitOptions(projectPath, framework) {
|
|
|
392
559
|
const defaultIconSource = findDefaultIconSource(projectPath);
|
|
393
560
|
const defaultName = detectedName || (framework ? `${framework} App` : "My PWA");
|
|
394
561
|
const defaultShortName = defaultName.substring(0, 12);
|
|
395
|
-
console.log(
|
|
562
|
+
console.log(import_chalk4.default.blue("\n\u{1F4CB} Configuration PWA\n"));
|
|
396
563
|
const answers = await import_inquirer.default.prompt([
|
|
397
564
|
{
|
|
398
565
|
type: "input",
|
|
@@ -434,8 +601,8 @@ async function promptInitOptions(projectPath, framework) {
|
|
|
434
601
|
if (!input || input.trim().length === 0) {
|
|
435
602
|
return true;
|
|
436
603
|
}
|
|
437
|
-
const fullPath = (0,
|
|
438
|
-
if (!(0,
|
|
604
|
+
const fullPath = (0, import_fs4.existsSync)(input) ? input : (0, import_path4.join)(projectPath, input);
|
|
605
|
+
if (!(0, import_fs4.existsSync)(fullPath)) {
|
|
439
606
|
return `Le fichier n'existe pas: ${input}`;
|
|
440
607
|
}
|
|
441
608
|
return true;
|
|
@@ -491,11 +658,11 @@ async function promptInitOptions(projectPath, framework) {
|
|
|
491
658
|
// src/index.ts
|
|
492
659
|
var import_meta = {};
|
|
493
660
|
var __filename = (0, import_url.fileURLToPath)(import_meta.url);
|
|
494
|
-
var __dirname = (0,
|
|
495
|
-
var packageJsonPath = (0,
|
|
661
|
+
var __dirname = (0, import_path5.dirname)(__filename);
|
|
662
|
+
var packageJsonPath = (0, import_path5.join)(__dirname, "../package.json");
|
|
496
663
|
var version = "0.0.0";
|
|
497
664
|
try {
|
|
498
|
-
const packageJsonContent = (0,
|
|
665
|
+
const packageJsonContent = (0, import_fs5.readFileSync)(packageJsonPath, "utf-8");
|
|
499
666
|
const packageJson = JSON.parse(packageJsonContent);
|
|
500
667
|
version = packageJson.version || "0.0.0";
|
|
501
668
|
} catch {
|
|
@@ -509,14 +676,14 @@ program.command("init").description("Initialize PWA in your project").option("-p
|
|
|
509
676
|
const hasOptions = options.name || options.shortName || options.iconSource || options.themeColor || options.backgroundColor || options.skipIcons !== void 0;
|
|
510
677
|
let finalOptions = { ...options };
|
|
511
678
|
if (!hasOptions) {
|
|
512
|
-
console.log(
|
|
679
|
+
console.log(import_chalk5.default.blue("\u{1F50D} Scanning project..."));
|
|
513
680
|
const scanResult = await (0, import_universal_pwa_core8.scanProject)({
|
|
514
681
|
projectPath,
|
|
515
682
|
includeAssets: false,
|
|
516
683
|
includeArchitecture: false
|
|
517
684
|
});
|
|
518
|
-
console.log(
|
|
519
|
-
console.log(
|
|
685
|
+
console.log(import_chalk5.default.green(`\u2713 Framework detected: ${scanResult.framework.framework ?? "Unknown"}`));
|
|
686
|
+
console.log(import_chalk5.default.green(`\u2713 Architecture: ${scanResult.architecture.architecture}`));
|
|
520
687
|
const promptAnswers = await promptInitOptions(projectPath, scanResult.framework.framework);
|
|
521
688
|
finalOptions = {
|
|
522
689
|
...options,
|
|
@@ -542,7 +709,7 @@ program.command("init").description("Initialize PWA in your project").option("-p
|
|
|
542
709
|
});
|
|
543
710
|
process.exit(result.success ? 0 : 1);
|
|
544
711
|
} catch (error) {
|
|
545
|
-
console.error(
|
|
712
|
+
console.error(import_chalk5.default.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
546
713
|
process.exit(1);
|
|
547
714
|
}
|
|
548
715
|
});
|
|
@@ -555,31 +722,44 @@ program.command("preview").description("Preview PWA setup").option("-p, --projec
|
|
|
555
722
|
});
|
|
556
723
|
process.exit(result.success ? 0 : 1);
|
|
557
724
|
} catch (error) {
|
|
558
|
-
console.error(
|
|
725
|
+
console.error(import_chalk5.default.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
559
726
|
process.exit(1);
|
|
560
727
|
}
|
|
561
728
|
});
|
|
562
729
|
program.command("scan").description("Scan project and detect framework/architecture").option("-p, --project-path <path>", "Project path", process.cwd()).action(async (options) => {
|
|
563
730
|
try {
|
|
564
|
-
console.log(
|
|
731
|
+
console.log(import_chalk5.default.blue("\u{1F50D} Scanning project..."));
|
|
565
732
|
const result = await (0, import_universal_pwa_core8.scanProject)({
|
|
566
733
|
projectPath: options.projectPath ?? process.cwd(),
|
|
567
734
|
includeAssets: true,
|
|
568
735
|
includeArchitecture: true
|
|
569
736
|
});
|
|
570
|
-
console.log(
|
|
737
|
+
console.log(import_chalk5.default.green(`
|
|
571
738
|
\u2713 Framework: ${result.framework.framework ?? "Unknown"}`));
|
|
572
|
-
console.log(
|
|
573
|
-
console.log(
|
|
574
|
-
console.log(
|
|
739
|
+
console.log(import_chalk5.default.green(`\u2713 Architecture: ${result.architecture.architecture}`));
|
|
740
|
+
console.log(import_chalk5.default.green(`\u2713 Build Tool: ${result.architecture.buildTool ?? "Unknown"}`));
|
|
741
|
+
console.log(import_chalk5.default.gray(`
|
|
575
742
|
Assets found:`));
|
|
576
|
-
console.log(
|
|
577
|
-
console.log(
|
|
578
|
-
console.log(
|
|
579
|
-
console.log(
|
|
743
|
+
console.log(import_chalk5.default.gray(` - JavaScript: ${result.assets.javascript.length} files`));
|
|
744
|
+
console.log(import_chalk5.default.gray(` - CSS: ${result.assets.css.length} files`));
|
|
745
|
+
console.log(import_chalk5.default.gray(` - Images: ${result.assets.images.length} files`));
|
|
746
|
+
console.log(import_chalk5.default.gray(` - Fonts: ${result.assets.fonts.length} files`));
|
|
580
747
|
process.exit(0);
|
|
581
748
|
} catch (error) {
|
|
582
|
-
console.error(
|
|
749
|
+
console.error(import_chalk5.default.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
750
|
+
process.exit(1);
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
program.command("verify").description("Verify PWA setup and check for missing files").option("-p, --project-path <path>", "Project path", process.cwd()).option("-o, --output-dir <dir>", "Output directory", "public").option("--no-docker", "Skip Dockerfile checks").action(async (options) => {
|
|
754
|
+
try {
|
|
755
|
+
const result = await verifyCommand({
|
|
756
|
+
projectPath: options.projectPath,
|
|
757
|
+
outputDir: options.outputDir,
|
|
758
|
+
checkDocker: options.docker !== false
|
|
759
|
+
});
|
|
760
|
+
process.exit(result.success ? 0 : 1);
|
|
761
|
+
} catch (error) {
|
|
762
|
+
console.error(import_chalk5.default.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
583
763
|
process.exit(1);
|
|
584
764
|
}
|
|
585
765
|
});
|
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 as
|
|
5
|
+
import chalk5 from "chalk";
|
|
6
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
|
-
import { dirname, join as
|
|
8
|
+
import { dirname, join as join5 } from "path";
|
|
9
9
|
|
|
10
10
|
// src/commands/init.ts
|
|
11
11
|
import { scanProject } from "@julien-lin/universal-pwa-core";
|
|
@@ -190,21 +190,46 @@ async function initCommand(options = {}) {
|
|
|
190
190
|
try {
|
|
191
191
|
const htmlFiles = await glob("**/*.html", {
|
|
192
192
|
cwd: result.projectPath,
|
|
193
|
-
ignore: ["**/node_modules/**", "
|
|
193
|
+
ignore: ["**/node_modules/**", "**/.next/**", "**/.nuxt/**"],
|
|
194
194
|
absolute: true
|
|
195
195
|
});
|
|
196
|
+
htmlFiles.sort((a, b) => {
|
|
197
|
+
const aInDist = a.includes("/dist/");
|
|
198
|
+
const bInDist = b.includes("/dist/");
|
|
199
|
+
const aInPublic = a.includes("/public/");
|
|
200
|
+
const bInPublic = b.includes("/public/");
|
|
201
|
+
if (aInDist && !bInDist) return -1;
|
|
202
|
+
if (!aInDist && bInDist) return 1;
|
|
203
|
+
if (aInPublic && !bInPublic) return -1;
|
|
204
|
+
if (!aInPublic && bInPublic) return 1;
|
|
205
|
+
return 0;
|
|
206
|
+
});
|
|
196
207
|
let injectedCount = 0;
|
|
197
208
|
for (const htmlFile of htmlFiles.slice(0, 10)) {
|
|
198
209
|
try {
|
|
199
210
|
const normalizePathForInjection = (fullPath, basePath, outputDir2, fallback) => {
|
|
200
211
|
if (!fullPath) return fallback;
|
|
201
212
|
try {
|
|
213
|
+
const htmlInDist = htmlFile.includes("/dist/");
|
|
214
|
+
const swInDist = fullPath.includes("/dist/");
|
|
215
|
+
if (htmlInDist && swInDist) {
|
|
216
|
+
const distIndex = fullPath.indexOf("/dist/");
|
|
217
|
+
if (distIndex !== -1) {
|
|
218
|
+
const distPath = fullPath.substring(distIndex + 6);
|
|
219
|
+
return distPath.startsWith("/") ? distPath : `/${distPath}`;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
202
222
|
const rel = relativePath(fullPath, basePath);
|
|
203
223
|
let normalized = rel.startsWith("/") ? rel : `/${rel}`;
|
|
204
224
|
const outputDirName = outputDir2.replace(basePath, "").replace(/^\/+|\/+$/g, "");
|
|
205
225
|
if (outputDirName && normalized.startsWith(`/${outputDirName}/`)) {
|
|
206
226
|
normalized = normalized.replace(`/${outputDirName}/`, "/");
|
|
207
227
|
}
|
|
228
|
+
if (normalized.includes("/dist/")) {
|
|
229
|
+
const distIndex = normalized.indexOf("/dist/");
|
|
230
|
+
normalized = normalized.substring(distIndex + 6);
|
|
231
|
+
normalized = normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
232
|
+
}
|
|
208
233
|
return normalized;
|
|
209
234
|
} catch {
|
|
210
235
|
return fallback;
|
|
@@ -324,19 +349,161 @@ function previewCommand(options = {}) {
|
|
|
324
349
|
}
|
|
325
350
|
}
|
|
326
351
|
|
|
352
|
+
// src/commands/verify.ts
|
|
353
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
354
|
+
import { join as join3, resolve as resolve3 } from "path";
|
|
355
|
+
import chalk3 from "chalk";
|
|
356
|
+
function verifyCommand(options = {}) {
|
|
357
|
+
const {
|
|
358
|
+
projectPath = process.cwd(),
|
|
359
|
+
outputDir,
|
|
360
|
+
checkDocker = true
|
|
361
|
+
} = options;
|
|
362
|
+
const result = {
|
|
363
|
+
success: false,
|
|
364
|
+
projectPath: resolve3(projectPath),
|
|
365
|
+
outputDir: "",
|
|
366
|
+
filesFound: [],
|
|
367
|
+
filesMissing: [],
|
|
368
|
+
warnings: [],
|
|
369
|
+
errors: [],
|
|
370
|
+
dockerfileFound: false,
|
|
371
|
+
dockerfileNeedsUpdate: false,
|
|
372
|
+
dockerfileSuggestions: []
|
|
373
|
+
};
|
|
374
|
+
try {
|
|
375
|
+
if (!existsSync3(result.projectPath)) {
|
|
376
|
+
result.errors.push(`Project path does not exist: ${result.projectPath}`);
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
379
|
+
console.log(chalk3.blue("\u{1F50D} Verifying PWA setup..."));
|
|
380
|
+
const finalOutputDir = outputDir ?? join3(result.projectPath, "public");
|
|
381
|
+
result.outputDir = finalOutputDir;
|
|
382
|
+
const requiredFiles = [
|
|
383
|
+
"sw.js",
|
|
384
|
+
"manifest.json",
|
|
385
|
+
"icon-192x192.png",
|
|
386
|
+
"icon-512x512.png",
|
|
387
|
+
"apple-touch-icon.png"
|
|
388
|
+
];
|
|
389
|
+
const recommendedFiles = [
|
|
390
|
+
"icon-72x72.png",
|
|
391
|
+
"icon-96x96.png",
|
|
392
|
+
"icon-128x128.png",
|
|
393
|
+
"icon-144x144.png",
|
|
394
|
+
"icon-152x152.png",
|
|
395
|
+
"icon-384x384.png"
|
|
396
|
+
];
|
|
397
|
+
console.log(chalk3.blue("\u{1F4CB} Checking required PWA files..."));
|
|
398
|
+
for (const file of requiredFiles) {
|
|
399
|
+
const filePath = join3(finalOutputDir, file);
|
|
400
|
+
if (existsSync3(filePath)) {
|
|
401
|
+
result.filesFound.push(file);
|
|
402
|
+
console.log(chalk3.green(` \u2713 ${file}`));
|
|
403
|
+
} else {
|
|
404
|
+
result.filesMissing.push(file);
|
|
405
|
+
result.errors.push(`Missing required file: ${file}`);
|
|
406
|
+
console.log(chalk3.red(` \u2717 ${file} (MISSING)`));
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
console.log(chalk3.blue("\u{1F4CB} Checking recommended PWA files..."));
|
|
410
|
+
for (const file of recommendedFiles) {
|
|
411
|
+
const filePath = join3(finalOutputDir, file);
|
|
412
|
+
if (existsSync3(filePath)) {
|
|
413
|
+
result.filesFound.push(file);
|
|
414
|
+
console.log(chalk3.green(` \u2713 ${file}`));
|
|
415
|
+
} else {
|
|
416
|
+
result.filesMissing.push(file);
|
|
417
|
+
result.warnings.push(`Missing recommended file: ${file}`);
|
|
418
|
+
console.log(chalk3.yellow(` \u26A0 ${file} (recommended)`));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (checkDocker) {
|
|
422
|
+
const dockerfilePath = join3(result.projectPath, "Dockerfile");
|
|
423
|
+
const dockerfileExists = existsSync3(dockerfilePath);
|
|
424
|
+
result.dockerfileFound = dockerfileExists;
|
|
425
|
+
if (dockerfileExists) {
|
|
426
|
+
console.log(chalk3.blue("\u{1F433} Checking Dockerfile..."));
|
|
427
|
+
const dockerfileContent = readFileSync(dockerfilePath, "utf-8");
|
|
428
|
+
const pwaFilesInDockerfile = [
|
|
429
|
+
"sw.js",
|
|
430
|
+
"manifest.json",
|
|
431
|
+
"icon-",
|
|
432
|
+
"apple-touch-icon.png"
|
|
433
|
+
];
|
|
434
|
+
let needsUpdate = false;
|
|
435
|
+
for (const file of pwaFilesInDockerfile) {
|
|
436
|
+
if (!dockerfileContent.includes(file)) {
|
|
437
|
+
needsUpdate = true;
|
|
438
|
+
result.dockerfileNeedsUpdate = true;
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (needsUpdate) {
|
|
443
|
+
result.warnings.push("Dockerfile may not copy all PWA files");
|
|
444
|
+
console.log(chalk3.yellow(" \u26A0 Dockerfile may need updates to copy PWA files"));
|
|
445
|
+
if (!dockerfileContent.includes("sw.js")) {
|
|
446
|
+
result.dockerfileSuggestions.push("COPY sw.js /usr/share/nginx/html/");
|
|
447
|
+
}
|
|
448
|
+
if (!dockerfileContent.includes("manifest.json")) {
|
|
449
|
+
result.dockerfileSuggestions.push("COPY manifest.json /usr/share/nginx/html/");
|
|
450
|
+
}
|
|
451
|
+
if (!dockerfileContent.includes("icon-")) {
|
|
452
|
+
result.dockerfileSuggestions.push("COPY icon-*.png /usr/share/nginx/html/");
|
|
453
|
+
}
|
|
454
|
+
if (!dockerfileContent.includes("apple-touch-icon.png")) {
|
|
455
|
+
result.dockerfileSuggestions.push("COPY apple-touch-icon.png /usr/share/nginx/html/");
|
|
456
|
+
}
|
|
457
|
+
if (result.dockerfileSuggestions.length > 0) {
|
|
458
|
+
console.log(chalk3.yellow("\n Suggested Dockerfile additions:"));
|
|
459
|
+
result.dockerfileSuggestions.forEach((suggestion) => {
|
|
460
|
+
console.log(chalk3.gray(` ${suggestion}`));
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
} else {
|
|
464
|
+
console.log(chalk3.green(" \u2713 Dockerfile appears to copy PWA files"));
|
|
465
|
+
}
|
|
466
|
+
} else {
|
|
467
|
+
console.log(chalk3.gray(" \u2139 No Dockerfile found (skipping Docker checks)"));
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
console.log(chalk3.blue("\u{1F4C4} Checking HTML files..."));
|
|
471
|
+
result.success = result.filesMissing.length === 0 && result.errors.length === 0;
|
|
472
|
+
console.log(chalk3.blue("\n\u{1F4CA} Summary:"));
|
|
473
|
+
console.log(chalk3.gray(` Files found: ${result.filesFound.length}`));
|
|
474
|
+
console.log(chalk3.gray(` Files missing: ${result.filesMissing.length}`));
|
|
475
|
+
console.log(chalk3.gray(` Warnings: ${result.warnings.length}`));
|
|
476
|
+
console.log(chalk3.gray(` Errors: ${result.errors.length}`));
|
|
477
|
+
if (result.success) {
|
|
478
|
+
console.log(chalk3.green("\n\u2705 PWA setup is complete!"));
|
|
479
|
+
} else {
|
|
480
|
+
console.log(chalk3.red("\n\u274C PWA setup has issues that need to be fixed."));
|
|
481
|
+
if (result.dockerfileNeedsUpdate) {
|
|
482
|
+
console.log(chalk3.yellow("\n\u{1F4A1} Tip: Update your Dockerfile to copy PWA files."));
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return result;
|
|
486
|
+
} catch (error) {
|
|
487
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
488
|
+
result.errors.push(`Verification failed: ${errorMessage}`);
|
|
489
|
+
console.log(chalk3.red(`\u2717 Verification failed: ${errorMessage}`));
|
|
490
|
+
return result;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
327
494
|
// src/index.ts
|
|
328
495
|
import { scanProject as scanProject2 } from "@julien-lin/universal-pwa-core";
|
|
329
496
|
|
|
330
497
|
// src/prompts.ts
|
|
331
498
|
import inquirer from "inquirer";
|
|
332
|
-
import { existsSync as
|
|
333
|
-
import { join as
|
|
334
|
-
import
|
|
499
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
500
|
+
import { join as join4 } from "path";
|
|
501
|
+
import chalk4 from "chalk";
|
|
335
502
|
function detectProjectName(projectPath) {
|
|
336
503
|
try {
|
|
337
|
-
const packageJsonPath2 =
|
|
338
|
-
if (
|
|
339
|
-
const packageJsonContent =
|
|
504
|
+
const packageJsonPath2 = join4(projectPath, "package.json");
|
|
505
|
+
if (existsSync4(packageJsonPath2)) {
|
|
506
|
+
const packageJsonContent = readFileSync2(packageJsonPath2, "utf-8");
|
|
340
507
|
const packageJson = JSON.parse(packageJsonContent);
|
|
341
508
|
return packageJson.name || void 0;
|
|
342
509
|
}
|
|
@@ -357,8 +524,8 @@ function findDefaultIconSource(projectPath) {
|
|
|
357
524
|
"icon.png"
|
|
358
525
|
];
|
|
359
526
|
for (const path of commonPaths) {
|
|
360
|
-
const fullPath =
|
|
361
|
-
if (
|
|
527
|
+
const fullPath = join4(projectPath, path);
|
|
528
|
+
if (existsSync4(fullPath)) {
|
|
362
529
|
return path;
|
|
363
530
|
}
|
|
364
531
|
}
|
|
@@ -369,7 +536,7 @@ async function promptInitOptions(projectPath, framework) {
|
|
|
369
536
|
const defaultIconSource = findDefaultIconSource(projectPath);
|
|
370
537
|
const defaultName = detectedName || (framework ? `${framework} App` : "My PWA");
|
|
371
538
|
const defaultShortName = defaultName.substring(0, 12);
|
|
372
|
-
console.log(
|
|
539
|
+
console.log(chalk4.blue("\n\u{1F4CB} Configuration PWA\n"));
|
|
373
540
|
const answers = await inquirer.prompt([
|
|
374
541
|
{
|
|
375
542
|
type: "input",
|
|
@@ -411,8 +578,8 @@ async function promptInitOptions(projectPath, framework) {
|
|
|
411
578
|
if (!input || input.trim().length === 0) {
|
|
412
579
|
return true;
|
|
413
580
|
}
|
|
414
|
-
const fullPath =
|
|
415
|
-
if (!
|
|
581
|
+
const fullPath = existsSync4(input) ? input : join4(projectPath, input);
|
|
582
|
+
if (!existsSync4(fullPath)) {
|
|
416
583
|
return `Le fichier n'existe pas: ${input}`;
|
|
417
584
|
}
|
|
418
585
|
return true;
|
|
@@ -468,10 +635,10 @@ async function promptInitOptions(projectPath, framework) {
|
|
|
468
635
|
// src/index.ts
|
|
469
636
|
var __filename = fileURLToPath(import.meta.url);
|
|
470
637
|
var __dirname = dirname(__filename);
|
|
471
|
-
var packageJsonPath =
|
|
638
|
+
var packageJsonPath = join5(__dirname, "../package.json");
|
|
472
639
|
var version = "0.0.0";
|
|
473
640
|
try {
|
|
474
|
-
const packageJsonContent =
|
|
641
|
+
const packageJsonContent = readFileSync3(packageJsonPath, "utf-8");
|
|
475
642
|
const packageJson = JSON.parse(packageJsonContent);
|
|
476
643
|
version = packageJson.version || "0.0.0";
|
|
477
644
|
} catch {
|
|
@@ -485,14 +652,14 @@ program.command("init").description("Initialize PWA in your project").option("-p
|
|
|
485
652
|
const hasOptions = options.name || options.shortName || options.iconSource || options.themeColor || options.backgroundColor || options.skipIcons !== void 0;
|
|
486
653
|
let finalOptions = { ...options };
|
|
487
654
|
if (!hasOptions) {
|
|
488
|
-
console.log(
|
|
655
|
+
console.log(chalk5.blue("\u{1F50D} Scanning project..."));
|
|
489
656
|
const scanResult = await scanProject2({
|
|
490
657
|
projectPath,
|
|
491
658
|
includeAssets: false,
|
|
492
659
|
includeArchitecture: false
|
|
493
660
|
});
|
|
494
|
-
console.log(
|
|
495
|
-
console.log(
|
|
661
|
+
console.log(chalk5.green(`\u2713 Framework detected: ${scanResult.framework.framework ?? "Unknown"}`));
|
|
662
|
+
console.log(chalk5.green(`\u2713 Architecture: ${scanResult.architecture.architecture}`));
|
|
496
663
|
const promptAnswers = await promptInitOptions(projectPath, scanResult.framework.framework);
|
|
497
664
|
finalOptions = {
|
|
498
665
|
...options,
|
|
@@ -518,7 +685,7 @@ program.command("init").description("Initialize PWA in your project").option("-p
|
|
|
518
685
|
});
|
|
519
686
|
process.exit(result.success ? 0 : 1);
|
|
520
687
|
} catch (error) {
|
|
521
|
-
console.error(
|
|
688
|
+
console.error(chalk5.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
522
689
|
process.exit(1);
|
|
523
690
|
}
|
|
524
691
|
});
|
|
@@ -531,31 +698,44 @@ program.command("preview").description("Preview PWA setup").option("-p, --projec
|
|
|
531
698
|
});
|
|
532
699
|
process.exit(result.success ? 0 : 1);
|
|
533
700
|
} catch (error) {
|
|
534
|
-
console.error(
|
|
701
|
+
console.error(chalk5.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
535
702
|
process.exit(1);
|
|
536
703
|
}
|
|
537
704
|
});
|
|
538
705
|
program.command("scan").description("Scan project and detect framework/architecture").option("-p, --project-path <path>", "Project path", process.cwd()).action(async (options) => {
|
|
539
706
|
try {
|
|
540
|
-
console.log(
|
|
707
|
+
console.log(chalk5.blue("\u{1F50D} Scanning project..."));
|
|
541
708
|
const result = await scanProject2({
|
|
542
709
|
projectPath: options.projectPath ?? process.cwd(),
|
|
543
710
|
includeAssets: true,
|
|
544
711
|
includeArchitecture: true
|
|
545
712
|
});
|
|
546
|
-
console.log(
|
|
713
|
+
console.log(chalk5.green(`
|
|
547
714
|
\u2713 Framework: ${result.framework.framework ?? "Unknown"}`));
|
|
548
|
-
console.log(
|
|
549
|
-
console.log(
|
|
550
|
-
console.log(
|
|
715
|
+
console.log(chalk5.green(`\u2713 Architecture: ${result.architecture.architecture}`));
|
|
716
|
+
console.log(chalk5.green(`\u2713 Build Tool: ${result.architecture.buildTool ?? "Unknown"}`));
|
|
717
|
+
console.log(chalk5.gray(`
|
|
551
718
|
Assets found:`));
|
|
552
|
-
console.log(
|
|
553
|
-
console.log(
|
|
554
|
-
console.log(
|
|
555
|
-
console.log(
|
|
719
|
+
console.log(chalk5.gray(` - JavaScript: ${result.assets.javascript.length} files`));
|
|
720
|
+
console.log(chalk5.gray(` - CSS: ${result.assets.css.length} files`));
|
|
721
|
+
console.log(chalk5.gray(` - Images: ${result.assets.images.length} files`));
|
|
722
|
+
console.log(chalk5.gray(` - Fonts: ${result.assets.fonts.length} files`));
|
|
556
723
|
process.exit(0);
|
|
557
724
|
} catch (error) {
|
|
558
|
-
console.error(
|
|
725
|
+
console.error(chalk5.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
726
|
+
process.exit(1);
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
program.command("verify").description("Verify PWA setup and check for missing files").option("-p, --project-path <path>", "Project path", process.cwd()).option("-o, --output-dir <dir>", "Output directory", "public").option("--no-docker", "Skip Dockerfile checks").action(async (options) => {
|
|
730
|
+
try {
|
|
731
|
+
const result = await verifyCommand({
|
|
732
|
+
projectPath: options.projectPath,
|
|
733
|
+
outputDir: options.outputDir,
|
|
734
|
+
checkDocker: options.docker !== false
|
|
735
|
+
});
|
|
736
|
+
process.exit(result.success ? 0 : 1);
|
|
737
|
+
} catch (error) {
|
|
738
|
+
console.error(chalk5.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
559
739
|
process.exit(1);
|
|
560
740
|
}
|
|
561
741
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@julien-lin/universal-pwa-cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.7",
|
|
4
4
|
"description": "CLI to transform any web project into a PWA",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pwa",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"inquirer": "^12.0.0",
|
|
63
63
|
"pino": "^9.14.0",
|
|
64
64
|
"zod": "^4.2.1",
|
|
65
|
-
"@julien-lin/universal-pwa-core": "^1.2.
|
|
65
|
+
"@julien-lin/universal-pwa-core": "^1.2.7"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@vitest/coverage-v8": "^2.1.4",
|