create-next-pro-cli 0.1.11 → 0.1.13

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/install.sh CHANGED
@@ -1,18 +1,73 @@
1
1
  #!/bin/bash
2
- # Script d'installation pour create-next-pro
2
+ # Installation script for create-next-pro
3
+
4
+ # Detect runtimes
5
+ bun_path=$(command -v bun)
6
+ deno_path=$(command -v deno)
7
+ node_path=$(command -v node)
8
+
9
+ if [ -n "$bun_path" ]; then
10
+ echo "Bun detected at $bun_path. Using Bun shebang."
11
+ sed -i '1s|.*|#!/usr/bin/env bun|' dist/bin.js
12
+ elif [ -n "$deno_path" ]; then
13
+ echo "Deno detected at $deno_path. Using Deno shebang."
14
+ sed -i '1s|.*|#!/usr/bin/env deno|' dist/bin.js
15
+ elif [ -n "$node_path" ]; then
16
+ echo "Node.js detected at $node_path. Using Node shebang."
17
+ sed -i '1s|.*|#!/usr/bin/env node|' dist/bin.js
18
+ else
19
+ echo "No compatible runtime (Bun, Deno, Node.js) found."
20
+ read -p "Do you want to install Bun? [Y/n] " yn
21
+ case $yn in
22
+ [Nn]*)
23
+ echo "Installation cancelled. Do you want to delete the parent folder? [Y/n] "
24
+ read delyn
25
+ case $delyn in
26
+ [Nn]*) echo "Folder kept."; exit 1;;
27
+ *)
28
+ parent_dir="$(dirname $(pwd))"
29
+ echo "Deleting folder $parent_dir..."
30
+ rm -rf "$parent_dir"
31
+ echo "Folder deleted."
32
+ exit 1
33
+ ;;
34
+ esac
35
+ ;;
36
+ *)
37
+ echo "Please install Bun, node or Deno!"
38
+ echo "Installation instructions:"
39
+ echo "Bun: https://bun.com/"
40
+ echo "Node.js: https://nodejs.org/"
41
+ echo "Deno: https://deno.land/"
42
+ exit 1
43
+ ;;
44
+ esac
45
+ fi
3
46
 
4
47
  COMPLETION_SCRIPT="$(pwd)/create-next-pro-completion.sh"
5
- BASHRC="$HOME/.bashrc"
6
48
 
7
- read -p "Voulez-vous ajouter l'autocomplétion create-next-pro à votre .bashrc ? [O/n] " yn
49
+ # Choose shell for autocompletion
50
+ read -p "Which shell do you use? [1] zsh, [2] bash (default: bash): " shell_choice
51
+ case $shell_choice in
52
+ 1|zsh|Zsh)
53
+ RC_FILE="$HOME/.zshrc"
54
+ SHELL_NAME="zsh"
55
+ ;;
56
+ *)
57
+ RC_FILE="$HOME/.bashrc"
58
+ SHELL_NAME="bash"
59
+ ;;
60
+ esac
61
+
62
+ read -p "Do you want to add create-next-pro autocompletion to your $SHELL_NAME rc file? [Y/n] " yn
8
63
  case $yn in
9
- [Nn]*) echo "ℹ️ Autocomplétion non ajoutée"; exit;;
64
+ [Nn]*) echo "ℹ️ Autocompletion not added"; exit;;
10
65
  *)
11
- if ! grep -q "$COMPLETION_SCRIPT" "$BASHRC"; then
12
- echo "source $COMPLETION_SCRIPT" >> "$BASHRC"
13
- echo "✅ Script d'autocomplétion ajouté à .bashrc"
66
+ if ! grep -q "$COMPLETION_SCRIPT" "$RC_FILE"; then
67
+ echo "source $COMPLETION_SCRIPT" >> "$RC_FILE"
68
+ echo "✅ Autocompletion script added to $RC_FILE"
14
69
  else
15
- echo "ℹ️ Script déjà présent dans .bashrc"
70
+ echo "ℹ️ Script already present in $RC_FILE"
16
71
  fi
17
72
  ;;
18
73
  esac
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-next-pro-cli",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Advanced Next.js project scaffolder with i18n, Tailwind, App Router and more.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,9 +16,13 @@
16
16
  "dev": "bun run bin.ts",
17
17
  "scaffold-dev": "bun run src/scaffold-dev.ts",
18
18
  "postinstall": "bash install.sh",
19
+ "test": "bun test",
20
+ "build": "bun build bin.ts --outdir dist --target bun",
21
+ "prepublishOnly": "bun run build",
19
22
  "commit": "cz",
20
23
  "release": "bunx --bun standard-version",
21
- "prepare-husky": "husky"
24
+ "prepare-husky": "husky",
25
+ "version-patch": "npm version patch"
22
26
  },
