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/src/injector.js
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { promises as fsp } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { copyDirRecursive, writeFileRecursive } from "./utils/fs.js";
|
|
4
|
+
|
|
5
|
+
export async function injectArchitecture(projectPath, templatesDir, architecture, language) {
|
|
6
|
+
const ext = language === "ts" ? "tsx" : "jsx";
|
|
7
|
+
const srcDir = path.join(templatesDir, "architectures", architecture, "src");
|
|
8
|
+
|
|
9
|
+
// Remove Vite's default App files BEFORE copying architecture
|
|
10
|
+
const defaultAppTsx = path.join(projectPath, "src", "App.tsx");
|
|
11
|
+
const defaultAppJsx = path.join(projectPath, "src", "App.jsx");
|
|
12
|
+
try { await fsp.rm(defaultAppTsx, { force: true }); } catch {}
|
|
13
|
+
try { await fsp.rm(defaultAppJsx, { force: true }); } catch {}
|
|
14
|
+
|
|
15
|
+
// Copy the architecture tree into the project's src/
|
|
16
|
+
await copyDirRecursive(srcDir, path.join(projectPath, "src"), (name) => {
|
|
17
|
+
// Skip files that don't match the language
|
|
18
|
+
if (language === "ts" && name.endsWith(".jsx")) return false;
|
|
19
|
+
if (language === "js" && name.endsWith(".tsx")) return false;
|
|
20
|
+
// Skip .ts files when JS, skip .js when TS (for non-component files)
|
|
21
|
+
if (language === "ts" && name.endsWith(".js") && !name.endsWith(".config.js")) return false;
|
|
22
|
+
if (language === "js" && name.endsWith(".ts") && !name.endsWith(".config.ts")) return false;
|
|
23
|
+
return true;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Copy the architecture's main.tsx/main.jsx to src/main.{ext}
|
|
27
|
+
const mainSrc = path.join(
|
|
28
|
+
templatesDir,
|
|
29
|
+
"architectures",
|
|
30
|
+
architecture,
|
|
31
|
+
`main.${ext}`
|
|
32
|
+
);
|
|
33
|
+
try {
|
|
34
|
+
await fsp.access(mainSrc);
|
|
35
|
+
await fsp.copyFile(mainSrc, path.join(projectPath, "src", `main.${ext}`));
|
|
36
|
+
} catch {
|
|
37
|
+
// main file doesn't exist in template, skip
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Remove the opposite language's main file (Vite creates main.jsx by default)
|
|
41
|
+
const oppositeExt = language === "ts" ? "jsx" : "tsx";
|
|
42
|
+
const oppositeMain = path.join(projectPath, "src", `main.${oppositeExt}`);
|
|
43
|
+
try { await fsp.rm(oppositeMain, { force: true }); } catch {}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function injectConditionals(projectPath, templatesDir, responses, architecture, language) {
|
|
47
|
+
const ext = language === "ts" ? "tsx" : "jsx";
|
|
48
|
+
|
|
49
|
+
// 1. State management
|
|
50
|
+
if (responses.stateManagement === "redux") {
|
|
51
|
+
await injectOverlay(
|
|
52
|
+
projectPath,
|
|
53
|
+
templatesDir,
|
|
54
|
+
`conditional/state/redux/${architecture}`,
|
|
55
|
+
language
|
|
56
|
+
);
|
|
57
|
+
await updateMainWithProvider(projectPath, "redux", architecture, ext);
|
|
58
|
+
} else if (responses.stateManagement === "zustand") {
|
|
59
|
+
await injectOverlay(
|
|
60
|
+
projectPath,
|
|
61
|
+
templatesDir,
|
|
62
|
+
`conditional/state/zustand/${architecture}`,
|
|
63
|
+
language
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 2. Router (overwrites App.tsx with routed version)
|
|
68
|
+
if (responses.router) {
|
|
69
|
+
await injectOverlay(
|
|
70
|
+
projectPath,
|
|
71
|
+
templatesDir,
|
|
72
|
+
`conditional/router/${architecture}`,
|
|
73
|
+
language
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 3. Icons (overwrites home page)
|
|
78
|
+
if (responses.iconLibrary && responses.iconLibrary !== "none") {
|
|
79
|
+
await injectOverlay(
|
|
80
|
+
projectPath,
|
|
81
|
+
templatesDir,
|
|
82
|
+
`conditional/icons/${responses.iconLibrary}/${architecture}`,
|
|
83
|
+
language
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 4. Axios
|
|
88
|
+
if (responses.axios) {
|
|
89
|
+
await injectOverlay(
|
|
90
|
+
projectPath,
|
|
91
|
+
templatesDir,
|
|
92
|
+
`conditional/axios/${architecture}`,
|
|
93
|
+
language
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 5. Testing
|
|
98
|
+
if (responses.testing === "vitest") {
|
|
99
|
+
await injectTesting(projectPath, templatesDir, "vitest", language, architecture);
|
|
100
|
+
} else if (responses.testing === "jest") {
|
|
101
|
+
await injectTesting(projectPath, templatesDir, "jest", language, architecture);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 6. Linter
|
|
105
|
+
if (responses.linter === "eslint") {
|
|
106
|
+
await injectLinter(projectPath, templatesDir, "eslint", language);
|
|
107
|
+
} else if (responses.linter === "oxlint") {
|
|
108
|
+
await injectLinter(projectPath, templatesDir, "oxlint", language);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 7. Create feature script (feature-based only)
|
|
112
|
+
if (architecture === "feature-based") {
|
|
113
|
+
await injectCreateFeatureScript(projectPath, templatesDir, language);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function injectOverlay(projectPath, templatesDir, overlayPath, language) {
|
|
118
|
+
const srcDir = path.join(templatesDir, overlayPath, "src");
|
|
119
|
+
try {
|
|
120
|
+
await fsp.access(srcDir);
|
|
121
|
+
await copyDirRecursive(srcDir, path.join(projectPath, "src"), (name) => {
|
|
122
|
+
if (language === "ts" && name.endsWith(".jsx")) return false;
|
|
123
|
+
if (language === "js" && name.endsWith(".tsx")) return false;
|
|
124
|
+
if (language === "ts" && name.endsWith(".js") && !name.endsWith(".config.js")) return false;
|
|
125
|
+
if (language === "js" && name.endsWith(".ts") && !name.endsWith(".config.ts")) return false;
|
|
126
|
+
return true;
|
|
127
|
+
});
|
|
128
|
+
} catch {
|
|
129
|
+
// overlay src dir doesn't exist
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function injectTesting(projectPath, templatesDir, framework, language, architecture) {
|
|
134
|
+
const testingDir = path.join(templatesDir, "conditional", "testing", framework);
|
|
135
|
+
|
|
136
|
+
// Copy config files to project root
|
|
137
|
+
const configExt = language === "ts" ? "ts" : "js";
|
|
138
|
+
const configSrc = path.join(testingDir, `${framework}.config.${configExt}`);
|
|
139
|
+
try {
|
|
140
|
+
await fsp.access(configSrc);
|
|
141
|
+
await fsp.copyFile(configSrc, path.join(projectPath, `${framework}.config.${configExt}`));
|
|
142
|
+
} catch {}
|
|
143
|
+
|
|
144
|
+
// Copy setup file
|
|
145
|
+
const setupSrc = path.join(testingDir, `src/test/setup.${configExt}`);
|
|
146
|
+
try {
|
|
147
|
+
await fsp.access(setupSrc);
|
|
148
|
+
const setupDest = path.join(projectPath, "src", "test", `setup.${configExt}`);
|
|
149
|
+
await writeFileRecursive(setupDest, await fsp.readFile(setupSrc, "utf8"));
|
|
150
|
+
} catch {}
|
|
151
|
+
|
|
152
|
+
// Copy test file
|
|
153
|
+
const testExt = language === "ts" ? "tsx" : "jsx";
|
|
154
|
+
const testSrc = path.join(testingDir, `src/__tests__/App.test.${testExt}`);
|
|
155
|
+
try {
|
|
156
|
+
await fsp.access(testSrc);
|
|
157
|
+
const testDest = path.join(projectPath, "src", "__tests__", `App.test.${testExt}`);
|
|
158
|
+
await writeFileRecursive(testDest, await fsp.readFile(testSrc, "utf8"));
|
|
159
|
+
} catch {}
|
|
160
|
+
|
|
161
|
+
// Add test script to package.json
|
|
162
|
+
const pkgPath = path.join(projectPath, "package.json");
|
|
163
|
+
try {
|
|
164
|
+
const content = await fsp.readFile(pkgPath, "utf8");
|
|
165
|
+
const pkg = JSON.parse(content);
|
|
166
|
+
if (!pkg.scripts) pkg.scripts = {};
|
|
167
|
+
if (framework === "vitest") {
|
|
168
|
+
pkg.scripts.test = "vitest";
|
|
169
|
+
pkg.scripts["test:run"] = "vitest run";
|
|
170
|
+
} else {
|
|
171
|
+
pkg.scripts.test = "jest";
|
|
172
|
+
pkg.scripts["test:watch"] = "jest --watch";
|
|
173
|
+
}
|
|
174
|
+
await fsp.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
175
|
+
} catch {}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function injectLinter(projectPath, templatesDir, linter, language) {
|
|
179
|
+
const lintingDir = path.join(templatesDir, "conditional", "linting", linter);
|
|
180
|
+
|
|
181
|
+
if (linter === "eslint") {
|
|
182
|
+
const configExt = language === "ts" ? "ts" : "js";
|
|
183
|
+
const configSrc = path.join(lintingDir, `eslint.config.${configExt}`);
|
|
184
|
+
try {
|
|
185
|
+
await fsp.access(configSrc);
|
|
186
|
+
await fsp.copyFile(configSrc, path.join(projectPath, `eslint.config.${configExt}`));
|
|
187
|
+
} catch {}
|
|
188
|
+
} else if (linter === "oxlint") {
|
|
189
|
+
const configSrc = path.join(lintingDir, "oxlintrc.json");
|
|
190
|
+
try {
|
|
191
|
+
await fsp.access(configSrc);
|
|
192
|
+
await fsp.copyFile(configSrc, path.join(projectPath, "oxlintrc.json"));
|
|
193
|
+
} catch {}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Add lint script to package.json
|
|
197
|
+
const pkgPath = path.join(projectPath, "package.json");
|
|
198
|
+
try {
|
|
199
|
+
const content = await fsp.readFile(pkgPath, "utf8");
|
|
200
|
+
const pkg = JSON.parse(content);
|
|
201
|
+
if (!pkg.scripts) pkg.scripts = {};
|
|
202
|
+
if (linter === "eslint") {
|
|
203
|
+
pkg.scripts.lint = "eslint .";
|
|
204
|
+
pkg.scripts["lint:fix"] = "eslint . --fix";
|
|
205
|
+
} else {
|
|
206
|
+
pkg.scripts.lint = "oxlint .";
|
|
207
|
+
}
|
|
208
|
+
await fsp.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
209
|
+
} catch {}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function injectCreateFeatureScript(projectPath, templatesDir, language) {
|
|
213
|
+
const scriptSrc = path.join(templatesDir, "architectures", "feature-based", "scripts", "create-feature.mjs");
|
|
214
|
+
const scriptDest = path.join(projectPath, "scripts", "create-feature.mjs");
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
await fsp.access(scriptSrc);
|
|
218
|
+
let content = await fsp.readFile(scriptSrc, "utf8");
|
|
219
|
+
|
|
220
|
+
// Adapt to JS if needed
|
|
221
|
+
if (language === "js") {
|
|
222
|
+
// Replace .ts extensions with .js in the initialFiles
|
|
223
|
+
content = content
|
|
224
|
+
.replace(/interfaces\/index\.ts/g, "interfaces/index.js")
|
|
225
|
+
.replace(/services\/index\.ts/g, "services/index.js")
|
|
226
|
+
.replace(/store\/index\.ts/g, "store/index.js")
|
|
227
|
+
.replace(/hooks\/index\.ts/g, "hooks/index.js")
|
|
228
|
+
.replace(/pages\/index\.ts/g, "pages/index.js");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
await writeFileRecursive(scriptDest, content);
|
|
232
|
+
} catch {}
|
|
233
|
+
|
|
234
|
+
// Add script to package.json
|
|
235
|
+
const pkgPath = path.join(projectPath, "package.json");
|
|
236
|
+
try {
|
|
237
|
+
const content = await fsp.readFile(pkgPath, "utf8");
|
|
238
|
+
const pkg = JSON.parse(content);
|
|
239
|
+
if (!pkg.scripts) pkg.scripts = {};
|
|
240
|
+
pkg.scripts["create:feature"] = "node scripts/create-feature.mjs";
|
|
241
|
+
await fsp.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
242
|
+
} catch {}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function updateMainWithProvider(projectPath, stateType, architecture, ext) {
|
|
246
|
+
const mainPath = path.join(projectPath, "src", `main.${ext}`);
|
|
247
|
+
try {
|
|
248
|
+
let content = await fsp.readFile(mainPath, "utf8");
|
|
249
|
+
|
|
250
|
+
if (architecture === "feature-based") {
|
|
251
|
+
if (!content.includes("StoreProvider")) {
|
|
252
|
+
content = content.replace(
|
|
253
|
+
/import App from ['"]@\/app\/App['"]\n?/,
|
|
254
|
+
"import App from '@/app/App'\nimport { StoreProvider } from '@/app/providers/StoreProvider'\n"
|
|
255
|
+
);
|
|
256
|
+
content = content.replace(
|
|
257
|
+
/<App \/>/,
|
|
258
|
+
"<StoreProvider>\n <App />\n </StoreProvider>"
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
if (!content.includes("StoreProvider")) {
|
|
263
|
+
content = content.replace(
|
|
264
|
+
/import App from ['"]\.\/App['"]\n?/,
|
|
265
|
+
"import App from './App'\nimport { StoreProvider } from './context/StoreProvider'\n"
|
|
266
|
+
);
|
|
267
|
+
content = content.replace(
|
|
268
|
+
/<App \/>/,
|
|
269
|
+
"<StoreProvider>\n <App />\n </StoreProvider>"
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
await fsp.writeFile(mainPath, content, "utf8");
|
|
275
|
+
} catch {}
|
|
276
|
+
}
|
package/src/main.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { isCancel, log, spinner, text } from "@clack/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
|
|
6
|
+
import { confirmEmptyFolder } from "./confirm-empty-folder.js";
|
|
7
|
+
import { cleanupBoilerplate } from "./cleanup.js";
|
|
8
|
+
import { configureProject } from "./configure.js";
|
|
9
|
+
import { setupCssFramework } from "./css.js";
|
|
10
|
+
import { injectArchitecture, injectConditionals } from "./injector.js";
|
|
11
|
+
import { getPkgManager } from "./utils/pkg-manager.js";
|
|
12
|
+
import { handleDevServer } from "./utils/dev-server.js";
|
|
13
|
+
import { getUserInputs, onCancel } from "./prompts.js";
|
|
14
|
+
import { runBaseInstall, runGitInit, runViteCreate } from "./scaffold.js";
|
|
15
|
+
import { installAllDeps } from "./dependencies.js";
|
|
16
|
+
import { generateReadme } from "./readme.js";
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
const TEMPLATES_DIR = path.join(__dirname, "../templates");
|
|
21
|
+
const CURRENT_DIR = process.cwd();
|
|
22
|
+
|
|
23
|
+
export async function main() {
|
|
24
|
+
const pkg = getPkgManager();
|
|
25
|
+
|
|
26
|
+
// 1. Project name
|
|
27
|
+
const cliArg = process.argv[2];
|
|
28
|
+
let projectName;
|
|
29
|
+
|
|
30
|
+
if (cliArg && cliArg.trim()) {
|
|
31
|
+
projectName = cliArg.trim();
|
|
32
|
+
log.step(chalk.gray(`Project name: ${chalk.bold(projectName)}`));
|
|
33
|
+
} else {
|
|
34
|
+
projectName = await text({
|
|
35
|
+
message: "What is your project name?",
|
|
36
|
+
placeholder: "my-app",
|
|
37
|
+
validate(value) {
|
|
38
|
+
if (!value || !value.trim()) return "Project name is required!";
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
if (isCancel(projectName)) onCancel();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 2. Handle existing folder
|
|
45
|
+
await confirmEmptyFolder(projectName);
|
|
46
|
+
|
|
47
|
+
// 3. Collect user preferences
|
|
48
|
+
const responses = await getUserInputs(projectName);
|
|
49
|
+
|
|
50
|
+
const projectPath = path.resolve(CURRENT_DIR, projectName);
|
|
51
|
+
|
|
52
|
+
// 4. Scaffold via vite create
|
|
53
|
+
const createSpin = spinner();
|
|
54
|
+
createSpin.start("Creating your Vite + React project...");
|
|
55
|
+
try {
|
|
56
|
+
await runViteCreate(projectPath, projectName, pkg, responses.language);
|
|
57
|
+
createSpin.stop(chalk.green("Project created successfully!"));
|
|
58
|
+
} catch (err) {
|
|
59
|
+
createSpin.stop(chalk.red("Failed to create project."));
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 5. Install base dependencies
|
|
64
|
+
const depSpin = spinner();
|
|
65
|
+
depSpin.start(`Installing dependencies using ${pkg}...`);
|
|
66
|
+
try {
|
|
67
|
+
await runBaseInstall(projectPath, pkg);
|
|
68
|
+
depSpin.stop(chalk.green("Dependencies installed."));
|
|
69
|
+
} catch (err) {
|
|
70
|
+
depSpin.stop(chalk.red("Failed to install dependencies."));
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 6. Git init (if requested)
|
|
75
|
+
if (responses.gitInit) {
|
|
76
|
+
const gitSpin = spinner();
|
|
77
|
+
gitSpin.start("Initializing git repository...");
|
|
78
|
+
try {
|
|
79
|
+
await runGitInit(projectPath);
|
|
80
|
+
gitSpin.stop(chalk.green("Git repository initialized."));
|
|
81
|
+
} catch (err) {
|
|
82
|
+
gitSpin.stop(chalk.red("Git initialization failed."));
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 7. Inject architecture templates
|
|
88
|
+
const archSpin = spinner();
|
|
89
|
+
archSpin.start(
|
|
90
|
+
`Setting up ${responses.architecture === "feature-based" ? "feature-based" : "component-based"} architecture...`
|
|
91
|
+
);
|
|
92
|
+
try {
|
|
93
|
+
await injectArchitecture(
|
|
94
|
+
projectPath,
|
|
95
|
+
TEMPLATES_DIR,
|
|
96
|
+
responses.architecture,
|
|
97
|
+
responses.language
|
|
98
|
+
);
|
|
99
|
+
archSpin.stop(chalk.green("Architecture scaffolding complete."));
|
|
100
|
+
} catch (err) {
|
|
101
|
+
archSpin.stop(chalk.red("Failed to scaffold architecture."));
|
|
102
|
+
throw err;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 8. Inject conditional features
|
|
106
|
+
const condSpin = spinner();
|
|
107
|
+
condSpin.start("Installing additional features...");
|
|
108
|
+
try {
|
|
109
|
+
await injectConditionals(
|
|
110
|
+
projectPath,
|
|
111
|
+
TEMPLATES_DIR,
|
|
112
|
+
responses,
|
|
113
|
+
responses.architecture,
|
|
114
|
+
responses.language
|
|
115
|
+
);
|
|
116
|
+
condSpin.stop(chalk.green("Features configured."));
|
|
117
|
+
} catch (err) {
|
|
118
|
+
condSpin.stop(chalk.red("Failed to configure features."));
|
|
119
|
+
throw err;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 9. Install all additional dependencies in one batch
|
|
123
|
+
const extrasSpin = spinner();
|
|
124
|
+
extrasSpin.start("Installing additional dependencies...");
|
|
125
|
+
try {
|
|
126
|
+
await installAllDeps(responses, pkg, projectPath);
|
|
127
|
+
extrasSpin.stop(chalk.green("Additional dependencies installed."));
|
|
128
|
+
} catch (err) {
|
|
129
|
+
extrasSpin.stop(chalk.red("Failed to install additional dependencies."));
|
|
130
|
+
throw err;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 10. CSS framework setup
|
|
134
|
+
const cssSpin = spinner();
|
|
135
|
+
const cssLabel =
|
|
136
|
+
responses.cssFramework === "none"
|
|
137
|
+
? "CSS reset"
|
|
138
|
+
: responses.cssFramework === "tailwind"
|
|
139
|
+
? "Tailwind CSS"
|
|
140
|
+
: "Bootstrap";
|
|
141
|
+
if (responses.cssFramework !== "none") {
|
|
142
|
+
cssSpin.start(`Setting up ${cssLabel}...`);
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
await setupCssFramework({
|
|
146
|
+
projectPath,
|
|
147
|
+
templatesDir: TEMPLATES_DIR,
|
|
148
|
+
language: responses.language,
|
|
149
|
+
cssFramework: responses.cssFramework,
|
|
150
|
+
ext: responses.language === "ts" ? "tsx" : "jsx",
|
|
151
|
+
pkg,
|
|
152
|
+
});
|
|
153
|
+
if (responses.cssFramework !== "none") {
|
|
154
|
+
cssSpin.stop(chalk.green(`${cssLabel} setup complete.`));
|
|
155
|
+
}
|
|
156
|
+
} catch (err) {
|
|
157
|
+
cssSpin.stop(chalk.red(`Failed to setup ${cssLabel}.`));
|
|
158
|
+
throw err;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 11. Configure path aliases + package.json
|
|
162
|
+
const configSpin = spinner();
|
|
163
|
+
configSpin.start("Configuring project...");
|
|
164
|
+
try {
|
|
165
|
+
await configureProject(projectPath, responses.language, responses.cssFramework);
|
|
166
|
+
configSpin.stop(chalk.green("Project configured."));
|
|
167
|
+
} catch (err) {
|
|
168
|
+
configSpin.stop(chalk.red("Failed to configure project."));
|
|
169
|
+
throw err;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 12. Cleanup boilerplate
|
|
173
|
+
await cleanupBoilerplate(projectPath);
|
|
174
|
+
|
|
175
|
+
// 13. Generate README.md + LICENSE (if requested)
|
|
176
|
+
if (responses.readme) {
|
|
177
|
+
const readmeSpin = spinner();
|
|
178
|
+
readmeSpin.start("Generating README.md and LICENSE...");
|
|
179
|
+
try {
|
|
180
|
+
await generateReadme(projectPath, projectName, responses);
|
|
181
|
+
readmeSpin.stop(chalk.green("README.md and LICENSE generated."));
|
|
182
|
+
} catch (err) {
|
|
183
|
+
readmeSpin.stop(chalk.red("Failed to generate README.md and LICENSE."));
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
log.step(chalk.green("\nProject setup complete!"));
|
|
189
|
+
log.message(
|
|
190
|
+
chalk.gray(
|
|
191
|
+
` Architecture: ${responses.architecture === "feature-based" ? "Feature-based" : "Component-based"}`
|
|
192
|
+
)
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
// 14. Offer dev server
|
|
196
|
+
await handleDevServer(pkg, projectName);
|
|
197
|
+
}
|
package/src/prompts.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { cancel, confirm, isCancel, note, select, text } from "@clack/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { configExists, loadConfig, saveConfig } from "./config-cache.js";
|
|
4
|
+
|
|
5
|
+
export function onCancel() {
|
|
6
|
+
console.log(chalk.gray("\n\nOperation cancelled.\n"));
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function formatConfig(responses) {
|
|
11
|
+
const lang = responses.language === "ts" ? "TypeScript" : "JavaScript";
|
|
12
|
+
const arch =
|
|
13
|
+
responses.architecture === "feature-based"
|
|
14
|
+
? "Feature-based"
|
|
15
|
+
: "Component-based";
|
|
16
|
+
const css =
|
|
17
|
+
responses.cssFramework === "none"
|
|
18
|
+
? "None"
|
|
19
|
+
: responses.cssFramework === "tailwind"
|
|
20
|
+
? "Tailwind CSS"
|
|
21
|
+
: "Bootstrap";
|
|
22
|
+
const testing =
|
|
23
|
+
responses.testing === "none"
|
|
24
|
+
? "None"
|
|
25
|
+
: responses.testing === "vitest"
|
|
26
|
+
? "Vitest"
|
|
27
|
+
: "Jest";
|
|
28
|
+
const state =
|
|
29
|
+
responses.stateManagement === "none"
|
|
30
|
+
? "None"
|
|
31
|
+
: responses.stateManagement === "redux"
|
|
32
|
+
? "Redux Toolkit"
|
|
33
|
+
: "Zustand";
|
|
34
|
+
|
|
35
|
+
return [
|
|
36
|
+
`• ${chalk.bold("Architecture:")} ${chalk.green(arch)}`,
|
|
37
|
+
`• ${chalk.bold("Language:")} ${chalk.green(lang)}`,
|
|
38
|
+
`• ${chalk.bold("CSS:")} ${chalk.yellow(css)}`,
|
|
39
|
+
`• ${chalk.bold("Testing:")} ${chalk.magenta(testing)}`,
|
|
40
|
+
`• ${chalk.bold("Router:")} ${responses.router ? chalk.green("Yes") : chalk.red("No")}`,
|
|
41
|
+
`• ${chalk.bold("State:")} ${chalk.blue(state)}`,
|
|
42
|
+
`• ${chalk.bold("Icons:")} ${chalk.blue(responses.iconLibrary === "none" ? "None" : responses.iconLibrary === "lucide" ? "Lucide" : "Huge")}`,
|
|
43
|
+
`• ${chalk.bold("Axios:")} ${responses.axios ? chalk.green("Yes") : chalk.red("No")}`,
|
|
44
|
+
`• ${chalk.bold("Linter:")} ${responses.linter === "oxlint" ? chalk.green("Oxlint") : responses.linter === "eslint" ? chalk.yellow("ESLint") : chalk.red("None")}`,
|
|
45
|
+
`• ${chalk.bold("Git:")} ${responses.gitInit ? chalk.green("Yes") : chalk.red("No")}`,
|
|
46
|
+
`• ${chalk.bold("README:")} ${responses.readme ? chalk.green("Yes") : chalk.red("No")}`,
|
|
47
|
+
].join("\n");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function getUserInputs(projectName) {
|
|
51
|
+
let oldConfig = {};
|
|
52
|
+
let useOldConfig = false;
|
|
53
|
+
|
|
54
|
+
if (await configExists()) {
|
|
55
|
+
oldConfig = await loadConfig();
|
|
56
|
+
oldConfig.projectName = projectName;
|
|
57
|
+
|
|
58
|
+
note(
|
|
59
|
+
`Previous setup found:\n\n${formatConfig(oldConfig)}\n`,
|
|
60
|
+
"Previous Configuration"
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const useExisting = await select({
|
|
64
|
+
message: "How would you like to proceed?",
|
|
65
|
+
options: [
|
|
66
|
+
{ value: true, label: "Continue with previous setup" },
|
|
67
|
+
{ value: false, label: "Start fresh (new setup)" },
|
|
68
|
+
],
|
|
69
|
+
initialValue: true,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (isCancel(useExisting)) onCancel();
|
|
73
|
+
useOldConfig = useExisting;
|
|
74
|
+
|
|
75
|
+
if (useOldConfig) {
|
|
76
|
+
return { ...oldConfig, projectName };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Quick Setup
|
|
81
|
+
const quickSetup = await confirm({
|
|
82
|
+
message:
|
|
83
|
+
"Quick Setup? (TypeScript + Tailwind + Feature-based + Router + ESLint + Vitest)",
|
|
84
|
+
initialValue: true,
|
|
85
|
+
});
|
|
86
|
+
if (isCancel(quickSetup)) onCancel();
|
|
87
|
+
|
|
88
|
+
if (quickSetup) {
|
|
89
|
+
const responses = {
|
|
90
|
+
projectName,
|
|
91
|
+
architecture: "feature-based",
|
|
92
|
+
language: "ts",
|
|
93
|
+
cssFramework: "tailwind",
|
|
94
|
+
testing: "vitest",
|
|
95
|
+
router: true,
|
|
96
|
+
stateManagement: "none",
|
|
97
|
+
iconLibrary: "none",
|
|
98
|
+
axios: false,
|
|
99
|
+
linter: "eslint",
|
|
100
|
+
gitInit: false,
|
|
101
|
+
readme: true,
|
|
102
|
+
};
|
|
103
|
+
await saveConfig(responses);
|
|
104
|
+
return responses;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Custom setup
|
|
108
|
+
const architecture = await select({
|
|
109
|
+
message: "Which project architecture do you want?",
|
|
110
|
+
options: [
|
|
111
|
+
{
|
|
112
|
+
label: "Feature-based",
|
|
113
|
+
value: "feature-based",
|
|
114
|
+
hint: "Scales to large apps — code grouped by business domain",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
label: "Component-based",
|
|
118
|
+
value: "component-based",
|
|
119
|
+
hint: "Small apps / component libraries — code grouped by UI type",
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
initialValue: "feature-based",
|
|
123
|
+
});
|
|
124
|
+
if (isCancel(architecture)) onCancel();
|
|
125
|
+
|
|
126
|
+
const language = await select({
|
|
127
|
+
message: "Which language do you want to use?",
|
|
128
|
+
options: [
|
|
129
|
+
{ label: "TypeScript", value: "ts" },
|
|
130
|
+
{ label: "JavaScript", value: "js" },
|
|
131
|
+
],
|
|
132
|
+
initialValue: "ts",
|
|
133
|
+
});
|
|
134
|
+
if (isCancel(language)) onCancel();
|
|
135
|
+
|
|
136
|
+
const cssFramework = await select({
|
|
137
|
+
message: "Which CSS framework do you want to use?",
|
|
138
|
+
options: [
|
|
139
|
+
{ label: "Tailwind CSS", value: "tailwind" },
|
|
140
|
+
{ label: "Bootstrap", value: "bootstrap" },
|
|
141
|
+
{ label: "None", value: "none" },
|
|
142
|
+
],
|
|
143
|
+
initialValue: "tailwind",
|
|
144
|
+
});
|
|
145
|
+
if (isCancel(cssFramework)) onCancel();
|
|
146
|
+
|
|
147
|
+
const testing = await select({
|
|
148
|
+
message: "Which testing framework do you want to set up?",
|
|
149
|
+
options: [
|
|
150
|
+
{ label: "Vitest", value: "vitest" },
|
|
151
|
+
{ label: "Jest", value: "jest" },
|
|
152
|
+
{ label: "None", value: "none" },
|
|
153
|
+
],
|
|
154
|
+
initialValue: "vitest",
|
|
155
|
+
});
|
|
156
|
+
if (isCancel(testing)) onCancel();
|
|
157
|
+
|
|
158
|
+
const router = await confirm({
|
|
159
|
+
message: "Would you like to install React Router?",
|
|
160
|
+
initialValue: true,
|
|
161
|
+
});
|
|
162
|
+
if (isCancel(router)) onCancel();
|
|
163
|
+
|
|
164
|
+
const stateManagement = await select({
|
|
165
|
+
message: "Which state management library do you want to use?",
|
|
166
|
+
options: [
|
|
167
|
+
{ label: "None", value: "none" },
|
|
168
|
+
{ label: "Redux Toolkit", value: "redux" },
|
|
169
|
+
{ label: "Zustand", value: "zustand" },
|
|
170
|
+
],
|
|
171
|
+
initialValue: "none",
|
|
172
|
+
});
|
|
173
|
+
if (isCancel(stateManagement)) onCancel();
|
|
174
|
+
|
|
175
|
+
const iconLibrary = await select({
|
|
176
|
+
message: "Which icon library would you like to use?",
|
|
177
|
+
options: [
|
|
178
|
+
{ label: "None", value: "none" },
|
|
179
|
+
{ label: "Lucide Icons", value: "lucide" },
|
|
180
|
+
{ label: "Huge Icons", value: "huge" },
|
|
181
|
+
],
|
|
182
|
+
initialValue: "none",
|
|
183
|
+
});
|
|
184
|
+
if (isCancel(iconLibrary)) onCancel();
|
|
185
|
+
|
|
186
|
+
const axios = await confirm({
|
|
187
|
+
message: "Would you like to install Axios?",
|
|
188
|
+
initialValue: false,
|
|
189
|
+
});
|
|
190
|
+
if (isCancel(axios)) onCancel();
|
|
191
|
+
|
|
192
|
+
const gitInit = await confirm({
|
|
193
|
+
message: "Would you like to initialize a Git repository?",
|
|
194
|
+
initialValue: false,
|
|
195
|
+
});
|
|
196
|
+
if (isCancel(gitInit)) onCancel();
|
|
197
|
+
|
|
198
|
+
const linter = await select({
|
|
199
|
+
message: "Which linter do you want to use?",
|
|
200
|
+
options: [
|
|
201
|
+
{ label: "ESLint", value: "eslint", hint: "Industry standard JavaScript linter (Recommended)" },
|
|
202
|
+
{ label: "Oxlint", value: "oxlint", hint: "Fast Rust-based linter" },
|
|
203
|
+
{ label: "None", value: "none" },
|
|
204
|
+
],
|
|
205
|
+
initialValue: "eslint",
|
|
206
|
+
});
|
|
207
|
+
if (isCancel(linter)) onCancel();
|
|
208
|
+
|
|
209
|
+
const readme = await confirm({
|
|
210
|
+
message: "Would you like to generate a README.md and LICENSE?",
|
|
211
|
+
initialValue: true,
|
|
212
|
+
});
|
|
213
|
+
if (isCancel(readme)) onCancel();
|
|
214
|
+
|
|
215
|
+
const responses = {
|
|
216
|
+
projectName,
|
|
217
|
+
architecture,
|
|
218
|
+
language,
|
|
219
|
+
cssFramework,
|
|
220
|
+
testing,
|
|
221
|
+
router,
|
|
222
|
+
stateManagement,
|
|
223
|
+
iconLibrary,
|
|
224
|
+
axios,
|
|
225
|
+
linter,
|
|
226
|
+
gitInit,
|
|
227
|
+
readme,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
await saveConfig(responses);
|
|
231
|
+
return responses;
|
|
232
|
+
}
|