create-lumen 1.0.0
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/LICENSE +21 -0
- package/README.md +115 -0
- package/bin/cli.js +17 -0
- package/package.json +42 -0
- package/src/cleanup.js +7 -0
- package/src/config-cache.js +27 -0
- package/src/configure.js +161 -0
- package/src/confirm-empty-folder.js +45 -0
- package/src/css.js +59 -0
- package/src/dependencies.js +101 -0
- package/src/injector.js +276 -0
- package/src/main.js +197 -0
- package/src/prompts.js +232 -0
- package/src/readme.js +178 -0
- package/src/scaffold.js +29 -0
- package/src/utils/dev-server.js +34 -0
- package/src/utils/fs.js +74 -0
- package/src/utils/pkg-manager.js +17 -0
- package/src/utils/yarn-v1.js +10 -0
- package/templates/architectures/component-based/main.jsx +10 -0
- package/templates/architectures/component-based/main.tsx +10 -0
- package/templates/architectures/component-based/src/App.jsx +11 -0
- package/templates/architectures/component-based/src/App.tsx +11 -0
- package/templates/architectures/component-based/src/components/common/Button.jsx +30 -0
- package/templates/architectures/component-based/src/components/common/Button.tsx +35 -0
- package/templates/architectures/component-based/src/components/common/index.js +1 -0
- package/templates/architectures/component-based/src/components/common/index.ts +1 -0
- package/templates/architectures/component-based/src/components/form/.gitkeep +0 -0
- package/templates/architectures/component-based/src/components/layout/.gitkeep +0 -0
- package/templates/architectures/component-based/src/config/constants.js +2 -0
- package/templates/architectures/component-based/src/config/constants.ts +2 -0
- package/templates/architectures/component-based/src/config/routes.js +7 -0
- package/templates/architectures/component-based/src/config/routes.ts +7 -0
- package/templates/architectures/component-based/src/context/AppContext.jsx +13 -0
- package/templates/architectures/component-based/src/context/AppContext.tsx +13 -0
- package/templates/architectures/component-based/src/hooks/index.js +1 -0
- package/templates/architectures/component-based/src/hooks/index.ts +1 -0
- package/templates/architectures/component-based/src/hooks/useLocalStorage.jsx +28 -0
- package/templates/architectures/component-based/src/hooks/useLocalStorage.ts +28 -0
- package/templates/architectures/component-based/src/layouts/MainLayout.jsx +29 -0
- package/templates/architectures/component-based/src/layouts/MainLayout.tsx +33 -0
- package/templates/architectures/component-based/src/layouts/index.js +1 -0
- package/templates/architectures/component-based/src/layouts/index.ts +1 -0
- package/templates/architectures/component-based/src/pages/Home/Home.jsx +16 -0
- package/templates/architectures/component-based/src/pages/Home/Home.tsx +16 -0
- package/templates/architectures/component-based/src/pages/Home/index.js +1 -0
- package/templates/architectures/component-based/src/pages/Home/index.ts +1 -0
- package/templates/architectures/component-based/src/routes/index.jsx +11 -0
- package/templates/architectures/component-based/src/routes/index.tsx +11 -0
- package/templates/architectures/component-based/src/services/.gitkeep +0 -0
- package/templates/architectures/component-based/src/store/.gitkeep +0 -0
- package/templates/architectures/component-based/src/styles/globals.css +7 -0
- package/templates/architectures/component-based/src/utils/cn.jsx +3 -0
- package/templates/architectures/component-based/src/utils/cn.ts +3 -0
- package/templates/architectures/component-based/src/utils/index.js +1 -0
- package/templates/architectures/component-based/src/utils/index.ts +1 -0
- package/templates/architectures/feature-based/main.jsx +10 -0
- package/templates/architectures/feature-based/main.tsx +10 -0
- package/templates/architectures/feature-based/scripts/create-feature.mjs +65 -0
- package/templates/architectures/feature-based/src/app/App.jsx +10 -0
- package/templates/architectures/feature-based/src/app/App.tsx +10 -0
- package/templates/architectures/feature-based/src/app/providers/ThemeProvider.jsx +5 -0
- package/templates/architectures/feature-based/src/app/providers/ThemeProvider.tsx +5 -0
- package/templates/architectures/feature-based/src/components/ui/Button.jsx +23 -0
- package/templates/architectures/feature-based/src/components/ui/Button.tsx +28 -0
- package/templates/architectures/feature-based/src/components/ui/index.js +1 -0
- package/templates/architectures/feature-based/src/components/ui/index.ts +1 -0
- package/templates/architectures/feature-based/src/config/constants.js +2 -0
- package/templates/architectures/feature-based/src/config/constants.ts +2 -0
- package/templates/architectures/feature-based/src/features/home/components/.gitkeep +0 -0
- package/templates/architectures/feature-based/src/features/home/hooks/.gitkeep +0 -0
- package/templates/architectures/feature-based/src/features/home/index.js +1 -0
- package/templates/architectures/feature-based/src/features/home/index.ts +1 -0
- package/templates/architectures/feature-based/src/features/home/interfaces/.gitkeep +0 -0
- package/templates/architectures/feature-based/src/features/home/layouts/.gitkeep +0 -0
- package/templates/architectures/feature-based/src/features/home/pages/HomePage.jsx +14 -0
- package/templates/architectures/feature-based/src/features/home/pages/HomePage.tsx +14 -0
- package/templates/architectures/feature-based/src/features/home/services/.gitkeep +0 -0
- package/templates/architectures/feature-based/src/features/home/store/.gitkeep +0 -0
- package/templates/architectures/feature-based/src/hooks/index.js +1 -0
- package/templates/architectures/feature-based/src/hooks/index.ts +1 -0
- package/templates/architectures/feature-based/src/hooks/useLocalStorage.jsx +28 -0
- package/templates/architectures/feature-based/src/hooks/useLocalStorage.ts +28 -0
- package/templates/architectures/feature-based/src/lib/.gitkeep +0 -0
- package/templates/architectures/feature-based/src/stores/.gitkeep +0 -0
- package/templates/architectures/feature-based/src/styles/globals.css +1 -0
- package/templates/architectures/feature-based/src/types/index.js +13 -0
- package/templates/architectures/feature-based/src/types/index.ts +11 -0
- package/templates/architectures/feature-based/src/utils/cn.jsx +3 -0
- package/templates/architectures/feature-based/src/utils/cn.ts +6 -0
- package/templates/architectures/feature-based/src/utils/index.js +1 -0
- package/templates/architectures/feature-based/src/utils/index.ts +1 -0
- package/templates/conditional/axios/component-based/src/services/axios.jsx +19 -0
- package/templates/conditional/axios/component-based/src/services/axios.ts +19 -0
- package/templates/conditional/axios/component-based/src/services/index.js +1 -0
- package/templates/conditional/axios/component-based/src/services/index.ts +1 -0
- package/templates/conditional/axios/feature-based/src/lib/axios.jsx +19 -0
- package/templates/conditional/axios/feature-based/src/lib/axios.ts +19 -0
- package/templates/conditional/axios/feature-based/src/lib/index.js +1 -0
- package/templates/conditional/axios/feature-based/src/lib/index.ts +1 -0
- package/templates/conditional/icons/huge/component-based/src/pages/Home/Home.jsx +20 -0
- package/templates/conditional/icons/huge/component-based/src/pages/Home/Home.tsx +20 -0
- package/templates/conditional/icons/huge/feature-based/src/features/home/pages/HomePage.jsx +20 -0
- package/templates/conditional/icons/huge/feature-based/src/features/home/pages/HomePage.tsx +20 -0
- package/templates/conditional/icons/lucide/component-based/src/pages/Home/Home.jsx +20 -0
- package/templates/conditional/icons/lucide/component-based/src/pages/Home/Home.tsx +20 -0
- package/templates/conditional/icons/lucide/feature-based/src/features/home/pages/HomePage.jsx +20 -0
- package/templates/conditional/icons/lucide/feature-based/src/features/home/pages/HomePage.tsx +20 -0
- package/templates/conditional/linting/eslint/eslint.config.js +31 -0
- package/templates/conditional/linting/eslint/eslint.config.ts +28 -0
- package/templates/conditional/linting/oxlint/oxlintrc.json +9 -0
- package/templates/conditional/router/component-based/src/App.jsx +11 -0
- package/templates/conditional/router/component-based/src/App.tsx +11 -0
- package/templates/conditional/router/component-based/src/routes/index.jsx +13 -0
- package/templates/conditional/router/component-based/src/routes/index.tsx +13 -0
- package/templates/conditional/router/feature-based/src/app/App.jsx +6 -0
- package/templates/conditional/router/feature-based/src/app/App.tsx +6 -0
- package/templates/conditional/router/feature-based/src/app/router.jsx +13 -0
- package/templates/conditional/router/feature-based/src/app/router.tsx +13 -0
- package/templates/conditional/state/redux/component-based/src/context/StoreProvider.jsx +7 -0
- package/templates/conditional/state/redux/component-based/src/context/StoreProvider.tsx +7 -0
- package/templates/conditional/state/redux/component-based/src/pages/Home/counterSlice.jsx +20 -0
- package/templates/conditional/state/redux/component-based/src/pages/Home/counterSlice.ts +20 -0
- package/templates/conditional/state/redux/component-based/src/store/hooks.js +4 -0
- package/templates/conditional/state/redux/component-based/src/store/hooks.ts +4 -0
- package/templates/conditional/state/redux/component-based/src/store/index.js +8 -0
- package/templates/conditional/state/redux/component-based/src/store/index.ts +11 -0
- package/templates/conditional/state/redux/feature-based/src/app/providers/StoreProvider.jsx +7 -0
- package/templates/conditional/state/redux/feature-based/src/app/providers/StoreProvider.tsx +7 -0
- package/templates/conditional/state/redux/feature-based/src/features/home/store/counterSlice.jsx +20 -0
- package/templates/conditional/state/redux/feature-based/src/features/home/store/counterSlice.ts +20 -0
- package/templates/conditional/state/redux/feature-based/src/stores/hooks.js +4 -0
- package/templates/conditional/state/redux/feature-based/src/stores/hooks.ts +4 -0
- package/templates/conditional/state/redux/feature-based/src/stores/index.js +8 -0
- package/templates/conditional/state/redux/feature-based/src/stores/index.ts +11 -0
- package/templates/conditional/state/zustand/component-based/src/store/useCounterStore.jsx +9 -0
- package/templates/conditional/state/zustand/component-based/src/store/useCounterStore.ts +9 -0
- package/templates/conditional/state/zustand/feature-based/src/stores/useCounterStore.jsx +9 -0
- package/templates/conditional/state/zustand/feature-based/src/stores/useCounterStore.ts +9 -0
- package/templates/conditional/testing/jest/jest.config.js +12 -0
- package/templates/conditional/testing/jest/jest.config.ts +13 -0
- package/templates/conditional/testing/jest/jest.setup.js +1 -0
- package/templates/conditional/testing/jest/jest.setup.ts +1 -0
- package/templates/conditional/testing/jest/src/__tests__/App.test.jsx +9 -0
- package/templates/conditional/testing/jest/src/__tests__/App.test.tsx +9 -0
- package/templates/conditional/testing/vitest/src/__tests__/App.test.jsx +10 -0
- package/templates/conditional/testing/vitest/src/__tests__/App.test.tsx +10 -0
- package/templates/conditional/testing/vitest/src/test/setup.js +1 -0
- package/templates/conditional/testing/vitest/src/test/setup.ts +1 -0
- package/templates/conditional/testing/vitest/vitest.config.js +18 -0
- package/templates/conditional/testing/vitest/vitest.config.ts +18 -0
- package/templates/css/bootstrap/src/index.css +1 -0
- package/templates/css/none/src/index.css +18 -0
- package/templates/css/tailwind/src/index.css +1 -0
- package/templates/css/tailwind/vite.config.js +15 -0
- package/templates/css/tailwind/vite.config.ts +15 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# create-lumen
|
|
2
|
+
|
|
3
|
+
Scaffold a React + Vite project with your choice of **feature-based** or **component-based** architecture.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm create lumen
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## What it does
|
|
10
|
+
|
|
11
|
+
Creates a production-ready React + Vite project with:
|
|
12
|
+
|
|
13
|
+
- **Architecture choice** — feature-based (domain-driven) or component-based (grouped by type)
|
|
14
|
+
- **TypeScript or JavaScript**
|
|
15
|
+
- **CSS framework** — Tailwind CSS, Bootstrap, or vanilla CSS
|
|
16
|
+
- **State management** — Zustand, Redux Toolkit, or none
|
|
17
|
+
- **React Router** — client-side routing (optional)
|
|
18
|
+
- **Testing** — Vitest or Jest (optional)
|
|
19
|
+
- **Linter** — ESLint (default) or Oxlint
|
|
20
|
+
- **Icon library** — Lucide React, Huge Icons, or none
|
|
21
|
+
- **Axios** — pre-configured HTTP client (optional)
|
|
22
|
+
- **README + LICENSE** — auto-generated (MIT)
|
|
23
|
+
- **Git init** — auto-initialize a git repo
|
|
24
|
+
- **Path alias** — `@/` imports configured out of the box
|
|
25
|
+
|
|
26
|
+
Your choices are cached and used as defaults next time.
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
### From npm (recommended)
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm create lumen my-app
|
|
34
|
+
cd my-app
|
|
35
|
+
npm run dev
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### From this repo
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
git clone https://github.com/Its-JrDev/create-lumen.git
|
|
42
|
+
cd create-lumen
|
|
43
|
+
npm install
|
|
44
|
+
npm link
|
|
45
|
+
npx create-lumen my-app
|
|
46
|
+
cd my-app
|
|
47
|
+
npm run dev
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Quick setup defaults
|
|
51
|
+
|
|
52
|
+
TypeScript · Tailwind CSS · Feature-based · Router · ESLint · Vitest
|
|
53
|
+
|
|
54
|
+
## Architecture
|
|
55
|
+
|
|
56
|
+
### Feature-based
|
|
57
|
+
|
|
58
|
+
Organized by domain. Each feature is self-contained:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
src/
|
|
62
|
+
├── app/ # App root
|
|
63
|
+
├── features/
|
|
64
|
+
│ └── home/
|
|
65
|
+
│ ├── pages/
|
|
66
|
+
│ ├── components/
|
|
67
|
+
│ └── services/
|
|
68
|
+
├── layouts/
|
|
69
|
+
├── pages/
|
|
70
|
+
├── routes/
|
|
71
|
+
├── store/
|
|
72
|
+
├── hooks/
|
|
73
|
+
└── utils/
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Scaffold new features instantly:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm run create:feature auth
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Component-based
|
|
83
|
+
|
|
84
|
+
Organized by type. Components grouped by their role:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
src/
|
|
88
|
+
├── components/
|
|
89
|
+
│ ├── common/ # Shared, reusable
|
|
90
|
+
│ ├── form/ # Form elements
|
|
91
|
+
│ └── layout/ # Layout primitives
|
|
92
|
+
├── pages/
|
|
93
|
+
├── layouts/
|
|
94
|
+
├── routes/
|
|
95
|
+
├── store/
|
|
96
|
+
├── hooks/
|
|
97
|
+
└── utils/
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Path alias
|
|
101
|
+
|
|
102
|
+
All projects use the `@/` path alias:
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
import { Button } from "@/components/common/Button";
|
|
106
|
+
import { useAuth } from "@/hooks/useAuth";
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Requirements
|
|
110
|
+
|
|
111
|
+
- Node.js >= 18
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { main } from "../src/main.js";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
|
|
6
|
+
console.log(
|
|
7
|
+
chalk.bold.cyan("\n ✦ LUMEN") +
|
|
8
|
+
chalk.gray(" v1.0.0") +
|
|
9
|
+
chalk.gray(
|
|
10
|
+
"\n Scaffold a React + Vite project with architecture choice\n"
|
|
11
|
+
)
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
main().catch((e) => {
|
|
15
|
+
console.error(chalk.red("\nError:"), e.message || e);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-lumen",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Scaffold a React + Vite project with architecture choice: feature-based or component-based.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-lumen": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"src/",
|
|
12
|
+
"templates/",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"scaffold",
|
|
21
|
+
"create",
|
|
22
|
+
"cli",
|
|
23
|
+
"vite",
|
|
24
|
+
"react",
|
|
25
|
+
"typescript",
|
|
26
|
+
"feature-based",
|
|
27
|
+
"component-based",
|
|
28
|
+
"architecture",
|
|
29
|
+
"starter",
|
|
30
|
+
"boilerplate"
|
|
31
|
+
],
|
|
32
|
+
"author": "",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@clack/prompts": "^0.11.0",
|
|
36
|
+
"chalk": "^5.6.2",
|
|
37
|
+
"execa": "^9.6.0"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/cleanup.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { deleteIfExists } from "./utils/fs.js";
|
|
2
|
+
|
|
3
|
+
export async function cleanupBoilerplate(projectPath) {
|
|
4
|
+
await deleteIfExists(`${projectPath}/public`);
|
|
5
|
+
await deleteIfExists(`${projectPath}/src/assets`);
|
|
6
|
+
await deleteIfExists(`${projectPath}/.oxlintrc.json`);
|
|
7
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { access, readFile, writeFile } from "fs/promises";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
const CONFIG_PATH = path.join(os.homedir(), ".lumen-config.json");
|
|
6
|
+
|
|
7
|
+
export async function configExists() {
|
|
8
|
+
try {
|
|
9
|
+
await access(CONFIG_PATH);
|
|
10
|
+
return true;
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function loadConfig() {
|
|
17
|
+
try {
|
|
18
|
+
const content = await readFile(CONFIG_PATH, "utf-8");
|
|
19
|
+
return JSON.parse(content);
|
|
20
|
+
} catch {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function saveConfig(data) {
|
|
26
|
+
await writeFile(CONFIG_PATH, JSON.stringify(data, null, 2), "utf-8");
|
|
27
|
+
}
|
package/src/configure.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { promises as fsp } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { readFileContent } from "./utils/fs.js";
|
|
4
|
+
|
|
5
|
+
export async function configureProject(projectPath, language, cssFramework) {
|
|
6
|
+
const ext = language === "ts" ? "ts" : "js";
|
|
7
|
+
|
|
8
|
+
// Set up tsconfig/jsconfig with @/* paths
|
|
9
|
+
if (language === "ts") {
|
|
10
|
+
await configureTsconfig(projectPath);
|
|
11
|
+
} else {
|
|
12
|
+
await configureJsconfig(projectPath);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Add tsconfigPaths: true to vite.config for non-tailwind setups
|
|
16
|
+
if (cssFramework !== "tailwind") {
|
|
17
|
+
await addTsconfigPathsToViteConfig(projectPath, ext);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Add scripts to package.json
|
|
21
|
+
await configurePackageJson(projectPath);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function configureTsconfig(projectPath) {
|
|
25
|
+
// Create/update tsconfig.app.json with paths
|
|
26
|
+
const tsconfigAppPath = path.join(projectPath, "tsconfig.app.json");
|
|
27
|
+
try {
|
|
28
|
+
const content = await readFileContent(tsconfigAppPath);
|
|
29
|
+
const config = JSON.parse(content);
|
|
30
|
+
|
|
31
|
+
if (!config.compilerOptions) config.compilerOptions = {};
|
|
32
|
+
config.compilerOptions.baseUrl = ".";
|
|
33
|
+
config.compilerOptions.paths = {
|
|
34
|
+
"@/*": ["./src/*"],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
await fsp.writeFile(
|
|
38
|
+
tsconfigAppPath,
|
|
39
|
+
JSON.stringify(config, null, 2) + "\n",
|
|
40
|
+
"utf8"
|
|
41
|
+
);
|
|
42
|
+
} catch {
|
|
43
|
+
// Create tsconfig.app.json if it doesn't exist
|
|
44
|
+
const config = {
|
|
45
|
+
compilerOptions: {
|
|
46
|
+
target: "ES2020",
|
|
47
|
+
useDefineForClassFields: true,
|
|
48
|
+
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
49
|
+
module: "ESNext",
|
|
50
|
+
skipLibCheck: true,
|
|
51
|
+
moduleResolution: "bundler",
|
|
52
|
+
allowImportingTsExtensions: true,
|
|
53
|
+
resolveJsonModule: true,
|
|
54
|
+
isolatedModules: true,
|
|
55
|
+
noEmit: true,
|
|
56
|
+
jsx: "react-jsx",
|
|
57
|
+
strict: true,
|
|
58
|
+
noUnusedLocals: true,
|
|
59
|
+
noUnusedParameters: true,
|
|
60
|
+
noFallthroughCasesInSwitch: true,
|
|
61
|
+
baseUrl: ".",
|
|
62
|
+
paths: {
|
|
63
|
+
"@/*": ["./src/*"],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
include: ["src"],
|
|
67
|
+
};
|
|
68
|
+
await fsp.writeFile(
|
|
69
|
+
tsconfigAppPath,
|
|
70
|
+
JSON.stringify(config, null, 2) + "\n",
|
|
71
|
+
"utf8"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Create/update root tsconfig.json with references
|
|
76
|
+
const rootTsconfigPath = path.join(projectPath, "tsconfig.json");
|
|
77
|
+
const rootTsconfig = {
|
|
78
|
+
files: [],
|
|
79
|
+
references: [{ path: "./tsconfig.app.json" }],
|
|
80
|
+
};
|
|
81
|
+
await fsp.writeFile(
|
|
82
|
+
rootTsconfigPath,
|
|
83
|
+
JSON.stringify(rootTsconfig, null, 2) + "\n",
|
|
84
|
+
"utf8"
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function configureJsconfig(projectPath) {
|
|
89
|
+
const jsconfigPath = path.join(projectPath, "jsconfig.json");
|
|
90
|
+
const config = {
|
|
91
|
+
compilerOptions: {
|
|
92
|
+
baseUrl: ".",
|
|
93
|
+
paths: {
|
|
94
|
+
"@/*": ["./src/*"],
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
include: ["src"],
|
|
98
|
+
};
|
|
99
|
+
await fsp.writeFile(jsconfigPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function addTsconfigPathsToViteConfig(projectPath, ext) {
|
|
103
|
+
const viteConfigPath = path.join(projectPath, `vite.config.${ext}`);
|
|
104
|
+
try {
|
|
105
|
+
let content = await readFileContent(viteConfigPath);
|
|
106
|
+
|
|
107
|
+
if (content.includes("tsconfigPaths")) return;
|
|
108
|
+
|
|
109
|
+
const lines = content.split("\n");
|
|
110
|
+
|
|
111
|
+
// Find where imports end (last import line index)
|
|
112
|
+
let lastImportIdx = -1;
|
|
113
|
+
for (let i = 0; i < lines.length; i++) {
|
|
114
|
+
if (lines[i].startsWith("import ")) lastImportIdx = i;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Insert path/fileURLToPath imports after last import
|
|
118
|
+
const newImports = [
|
|
119
|
+
'import path from "path";',
|
|
120
|
+
'import { fileURLToPath } from "url";',
|
|
121
|
+
"const __dirname = path.dirname(fileURLToPath(import.meta.url));",
|
|
122
|
+
];
|
|
123
|
+
lines.splice(lastImportIdx + 1, 0, ...newImports);
|
|
124
|
+
|
|
125
|
+
let result = lines.join("\n");
|
|
126
|
+
|
|
127
|
+
if (result.includes("resolve:")) {
|
|
128
|
+
result = result.replace(
|
|
129
|
+
/resolve:\s*\{/,
|
|
130
|
+
'resolve: {\n tsconfigPaths: true,\n alias: [{ find: "@", replacement: path.resolve(__dirname, "src") }],'
|
|
131
|
+
);
|
|
132
|
+
} else {
|
|
133
|
+
result = result.replace(
|
|
134
|
+
/defineConfig\(\{/,
|
|
135
|
+
'defineConfig({\n resolve: {\n tsconfigPaths: true,\n alias: [{ find: "@", replacement: path.resolve(__dirname, "src") }],\n },'
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
await fsp.writeFile(viteConfigPath, result, "utf8");
|
|
140
|
+
} catch {
|
|
141
|
+
// file doesn't exist, skip
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function configurePackageJson(projectPath) {
|
|
146
|
+
const pkgPath = path.join(projectPath, "package.json");
|
|
147
|
+
try {
|
|
148
|
+
const content = await readFileContent(pkgPath);
|
|
149
|
+
const pkg = JSON.parse(content);
|
|
150
|
+
|
|
151
|
+
if (!pkg.scripts) pkg.scripts = {};
|
|
152
|
+
|
|
153
|
+
pkg.scripts.dev = "vite";
|
|
154
|
+
pkg.scripts.build = "vite build";
|
|
155
|
+
pkg.scripts.preview = "vite preview";
|
|
156
|
+
|
|
157
|
+
await fsp.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
158
|
+
} catch {
|
|
159
|
+
// file doesn't exist, skip
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isCancel, select } from "@clack/prompts";
|
|
2
|
+
import { access, mkdir, readdir, rm } from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { onCancel } from "./prompts.js";
|
|
5
|
+
|
|
6
|
+
export async function confirmEmptyFolder(projectName) {
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
const targetPath = path.resolve(cwd, projectName || "");
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
await access(targetPath);
|
|
12
|
+
} catch {
|
|
13
|
+
return targetPath;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const files = await readdir(targetPath);
|
|
17
|
+
if (files.length === 0) return targetPath;
|
|
18
|
+
|
|
19
|
+
const remove = await select({
|
|
20
|
+
message: `Folder "${projectName}" already exists and is not empty. How would you like to proceed?`,
|
|
21
|
+
options: [
|
|
22
|
+
{ label: "Delete all files and continue", value: true },
|
|
23
|
+
{ label: "Cancel setup", value: false },
|
|
24
|
+
],
|
|
25
|
+
initialValue: true,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (isCancel(remove) || !remove) {
|
|
29
|
+
onCancel();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (targetPath === cwd) {
|
|
33
|
+
const inside = await readdir(targetPath);
|
|
34
|
+
await Promise.all(
|
|
35
|
+
inside.map((name) =>
|
|
36
|
+
rm(path.join(targetPath, name), { recursive: true, force: true })
|
|
37
|
+
)
|
|
38
|
+
);
|
|
39
|
+
} else {
|
|
40
|
+
await rm(targetPath, { recursive: true, force: true });
|
|
41
|
+
await mkdir(targetPath);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return targetPath;
|
|
45
|
+
}
|
package/src/css.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { promises as fsp } from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { deleteIfExists } from "./utils/fs.js";
|
|
5
|
+
|
|
6
|
+
export async function setupCssFramework({
|
|
7
|
+
projectPath,
|
|
8
|
+
templatesDir,
|
|
9
|
+
language,
|
|
10
|
+
cssFramework,
|
|
11
|
+
ext,
|
|
12
|
+
pkg,
|
|
13
|
+
}) {
|
|
14
|
+
process.chdir(projectPath);
|
|
15
|
+
const cssDir = path.join(templatesDir, "css");
|
|
16
|
+
const indexCssPath = path.join(projectPath, "src", "index.css");
|
|
17
|
+
|
|
18
|
+
if (cssFramework === "tailwind") {
|
|
19
|
+
const viteConfigFile = `vite.config.${language === "ts" ? "ts" : "js"}`;
|
|
20
|
+
const viteConfigTarget = path.join(projectPath, viteConfigFile);
|
|
21
|
+
const viteConfigContent = await fsp.readFile(
|
|
22
|
+
path.join(cssDir, "tailwind", viteConfigFile),
|
|
23
|
+
"utf8"
|
|
24
|
+
);
|
|
25
|
+
await fsp.writeFile(viteConfigTarget, viteConfigContent, "utf8");
|
|
26
|
+
|
|
27
|
+
const indexCssContent = await fsp.readFile(
|
|
28
|
+
path.join(cssDir, "tailwind", "src", "index.css"),
|
|
29
|
+
"utf8"
|
|
30
|
+
);
|
|
31
|
+
await fsp.writeFile(indexCssPath, indexCssContent, "utf8");
|
|
32
|
+
} else if (cssFramework === "bootstrap") {
|
|
33
|
+
const mainFile = path.join(projectPath, "src", `main.${ext}`);
|
|
34
|
+
if (fs.existsSync(mainFile)) {
|
|
35
|
+
let mainContent = await fsp.readFile(mainFile, "utf8");
|
|
36
|
+
if (
|
|
37
|
+
!mainContent.includes("bootstrap/dist/css/bootstrap.min.css")
|
|
38
|
+
) {
|
|
39
|
+
mainContent =
|
|
40
|
+
"import 'bootstrap/dist/css/bootstrap.min.css';\n" + mainContent;
|
|
41
|
+
await fsp.writeFile(mainFile, mainContent, "utf8");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const indexCssContent = await fsp.readFile(
|
|
45
|
+
path.join(cssDir, "bootstrap", "src", "index.css"),
|
|
46
|
+
"utf8"
|
|
47
|
+
);
|
|
48
|
+
await fsp.writeFile(indexCssPath, indexCssContent, "utf8");
|
|
49
|
+
} else {
|
|
50
|
+
const indexCssContent = await fsp.readFile(
|
|
51
|
+
path.join(cssDir, "none", "src", "index.css"),
|
|
52
|
+
"utf8"
|
|
53
|
+
);
|
|
54
|
+
await fsp.writeFile(indexCssPath, indexCssContent, "utf8");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Clean up Vite's default App.css
|
|
58
|
+
await deleteIfExists(path.join(projectPath, "src", "App.css"));
|
|
59
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
|
|
3
|
+
export async function installDeps(pkgManager, dev, packages, cwd) {
|
|
4
|
+
let cmd, args;
|
|
5
|
+
switch (pkgManager) {
|
|
6
|
+
case "pnpm":
|
|
7
|
+
cmd = "pnpm";
|
|
8
|
+
args = dev ? ["add", "-D", ...packages] : ["add", ...packages];
|
|
9
|
+
break;
|
|
10
|
+
case "yarn":
|
|
11
|
+
cmd = "yarn";
|
|
12
|
+
args = dev ? ["add", "-D", ...packages] : ["add", ...packages];
|
|
13
|
+
break;
|
|
14
|
+
case "bun":
|
|
15
|
+
cmd = "bun";
|
|
16
|
+
args = dev ? ["add", "-d", ...packages] : ["add", ...packages];
|
|
17
|
+
break;
|
|
18
|
+
default:
|
|
19
|
+
cmd = "npm";
|
|
20
|
+
args = dev
|
|
21
|
+
? ["install", "-D", ...packages]
|
|
22
|
+
: ["install", ...packages];
|
|
23
|
+
}
|
|
24
|
+
await execa(cmd, args, { stdio: "pipe", cwd });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function installAllDeps(responses, pkg, cwd) {
|
|
28
|
+
const deps = [];
|
|
29
|
+
const devDeps = [];
|
|
30
|
+
|
|
31
|
+
// CSS framework
|
|
32
|
+
if (responses.cssFramework === "tailwind") {
|
|
33
|
+
devDeps.push("tailwindcss", "@tailwindcss/vite");
|
|
34
|
+
deps.push("clsx", "tailwind-merge");
|
|
35
|
+
} else if (responses.cssFramework === "bootstrap") {
|
|
36
|
+
deps.push("bootstrap", "react-bootstrap");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// State management
|
|
40
|
+
if (responses.stateManagement === "redux") {
|
|
41
|
+
deps.push("@reduxjs/toolkit", "react-redux");
|
|
42
|
+
} else if (responses.stateManagement === "zustand") {
|
|
43
|
+
deps.push("zustand");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Router
|
|
47
|
+
if (responses.router) {
|
|
48
|
+
deps.push("react-router-dom");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Icons
|
|
52
|
+
if (responses.iconLibrary === "lucide") {
|
|
53
|
+
deps.push("lucide-react");
|
|
54
|
+
} else if (responses.iconLibrary === "huge") {
|
|
55
|
+
deps.push("@hugeicons/react", "@hugeicons/core-free-icons");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Axios
|
|
59
|
+
if (responses.axios) {
|
|
60
|
+
deps.push("axios");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Testing
|
|
64
|
+
if (responses.testing === "vitest") {
|
|
65
|
+
devDeps.push(
|
|
66
|
+
"vitest",
|
|
67
|
+
"@testing-library/react",
|
|
68
|
+
"@testing-library/jest-dom",
|
|
69
|
+
"jsdom"
|
|
70
|
+
);
|
|
71
|
+
} else if (responses.testing === "jest") {
|
|
72
|
+
devDeps.push(
|
|
73
|
+
"jest",
|
|
74
|
+
"ts-jest",
|
|
75
|
+
"@types/jest",
|
|
76
|
+
"@testing-library/react",
|
|
77
|
+
"@testing-library/jest-dom",
|
|
78
|
+
"jsdom"
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Linter
|
|
83
|
+
if (responses.linter === "eslint") {
|
|
84
|
+
devDeps.push("eslint", "@eslint/js", "eslint-plugin-react-hooks", "eslint-plugin-react-refresh", "globals");
|
|
85
|
+
if (responses.language === "ts") {
|
|
86
|
+
devDeps.push("typescript-eslint");
|
|
87
|
+
}
|
|
88
|
+
} else if (responses.linter === "oxlint") {
|
|
89
|
+
devDeps.push("oxlint");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Always re-install react + react-dom for consistency
|
|
93
|
+
deps.push("react", "react-dom");
|
|
94
|
+
|
|
95
|
+
if (deps.length > 0) {
|
|
96
|
+
await installDeps(pkg, false, deps, cwd);
|
|
97
|
+
}
|
|
98
|
+
if (devDeps.length > 0) {
|
|
99
|
+
await installDeps(pkg, true, devDeps, cwd);
|
|
100
|
+
}
|
|
101
|
+
}
|