@julien-lin/universal-pwa-cli 1.3.3 → 1.3.5
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 +116 -80
- package/dist/index.js +116 -80
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1734,9 +1734,9 @@ async function initCommand(options = {}) {
|
|
|
1734
1734
|
if (!skipInjection) {
|
|
1735
1735
|
console.log(import_chalk2.default.blue("\u{1F489} Injecting meta-tags..."));
|
|
1736
1736
|
try {
|
|
1737
|
-
const htmlFiles = await (0, import_glob.glob)("**/*.html", {
|
|
1737
|
+
const htmlFiles = await (0, import_glob.glob)("**/*.{html,twig,html.twig,blade.php}", {
|
|
1738
1738
|
cwd: result.projectPath,
|
|
1739
|
-
ignore: ["**/node_modules/**", "**/.next/**", "**/.nuxt/**"],
|
|
1739
|
+
ignore: ["**/node_modules/**", "**/.next/**", "**/.nuxt/**", "**/vendor/**"],
|
|
1740
1740
|
absolute: true
|
|
1741
1741
|
});
|
|
1742
1742
|
htmlFiles.sort((a, b) => {
|
|
@@ -1752,7 +1752,15 @@ async function initCommand(options = {}) {
|
|
|
1752
1752
|
});
|
|
1753
1753
|
const htmlFilesToProcess = maxHtmlFiles && maxHtmlFiles > 0 ? htmlFiles.slice(0, maxHtmlFiles) : htmlFiles;
|
|
1754
1754
|
if (htmlFiles.length > 0) {
|
|
1755
|
-
|
|
1755
|
+
const htmlCount = htmlFiles.filter((f) => f.endsWith(".html") && !f.endsWith(".html.twig")).length;
|
|
1756
|
+
const twigCount = htmlFiles.filter((f) => f.endsWith(".twig") || f.endsWith(".html.twig")).length;
|
|
1757
|
+
const bladeCount = htmlFiles.filter((f) => f.endsWith(".blade.php")).length;
|
|
1758
|
+
const fileTypes = [];
|
|
1759
|
+
if (htmlCount > 0) fileTypes.push(`${htmlCount} HTML`);
|
|
1760
|
+
if (twigCount > 0) fileTypes.push(`${twigCount} Twig`);
|
|
1761
|
+
if (bladeCount > 0) fileTypes.push(`${bladeCount} Blade`);
|
|
1762
|
+
const typeSummary = fileTypes.length > 0 ? ` (${fileTypes.join(", ")})` : "";
|
|
1763
|
+
console.log(import_chalk2.default.gray(` Found ${htmlFiles.length} template file(s)${typeSummary}${maxHtmlFiles && maxHtmlFiles > 0 ? ` (processing ${htmlFilesToProcess.length})` : ""}`));
|
|
1756
1764
|
}
|
|
1757
1765
|
for (const htmlFile of htmlFilesToProcess) {
|
|
1758
1766
|
const htmlRelativePath = (0, import_path2.relative)(result.projectPath, htmlFile);
|
|
@@ -1761,6 +1769,8 @@ async function initCommand(options = {}) {
|
|
|
1761
1769
|
}
|
|
1762
1770
|
}
|
|
1763
1771
|
let injectedCount = 0;
|
|
1772
|
+
let skippedCount = 0;
|
|
1773
|
+
let errorCount = 0;
|
|
1764
1774
|
let processedCount = 0;
|
|
1765
1775
|
const totalFiles = htmlFilesToProcess.length;
|
|
1766
1776
|
for (const htmlFile of htmlFilesToProcess) {
|
|
@@ -1810,8 +1820,29 @@ async function initCommand(options = {}) {
|
|
|
1810
1820
|
});
|
|
1811
1821
|
if (injectionResult.injected.length > 0) {
|
|
1812
1822
|
injectedCount++;
|
|
1823
|
+
if (totalFiles <= 10) {
|
|
1824
|
+
const relativePath2 = (0, import_path2.relative)(result.projectPath, htmlFile);
|
|
1825
|
+
console.log(import_chalk2.default.gray(` \u2713 ${relativePath2}: ${injectionResult.injected.length} tag(s) injected`));
|
|
1826
|
+
}
|
|
1827
|
+
} else if (injectionResult.skipped.length > 0 && injectionResult.injected.length === 0) {
|
|
1828
|
+
skippedCount++;
|
|
1829
|
+
if (totalFiles <= 10) {
|
|
1830
|
+
const relativePath2 = (0, import_path2.relative)(result.projectPath, htmlFile);
|
|
1831
|
+
console.log(import_chalk2.default.gray(` \u2298 ${relativePath2}: already has PWA tags`));
|
|
1832
|
+
}
|
|
1833
|
+
} else {
|
|
1834
|
+
if (totalFiles <= 10) {
|
|
1835
|
+
const relativePath2 = (0, import_path2.relative)(result.projectPath, htmlFile);
|
|
1836
|
+
console.log(import_chalk2.default.yellow(` \u26A0 ${relativePath2}: no tags injected (check warnings)`));
|
|
1837
|
+
}
|
|
1838
|
+
if (injectionResult.warnings.length > 0) {
|
|
1839
|
+
injectionResult.warnings.forEach((warning) => {
|
|
1840
|
+
result.warnings.push(`${(0, import_path2.relative)(result.projectPath, htmlFile)}: ${warning}`);
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1813
1843
|
}
|
|
1814
1844
|
} catch (error) {
|
|
1845
|
+
errorCount++;
|
|
1815
1846
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1816
1847
|
const errorCode = detectErrorCode(error);
|
|
1817
1848
|
const warningMessage = formatError(errorCode, `${htmlFile}: ${errorMessage}`);
|
|
@@ -1820,7 +1851,19 @@ async function initCommand(options = {}) {
|
|
|
1820
1851
|
}
|
|
1821
1852
|
}
|
|
1822
1853
|
result.htmlFilesInjected = injectedCount;
|
|
1823
|
-
|
|
1854
|
+
const fileTypeLabel = htmlFilesToProcess.some((f) => f.endsWith(".twig") || f.endsWith(".html.twig") || f.endsWith(".blade.php")) ? "template file(s)" : "HTML file(s)";
|
|
1855
|
+
if (injectedCount > 0) {
|
|
1856
|
+
console.log(import_chalk2.default.green(`\u2713 Injected meta-tags in ${injectedCount} ${fileTypeLabel}`));
|
|
1857
|
+
}
|
|
1858
|
+
if (skippedCount > 0) {
|
|
1859
|
+
console.log(import_chalk2.default.gray(` ${skippedCount} ${fileTypeLabel} already had PWA tags`));
|
|
1860
|
+
}
|
|
1861
|
+
if (errorCount > 0) {
|
|
1862
|
+
console.log(import_chalk2.default.yellow(` ${errorCount} ${fileTypeLabel} had errors (see warnings above)`));
|
|
1863
|
+
}
|
|
1864
|
+
if (injectedCount === 0 && skippedCount === 0 && errorCount === 0 && totalFiles > 0) {
|
|
1865
|
+
console.log(import_chalk2.default.yellow(`\u26A0 No tags were injected in ${totalFiles} ${fileTypeLabel}. Check if files contain valid HTML structure.`));
|
|
1866
|
+
}
|
|
1824
1867
|
} catch (error) {
|
|
1825
1868
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1826
1869
|
const errorCode = detectErrorCode(error);
|
|
@@ -2556,6 +2599,66 @@ function generateSuggestions(projectPath, framework, architecture, iconPath) {
|
|
|
2556
2599
|
}
|
|
2557
2600
|
|
|
2558
2601
|
// src/prompts.ts
|
|
2602
|
+
function validateName(input) {
|
|
2603
|
+
if (!input || input.trim().length === 0) {
|
|
2604
|
+
return "Le nom de l'application est requis";
|
|
2605
|
+
}
|
|
2606
|
+
if (input.length > 50) {
|
|
2607
|
+
return "Le nom doit faire moins de 50 caract\xE8res";
|
|
2608
|
+
}
|
|
2609
|
+
return true;
|
|
2610
|
+
}
|
|
2611
|
+
function validateShortName(input) {
|
|
2612
|
+
if (!input || input.trim().length === 0) {
|
|
2613
|
+
return "Le nom court est requis";
|
|
2614
|
+
}
|
|
2615
|
+
if (input.length > 12) {
|
|
2616
|
+
return "Le nom court doit faire maximum 12 caract\xE8res";
|
|
2617
|
+
}
|
|
2618
|
+
return true;
|
|
2619
|
+
}
|
|
2620
|
+
function filterShortName(input) {
|
|
2621
|
+
return input.trim().substring(0, 12);
|
|
2622
|
+
}
|
|
2623
|
+
function validateIconSource(input, projectPath) {
|
|
2624
|
+
if (!input || input.trim().length === 0) {
|
|
2625
|
+
return true;
|
|
2626
|
+
}
|
|
2627
|
+
const fullPath = (0, import_fs8.existsSync)(input) ? input : (0, import_path8.join)(projectPath, input);
|
|
2628
|
+
if (!(0, import_fs8.existsSync)(fullPath)) {
|
|
2629
|
+
const suggestions = [
|
|
2630
|
+
"public/logo.png",
|
|
2631
|
+
"src/assets/icon.png",
|
|
2632
|
+
"assets/logo.png",
|
|
2633
|
+
"logo.png"
|
|
2634
|
+
].join(", ");
|
|
2635
|
+
return `Le fichier n'existe pas: ${input}
|
|
2636
|
+
Suggestions: ${suggestions}`;
|
|
2637
|
+
}
|
|
2638
|
+
const ext = (0, import_path8.extname)(fullPath).toLowerCase();
|
|
2639
|
+
const supportedFormats = [".png", ".jpg", ".jpeg", ".svg", ".webp"];
|
|
2640
|
+
if (!supportedFormats.includes(ext)) {
|
|
2641
|
+
return `Format non support\xE9: ${ext}. Utilisez PNG, JPG, SVG ou WebP`;
|
|
2642
|
+
}
|
|
2643
|
+
return true;
|
|
2644
|
+
}
|
|
2645
|
+
function validateHexColor(input, fieldName) {
|
|
2646
|
+
if (!input || input.trim().length === 0) {
|
|
2647
|
+
return true;
|
|
2648
|
+
}
|
|
2649
|
+
const trimmed = input.trim();
|
|
2650
|
+
if (!/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(trimmed)) {
|
|
2651
|
+
return `Format hex invalide (ex: ${fieldName === "themeColor" ? "#ffffff ou #fff" : "#000000 ou #000"})`;
|
|
2652
|
+
}
|
|
2653
|
+
return true;
|
|
2654
|
+
}
|
|
2655
|
+
function filterHexColor(input) {
|
|
2656
|
+
const trimmed = input.trim();
|
|
2657
|
+
if (/^#[A-Fa-f0-9]{3}$/.test(trimmed)) {
|
|
2658
|
+
return `#${trimmed[1]}${trimmed[1]}${trimmed[2]}${trimmed[2]}${trimmed[3]}${trimmed[3]}`;
|
|
2659
|
+
}
|
|
2660
|
+
return trimmed;
|
|
2661
|
+
}
|
|
2559
2662
|
async function promptInitOptions(projectPath, framework, architecture = null) {
|
|
2560
2663
|
const suggestions = generateSuggestions(projectPath, framework, architecture);
|
|
2561
2664
|
const defaultName = suggestions.name.name;
|
|
@@ -2601,15 +2704,7 @@ async function promptInitOptions(projectPath, framework, architecture = null) {
|
|
|
2601
2704
|
name: "name",
|
|
2602
2705
|
message: "Nom de l'application:",
|
|
2603
2706
|
default: defaultName,
|
|
2604
|
-
validate:
|
|
2605
|
-
if (!input || input.trim().length === 0) {
|
|
2606
|
-
return "Le nom de l'application est requis";
|
|
2607
|
-
}
|
|
2608
|
-
if (input.length > 50) {
|
|
2609
|
-
return "Le nom doit faire moins de 50 caract\xE8res";
|
|
2610
|
-
}
|
|
2611
|
-
return true;
|
|
2612
|
-
}
|
|
2707
|
+
validate: validateName
|
|
2613
2708
|
},
|
|
2614
2709
|
{
|
|
2615
2710
|
type: "input",
|
|
@@ -2618,44 +2713,15 @@ async function promptInitOptions(projectPath, framework, architecture = null) {
|
|
|
2618
2713
|
default: (answers) => {
|
|
2619
2714
|
return answers.name ? answers.name.substring(0, 12) : defaultShortName;
|
|
2620
2715
|
},
|
|
2621
|
-
validate:
|
|
2622
|
-
|
|
2623
|
-
return "Le nom court est requis";
|
|
2624
|
-
}
|
|
2625
|
-
if (input.length > 12) {
|
|
2626
|
-
return "Le nom court doit faire maximum 12 caract\xE8res";
|
|
2627
|
-
}
|
|
2628
|
-
return true;
|
|
2629
|
-
},
|
|
2630
|
-
filter: (input) => input.trim().substring(0, 12)
|
|
2716
|
+
validate: validateShortName,
|
|
2717
|
+
filter: filterShortName
|
|
2631
2718
|
},
|
|
2632
2719
|
{
|
|
2633
2720
|
type: "input",
|
|
2634
2721
|
name: "iconSource",
|
|
2635
2722
|
message: "Chemin vers l'image source pour les ic\xF4nes:",
|
|
2636
2723
|
default: defaultIconSource,
|
|
2637
|
-
validate: (input) =>
|
|
2638
|
-
if (!input || input.trim().length === 0) {
|
|
2639
|
-
return true;
|
|
2640
|
-
}
|
|
2641
|
-
const fullPath = (0, import_fs8.existsSync)(input) ? input : (0, import_path8.join)(projectPath, input);
|
|
2642
|
-
if (!(0, import_fs8.existsSync)(fullPath)) {
|
|
2643
|
-
const suggestions2 = [
|
|
2644
|
-
"public/logo.png",
|
|
2645
|
-
"src/assets/icon.png",
|
|
2646
|
-
"assets/logo.png",
|
|
2647
|
-
"logo.png"
|
|
2648
|
-
].join(", ");
|
|
2649
|
-
return `Le fichier n'existe pas: ${input}
|
|
2650
|
-
Suggestions: ${suggestions2}`;
|
|
2651
|
-
}
|
|
2652
|
-
const ext = (0, import_path8.extname)(fullPath).toLowerCase();
|
|
2653
|
-
const supportedFormats = [".png", ".jpg", ".jpeg", ".svg", ".webp"];
|
|
2654
|
-
if (!supportedFormats.includes(ext)) {
|
|
2655
|
-
return `Format non support\xE9: ${ext}. Utilisez PNG, JPG, SVG ou WebP`;
|
|
2656
|
-
}
|
|
2657
|
-
return true;
|
|
2658
|
-
}
|
|
2724
|
+
validate: (input) => validateIconSource(input, projectPath)
|
|
2659
2725
|
},
|
|
2660
2726
|
{
|
|
2661
2727
|
type: "confirm",
|
|
@@ -2671,46 +2737,16 @@ Suggestions: ${suggestions2}`;
|
|
|
2671
2737
|
name: "themeColor",
|
|
2672
2738
|
message: "Couleur du th\xE8me (hex, ex: #ffffff):",
|
|
2673
2739
|
default: suggestions.colors.themeColor,
|
|
2674
|
-
validate: (input) =>
|
|
2675
|
-
|
|
2676
|
-
return true;
|
|
2677
|
-
}
|
|
2678
|
-
const trimmed = input.trim();
|
|
2679
|
-
if (!/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(trimmed)) {
|
|
2680
|
-
return "Format hex invalide (ex: #ffffff ou #fff)";
|
|
2681
|
-
}
|
|
2682
|
-
return true;
|
|
2683
|
-
},
|
|
2684
|
-
filter: (input) => {
|
|
2685
|
-
const trimmed = input.trim();
|
|
2686
|
-
if (/^#[A-Fa-f0-9]{3}$/.test(trimmed)) {
|
|
2687
|
-
return `#${trimmed[1]}${trimmed[1]}${trimmed[2]}${trimmed[2]}${trimmed[3]}${trimmed[3]}`;
|
|
2688
|
-
}
|
|
2689
|
-
return trimmed;
|
|
2690
|
-
}
|
|
2740
|
+
validate: (input) => validateHexColor(input, "themeColor"),
|
|
2741
|
+
filter: filterHexColor
|
|
2691
2742
|
},
|
|
2692
2743
|
{
|
|
2693
2744
|
type: "input",
|
|
2694
2745
|
name: "backgroundColor",
|
|
2695
2746
|
message: "Couleur de fond (hex, ex: #000000):",
|
|
2696
2747
|
default: suggestions.colors.backgroundColor,
|
|
2697
|
-
validate: (input) =>
|
|
2698
|
-
|
|
2699
|
-
return true;
|
|
2700
|
-
}
|
|
2701
|
-
const trimmed = input.trim();
|
|
2702
|
-
if (!/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(trimmed)) {
|
|
2703
|
-
return "Format hex invalide (ex: #000000 ou #000)";
|
|
2704
|
-
}
|
|
2705
|
-
return true;
|
|
2706
|
-
},
|
|
2707
|
-
filter: (input) => {
|
|
2708
|
-
const trimmed = input.trim();
|
|
2709
|
-
if (/^#[A-Fa-f0-9]{3}$/.test(trimmed)) {
|
|
2710
|
-
return `#${trimmed[1]}${trimmed[1]}${trimmed[2]}${trimmed[2]}${trimmed[3]}${trimmed[3]}`;
|
|
2711
|
-
}
|
|
2712
|
-
return trimmed;
|
|
2713
|
-
}
|
|
2748
|
+
validate: (input) => validateHexColor(input, "backgroundColor"),
|
|
2749
|
+
filter: filterHexColor
|
|
2714
2750
|
}
|
|
2715
2751
|
]);
|
|
2716
2752
|
configAnswers.skipIcons = !configAnswers.skipIcons;
|
|
@@ -2732,7 +2768,7 @@ Suggestions: ${suggestions2}`;
|
|
|
2732
2768
|
// package.json
|
|
2733
2769
|
var package_default = {
|
|
2734
2770
|
name: "@julien-lin/universal-pwa-cli",
|
|
2735
|
-
version: "1.3.
|
|
2771
|
+
version: "1.3.5",
|
|
2736
2772
|
description: "CLI to transform any web project into a PWA",
|
|
2737
2773
|
keywords: [
|
|
2738
2774
|
"pwa",
|
package/dist/index.js
CHANGED
|
@@ -749,9 +749,9 @@ async function initCommand(options = {}) {
|
|
|
749
749
|
if (!skipInjection) {
|
|
750
750
|
console.log(chalk2.blue("\u{1F489} Injecting meta-tags..."));
|
|
751
751
|
try {
|
|
752
|
-
const htmlFiles = await glob("**/*.html", {
|
|
752
|
+
const htmlFiles = await glob("**/*.{html,twig,html.twig,blade.php}", {
|
|
753
753
|
cwd: result.projectPath,
|
|
754
|
-
ignore: ["**/node_modules/**", "**/.next/**", "**/.nuxt/**"],
|
|
754
|
+
ignore: ["**/node_modules/**", "**/.next/**", "**/.nuxt/**", "**/vendor/**"],
|
|
755
755
|
absolute: true
|
|
756
756
|
});
|
|
757
757
|
htmlFiles.sort((a, b) => {
|
|
@@ -767,7 +767,15 @@ async function initCommand(options = {}) {
|
|
|
767
767
|
});
|
|
768
768
|
const htmlFilesToProcess = maxHtmlFiles && maxHtmlFiles > 0 ? htmlFiles.slice(0, maxHtmlFiles) : htmlFiles;
|
|
769
769
|
if (htmlFiles.length > 0) {
|
|
770
|
-
|
|
770
|
+
const htmlCount = htmlFiles.filter((f) => f.endsWith(".html") && !f.endsWith(".html.twig")).length;
|
|
771
|
+
const twigCount = htmlFiles.filter((f) => f.endsWith(".twig") || f.endsWith(".html.twig")).length;
|
|
772
|
+
const bladeCount = htmlFiles.filter((f) => f.endsWith(".blade.php")).length;
|
|
773
|
+
const fileTypes = [];
|
|
774
|
+
if (htmlCount > 0) fileTypes.push(`${htmlCount} HTML`);
|
|
775
|
+
if (twigCount > 0) fileTypes.push(`${twigCount} Twig`);
|
|
776
|
+
if (bladeCount > 0) fileTypes.push(`${bladeCount} Blade`);
|
|
777
|
+
const typeSummary = fileTypes.length > 0 ? ` (${fileTypes.join(", ")})` : "";
|
|
778
|
+
console.log(chalk2.gray(` Found ${htmlFiles.length} template file(s)${typeSummary}${maxHtmlFiles && maxHtmlFiles > 0 ? ` (processing ${htmlFilesToProcess.length})` : ""}`));
|
|
771
779
|
}
|
|
772
780
|
for (const htmlFile of htmlFilesToProcess) {
|
|
773
781
|
const htmlRelativePath = relative(result.projectPath, htmlFile);
|
|
@@ -776,6 +784,8 @@ async function initCommand(options = {}) {
|
|
|
776
784
|
}
|
|
777
785
|
}
|
|
778
786
|
let injectedCount = 0;
|
|
787
|
+
let skippedCount = 0;
|
|
788
|
+
let errorCount = 0;
|
|
779
789
|
let processedCount = 0;
|
|
780
790
|
const totalFiles = htmlFilesToProcess.length;
|
|
781
791
|
for (const htmlFile of htmlFilesToProcess) {
|
|
@@ -825,8 +835,29 @@ async function initCommand(options = {}) {
|
|
|
825
835
|
});
|
|
826
836
|
if (injectionResult.injected.length > 0) {
|
|
827
837
|
injectedCount++;
|
|
838
|
+
if (totalFiles <= 10) {
|
|
839
|
+
const relativePath2 = relative(result.projectPath, htmlFile);
|
|
840
|
+
console.log(chalk2.gray(` \u2713 ${relativePath2}: ${injectionResult.injected.length} tag(s) injected`));
|
|
841
|
+
}
|
|
842
|
+
} else if (injectionResult.skipped.length > 0 && injectionResult.injected.length === 0) {
|
|
843
|
+
skippedCount++;
|
|
844
|
+
if (totalFiles <= 10) {
|
|
845
|
+
const relativePath2 = relative(result.projectPath, htmlFile);
|
|
846
|
+
console.log(chalk2.gray(` \u2298 ${relativePath2}: already has PWA tags`));
|
|
847
|
+
}
|
|
848
|
+
} else {
|
|
849
|
+
if (totalFiles <= 10) {
|
|
850
|
+
const relativePath2 = relative(result.projectPath, htmlFile);
|
|
851
|
+
console.log(chalk2.yellow(` \u26A0 ${relativePath2}: no tags injected (check warnings)`));
|
|
852
|
+
}
|
|
853
|
+
if (injectionResult.warnings.length > 0) {
|
|
854
|
+
injectionResult.warnings.forEach((warning) => {
|
|
855
|
+
result.warnings.push(`${relative(result.projectPath, htmlFile)}: ${warning}`);
|
|
856
|
+
});
|
|
857
|
+
}
|
|
828
858
|
}
|
|
829
859
|
} catch (error) {
|
|
860
|
+
errorCount++;
|
|
830
861
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
831
862
|
const errorCode = detectErrorCode(error);
|
|
832
863
|
const warningMessage = formatError(errorCode, `${htmlFile}: ${errorMessage}`);
|
|
@@ -835,7 +866,19 @@ async function initCommand(options = {}) {
|
|
|
835
866
|
}
|
|
836
867
|
}
|
|
837
868
|
result.htmlFilesInjected = injectedCount;
|
|
838
|
-
|
|
869
|
+
const fileTypeLabel = htmlFilesToProcess.some((f) => f.endsWith(".twig") || f.endsWith(".html.twig") || f.endsWith(".blade.php")) ? "template file(s)" : "HTML file(s)";
|
|
870
|
+
if (injectedCount > 0) {
|
|
871
|
+
console.log(chalk2.green(`\u2713 Injected meta-tags in ${injectedCount} ${fileTypeLabel}`));
|
|
872
|
+
}
|
|
873
|
+
if (skippedCount > 0) {
|
|
874
|
+
console.log(chalk2.gray(` ${skippedCount} ${fileTypeLabel} already had PWA tags`));
|
|
875
|
+
}
|
|
876
|
+
if (errorCount > 0) {
|
|
877
|
+
console.log(chalk2.yellow(` ${errorCount} ${fileTypeLabel} had errors (see warnings above)`));
|
|
878
|
+
}
|
|
879
|
+
if (injectedCount === 0 && skippedCount === 0 && errorCount === 0 && totalFiles > 0) {
|
|
880
|
+
console.log(chalk2.yellow(`\u26A0 No tags were injected in ${totalFiles} ${fileTypeLabel}. Check if files contain valid HTML structure.`));
|
|
881
|
+
}
|
|
839
882
|
} catch (error) {
|
|
840
883
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
841
884
|
const errorCode = detectErrorCode(error);
|
|
@@ -1569,6 +1612,66 @@ function generateSuggestions(projectPath, framework, architecture, iconPath) {
|
|
|
1569
1612
|
}
|
|
1570
1613
|
|
|
1571
1614
|
// src/prompts.ts
|
|
1615
|
+
function validateName(input) {
|
|
1616
|
+
if (!input || input.trim().length === 0) {
|
|
1617
|
+
return "Le nom de l'application est requis";
|
|
1618
|
+
}
|
|
1619
|
+
if (input.length > 50) {
|
|
1620
|
+
return "Le nom doit faire moins de 50 caract\xE8res";
|
|
1621
|
+
}
|
|
1622
|
+
return true;
|
|
1623
|
+
}
|
|
1624
|
+
function validateShortName(input) {
|
|
1625
|
+
if (!input || input.trim().length === 0) {
|
|
1626
|
+
return "Le nom court est requis";
|
|
1627
|
+
}
|
|
1628
|
+
if (input.length > 12) {
|
|
1629
|
+
return "Le nom court doit faire maximum 12 caract\xE8res";
|
|
1630
|
+
}
|
|
1631
|
+
return true;
|
|
1632
|
+
}
|
|
1633
|
+
function filterShortName(input) {
|
|
1634
|
+
return input.trim().substring(0, 12);
|
|
1635
|
+
}
|
|
1636
|
+
function validateIconSource(input, projectPath) {
|
|
1637
|
+
if (!input || input.trim().length === 0) {
|
|
1638
|
+
return true;
|
|
1639
|
+
}
|
|
1640
|
+
const fullPath = existsSync7(input) ? input : join7(projectPath, input);
|
|
1641
|
+
if (!existsSync7(fullPath)) {
|
|
1642
|
+
const suggestions = [
|
|
1643
|
+
"public/logo.png",
|
|
1644
|
+
"src/assets/icon.png",
|
|
1645
|
+
"assets/logo.png",
|
|
1646
|
+
"logo.png"
|
|
1647
|
+
].join(", ");
|
|
1648
|
+
return `Le fichier n'existe pas: ${input}
|
|
1649
|
+
Suggestions: ${suggestions}`;
|
|
1650
|
+
}
|
|
1651
|
+
const ext = extname(fullPath).toLowerCase();
|
|
1652
|
+
const supportedFormats = [".png", ".jpg", ".jpeg", ".svg", ".webp"];
|
|
1653
|
+
if (!supportedFormats.includes(ext)) {
|
|
1654
|
+
return `Format non support\xE9: ${ext}. Utilisez PNG, JPG, SVG ou WebP`;
|
|
1655
|
+
}
|
|
1656
|
+
return true;
|
|
1657
|
+
}
|
|
1658
|
+
function validateHexColor(input, fieldName) {
|
|
1659
|
+
if (!input || input.trim().length === 0) {
|
|
1660
|
+
return true;
|
|
1661
|
+
}
|
|
1662
|
+
const trimmed = input.trim();
|
|
1663
|
+
if (!/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(trimmed)) {
|
|
1664
|
+
return `Format hex invalide (ex: ${fieldName === "themeColor" ? "#ffffff ou #fff" : "#000000 ou #000"})`;
|
|
1665
|
+
}
|
|
1666
|
+
return true;
|
|
1667
|
+
}
|
|
1668
|
+
function filterHexColor(input) {
|
|
1669
|
+
const trimmed = input.trim();
|
|
1670
|
+
if (/^#[A-Fa-f0-9]{3}$/.test(trimmed)) {
|
|
1671
|
+
return `#${trimmed[1]}${trimmed[1]}${trimmed[2]}${trimmed[2]}${trimmed[3]}${trimmed[3]}`;
|
|
1672
|
+
}
|
|
1673
|
+
return trimmed;
|
|
1674
|
+
}
|
|
1572
1675
|
async function promptInitOptions(projectPath, framework, architecture = null) {
|
|
1573
1676
|
const suggestions = generateSuggestions(projectPath, framework, architecture);
|
|
1574
1677
|
const defaultName = suggestions.name.name;
|
|
@@ -1614,15 +1717,7 @@ async function promptInitOptions(projectPath, framework, architecture = null) {
|
|
|
1614
1717
|
name: "name",
|
|
1615
1718
|
message: "Nom de l'application:",
|
|
1616
1719
|
default: defaultName,
|
|
1617
|
-
validate:
|
|
1618
|
-
if (!input || input.trim().length === 0) {
|
|
1619
|
-
return "Le nom de l'application est requis";
|
|
1620
|
-
}
|
|
1621
|
-
if (input.length > 50) {
|
|
1622
|
-
return "Le nom doit faire moins de 50 caract\xE8res";
|
|
1623
|
-
}
|
|
1624
|
-
return true;
|
|
1625
|
-
}
|
|
1720
|
+
validate: validateName
|
|
1626
1721
|
},
|
|
1627
1722
|
{
|
|
1628
1723
|
type: "input",
|
|
@@ -1631,44 +1726,15 @@ async function promptInitOptions(projectPath, framework, architecture = null) {
|
|
|
1631
1726
|
default: (answers) => {
|
|
1632
1727
|
return answers.name ? answers.name.substring(0, 12) : defaultShortName;
|
|
1633
1728
|
},
|
|
1634
|
-
validate:
|
|
1635
|
-
|
|
1636
|
-
return "Le nom court est requis";
|
|
1637
|
-
}
|
|
1638
|
-
if (input.length > 12) {
|
|
1639
|
-
return "Le nom court doit faire maximum 12 caract\xE8res";
|
|
1640
|
-
}
|
|
1641
|
-
return true;
|
|
1642
|
-
},
|
|
1643
|
-
filter: (input) => input.trim().substring(0, 12)
|
|
1729
|
+
validate: validateShortName,
|
|
1730
|
+
filter: filterShortName
|
|
1644
1731
|
},
|
|
1645
1732
|
{
|
|
1646
1733
|
type: "input",
|
|
1647
1734
|
name: "iconSource",
|
|
1648
1735
|
message: "Chemin vers l'image source pour les ic\xF4nes:",
|
|
1649
1736
|
default: defaultIconSource,
|
|
1650
|
-
validate: (input) =>
|
|
1651
|
-
if (!input || input.trim().length === 0) {
|
|
1652
|
-
return true;
|
|
1653
|
-
}
|
|
1654
|
-
const fullPath = existsSync7(input) ? input : join7(projectPath, input);
|
|
1655
|
-
if (!existsSync7(fullPath)) {
|
|
1656
|
-
const suggestions2 = [
|
|
1657
|
-
"public/logo.png",
|
|
1658
|
-
"src/assets/icon.png",
|
|
1659
|
-
"assets/logo.png",
|
|
1660
|
-
"logo.png"
|
|
1661
|
-
].join(", ");
|
|
1662
|
-
return `Le fichier n'existe pas: ${input}
|
|
1663
|
-
Suggestions: ${suggestions2}`;
|
|
1664
|
-
}
|
|
1665
|
-
const ext = extname(fullPath).toLowerCase();
|
|
1666
|
-
const supportedFormats = [".png", ".jpg", ".jpeg", ".svg", ".webp"];
|
|
1667
|
-
if (!supportedFormats.includes(ext)) {
|
|
1668
|
-
return `Format non support\xE9: ${ext}. Utilisez PNG, JPG, SVG ou WebP`;
|
|
1669
|
-
}
|
|
1670
|
-
return true;
|
|
1671
|
-
}
|
|
1737
|
+
validate: (input) => validateIconSource(input, projectPath)
|
|
1672
1738
|
},
|
|
1673
1739
|
{
|
|
1674
1740
|
type: "confirm",
|
|
@@ -1684,46 +1750,16 @@ Suggestions: ${suggestions2}`;
|
|
|
1684
1750
|
name: "themeColor",
|
|
1685
1751
|
message: "Couleur du th\xE8me (hex, ex: #ffffff):",
|
|
1686
1752
|
default: suggestions.colors.themeColor,
|
|
1687
|
-
validate: (input) =>
|
|
1688
|
-
|
|
1689
|
-
return true;
|
|
1690
|
-
}
|
|
1691
|
-
const trimmed = input.trim();
|
|
1692
|
-
if (!/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(trimmed)) {
|
|
1693
|
-
return "Format hex invalide (ex: #ffffff ou #fff)";
|
|
1694
|
-
}
|
|
1695
|
-
return true;
|
|
1696
|
-
},
|
|
1697
|
-
filter: (input) => {
|
|
1698
|
-
const trimmed = input.trim();
|
|
1699
|
-
if (/^#[A-Fa-f0-9]{3}$/.test(trimmed)) {
|
|
1700
|
-
return `#${trimmed[1]}${trimmed[1]}${trimmed[2]}${trimmed[2]}${trimmed[3]}${trimmed[3]}`;
|
|
1701
|
-
}
|
|
1702
|
-
return trimmed;
|
|
1703
|
-
}
|
|
1753
|
+
validate: (input) => validateHexColor(input, "themeColor"),
|
|
1754
|
+
filter: filterHexColor
|
|
1704
1755
|
},
|
|
1705
1756
|
{
|
|
1706
1757
|
type: "input",
|
|
1707
1758
|
name: "backgroundColor",
|
|
1708
1759
|
message: "Couleur de fond (hex, ex: #000000):",
|
|
1709
1760
|
default: suggestions.colors.backgroundColor,
|
|
1710
|
-
validate: (input) =>
|
|
1711
|
-
|
|
1712
|
-
return true;
|
|
1713
|
-
}
|
|
1714
|
-
const trimmed = input.trim();
|
|
1715
|
-
if (!/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(trimmed)) {
|
|
1716
|
-
return "Format hex invalide (ex: #000000 ou #000)";
|
|
1717
|
-
}
|
|
1718
|
-
return true;
|
|
1719
|
-
},
|
|
1720
|
-
filter: (input) => {
|
|
1721
|
-
const trimmed = input.trim();
|
|
1722
|
-
if (/^#[A-Fa-f0-9]{3}$/.test(trimmed)) {
|
|
1723
|
-
return `#${trimmed[1]}${trimmed[1]}${trimmed[2]}${trimmed[2]}${trimmed[3]}${trimmed[3]}`;
|
|
1724
|
-
}
|
|
1725
|
-
return trimmed;
|
|
1726
|
-
}
|
|
1761
|
+
validate: (input) => validateHexColor(input, "backgroundColor"),
|
|
1762
|
+
filter: filterHexColor
|
|
1727
1763
|
}
|
|
1728
1764
|
]);
|
|
1729
1765
|
configAnswers.skipIcons = !configAnswers.skipIcons;
|
|
@@ -1745,7 +1781,7 @@ Suggestions: ${suggestions2}`;
|
|
|
1745
1781
|
// package.json
|
|
1746
1782
|
var package_default = {
|
|
1747
1783
|
name: "@julien-lin/universal-pwa-cli",
|
|
1748
|
-
version: "1.3.
|
|
1784
|
+
version: "1.3.5",
|
|
1749
1785
|
description: "CLI to transform any web project into a PWA",
|
|
1750
1786
|
keywords: [
|
|
1751
1787
|
"pwa",
|
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.5",
|
|
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.3.
|
|
65
|
+
"@julien-lin/universal-pwa-core": "^1.3.5"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@vitest/coverage-v8": "^2.1.4",
|