23
27
  "config": {
24
28
  "commitizen": {
@@ -0,0 +1,81 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ width="700"
6
+ height="100"
7
+ viewBox="0 0 185.20833 26.458333"
8
+ version="1.1"
9
+ id="svg1"
10
+ inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
11
+ sodipodi:docname="cnp-banner.svg"
12
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ xmlns:svg="http://www.w3.org/2000/svg">
16
+ <sodipodi:namedview
17
+ id="namedview1"
18
+ pagecolor="#ffffff"
19
+ bordercolor="#000000"
20
+ borderopacity="0.25"
21
+ inkscape:showpageshadow="2"
22
+ inkscape:pageopacity="0.0"
23
+ inkscape:pagecheckerboard="0"
24
+ inkscape:deskcolor="#d1d1d1"
25
+ inkscape:document-units="mm"
26
+ inkscape:zoom="0.71179155"
27
+ inkscape:cx="303.45963"
28
+ inkscape:cy="187.55491"
29
+ inkscape:window-width="1920"
30
+ inkscape:window-height="1008"
31
+ inkscape:window-x="0"
32
+ inkscape:window-y="0"
33
+ inkscape:window-maximized="1"
34
+ inkscape:current-layer="layer1" />
35
+ <defs
36
+ id="defs1" />
37
+ <g
38
+ inkscape:label="Layer 1"
39
+ inkscape:groupmode="layer"
40
+ id="layer1">
41
+ <rect
42
+ style="fill:#ffffff;stroke-width:0.264583;-inkscape-stroke:none;paint-order:stroke fill markers"
43
+ id="rect1"
44
+ width="185.20833"
45
+ height="26.458332"
46
+ x="3.4769374e-07"
47
+ y="0" />
48
+ <g
49
+ id="g2"
50
+ transform="matrix(0.23571432,0,0,0.23571432,67.837276,-0.74839165)">
51
+ <rect
52
+ style="fill:#ffffff;stroke-width:0.237056;-inkscape-stroke:none;paint-order:stroke fill markers;stop-color:#000000"
53
+ id="rect1073"
54
+ width="79.375"
55
+ height="79.375"
56
+ x="-156.2364"
57
+ y="-8.4987068"
58
+ transform="rotate(-135.57968)" />
59
+ <path
60
+ d="m 132.55756,83.218881 c -1.45087,-1.07808 -1.84684,-2.59607 -0.99285,-4.02673 0.54618,-0.89584 7.87631,-10.06176 11.2959,-14.1249 1.06183,-1.26167 1.9306,-2.363 1.9306,-2.4474 0,-0.0844 -6.83635,-0.15346 -15.1919,-0.15346 -8.35554,0 -15.38463,-0.074 -15.6202,-0.16435 -0.73819,-0.28327 -1.20248,-1.57864 -1.20248,-3.3549 0,-1.93603 0.36655,-3.21887 1.01068,-3.53714 0.24449,-0.12081 7.31893,-0.27918 15.72098,-0.35194 l 15.27645,-0.13229 -1.9037,-2.24896 -11.50805,-13.60601 c -0.83569,-1.42821 -0.0855,-3.37279 1.59567,-4.13606 2.32887,-1.05734 3.88706,-0.26685 6.08757,2.1005 l 22.13915,21.69565 -23.95955,24.44933 c -1.78729,1.82382 -3.75874,0.53759 -4.67827,0.0387 z"
61
+ style="fill:#000000;stroke-width:0.264583"
62
+ id="path953-7"
63
+ sodipodi:nodetypes="ccssssssscccscccc" />
64
+ <path
65
+ d="m 120.13315,84.150121 c -1.12651,-0.20242 -1.88419,-0.91766 -2.16629,-2.04496 -0.0953,-0.38064 -0.0841,-1.27268 0.0209,-1.66677 0.17999,-0.67594 0.65632,-1.33366 1.19401,-1.64878 0.57143,-0.33488 1.51452,-0.46224 2.21796,-0.29954 0.93337,0.21589 1.64653,0.94136 1.91183,1.94483 0.11255,0.42567 0.11327,1.34826 0.003,1.76919 -0.24116,0.90721 -0.85714,1.57157 -1.70047,1.83404 -0.39354,0.12248 -1.11633,0.1772 -1.47926,0.11199 z m 1.01433,-1.09621 c 0.37224,-0.11055 0.68366,-0.39029 0.85405,-0.76723 0.11594,-0.25648 0.13269,-0.37842 0.13269,-0.96662 0,-0.65007 -0.006,-0.68612 -0.18717,-1.0533 -0.29739,-0.60407 -0.77766,-0.86872 -1.4636,-0.80652 -0.41619,0.0377 -0.75054,0.21803 -0.99189,0.53485 -0.27852,0.36566 -0.36924,0.75248 -0.34215,1.45897 0.0193,0.50517 0.0449,0.63742 0.16873,0.87508 0.19527,0.37474 0.42541,0.58209 0.78326,0.70568 0.36708,0.1268 0.66508,0.13224 1.04608,0.0191 z m -35.997586,-1.76067 v -2.79972 h 0.604838 0.604811 l 1.043411,1.55385 c 0.573854,0.85461 1.13321,1.67975 1.242986,1.83364 l 0.199575,0.27979 v -1.83364 -1.83364 h 0.587957 0.587931 v 2.79972 2.79972 h -0.603277 -0.603303 l -1.10617,-1.60983 c -0.608383,-0.88542 -1.15525,-1.67284 -1.215284,-1.74983 -0.108823,-0.13961 -0.109114,-0.13532 -0.110358,1.60984 v 1.74982 h -0.61595 -0.61595 z m 5.935372,0 v -2.79972 h 1.791837 1.791812 v 0.53195 0.53195 h -1.203881 -1.203881 v 0.61593 0.61594 h 1.119902 1.119876 v 0.50277 0.50277 l -1.105853,0.0152 -1.105932,0.0152 v 0.60615 0.60614 l 1.206659,-0.006 c 0.663654,-0.003 1.218036,0.005 1.231874,0.0189 0.01402,0.0137 0.0172,0.25398 0.0071,0.53385 l -0.01799,0.50887 h -1.815836 -1.815862 z m 5.18562,1.39391 c 0.50887,-0.7732 0.91242,-1.43911 0.89683,-1.4798 -0.0156,-0.0407 -0.4032,-0.63646 -0.86127,-1.32392 -0.45802,-0.68746 -0.85156,-1.28142 -0.87453,-1.31992 -0.0324,-0.0545 0.11399,-0.07 0.66117,-0.07 h 0.70281 l 0.56836,0.87137 0.56832,0.87136 0.56597,-0.87114 0.56594,-0.87113 0.65794,-2.3e-4 c 0.361854,-1.1e-4 0.657944,0.013 0.657944,0.0291 0,0.0161 -0.383784,0.61459 -0.852804,1.32987 -0.46903,0.71527 -0.85254,1.35088 -0.85225,1.41248 0,0.0616 0.40952,0.72303 0.90935,1.46985 l 0.908844,1.35787 h -0.720174 -0.72017 l -0.58298,-0.89427 -0.58296,-0.89426 -0.5824,0.89426 -0.58235,0.89426 h -0.68839 -0.68839 z m 6.349234,-0.86196 v -2.26777 h -0.86791 -0.86789 v -0.53195 -0.53195 h 2.29579 2.29577 v 0.53195 0.53195 h -0.81196 -0.81187 v 2.26777 2.26777 h -0.61595 -0.61595 z m 5.37549,-0.53829 v -2.80606 l 1.35787,0.0251 c 1.56379,0.0289 1.77927,0.0784 2.27147,0.52086 0.40214,0.36156 0.54123,0.70722 0.54176,1.34649 0,0.4552 -0.0169,0.54222 -0.16851,0.83832 -0.11337,0.22173 -0.25821,0.39786 -0.4404,0.53555 -0.41629,0.31464 -0.73226,0.39826 -1.61888,0.42848 l -0.7724,0.0263 0.0164,0.94555 0.0164,0.94556 -0.60198,-6e-5 -0.6019,-1.1e-4 z m 2.55992,-0.19915 c 0.27379,-0.12994 0.4073,-0.37161 0.40757,-0.73777 2.1e-4,-0.34865 -0.10724,-0.56367 -0.35735,-0.71477 -0.14261,-0.0862 -0.28667,-0.1054 -0.79034,-0.10557 l -0.61592,-2.3e-4 -0.0156,0.82592 -0.0156,0.82592 h 0.595 c 0.43976,0 0.64635,-0.0244 0.79203,-0.0935 z m 2.42359,0.1999 v -2.80531 l 1.41388,0.0233 c 1.58869,0.0261 1.80776,0.0711 2.2688,0.46574 0.64378,0.55106 0.75737,1.70656 0.2382,2.42324 -0.15367,0.21212 -0.52951,0.53202 -0.68662,0.58439 -0.0489,0.0163 0.6273,1.21488 1.12163,1.98799 l 0.0806,0.12599 h -0.69387 -0.69387 l -0.53046,-0.97823 -0.53044,-0.97822 -0.37796,-0.002 -0.37795,-0.002 v 0.97991 0.9799 h -0.61595 -0.61593 v -2.80531 z m 2.62982,-0.26047 c 0.50268,-0.25359 0.54642,-1.08912 0.0732,-1.39924 -0.11644,-0.0763 -0.29922,-0.10301 -0.81076,-0.11842 l -0.66043,-0.0199 v 0.81782 0.81782 l 0.60193,-1.1e-4 c 0.46162,-6e-5 0.64719,-0.0229 0.79602,-0.098 z m -25.195557,-4.23547 c -1.241928,-0.27659 -1.979798,-1.35217 -1.98247,-2.88973 -0.0026,-0.92285 0.198279,-1.53911 0.668972,-2.06263 0.771578,-0.85819 2.09632,-1.12668 3.314012,-0.67169 0.599308,0.22394 1.232271,1.01155 1.336437,1.66307 l 0.03699,0.23133 h -0.648441 -0.64852 l -0.117317,-0.26528 c -0.148961,-0.33675 -0.305779,-0.5066 -0.587825,-0.63679 -0.270325,-0.12475 -0.899266,-0.14114 -1.163691,-0.0303 -0.746892,0.31299 -1.100984,1.33312 -0.848333,2.44397 0.15076,0.66289 0.548375,1.07865 1.136094,1.18795 0.662094,0.12313 1.226688,-0.11662 1.467088,-0.62298 l 0.135202,-0.28473 h 0.627698 0.627697 l -0.08522,0.29397 c -0.179387,0.61874 -0.666565,1.22041 -1.17213,1.44759 -0.594307,0.26705 -1.427983,0.3451 -2.096241,0.19627 z m 4.255532,-2.83698 v -2.85896 l 1.553841,0.0173 c 1.74165,0.0193 1.82801,0.0366 2.29595,0.45935 0.77055,0.69616 0.64178,2.2391 -0.23537,2.8201 -0.14579,0.0965 -0.28985,0.18382 -0.32015,0.19393 -0.0304,0.0101 0.19378,0.42089 0.49805,0.91286 0.30433,0.49196 0.61064,0.98897 0.6808,1.10446 l 0.12756,0.20998 h -0.72483 -0.72485 l -0.58161,-1.0219 -0.58158,-1.0219 h -0.37772 -0.37772 v 1.0219 1.0219 h -0.61595 -0.615945 z m 2.652161,-0.29794 c 0.30876,-0.12902 0.42756,-0.33312 0.42756,-0.73472 0,-0.27308 -0.0265,-0.38238 -0.12142,-0.50318 -0.19468,-0.2475 -0.42013,-0.30874 -1.13667,-0.30874 h -0.64574 v 0.81192 0.81192 h 0.64574 c 0.42209,0 0.70972,-0.0267 0.83053,-0.0772 z m 2.779314,0.27318 v -2.88371 h 1.87582 1.87581 v 0.53088 0.53087 l -1.24589,0.0151 -1.24587,0.0151 -0.0159,0.62994 -0.0159,0.62994 h 1.17771 1.17777 v 0.53195 0.53194 h -1.17589 -1.17591 v 0.61491 0.61491 l 1.30185,0.015 1.30191,0.015 0.0161,0.54595 0.0161,0.54594 h -1.93381 -1.93382 z m 4.53557,2.86485 c 0,-0.0123 0.34933,-0.96434 0.77631,-2.11557 0.42699,-1.15123 0.89988,-2.42864 1.05085,-2.83868 l 0.27448,-0.74554 h 0.75377 c 0.57441,0 0.75901,0.0175 0.77562,0.0736 0.0212,0.0711 1.97409,5.34662 2.05423,5.54883 0.0368,0.0929 -0.008,0.0987 -0.64622,0.084 l -0.68572,-0.0158 -0.20979,-0.58795 -0.20976,-0.58794 -1.11453,-0.0151 -1.11448,-0.0151 -0.19809,0.60306 -0.19812,0.60306 -0.65426,0.0158 c -0.35989,0.009 -0.65429,0.006 -0.65429,-0.007 z m 3.22461,-3.3268 c -0.19291,-0.57744 -0.36325,-1.08769 -0.37852,-1.13389 -0.0153,-0.0462 -0.18682,0.39476 -0.38129,0.97991 -0.19444,0.58514 -0.3651,1.09539 -0.37925,1.13388 -0.0198,0.0538 0.14938,0.07 0.73202,0.07 h 0.75777 z m 3.83069,0.9939 v -2.35177 h -0.86794 -0.86791 v -0.50394 -0.50396 h 2.3518 2.35175 v 0.5025 0.50249 l -0.85392,0.0155 -0.85392,0.0154 -0.0145,2.33777 -0.0145,2.33777 h -0.61532 -0.61534 z m 3.86363,-0.53195 v -2.88371 h 1.87579 1.87581 l 6e-5,0.54595 5e-5,0.54594 -1.25992,-0.016 -1.25992,-0.016 v 0.64598 0.64598 h 1.17589 1.17586 v 0.53194 0.53195 h -1.17586 -1.17589 v 0.61594 0.61594 h 1.31585 1.31587 v 0.55994 0.55995 h -1.93179 -1.9318 z"
66
+ style="fill:#000000;stroke-width:0.0559946"
67
+ id="path996-5" />
68
+ <path
69
+ d="m 85.997863,42.113301 c -0.701702,-0.70171 -0.831586,-1.69085 -0.34208,-2.60549 0.169042,-0.31587 1.756092,-2.18327 3.526763,-4.14979 l 3.219424,-3.57549 -2.895706,-3.175 c -1.592633,-1.74625 -3.119728,-3.53219 -3.393493,-3.96875 -0.606134,-0.96652 -0.637407,-1.93468 -0.08789,-2.71925 0.362347,-0.5173 0.646694,-0.58804 2.363734,-0.58804 h 1.951858 l 4.309639,4.6261 c 2.370294,2.54436 4.400974,4.87297 4.512574,5.1747 0.12208,0.32998 0.0533,0.88519 -0.17243,1.39317 -0.5002,1.12533 -8.186048,9.48899 -9.092749,9.89461 -0.391953,0.17534 -1.283573,0.32417 -1.981411,0.33073 -1.004067,0.009 -1.404303,-0.12358 -1.918229,-0.6375 z m 18.463737,-10.12272 0.0691,-10.791385 h 2.55156 2.55156 l 4.19531,6.800235 c 2.30743,3.74013 4.28828,6.80599 4.40189,6.81302 0.11362,0.007 0.19522,-3.03293 0.18134,-6.75546 l -0.0252,-6.76825 2.39835,-0.09056 2.39835,-0.09056 v 10.83682 10.83682 l -2.68018,-0.0754 -2.68018,-0.0754 -4.00054,-6.13417 c -2.2003,-3.3738 -4.09385,-6.22537 -4.20788,-6.33684 -0.11402,-0.11146 -0.19476,2.64891 -0.17941,6.13417 l 0.0279,6.33684 -2.53554,0.0758 -2.53554,0.0758 z"
70
+ style="fill:#000000;stroke-width:0.264583"
71
+ id="path1243" />
72
+ <rect
73
+ style="fill:#000000;stroke-width:0.211033;-inkscape-stroke:none;paint-order:stroke fill markers;stop-color:#000000"
74
+ id="rect1326"
75
+ width="42.154644"
76
+ height="3.1116326"
77
+ x="61.955906"
78
+ y="57.454102" />
79
+ </g>
80
+ </g>
81
+ </svg>
@@ -1,8 +1,10 @@
1
1
  import { join } from "node:path";
2
2
  import { mkdir, readFile, writeFile, readdir } from "node:fs/promises";
3
3
  import prompts from "prompts";
4
- import { capitalize } from "./utils"; // Assuming you have a utility function to capitalize strings
5
- import { existsSync } from "node:fs";
4
+
5
+ import { capitalize, loadConfig } from "./utils";
6
+
7
+ import { existsSync, statSync } from "node:fs";
6
8
 
7
9
  export async function addComponent(args: string[]) {
8
10
  let componentName = args[1];
@@ -29,6 +31,15 @@ export async function addComponent(args: string[]) {
29
31
  componentName = response.componentName;
30
32
  }
31
33
 
34
+ const config = await loadConfig();
35
+ if (!config) {
36
+ console.error(
37
+ "❌ Configuration file cnp.config.json not found. Run this command from the project root."
38
+ );
39
+ return;
40
+ }
41
+ const useI18n = !!config.useI18n;
42
+
32
43
  const componentNameUpper = capitalize(componentName);
33
44
  const templatePath = join(
34
45
  import.meta.dir,
@@ -37,7 +48,14 @@ export async function addComponent(args: string[]) {
37
48
  "templates",
38
49
  "Component"
39
50
  );
40
- const messagesPath = join(process.cwd(), "messages");
51
+ let messagesPath: string | null = null;
52
+ if (useI18n) {
53
+ messagesPath = join(process.cwd(), "messages");
54
+ if (!existsSync(messagesPath)) {
55
+ console.error("❌ Messages directory missing. Ensure i18n was configured.");
56
+ return;
57
+ }
58
+ }
41
59
 
42
60
  // Determine target path for the component
43
61
  let componentTargetPath;
@@ -76,44 +94,46 @@ export async function addComponent(args: string[]) {
76
94
  );
77
95
  }
78
96
 
79
- // Add the component to each language's JSON file (only folders)
80
- const entries = await readdir(messagesPath, { withFileTypes: true });
81
- const langDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
82
- const jsonTemplate = join(templatePath, "component.json");
83
- if (!existsSync(jsonTemplate)) {
84
- console.error("❌ Template component.json not found:", jsonTemplate);
85
- return;
86
- }
87
- const jsonContent = await readFile(jsonTemplate, "utf-8");
88
- const parsed = JSON.parse(jsonContent);
89
97
 
90
- for (const locale of langDirs) {
91
- // Only process if messages/<locale> is a directory
92
- const localeDir = join(messagesPath, locale);
93
- if (
94
- !existsSync(localeDir) ||
95
- !require("node:fs").statSync(localeDir).isDirectory()
96
- )
97
- continue;
98
- let jsonTarget;
99
- if (pageScope) {
100
- jsonTarget = join(messagesPath, locale, `${pageScope}.json`);
101
- } else {
102
- jsonTarget = join(messagesPath, locale, `_global_ui.json`);
98
+ if (useI18n && messagesPath) {
99
+ const entries = await readdir(messagesPath, { withFileTypes: true });
100
+ const langDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
101
+ const jsonTemplate = join(templatePath, "component.json");
102
+ if (!existsSync(jsonTemplate)) {
103
+ console.error("❌ Template component.json not found:", jsonTemplate);
104
+ return;
105
+
103
106
  }
107
+ const jsonContent = await readFile(jsonTemplate, "utf-8");
108
+ const parsed = JSON.parse(jsonContent);
109
+
110
+ for (const locale of langDirs) {
111
+ // Only process if messages/<locale> is a directory
112
+ const localeDir = join(messagesPath, locale);
113
+ if (!existsSync(localeDir) || !statSync(localeDir).isDirectory())
114
+ continue;
115
+ let jsonTarget;
116
+ if (pageScope) {
117
+ jsonTarget = join(messagesPath, locale, `${pageScope}.json`);
118
+ } else {
119
+ jsonTarget = join(messagesPath, locale, `_global_ui.json`);
120
+ }
104
121
 
105
- let current: Record<string, any> = {};
106
- if (existsSync(jsonTarget)) {
107
- const jsonFile = await readFile(jsonTarget, "utf-8");
108
- current = JSON.parse(jsonFile) as Record<string, any>;
122
+ let current: Record<string, any> = {};
123
+ if (existsSync(jsonTarget)) {
124
+ const jsonFile = await readFile(jsonTarget, "utf-8");
125
+ current = JSON.parse(jsonFile) as Record<string, any>;
126
+ }
127
+ current[componentNameUpper] = parsed;
128
+ await writeFile(jsonTarget, JSON.stringify(current, null, 2));
109
129
  }
110
- current[componentNameUpper] = parsed;
111
- await writeFile(jsonTarget, JSON.stringify(current, null, 2));
130
+ } else {
131
+ console.log("ℹ️ Skipping translation entries; next-intl not enabled.");
112
132
  }
113
133
 
114
134
  console.log(
115
135
  `✅ Component "${componentNameUpper}" added ${
116
136
  pageScope ? `to page ${pageScope}` : "globally"
117
- } with localized messages.`
137
+ }${useI18n ? " with localized messages" : ""}.`
118
138
  );
119
139
  }
@@ -1,8 +1,10 @@
1
1
  import { join } from "node:path";
2
2
  import { mkdir, readFile, writeFile, readdir } from "node:fs/promises";
3
3
  import prompts from "prompts";
4
- import { capitalize, toFileName } from "./utils"; // Assuming you have a utility function to capitalize strings
5
- import { existsSync } from "node:fs";
4
+
5
+ import { capitalize, toFileName, loadConfig } from "./utils";
6
+
7
+ import { existsSync, statSync } from "node:fs";
6
8
 
7
9
  export async function addPage(args: string[]) {
8
10
  let pageName = args[1];
@@ -79,11 +81,34 @@ export async function addPage(args: string[]) {
79
81
  if (longFlags.has("--" + flag)) flags.add(flag);
80
82
  }
81
83
 
82
- const srcPath = join(process.cwd(), "src", "app", "[locale]");
83
- const messagesPath = join(process.cwd(), "messages");
84
+ const config = await loadConfig();
85
+ if (!config) {
86
+ console.error("❌ Configuration file cnp.config.json not found. Run this command from the project root.");
87
+ return;
88
+ }
89
+ const useI18n = !!config.useI18n;
90
+
91
+ const srcSegments = ["src", "app"];
92
+ if (useI18n) srcSegments.push("[locale]");
93
+ const srcPath = join(process.cwd(), ...srcSegments);
94
+ if (!existsSync(srcPath)) {
95
+ console.error(`❌ Expected directory not found: ${srcPath}`);
96
+ return;
97
+ }
98
+
99
+ let messagesPath: string | null = null;
100
+ let locales: string[] = [];
101
+ if (useI18n) {
102
+ messagesPath = join(process.cwd(), "messages");
103
+ if (!existsSync(messagesPath)) {
104
+ console.error("❌ Messages directory missing. Ensure i18n was configured.");
105
+ return;
106
+ }
107
+ const entries = await readdir(messagesPath, { withFileTypes: true });
108
+ locales = entries.filter((e) => e.isDirectory()).map((e) => e.name);
109
+ }
110
+
84
111
  const templatePath = join(import.meta.dir, "..", "..", "templates", "Page");
85
- const entries = await readdir(messagesPath, { withFileTypes: true });
86
- const locales = entries.filter((e) => e.isDirectory()).map((e) => e.name);
87
112
 
88
113
  // Create folders/files for nested or simple page
89
114
  let uiPageDir, localePagePath, jsonFileName;
@@ -130,41 +155,47 @@ export async function addPage(args: string[]) {
130
155
  console.log(`📄 File created: ${dst}`);
131
156
  }
132
157
 
133
- // Add JSON to parent object if nested, otherwise create a simple file
134
- const jsonTemplate = join(templatePath, "page.json");
135
- if (!existsSync(jsonTemplate)) {
136
- console.warn("⚠️ Missing template page.json.");
137
- }
138
- const content = await readFile(jsonTemplate, "utf-8");
139
- const replaced = content
140
- .replace(/template/g, childName || pageName)
141
- .replace(/Template/g, capitalize(childName || pageName));
142
- for (const locale of locales) {
143
- // Only process if messages/<locale> is a directory
144
- const localeDir = join(messagesPath, locale);
145
- if (
146
- !existsSync(localeDir) ||
147
- !require("node:fs").statSync(localeDir).isDirectory()
148
- )
149
- continue;
150
- const jsonTarget = join(messagesPath, locale, `${jsonFileName}.json`);
151
- let current: Record<string, any> = {};
152
- if (existsSync(jsonTarget)) {
153
- const jsonFile = await readFile(jsonTarget, "utf-8");
154
- try {
155
- current = JSON.parse(jsonFile) as Record<string, any>;
156
- } catch {
157
- current = {};
158
- }
159
- }
160
- if (parentName && childName) {
161
- current[childName] = JSON.parse(replaced);
158
+ if (useI18n && messagesPath) {
159
+ const jsonTemplate = join(templatePath, "page.json");
160
+ if (!existsSync(jsonTemplate)) {
161
+ console.warn("⚠️ Missing template page.json.");
162
+
162
163
  } else {
163
- // fichier simple
164
- current = JSON.parse(replaced);
164
+ const content = await readFile(jsonTemplate, "utf-8");
165
+ const replaced = content
166
+ .replace(/template/g, childName || pageName)
167
+ .replace(/Template/g, capitalize(childName || pageName));
168
+ for (const locale of locales) {
169
+ // Only process if messages/<locale> is a directory
170
+ const localeDir = join(messagesPath, locale);
171
+ if (!existsSync(localeDir) || !statSync(localeDir).isDirectory())
172
+ continue;
173
+ const jsonTarget = join(messagesPath, locale, `${jsonFileName}.json`);
174
+ let current: Record<string, any> = {};
175
+ if (existsSync(jsonTarget)) {
176
+ const jsonFile = await readFile(jsonTarget, "utf-8");
177
+ try {
178
+ current = JSON.parse(jsonFile) as Record<string, any>;
179
+ } catch {
180
+ current = {};
181
+ }
182
+ }
183
+ if (parentName && childName) {
184
+ current[childName] = JSON.parse(replaced);
185
+ } else {
186
+ // fichier simple
187
+ current = JSON.parse(replaced);
188
+ }
189
+ await writeFile(jsonTarget, JSON.stringify(current, null, 2));
190
+ }
165
191
  }
166
- await writeFile(jsonTarget, JSON.stringify(current, null, 2));
192
+ } else {
193
+ console.log("ℹ️ Skipping translation templates; next-intl not enabled.");
167
194
  }
168
195
 
169
- console.log(`✅ Page "${pageName}" with templates added for each locale.`);
196
+ console.log(
197
+ `✅ Page "${pageName}" with templates added${
198
+ useI18n ? " for each locale" : ""
199
+ }.`
200
+ );
170
201
  }
@@ -0,0 +1,23 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { toFileName } from "./utils";
3
+
4
+ describe("toFileName", () => {
5
+ const cases: [string, string][] = [
6
+ ["layout", "layout.tsx"],
7
+ ["page", "page.tsx"],
8
+ ["loading", "loading.tsx"],
9
+ ["not-found", "not-found.tsx"],
10
+ ["error", "error.tsx"],
11
+ ["global-error", "global-error.tsx"],
12
+ ["route", "route.ts"],
13
+ ["template", "template.tsx"],
14
+ ["default", "default.tsx"],
15
+ ["foo", "foo.tsx"],
16
+ ];
17
+
18
+ for (const [key, expected] of cases) {
19
+ test(`maps "${key}" to "${expected}"`, () => {
20
+ expect(toFileName(key)).toBe(expected);
21
+ });
22
+ }
23
+ });
package/src/lib/utils.ts CHANGED
@@ -1,3 +1,7 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+
1
5
  /**
2
6
  * Capitalize the first letter of a string.
3
7
  */
@@ -5,6 +9,26 @@ export function capitalize(str: string): string {
5
9
  return str.charAt(0).toUpperCase() + str.slice(1);
6
10
  }
7
11
 
12
+ export interface CNPConfig {
13
+ useI18n?: boolean;
14
+ [key: string]: any;
15
+ }
16
+
17
+ /**
18
+ * Load CLI configuration from the project root.
19
+ * Returns null if the configuration file is missing or invalid.
20
+ */
21
+ export async function loadConfig(): Promise<CNPConfig | null> {
22
+ const configPath = join(process.cwd(), "cnp.config.json");
23
+ if (!existsSync(configPath)) return null;
24
+ try {
25
+ const raw = await readFile(configPath, "utf-8");
26
+ return JSON.parse(raw) as CNPConfig;
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
8
32
  /**
9
33
  * Map a key to its corresponding file name for page/component templates.
10
34
  * @param key string
package/src/scaffold.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/scaffold.ts
2
2
 
3
- import { cp, mkdir, rm } from "node:fs/promises";
3
+ import { cp, mkdir, rm, writeFile, readFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
5
  import { existsSync } from "node:fs";
6
6
 
@@ -36,7 +36,7 @@ export async function scaffoldProject(options: ScaffoldOptions) {
36
36
  "..",
37
37
  "templates",
38
38
  "Projects",
39
- "default"
39
+ "default",
40
40
  );
41
41
 
42
42
  // Check if target directory exists
@@ -46,7 +46,7 @@ export async function scaffoldProject(options: ScaffoldOptions) {
46
46
  await rm(targetPath, { recursive: true, force: true });
47
47
  } else {
48
48
  console.error(
49
- "❌ Target directory already exists. Use --force to overwrite."
49
+ "❌ Target directory already exists. Use --force to overwrite.",
50
50
  );
51
51
  process.exit(1);
52
52
  }
@@ -59,7 +59,23 @@ export async function scaffoldProject(options: ScaffoldOptions) {
59
59
  console.log("📦 Copying files from template...");
60
60
  await cp(templatePath, targetPath, { recursive: true });
61
61
 
62
- // Future customization: remove unused files if useTailwind === false, etc.
62
+ // Apply configuration: add dependencies or files based on prompt choices
63
+ const pkgPath = join(targetPath, "package.json");
64
+ if (existsSync(pkgPath)) {
65
+ const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
66
+ pkg.dependencies = pkg.dependencies || {};
67
+ if (options.useI18n) {
68
+ pkg.dependencies["next-intl"] =
69
+ pkg.dependencies["next-intl"] || "^4.3.5";
70
+ }
71
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2));
72
+ }
73
+
74
+ // Write CLI configuration to project root
75
+ await writeFile(
76
+ join(targetPath, "cnp.config.json"),
77
+ JSON.stringify(options, null, 2),
78
+ );
63
79
 
64
80
  console.log("✅ Project scaffolded successfully!");
65
81
  console.log(`➡️ cd ${options.projectName} && bun install && bun dev`);
package/.husky/commit-msg DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env sh
2
- bunx --bun commitlint --edit "$1"
package/.husky/pre-commit DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env sh
2
- bunx --bun lint-staged
@@ -1,33 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- We as members, contributors, and leaders pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
-
7
- ## Our Standards
8
-
9
- Examples of behavior that contributes to a positive environment for our community include:
10
-
11
- - Using welcoming and inclusive language
12
- - Being respectful of differing viewpoints and experiences
13
- - Gracefully accepting constructive criticism
14
- - Focusing on what is best for the community
15
- - Showing empathy towards other community members
16
-
17
- Examples of unacceptable behavior include:
18
-
19
- - The use of sexualized language or imagery and unwelcome sexual attention or advances
20
- - Trolling, insulting/derogatory comments, and personal or political attacks
21
- - Public or private harassment
22
- - Publishing others' private information, such as a physical or electronic address, without explicit permission
23
- - Other conduct which could reasonably be considered inappropriate in a professional setting
24
-
25
- ## Enforcement
26
-
27
- Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
28
-
29
- Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
30
-
31
- ## Attribution
32
-
33
- This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
package/CONTRIBUTING.md DELETED
@@ -1,44 +0,0 @@
1
- # Contributing to create-next-pro-cli
2
-
3
- Thank you for considering contributing! Here’s how to get started:
4
-
5
- ## Branches
6
-
7
- - `master`: Stable releases only.
8
- - `dev`: All new features and fixes are merged here first.
9
- - Create your feature/fix branch from `dev`:
10
- - `feature/your-feature-name`
11
- - `fix/your-bug-description`
12
-
13
- ## Workflow
14
-
15
- 1. Fork the repository.
16
- 2. Create your branch from `dev`.
17
- 3. Commit your changes with clear messages.
18
- 4. Open a Pull Request (PR) to `dev`.
19
- 5. Describe your changes and reference any related issues.
20
-
21
- ## Code Style
22
-
23
- - Use TypeScript and follow existing formatting.
24
- - Document your code with comments.
25
- - Run `bun lint` and `bun test` before submitting.
26
-
27
- ## Issues
28
-
29
- - Search for existing issues before opening a new one.
30
- - Be clear and provide context, steps to reproduce, and screenshots if possible.
31
-
32
- ## Review & Merge
33
-
34
- - All PRs are reviewed by maintainers or the community.
35
- - Once approved, PRs are merged into `dev`, then into `master` for releases.
36
-
37
- ## Community
38
-
39
- - Be respectful and constructive.
40
- - Use GitHub Discussions for questions and ideas.
41
-
42
- ---
43
-
44
- Happy coding! 🚀