@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.
Files changed (3) hide show
  1. package/dist/index.cjs +116 -80
  2. package/dist/index.js +116 -80
  3. 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
- console.log(import_chalk2.default.gray(` Found ${htmlFiles.length} HTML file(s)${maxHtmlFiles && maxHtmlFiles > 0 ? ` (processing ${htmlFilesToProcess.length})` : ""}`));
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
- console.log(import_chalk2.default.green(`\u2713 Injected meta-tags in ${injectedCount} HTML file(s)`));
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: (input) => {
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: (input) => {
2622
- if (!input || input.trim().length === 0) {
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
- if (!input || input.trim().length === 0) {
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
- if (!input || input.trim().length === 0) {
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.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
- console.log(chalk2.gray(` Found ${htmlFiles.length} HTML file(s)${maxHtmlFiles && maxHtmlFiles > 0 ? ` (processing ${htmlFilesToProcess.length})` : ""}`));
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
- console.log(chalk2.green(`\u2713 Injected meta-tags in ${injectedCount} HTML file(s)`));
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: (input) => {
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: (input) => {
1635
- if (!input || input.trim().length === 0) {
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
- if (!input || input.trim().length === 0) {
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
- if (!input || input.trim().length === 0) {
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.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",
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.3"
65
+ "@julien-lin/universal-pwa-core": "^1.3.5"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@vitest/coverage-v8": "^2.1.4",