├── .gitignore
├── .npmignore
├── README.md
├── index.js
├── package-lock.json
├── package.json
├── tasks
├── checks.js
├── create-directory-content.js
├── create-project.js
├── installation.js
├── output.js
└── question.js
└── template
├── .DS_Store
├── .prettierignore
├── .prettierrc
├── .storybook
├── main.js
└── preview.js
├── .swcrc
├── README.md
├── custom.d.ts
├── env
├── eslint.config.mjs
├── gitignore
├── jest.swc.config.js
├── package.json
├── src
├── .DS_Store
├── app.test.ts
├── app.tsx
├── assets
│ ├── .DS_Store
│ ├── images
│ │ ├── .DS_Store
│ │ └── favicon.png
│ └── svg
│ │ └── logo.svg
├── components
│ ├── error-boundary
│ │ ├── index.tsx
│ │ └── layout.tsx
│ └── title
│ │ ├── index.tsx
│ │ └── stories.tsx
├── index.html
├── index.tsx
└── pages
│ └── home
│ ├── home.css
│ └── index.tsx
├── tsconfig.json
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /build
3 | .DS_Store
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /node_modules
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # ALRIGHT REACT APP
4 |
5 | Professional React app generator. Comes with an exposed, unopinionated, high performance configuration.
6 | Jest, SWC, Storybook 7, Typescript 5, Webpack 5.
7 |
8 |
9 |
10 | # GETTING STARTED
11 |
12 | In your terminal, type:
13 |
14 | ```js
15 | npx alright-react-app
16 | ```
17 |
18 | The `npx` command is available if `npm` is installed globally on your computer.
19 | **Alright React App** will ask you to enter an application name. Then you just have to press `enter`.
20 |
21 | The following folder will be created at the root of your current working directory:
22 |
23 | ```
24 | my-app
25 | ├── webpack.config.js
26 | ├── tsconfig.json
27 | ├── README.md
28 | ├── package.json
29 | ├── jest.swc.config.js
30 | ├── custom.d.ts
31 | ├── .swcrc
32 | ├── .gitignore
33 | ├── .eslintrc.js
34 | ├── .eslintignore
35 | ├── .env
36 | └── src
37 | ├── index.tsx
38 | ├── index.html
39 | ├── app.tsx
40 | ├── app.test.ts
41 | └── pages
42 | └── home
43 | ├── index.tsx
44 | ├── home.css
45 | └── components
46 | └── error-boundary
47 | ├── index.tsx
48 | ├── layout.tsx
49 | └── title
50 | ├── index.tsx
51 | ├── stories.tsx
52 | └── assets
53 | └── images
54 | ├── favicon.png
55 | └── svg
56 | ├── logo.svg
57 | └── .storybook
58 | ├── main.js
59 | ├── preview.js
60 |
61 | ```
62 |
63 | Once the application is created, type `cd my-app'.
64 |
65 | # COMMANDS
66 |
67 | **DEV**: `npm run dev`
68 |
69 | **BUILD**: `npm run build`
70 |
71 | **STORYBOOK**: `npm run storybook`
72 |
73 | **TEST**: `npm run test`
74 |
75 | # STORYBOOK
76 |
77 | You must install Storybook globally: `npm i -g @storybook/cli` to use it.
78 |
79 | ## CREDITS
80 |
81 | DoneDeal0
82 |
83 | ## SUPPORT
84 |
85 | If you or your company is using the Alright React App, please show your support by buying me a coffee:
86 | https://www.buymeacoffee.com/donedeal0
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | ## CONTRIBUTING
95 |
96 | Pull requests are welcome!
97 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | "use strict";
3 | import { dirname } from "path";
4 | import { fileURLToPath } from "url";
5 | import { createProject } from "./tasks/create-project.js";
6 | import { nodeErrorMessage, introMessage } from "./tasks/output.js";
7 | import { isValidNodeVersion } from "./tasks/checks.js";
8 |
9 | if (isValidNodeVersion()) {
10 | introMessage();
11 | createProject(process.cwd(), dirname(fileURLToPath(import.meta.url)));
12 | } else {
13 | nodeErrorMessage();
14 | }
15 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "alright-react-app",
3 | "version": "2.0.6",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "alright-react-app",
9 | "version": "2.0.6",
10 | "license": "ISC",
11 | "dependencies": {
12 | "chalk": "^5.4.1",
13 | "inquirer": "^12.4.2",
14 | "ora": "^8.2.0",
15 | "which": "^5.0.0"
16 | },
17 | "bin": {
18 | "alright-react-app": "index.js"
19 | },
20 | "engines": {
21 | "node": ">=18.0.0"
22 | }
23 | },
24 | "node_modules/@inquirer/checkbox": {
25 | "version": "4.1.2",
26 | "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.2.tgz",
27 | "integrity": "sha512-PL9ixC5YsPXzXhAZFUPmkXGxfgjkdfZdPEPPmt4kFwQ4LBMDG9n/nHXYRGGZSKZJs+d1sGKWgS2GiPzVRKUdtQ==",
28 | "dependencies": {
29 | "@inquirer/core": "^10.1.7",
30 | "@inquirer/figures": "^1.0.10",
31 | "@inquirer/type": "^3.0.4",
32 | "ansi-escapes": "^4.3.2",
33 | "yoctocolors-cjs": "^2.1.2"
34 | },
35 | "engines": {
36 | "node": ">=18"
37 | },
38 | "peerDependencies": {
39 | "@types/node": ">=18"
40 | },
41 | "peerDependenciesMeta": {
42 | "@types/node": {
43 | "optional": true
44 | }
45 | }
46 | },
47 | "node_modules/@inquirer/confirm": {
48 | "version": "5.1.6",
49 | "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.6.tgz",
50 | "integrity": "sha512-6ZXYK3M1XmaVBZX6FCfChgtponnL0R6I7k8Nu+kaoNkT828FVZTcca1MqmWQipaW2oNREQl5AaPCUOOCVNdRMw==",
51 | "dependencies": {
52 | "@inquirer/core": "^10.1.7",
53 | "@inquirer/type": "^3.0.4"
54 | },
55 | "engines": {
56 | "node": ">=18"
57 | },
58 | "peerDependencies": {
59 | "@types/node": ">=18"
60 | },
61 | "peerDependenciesMeta": {
62 | "@types/node": {
63 | "optional": true
64 | }
65 | }
66 | },
67 | "node_modules/@inquirer/core": {
68 | "version": "10.1.7",
69 | "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.7.tgz",
70 | "integrity": "sha512-AA9CQhlrt6ZgiSy6qoAigiA1izOa751ugX6ioSjqgJ+/Gd+tEN/TORk5sUYNjXuHWfW0r1n/a6ak4u/NqHHrtA==",
71 | "dependencies": {
72 | "@inquirer/figures": "^1.0.10",
73 | "@inquirer/type": "^3.0.4",
74 | "ansi-escapes": "^4.3.2",
75 | "cli-width": "^4.1.0",
76 | "mute-stream": "^2.0.0",
77 | "signal-exit": "^4.1.0",
78 | "wrap-ansi": "^6.2.0",
79 | "yoctocolors-cjs": "^2.1.2"
80 | },
81 | "engines": {
82 | "node": ">=18"
83 | },
84 | "peerDependencies": {
85 | "@types/node": ">=18"
86 | },
87 | "peerDependenciesMeta": {
88 | "@types/node": {
89 | "optional": true
90 | }
91 | }
92 | },
93 | "node_modules/@inquirer/editor": {
94 | "version": "4.2.7",
95 | "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.7.tgz",
96 | "integrity": "sha512-gktCSQtnSZHaBytkJKMKEuswSk2cDBuXX5rxGFv306mwHfBPjg5UAldw9zWGoEyvA9KpRDkeM4jfrx0rXn0GyA==",
97 | "dependencies": {
98 | "@inquirer/core": "^10.1.7",
99 | "@inquirer/type": "^3.0.4",
100 | "external-editor": "^3.1.0"
101 | },
102 | "engines": {
103 | "node": ">=18"
104 | },
105 | "peerDependencies": {
106 | "@types/node": ">=18"
107 | },
108 | "peerDependenciesMeta": {
109 | "@types/node": {
110 | "optional": true
111 | }
112 | }
113 | },
114 | "node_modules/@inquirer/expand": {
115 | "version": "4.0.9",
116 | "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.9.tgz",
117 | "integrity": "sha512-Xxt6nhomWTAmuSX61kVgglLjMEFGa+7+F6UUtdEUeg7fg4r9vaFttUUKrtkViYYrQBA5Ia1tkOJj2koP9BuLig==",
118 | "dependencies": {
119 | "@inquirer/core": "^10.1.7",
120 | "@inquirer/type": "^3.0.4",
121 | "yoctocolors-cjs": "^2.1.2"
122 | },
123 | "engines": {
124 | "node": ">=18"
125 | },
126 | "peerDependencies": {
127 | "@types/node": ">=18"
128 | },
129 | "peerDependenciesMeta": {
130 | "@types/node": {
131 | "optional": true
132 | }
133 | }
134 | },
135 | "node_modules/@inquirer/figures": {
136 | "version": "1.0.10",
137 | "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.10.tgz",
138 | "integrity": "sha512-Ey6176gZmeqZuY/W/nZiUyvmb1/qInjcpiZjXWi6nON+nxJpD1bxtSoBxNliGISae32n6OwbY+TSXPZ1CfS4bw==",
139 | "engines": {
140 | "node": ">=18"
141 | }
142 | },
143 | "node_modules/@inquirer/input": {
144 | "version": "4.1.6",
145 | "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.6.tgz",
146 | "integrity": "sha512-1f5AIsZuVjPT4ecA8AwaxDFNHny/tSershP/cTvTDxLdiIGTeILNcKozB0LaYt6mojJLUbOYhpIxicaYf7UKIQ==",
147 | "dependencies": {
148 | "@inquirer/core": "^10.1.7",
149 | "@inquirer/type": "^3.0.4"
150 | },
151 | "engines": {
152 | "node": ">=18"
153 | },
154 | "peerDependencies": {
155 | "@types/node": ">=18"
156 | },
157 | "peerDependenciesMeta": {
158 | "@types/node": {
159 | "optional": true
160 | }
161 | }
162 | },
163 | "node_modules/@inquirer/number": {
164 | "version": "3.0.9",
165 | "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.9.tgz",
166 | "integrity": "sha512-iN2xZvH3tyIYXLXBvlVh0npk1q/aVuKXZo5hj+K3W3D4ngAEq/DkLpofRzx6oebTUhBvOgryZ+rMV0yImKnG3w==",
167 | "dependencies": {
168 | "@inquirer/core": "^10.1.7",
169 | "@inquirer/type": "^3.0.4"
170 | },
171 | "engines": {
172 | "node": ">=18"
173 | },
174 | "peerDependencies": {
175 | "@types/node": ">=18"
176 | },
177 | "peerDependenciesMeta": {
178 | "@types/node": {
179 | "optional": true
180 | }
181 | }
182 | },
183 | "node_modules/@inquirer/password": {
184 | "version": "4.0.9",
185 | "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.9.tgz",
186 | "integrity": "sha512-xBEoOw1XKb0rIN208YU7wM7oJEHhIYkfG7LpTJAEW913GZeaoQerzf5U/LSHI45EVvjAdgNXmXgH51cUXKZcJQ==",
187 | "dependencies": {
188 | "@inquirer/core": "^10.1.7",
189 | "@inquirer/type": "^3.0.4",
190 | "ansi-escapes": "^4.3.2"
191 | },
192 | "engines": {
193 | "node": ">=18"
194 | },
195 | "peerDependencies": {
196 | "@types/node": ">=18"
197 | },
198 | "peerDependenciesMeta": {
199 | "@types/node": {
200 | "optional": true
201 | }
202 | }
203 | },
204 | "node_modules/@inquirer/prompts": {
205 | "version": "7.3.2",
206 | "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz",
207 | "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==",
208 | "dependencies": {
209 | "@inquirer/checkbox": "^4.1.2",
210 | "@inquirer/confirm": "^5.1.6",
211 | "@inquirer/editor": "^4.2.7",
212 | "@inquirer/expand": "^4.0.9",
213 | "@inquirer/input": "^4.1.6",
214 | "@inquirer/number": "^3.0.9",
215 | "@inquirer/password": "^4.0.9",
216 | "@inquirer/rawlist": "^4.0.9",
217 | "@inquirer/search": "^3.0.9",
218 | "@inquirer/select": "^4.0.9"
219 | },
220 | "engines": {
221 | "node": ">=18"
222 | },
223 | "peerDependencies": {
224 | "@types/node": ">=18"
225 | },
226 | "peerDependenciesMeta": {
227 | "@types/node": {
228 | "optional": true
229 | }
230 | }
231 | },
232 | "node_modules/@inquirer/rawlist": {
233 | "version": "4.0.9",
234 | "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.9.tgz",
235 | "integrity": "sha512-+5t6ebehKqgoxV8fXwE49HkSF2Rc9ijNiVGEQZwvbMI61/Q5RcD+jWD6Gs1tKdz5lkI8GRBL31iO0HjGK1bv+A==",
236 | "dependencies": {
237 | "@inquirer/core": "^10.1.7",
238 | "@inquirer/type": "^3.0.4",
239 | "yoctocolors-cjs": "^2.1.2"
240 | },
241 | "engines": {
242 | "node": ">=18"
243 | },
244 | "peerDependencies": {
245 | "@types/node": ">=18"
246 | },
247 | "peerDependenciesMeta": {
248 | "@types/node": {
249 | "optional": true
250 | }
251 | }
252 | },
253 | "node_modules/@inquirer/search": {
254 | "version": "3.0.9",
255 | "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.9.tgz",
256 | "integrity": "sha512-DWmKztkYo9CvldGBaRMr0ETUHgR86zE6sPDVOHsqz4ISe9o1LuiWfgJk+2r75acFclA93J/lqzhT0dTjCzHuoA==",
257 | "dependencies": {
258 | "@inquirer/core": "^10.1.7",
259 | "@inquirer/figures": "^1.0.10",
260 | "@inquirer/type": "^3.0.4",
261 | "yoctocolors-cjs": "^2.1.2"
262 | },
263 | "engines": {
264 | "node": ">=18"
265 | },
266 | "peerDependencies": {
267 | "@types/node": ">=18"
268 | },
269 | "peerDependenciesMeta": {
270 | "@types/node": {
271 | "optional": true
272 | }
273 | }
274 | },
275 | "node_modules/@inquirer/select": {
276 | "version": "4.0.9",
277 | "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.0.9.tgz",
278 | "integrity": "sha512-BpJyJe7Dkhv2kz7yG7bPSbJLQuu/rqyNlF1CfiiFeFwouegfH+zh13KDyt6+d9DwucKo7hqM3wKLLyJxZMO+Xg==",
279 | "dependencies": {
280 | "@inquirer/core": "^10.1.7",
281 | "@inquirer/figures": "^1.0.10",
282 | "@inquirer/type": "^3.0.4",
283 | "ansi-escapes": "^4.3.2",
284 | "yoctocolors-cjs": "^2.1.2"
285 | },
286 | "engines": {
287 | "node": ">=18"
288 | },
289 | "peerDependencies": {
290 | "@types/node": ">=18"
291 | },
292 | "peerDependenciesMeta": {
293 | "@types/node": {
294 | "optional": true
295 | }
296 | }
297 | },
298 | "node_modules/@inquirer/type": {
299 | "version": "3.0.4",
300 | "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.4.tgz",
301 | "integrity": "sha512-2MNFrDY8jkFYc9Il9DgLsHhMzuHnOYM1+CUYVWbzu9oT0hC7V7EcYvdCKeoll/Fcci04A+ERZ9wcc7cQ8lTkIA==",
302 | "engines": {
303 | "node": ">=18"
304 | },
305 | "peerDependencies": {
306 | "@types/node": ">=18"
307 | },
308 | "peerDependenciesMeta": {
309 | "@types/node": {
310 | "optional": true
311 | }
312 | }
313 | },
314 | "node_modules/ansi-escapes": {
315 | "version": "4.3.2",
316 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
317 | "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
318 | "dependencies": {
319 | "type-fest": "^0.21.3"
320 | },
321 | "engines": {
322 | "node": ">=8"
323 | },
324 | "funding": {
325 | "url": "https://github.com/sponsors/sindresorhus"
326 | }
327 | },
328 | "node_modules/ansi-regex": {
329 | "version": "6.0.1",
330 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
331 | "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
332 | "engines": {
333 | "node": ">=12"
334 | },
335 | "funding": {
336 | "url": "https://github.com/chalk/ansi-regex?sponsor=1"
337 | }
338 | },
339 | "node_modules/ansi-styles": {
340 | "version": "4.3.0",
341 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
342 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
343 | "dependencies": {
344 | "color-convert": "^2.0.1"
345 | },
346 | "engines": {
347 | "node": ">=8"
348 | },
349 | "funding": {
350 | "url": "https://github.com/chalk/ansi-styles?sponsor=1"
351 | }
352 | },
353 | "node_modules/chalk": {
354 | "version": "5.4.1",
355 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
356 | "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
357 | "engines": {
358 | "node": "^12.17.0 || ^14.13 || >=16.0.0"
359 | },
360 | "funding": {
361 | "url": "https://github.com/chalk/chalk?sponsor=1"
362 | }
363 | },
364 | "node_modules/chardet": {
365 | "version": "0.7.0",
366 | "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
367 | "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
368 | },
369 | "node_modules/cli-cursor": {
370 | "version": "5.0.0",
371 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
372 | "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
373 | "dependencies": {
374 | "restore-cursor": "^5.0.0"
375 | },
376 | "engines": {
377 | "node": ">=18"
378 | },
379 | "funding": {
380 | "url": "https://github.com/sponsors/sindresorhus"
381 | }
382 | },
383 | "node_modules/cli-spinners": {
384 | "version": "2.9.2",
385 | "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
386 | "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
387 | "engines": {
388 | "node": ">=6"
389 | },
390 | "funding": {
391 | "url": "https://github.com/sponsors/sindresorhus"
392 | }
393 | },
394 | "node_modules/cli-width": {
395 | "version": "4.1.0",
396 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
397 | "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
398 | "engines": {
399 | "node": ">= 12"
400 | }
401 | },
402 | "node_modules/color-convert": {
403 | "version": "2.0.1",
404 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
405 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
406 | "dependencies": {
407 | "color-name": "~1.1.4"
408 | },
409 | "engines": {
410 | "node": ">=7.0.0"
411 | }
412 | },
413 | "node_modules/color-name": {
414 | "version": "1.1.4",
415 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
416 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
417 | },
418 | "node_modules/emoji-regex": {
419 | "version": "10.4.0",
420 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
421 | "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="
422 | },
423 | "node_modules/external-editor": {
424 | "version": "3.1.0",
425 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
426 | "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
427 | "dependencies": {
428 | "chardet": "^0.7.0",
429 | "iconv-lite": "^0.4.24",
430 | "tmp": "^0.0.33"
431 | },
432 | "engines": {
433 | "node": ">=4"
434 | }
435 | },
436 | "node_modules/get-east-asian-width": {
437 | "version": "1.3.0",
438 | "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
439 | "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
440 | "engines": {
441 | "node": ">=18"
442 | },
443 | "funding": {
444 | "url": "https://github.com/sponsors/sindresorhus"
445 | }
446 | },
447 | "node_modules/iconv-lite": {
448 | "version": "0.4.24",
449 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
450 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
451 | "dependencies": {
452 | "safer-buffer": ">= 2.1.2 < 3"
453 | },
454 | "engines": {
455 | "node": ">=0.10.0"
456 | }
457 | },
458 | "node_modules/inquirer": {
459 | "version": "12.4.2",
460 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.4.2.tgz",
461 | "integrity": "sha512-reyjHcwyK2LObXgTJH4T1Dpfhwu88LNPTZmg/KenmTsy3T8lN/kZT8Oo7UwwkB9q8+ss2qjjN7GV8oFAfyz9Xg==",
462 | "dependencies": {
463 | "@inquirer/core": "^10.1.7",
464 | "@inquirer/prompts": "^7.3.2",
465 | "@inquirer/type": "^3.0.4",
466 | "ansi-escapes": "^4.3.2",
467 | "mute-stream": "^2.0.0",
468 | "run-async": "^3.0.0",
469 | "rxjs": "^7.8.1"
470 | },
471 | "engines": {
472 | "node": ">=18"
473 | },
474 | "peerDependencies": {
475 | "@types/node": ">=18"
476 | },
477 | "peerDependenciesMeta": {
478 | "@types/node": {
479 | "optional": true
480 | }
481 | }
482 | },
483 | "node_modules/is-fullwidth-code-point": {
484 | "version": "3.0.0",
485 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
486 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
487 | "engines": {
488 | "node": ">=8"
489 | }
490 | },
491 | "node_modules/is-interactive": {
492 | "version": "2.0.0",
493 | "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
494 | "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
495 | "engines": {
496 | "node": ">=12"
497 | },
498 | "funding": {
499 | "url": "https://github.com/sponsors/sindresorhus"
500 | }
501 | },
502 | "node_modules/is-unicode-supported": {
503 | "version": "2.1.0",
504 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
505 | "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
506 | "engines": {
507 | "node": ">=18"
508 | },
509 | "funding": {
510 | "url": "https://github.com/sponsors/sindresorhus"
511 | }
512 | },
513 | "node_modules/isexe": {
514 | "version": "3.1.1",
515 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
516 | "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
517 | "engines": {
518 | "node": ">=16"
519 | }
520 | },
521 | "node_modules/log-symbols": {
522 | "version": "6.0.0",
523 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz",
524 | "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==",
525 | "dependencies": {
526 | "chalk": "^5.3.0",
527 | "is-unicode-supported": "^1.3.0"
528 | },
529 | "engines": {
530 | "node": ">=18"
531 | },
532 | "funding": {
533 | "url": "https://github.com/sponsors/sindresorhus"
534 | }
535 | },
536 | "node_modules/log-symbols/node_modules/is-unicode-supported": {
537 | "version": "1.3.0",
538 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz",
539 | "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==",
540 | "engines": {
541 | "node": ">=12"
542 | },
543 | "funding": {
544 | "url": "https://github.com/sponsors/sindresorhus"
545 | }
546 | },
547 | "node_modules/mimic-function": {
548 | "version": "5.0.1",
549 | "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
550 | "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
551 | "engines": {
552 | "node": ">=18"
553 | },
554 | "funding": {
555 | "url": "https://github.com/sponsors/sindresorhus"
556 | }
557 | },
558 | "node_modules/mute-stream": {
559 | "version": "2.0.0",
560 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz",
561 | "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==",
562 | "engines": {
563 | "node": "^18.17.0 || >=20.5.0"
564 | }
565 | },
566 | "node_modules/onetime": {
567 | "version": "7.0.0",
568 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
569 | "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
570 | "dependencies": {
571 | "mimic-function": "^5.0.0"
572 | },
573 | "engines": {
574 | "node": ">=18"
575 | },
576 | "funding": {
577 | "url": "https://github.com/sponsors/sindresorhus"
578 | }
579 | },
580 | "node_modules/ora": {
581 | "version": "8.2.0",
582 | "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz",
583 | "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==",
584 | "dependencies": {
585 | "chalk": "^5.3.0",
586 | "cli-cursor": "^5.0.0",
587 | "cli-spinners": "^2.9.2",
588 | "is-interactive": "^2.0.0",
589 | "is-unicode-supported": "^2.0.0",
590 | "log-symbols": "^6.0.0",
591 | "stdin-discarder": "^0.2.2",
592 | "string-width": "^7.2.0",
593 | "strip-ansi": "^7.1.0"
594 | },
595 | "engines": {
596 | "node": ">=18"
597 | },
598 | "funding": {
599 | "url": "https://github.com/sponsors/sindresorhus"
600 | }
601 | },
602 | "node_modules/os-tmpdir": {
603 | "version": "1.0.2",
604 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
605 | "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
606 | "engines": {
607 | "node": ">=0.10.0"
608 | }
609 | },
610 | "node_modules/restore-cursor": {
611 | "version": "5.1.0",
612 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
613 | "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
614 | "dependencies": {
615 | "onetime": "^7.0.0",
616 | "signal-exit": "^4.1.0"
617 | },
618 | "engines": {
619 | "node": ">=18"
620 | },
621 | "funding": {
622 | "url": "https://github.com/sponsors/sindresorhus"
623 | }
624 | },
625 | "node_modules/run-async": {
626 | "version": "3.0.0",
627 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz",
628 | "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==",
629 | "engines": {
630 | "node": ">=0.12.0"
631 | }
632 | },
633 | "node_modules/rxjs": {
634 | "version": "7.8.1",
635 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
636 | "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
637 | "dependencies": {
638 | "tslib": "^2.1.0"
639 | }
640 | },
641 | "node_modules/safer-buffer": {
642 | "version": "2.1.2",
643 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
644 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
645 | },
646 | "node_modules/signal-exit": {
647 | "version": "4.1.0",
648 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
649 | "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
650 | "engines": {
651 | "node": ">=14"
652 | },
653 | "funding": {
654 | "url": "https://github.com/sponsors/isaacs"
655 | }
656 | },
657 | "node_modules/stdin-discarder": {
658 | "version": "0.2.2",
659 | "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
660 | "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==",
661 | "engines": {
662 | "node": ">=18"
663 | },
664 | "funding": {
665 | "url": "https://github.com/sponsors/sindresorhus"
666 | }
667 | },
668 | "node_modules/string-width": {
669 | "version": "7.2.0",
670 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
671 | "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
672 | "dependencies": {
673 | "emoji-regex": "^10.3.0",
674 | "get-east-asian-width": "^1.0.0",
675 | "strip-ansi": "^7.1.0"
676 | },
677 | "engines": {
678 | "node": ">=18"
679 | },
680 | "funding": {
681 | "url": "https://github.com/sponsors/sindresorhus"
682 | }
683 | },
684 | "node_modules/strip-ansi": {
685 | "version": "7.1.0",
686 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
687 | "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
688 | "dependencies": {
689 | "ansi-regex": "^6.0.1"
690 | },
691 | "engines": {
692 | "node": ">=12"
693 | },
694 | "funding": {
695 | "url": "https://github.com/chalk/strip-ansi?sponsor=1"
696 | }
697 | },
698 | "node_modules/tmp": {
699 | "version": "0.0.33",
700 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
701 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
702 | "dependencies": {
703 | "os-tmpdir": "~1.0.2"
704 | },
705 | "engines": {
706 | "node": ">=0.6.0"
707 | }
708 | },
709 | "node_modules/tslib": {
710 | "version": "2.5.3",
711 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
712 | "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w=="
713 | },
714 | "node_modules/type-fest": {
715 | "version": "0.21.3",
716 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
717 | "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
718 | "engines": {
719 | "node": ">=10"
720 | },
721 | "funding": {
722 | "url": "https://github.com/sponsors/sindresorhus"
723 | }
724 | },
725 | "node_modules/which": {
726 | "version": "5.0.0",
727 | "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
728 | "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
729 | "dependencies": {
730 | "isexe": "^3.1.1"
731 | },
732 | "bin": {
733 | "node-which": "bin/which.js"
734 | },
735 | "engines": {
736 | "node": "^18.17.0 || >=20.5.0"
737 | }
738 | },
739 | "node_modules/wrap-ansi": {
740 | "version": "6.2.0",
741 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
742 | "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
743 | "dependencies": {
744 | "ansi-styles": "^4.0.0",
745 | "string-width": "^4.1.0",
746 | "strip-ansi": "^6.0.0"
747 | },
748 | "engines": {
749 | "node": ">=8"
750 | }
751 | },
752 | "node_modules/wrap-ansi/node_modules/ansi-regex": {
753 | "version": "5.0.1",
754 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
755 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
756 | "engines": {
757 | "node": ">=8"
758 | }
759 | },
760 | "node_modules/wrap-ansi/node_modules/emoji-regex": {
761 | "version": "8.0.0",
762 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
763 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
764 | },
765 | "node_modules/wrap-ansi/node_modules/string-width": {
766 | "version": "4.2.3",
767 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
768 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
769 | "dependencies": {
770 | "emoji-regex": "^8.0.0",
771 | "is-fullwidth-code-point": "^3.0.0",
772 | "strip-ansi": "^6.0.1"
773 | },
774 | "engines": {
775 | "node": ">=8"
776 | }
777 | },
778 | "node_modules/wrap-ansi/node_modules/strip-ansi": {
779 | "version": "6.0.1",
780 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
781 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
782 | "dependencies": {
783 | "ansi-regex": "^5.0.1"
784 | },
785 | "engines": {
786 | "node": ">=8"
787 | }
788 | },
789 | "node_modules/yoctocolors-cjs": {
790 | "version": "2.1.2",
791 | "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz",
792 | "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==",
793 | "engines": {
794 | "node": ">=18"
795 | },
796 | "funding": {
797 | "url": "https://github.com/sponsors/sindresorhus"
798 | }
799 | }
800 | }
801 | }
802 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "alright-react-app",
3 | "version": "2.0.7",
4 | "description": "Professional React app generator. Shipped with an exposed, unopinionated, highly-performant config. Jest-SWC, Storybook, SWC, Typescript, Webpack 5.",
5 | "main": "index.js",
6 | "author": "DoneDeal0",
7 | "license": "ISC",
8 | "type": "module",
9 | "declaration": true,
10 | "keywords": [
11 | "react",
12 | "reactjs",
13 | "create react app",
14 | "create-react-app",
15 | "alright pro react app",
16 | "alright-pro-react-app",
17 | "pro",
18 | "professional",
19 | "production",
20 | "generator",
21 | "swc"
22 | ],
23 | "bin": "./index.js",
24 | "repository": {
25 | "type": "git",
26 | "url": "git+https://github.com/DoneDeal0/alright-react-app"
27 | },
28 | "dependencies": {
29 | "chalk": "^5.4.1",
30 | "inquirer": "^12.4.2",
31 | "ora": "^8.2.0",
32 | "which": "^5.0.0"
33 | },
34 | "engines": {
35 | "node": ">=18.0.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tasks/checks.js:
--------------------------------------------------------------------------------
1 | import which from "which";
2 |
3 | export async function getCommand() {
4 | try {
5 | await which("npm");
6 | if (process.platform === "win32") return "npm.cmd";
7 | return "npm";
8 | } catch (err) {
9 | throw new Error(err);
10 | }
11 | }
12 |
13 | export function isValidNodeVersion() {
14 | const version = process.versions.node;
15 | if (!version) return false;
16 | const nodeNumber = Number(version.split(".").splice(0, 2).join("."));
17 | return nodeNumber >= 18.0
18 | }
--------------------------------------------------------------------------------
/tasks/create-directory-content.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | "use strict";
3 | import fs from "fs";
4 | import os from "os";
5 |
6 | async function writePackageJSON(path, fileContent, projectName) {
7 | const userName = os.userInfo().username || "";
8 | const parsedJSON = JSON.parse(fileContent);
9 | parsedJSON.name = projectName;
10 | parsedJSON.author = userName;
11 | const updatedJSON = JSON.stringify(parsedJSON, null, 4);
12 | return fs.writeFileSync(path, updatedJSON, "utf8");
13 | }
14 |
15 | async function createFile(srcPath, destPath, fileName, projectName) {
16 | const fileContent = fs.readFileSync(srcPath, "utf8");
17 | if (fileName === "package.json") {
18 | writePackageJSON(destPath, fileContent, projectName);
19 | } else if (fileName === "env" || fileName === "gitignore") {
20 | const dotPath = `${destPath.replace(fileName, `.${fileName}`)}`;
21 | fs.writeFileSync(dotPath, fileContent, "utf8");
22 | } else {
23 | fs.writeFileSync(destPath, fileContent, "utf8");
24 | }
25 | }
26 |
27 | export async function createDirectoryContent(
28 | templatePath,
29 | newPath,
30 | projectName,
31 | directory
32 | ) {
33 | try {
34 | const filesToCreate = fs.readdirSync(templatePath);
35 |
36 | for (const file of filesToCreate) {
37 | const currentPath = `${templatePath}/${file}`;
38 | const fileType = fs.statSync(currentPath);
39 |
40 | if (fileType.isFile()) {
41 | await createFile(
42 | currentPath,
43 | `${directory}/${newPath}/${file}`,
44 | file,
45 | projectName
46 | );
47 | }
48 |
49 | if (fileType.isDirectory()) {
50 | fs.mkdirSync(`${directory}/${newPath}/${file}`);
51 | await createDirectoryContent(
52 | currentPath,
53 | `${newPath}/${file}`,
54 | projectName,
55 | directory
56 | );
57 | }
58 | }
59 | } catch (err) {
60 | throw new Error(err);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/tasks/create-project.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | "use strict";
3 | import fs from "fs";
4 | import inquirer from "inquirer";
5 | import path from "path";
6 | import { getCommand } from "./checks.js";
7 | import { createDirectoryContent } from "./create-directory-content.js";
8 | import { installDependencies, initializeGit } from "./installation.js";
9 | import { errorMessage, preInstallMessage, postInstallMessage } from "./output.js";
10 | import { question } from "./question.js";
11 |
12 | export async function createProject(directory, __dirname) {
13 | try {
14 | const answer = await inquirer.prompt(question);
15 | const projectName = answer["project-name"];
16 | const templatePath = `${__dirname}/template`;
17 | const projectPath = path.join(directory, projectName);
18 | fs.mkdirSync(projectPath);
19 | await createDirectoryContent(
20 | templatePath,
21 | projectName,
22 | projectName,
23 | directory
24 | );
25 | process.chdir(projectPath);
26 | const command = await getCommand();
27 | preInstallMessage(projectName, command);
28 | await initializeGit(projectPath);
29 | await installDependencies(command, projectPath);
30 | postInstallMessage(projectName, "npm");
31 | } catch (err) {
32 | errorMessage(err)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tasks/installation.js:
--------------------------------------------------------------------------------
1 | import ora from "ora";
2 | import { spawn } from "child_process";
3 | import which from "which";
4 | import { promisify } from "util";
5 | import { gitMessage } from "./output.js";
6 |
7 | export async function installDependencies(command, projectPath) {
8 | return new Promise((resolve, reject) => {
9 | const installationProcess = spawn(command, ["install --force"], {
10 | cwd: projectPath,
11 | stdio: "ignore",
12 | });
13 | const spinner = ora("Installing dependencies...").start();
14 | spinner.color = "yellow";
15 | installationProcess.on("error", (error) => {
16 | spinner.fail("Failed to install dependencies");
17 | reject(error);
18 | });
19 |
20 | installationProcess.on("exit", () => {
21 | spinner.succeed("All dependencies have been installed");
22 | resolve();
23 | });
24 | });
25 | }
26 |
27 | export async function initializeGit(projectPath) {
28 | try {
29 | const hasGit = await which("git", { nothrow: true });
30 | if (hasGit) {
31 | const gitConfig = spawn("git", [
32 | "config",
33 | "--global",
34 | "init.defaultBranch",
35 | "main",
36 | ]);
37 | await promisify(gitConfig.on.bind(gitConfig))("close");
38 | const gitInit = spawn("git", ["init"], {
39 | cwd: projectPath,
40 | stdio: "ignore",
41 | });
42 | await promisify(gitInit.on.bind(gitInit))("close");
43 | gitMessage(hasGit);
44 | } else {
45 | gitMessage(hasGit);
46 | }
47 | } catch (_) {
48 | gitMessage(false);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/tasks/output.js:
--------------------------------------------------------------------------------
1 | import chalk from "chalk";
2 |
3 | export function introMessage() {
4 | console.log("", "\n", chalk.yellow.bold("ALRIGHT REACT APP"));
5 | console.log(
6 | "",
7 | "\n",
8 | "Alright, alright, alright! Let's create a new React app!",
9 | "\n"
10 | );
11 | }
12 |
13 | export function preInstallMessage(projectName, command) {
14 | console.log(
15 | "\n",
16 | `${chalk.yellow.bold("✓")}`,
17 | `${chalk.yellow.bold(projectName)}'s files and folders have been created`,
18 | "\n",
19 | `${chalk.yellow.bold("✓")}`,
20 | `${chalk.yellow.bold(command)} detected`
21 | );
22 | }
23 |
24 | export function gitMessage(hasGit) {
25 | if (hasGit) {
26 | console.log(
27 | `${chalk.yellow.bold(" ✓")}`,
28 | `${chalk.yellow.bold("git")} initialized`,
29 | "\n"
30 | );
31 | } else {
32 | console.log(
33 | `${chalk.red.bold(" ×")}`,
34 | `${chalk.red.bold("git")} couldn't be initialized`,
35 | "\n"
36 | );
37 | }
38 | }
39 |
40 | const runLine = (name) =>
41 | `now run ${chalk.bgYellow.black.bold(`cd ${name}`)} to get started!`;
42 |
43 | export function postInstallMessage(projectName, command) {
44 | console.log(
45 | "",
46 | "\n",
47 | "Project",
48 | chalk.yellow.bold(projectName),
49 | "was successfully created!",
50 | "\n",
51 | "\n",
52 | runLine(projectName),
53 | "\n",
54 | "\n",
55 | chalk.yellow.bold("DEV"),
56 | `: ${command} dev`,
57 | "\n",
58 | chalk.yellow.bold("BUILD"),
59 | `: ${command} build`,
60 | "\n",
61 | chalk.yellow.bold("STORYBOOK"),
62 | `: ${command} storybook`,
63 | "\n",
64 | chalk.yellow.bold("TEST"),
65 | `: ${command} test`,
66 | "\n"
67 | );
68 | }
69 |
70 |
71 | export function errorMessage(error) {
72 | console.error(chalk.red.bold("The following error occurred:", error));
73 | }
74 |
75 | export function nodeErrorMessage() {
76 | console.error(
77 | "\n",
78 | chalk.red.bold(
79 | `Alright React App requires Node 18.0.0 or higher. Your version is ${process.versions.node}.`
80 | ),
81 | "\n"
82 | );
83 | }
--------------------------------------------------------------------------------
/tasks/question.js:
--------------------------------------------------------------------------------
1 | export const question = [
2 | {
3 | name: "project-name",
4 | type: "input",
5 | message: "Project name:",
6 | validate: function (input) {
7 | if (/^([A-Za-z\-\_\d])+$/.test(input)) {
8 | return true;
9 | }
10 | return "Sorry, your project name must only include letters, numbers, dashes or underscores.";
11 | },
12 | },
13 | ];
14 |
--------------------------------------------------------------------------------
/template/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DoneDeal0/alright-react-app/9859f48433931ee2e159f6c75507ed0c642871e3/template/.DS_Store
--------------------------------------------------------------------------------
/template/.prettierignore:
--------------------------------------------------------------------------------
1 | assets/
2 | build/
3 | node_modules/
4 | webpack.config.js
--------------------------------------------------------------------------------
/template/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "semi": true,
4 | "singleQuote": false,
5 | "arrowParens": "always"
6 | }
7 |
--------------------------------------------------------------------------------
/template/.storybook/main.js:
--------------------------------------------------------------------------------
1 | import webpackConfig from "../webpack.config";
2 | const _webpackConfig = webpackConfig({ WEBPACK_SERVE: true });
3 |
4 | export default {
5 | stories: ["../**/stories.tsx"],
6 | addons: [
7 | "@storybook/addon-essentials",
8 | "@storybook/addon-actions",
9 | "@storybook/addon-interactions",
10 | ],
11 | framework: {
12 | name: "@storybook/react-webpack5",
13 | options: {
14 | fastRefresh: true,
15 | },
16 | },
17 | webpackFinal: async (config) => {
18 | return {
19 | ...config,
20 | resolve: {
21 | ...config.resolve,
22 | ..._webpackConfig.resolve,
23 | },
24 | module: {
25 | ...config.module,
26 | rules: [..._webpackConfig.module.rules],
27 | },
28 | };
29 | },
30 | docs: {
31 | autodocs: true,
32 | },
33 | };
34 |
--------------------------------------------------------------------------------
/template/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const preview = {
4 | decorators: [
5 | (Story) => (
6 |
Edit to see changes
12 |