@prmvx/frontend-forge 0.1.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 +224 -0
- package/dist/cli.js +662 -0
- package/dist/cli.js.map +1 -0
- package/dist/templates/architectures/next/enterprise/src/components/common/Button.tsx +17 -0
- package/dist/templates/architectures/next/enterprise/src/components/home/HomeView.tsx +41 -0
- package/dist/templates/architectures/next/enterprise/src/components/layout/Footer.tsx +9 -0
- package/dist/templates/architectures/next/enterprise/src/components/layout/Header.tsx +19 -0
- package/dist/templates/architectures/next/enterprise/src/config/env.ts +3 -0
- package/dist/templates/architectures/next/enterprise/src/constants/routes.ts +4 -0
- package/dist/templates/architectures/next/enterprise/src/features/dashboard/components/DashboardSummary.tsx +22 -0
- package/dist/templates/architectures/next/enterprise/src/features/dashboard/hooks/useDashboard.ts +18 -0
- package/dist/templates/architectures/next/enterprise/src/features/dashboard/services/dashboard-api.ts +22 -0
- package/dist/templates/architectures/next/enterprise/src/features/dashboard/types/index.ts +4 -0
- package/dist/templates/architectures/next/enterprise/src/hooks/useDebounce.ts +14 -0
- package/dist/templates/architectures/next/enterprise/src/hooks/useToggle.ts +15 -0
- package/dist/templates/architectures/next/enterprise/src/providers/index.tsx +17 -0
- package/dist/templates/architectures/next/enterprise/src/providers/theme-provider.tsx +11 -0
- package/dist/templates/architectures/next/enterprise/src/services/api.ts +22 -0
- package/dist/templates/architectures/next/enterprise/src/store/app-store.tsx +33 -0
- package/dist/templates/architectures/next/enterprise/src/types/user.ts +5 -0
- package/dist/templates/architectures/next/enterprise/src/utils/cn.ts +3 -0
- package/dist/templates/architectures/next/enterprise/src/utils/formatDate.ts +7 -0
- package/dist/templates/architectures/next/feature/src/config/env.ts +3 -0
- package/dist/templates/architectures/next/feature/src/features/home/components/HomeHero.tsx +38 -0
- package/dist/templates/architectures/next/feature/src/features/home/components/HomePage.tsx +19 -0
- package/dist/templates/architectures/next/feature/src/features/home/constants/routes.ts +4 -0
- package/dist/templates/architectures/next/feature/src/features/home/hooks/useHome.ts +18 -0
- package/dist/templates/architectures/next/feature/src/features/home/services/home-api.ts +22 -0
- package/dist/templates/architectures/next/feature/src/features/home/types/index.ts +4 -0
- package/dist/templates/architectures/next/feature/src/features/profile/components/ProfileCard.tsx +19 -0
- package/dist/templates/architectures/next/feature/src/features/profile/hooks/useProfile.ts +18 -0
- package/dist/templates/architectures/next/feature/src/features/profile/services/profile-api.ts +22 -0
- package/dist/templates/architectures/next/feature/src/features/profile/types/index.ts +5 -0
- package/dist/templates/architectures/next/feature/src/shared/components/common/Button.tsx +17 -0
- package/dist/templates/architectures/next/feature/src/shared/components/layout/Footer.tsx +9 -0
- package/dist/templates/architectures/next/feature/src/shared/components/layout/Header.tsx +19 -0
- package/dist/templates/architectures/next/feature/src/shared/hooks/useDebounce.ts +14 -0
- package/dist/templates/architectures/next/feature/src/shared/hooks/useToggle.ts +15 -0
- package/dist/templates/architectures/next/feature/src/shared/providers/index.tsx +8 -0
- package/dist/templates/architectures/next/feature/src/shared/providers/theme-provider.tsx +7 -0
- package/dist/templates/architectures/next/feature/src/shared/utils/cn.ts +3 -0
- package/dist/templates/architectures/next/feature/src/shared/utils/formatDate.ts +7 -0
- package/dist/templates/architectures/next/standard/src/components/common/Button.tsx +17 -0
- package/dist/templates/architectures/next/standard/src/components/home/HomeView.tsx +53 -0
- package/dist/templates/architectures/next/standard/src/components/layout/Footer.tsx +9 -0
- package/dist/templates/architectures/next/standard/src/components/layout/Header.tsx +19 -0
- package/dist/templates/architectures/next/standard/src/config/env.ts +3 -0
- package/dist/templates/architectures/next/standard/src/constants/routes.ts +4 -0
- package/dist/templates/architectures/next/standard/src/hooks/useDebounce.ts +14 -0
- package/dist/templates/architectures/next/standard/src/hooks/useToggle.ts +15 -0
- package/dist/templates/architectures/next/standard/src/providers/index.tsx +12 -0
- package/dist/templates/architectures/next/standard/src/providers/theme-provider.tsx +11 -0
- package/dist/templates/architectures/next/standard/src/services/api.ts +22 -0
- package/dist/templates/architectures/next/standard/src/types/user.ts +5 -0
- package/dist/templates/architectures/next/standard/src/utils/cn.ts +3 -0
- package/dist/templates/architectures/next/standard/src/utils/formatDate.ts +7 -0
- package/dist/templates/modules/axios/module.ts +7 -0
- package/dist/templates/modules/core/module.ts +5 -0
- package/dist/templates/modules/core/src/components/common/Button.tsx +12 -0
- package/dist/templates/modules/core/src/components/layout/Header.tsx +7 -0
- package/dist/templates/modules/core/src/config/env.ts +3 -0
- package/dist/templates/modules/core/src/constants/index.ts +1 -0
- package/dist/templates/modules/core/src/constants/routes.ts +3 -0
- package/dist/templates/modules/core/src/hooks/index.ts +1 -0
- package/dist/templates/modules/core/src/hooks/useToggle.ts +13 -0
- package/dist/templates/modules/core/src/providers/index.tsx +9 -0
- package/dist/templates/modules/core/src/services/api.ts +5 -0
- package/dist/templates/modules/core/src/utils/cn.ts +3 -0
- package/dist/templates/modules/core/src/utils/index.ts +1 -0
- package/dist/templates/modules/zustand/module.ts +7 -0
- package/package.json +70 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/commands/init.ts
|
|
7
|
+
import fs7 from "fs";
|
|
8
|
+
import path9 from "path";
|
|
9
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
10
|
+
import { intro, isCancel, cancel, spinner } from "@clack/prompts";
|
|
11
|
+
|
|
12
|
+
// src/utils/detectProject.ts
|
|
13
|
+
import fs from "fs";
|
|
14
|
+
function validateProject() {
|
|
15
|
+
if (!fs.existsSync("package.json")) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
"Could not find package.json.\nPlease run this command inside an existing Next.js project."
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
const pkg = JSON.parse(
|
|
21
|
+
fs.readFileSync("package.json", "utf-8")
|
|
22
|
+
);
|
|
23
|
+
if (!pkg.dependencies?.next) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
"Next.js project not detected.\nCurrent version only supports Next.js."
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/utils/detectProjectStructure.ts
|
|
31
|
+
import fs2 from "fs";
|
|
32
|
+
import path from "path";
|
|
33
|
+
function detectProjectStructure() {
|
|
34
|
+
const cwd = process.cwd();
|
|
35
|
+
if (fs2.existsSync(path.join(cwd, "app"))) {
|
|
36
|
+
return {
|
|
37
|
+
useSrc: false,
|
|
38
|
+
root: "",
|
|
39
|
+
appDir: "app"
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (fs2.existsSync(path.join(cwd, "src", "app"))) {
|
|
43
|
+
return {
|
|
44
|
+
useSrc: true,
|
|
45
|
+
root: "src",
|
|
46
|
+
appDir: "src/app"
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (fs2.existsSync(path.join(cwd, "src"))) {
|
|
50
|
+
return {
|
|
51
|
+
useSrc: true,
|
|
52
|
+
root: "src",
|
|
53
|
+
appDir: "src/app"
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
useSrc: false,
|
|
58
|
+
root: "",
|
|
59
|
+
appDir: "app"
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/utils/copyArchitecture.ts
|
|
64
|
+
import path4 from "path";
|
|
65
|
+
|
|
66
|
+
// src/utils/copyTemplate.ts
|
|
67
|
+
import fs3 from "fs-extra";
|
|
68
|
+
import path3 from "path";
|
|
69
|
+
import { fileURLToPath } from "url";
|
|
70
|
+
|
|
71
|
+
// src/utils/resolveTemplatePath.ts
|
|
72
|
+
import path2 from "path";
|
|
73
|
+
function normalizePath(relativePath) {
|
|
74
|
+
return relativePath.split(path2.sep).join("/");
|
|
75
|
+
}
|
|
76
|
+
function remapCoreModulePath(normalizedPath, architecture) {
|
|
77
|
+
if (architecture === "feature") {
|
|
78
|
+
if (normalizedPath.startsWith("src/components/")) {
|
|
79
|
+
return normalizedPath.replace(
|
|
80
|
+
"src/components/",
|
|
81
|
+
"src/shared/components/"
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (normalizedPath.startsWith("src/hooks/")) {
|
|
85
|
+
return normalizedPath.replace("src/hooks/", "src/shared/hooks/");
|
|
86
|
+
}
|
|
87
|
+
if (normalizedPath.startsWith("src/utils/")) {
|
|
88
|
+
return normalizedPath.replace("src/utils/", "src/shared/utils/");
|
|
89
|
+
}
|
|
90
|
+
if (normalizedPath.startsWith("src/providers/")) {
|
|
91
|
+
return normalizedPath.replace("src/providers/", "src/shared/providers/");
|
|
92
|
+
}
|
|
93
|
+
if (normalizedPath.startsWith("src/services/")) {
|
|
94
|
+
return normalizedPath.replace("src/services/", "src/features/home/services/");
|
|
95
|
+
}
|
|
96
|
+
if (normalizedPath.startsWith("src/constants/")) {
|
|
97
|
+
return normalizedPath.replace("src/constants/", "src/features/home/constants/");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return normalizedPath;
|
|
101
|
+
}
|
|
102
|
+
function resolveTemplatePath(relativePath, options) {
|
|
103
|
+
let normalized = normalizePath(relativePath);
|
|
104
|
+
if (normalized === "src/app" || normalized.startsWith("src/app/")) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
if (options.remapCore && options.architecture) {
|
|
108
|
+
normalized = remapCoreModulePath(normalized, options.architecture);
|
|
109
|
+
}
|
|
110
|
+
if (!options.structure.useSrc && normalized.startsWith("src/")) {
|
|
111
|
+
normalized = normalized.slice(4);
|
|
112
|
+
}
|
|
113
|
+
return normalized.split("/").join(path2.sep);
|
|
114
|
+
}
|
|
115
|
+
function shouldSkipTemplatePath(relativePath) {
|
|
116
|
+
const normalized = normalizePath(relativePath);
|
|
117
|
+
return normalized === "src/app" || normalized.startsWith("src/app/");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/utils/copyTemplate.ts
|
|
121
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
122
|
+
var __dirname = path3.dirname(__filename);
|
|
123
|
+
async function getAllFiles(dir) {
|
|
124
|
+
const entries = await fs3.readdir(dir, { withFileTypes: true });
|
|
125
|
+
const files = [];
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
const fullPath = path3.join(dir, entry.name);
|
|
128
|
+
if (entry.isDirectory()) {
|
|
129
|
+
files.push(...await getAllFiles(fullPath));
|
|
130
|
+
} else if (entry.isFile()) {
|
|
131
|
+
files.push(fullPath);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return files;
|
|
135
|
+
}
|
|
136
|
+
async function copyTemplateDir(relativePath, options) {
|
|
137
|
+
const templatePath = path3.resolve(__dirname, "../templates", relativePath);
|
|
138
|
+
const targetPath = process.cwd();
|
|
139
|
+
const files = await getAllFiles(templatePath);
|
|
140
|
+
const createdDirs = /* @__PURE__ */ new Set();
|
|
141
|
+
let created = 0;
|
|
142
|
+
let skipped = 0;
|
|
143
|
+
let failed = 0;
|
|
144
|
+
for (const file of files) {
|
|
145
|
+
if (options.exclude?.(file)) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const relativeFile = path3.relative(templatePath, file);
|
|
149
|
+
if (shouldSkipTemplatePath(relativeFile)) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const destRelative = resolveTemplatePath(relativeFile, {
|
|
153
|
+
structure: options.structure,
|
|
154
|
+
architecture: options.architecture,
|
|
155
|
+
remapCore: options.remapCore
|
|
156
|
+
});
|
|
157
|
+
if (!destRelative) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const dest = path3.join(targetPath, destRelative);
|
|
161
|
+
const destDir = path3.dirname(dest);
|
|
162
|
+
try {
|
|
163
|
+
if (await fs3.pathExists(dest)) {
|
|
164
|
+
skipped++;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (!createdDirs.has(destDir)) {
|
|
168
|
+
await fs3.ensureDir(destDir);
|
|
169
|
+
createdDirs.add(destDir);
|
|
170
|
+
}
|
|
171
|
+
await fs3.copy(file, dest);
|
|
172
|
+
created++;
|
|
173
|
+
} catch {
|
|
174
|
+
failed++;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return { created, skipped, failed };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/utils/copyArchitecture.ts
|
|
181
|
+
async function copyArchitecture(framework, architecture, structure) {
|
|
182
|
+
return copyTemplateDir(
|
|
183
|
+
path4.join("architectures", framework, architecture),
|
|
184
|
+
{ structure, architecture }
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/utils/copyModules.ts
|
|
189
|
+
import path5 from "path";
|
|
190
|
+
async function copyModules(modules2, structure, architecture) {
|
|
191
|
+
const stats = { created: 0, skipped: 0, failed: 0 };
|
|
192
|
+
for (const module of modules2) {
|
|
193
|
+
const result = await copyTemplateDir(path5.join("modules", module), {
|
|
194
|
+
structure,
|
|
195
|
+
architecture,
|
|
196
|
+
remapCore: module === "core",
|
|
197
|
+
exclude: (filePath) => filePath.endsWith("module.ts")
|
|
198
|
+
});
|
|
199
|
+
stats.created += result.created;
|
|
200
|
+
stats.skipped += result.skipped;
|
|
201
|
+
stats.failed += result.failed;
|
|
202
|
+
}
|
|
203
|
+
return stats;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/utils/extendProjectFiles.ts
|
|
207
|
+
import fs4 from "fs-extra";
|
|
208
|
+
import path6 from "path";
|
|
209
|
+
function getAppFilePath(structure, fileName) {
|
|
210
|
+
return path6.join(process.cwd(), structure.appDir, fileName);
|
|
211
|
+
}
|
|
212
|
+
function getProvidersImport(architecture) {
|
|
213
|
+
if (architecture === "feature") {
|
|
214
|
+
return "@/shared/providers";
|
|
215
|
+
}
|
|
216
|
+
return "@/providers";
|
|
217
|
+
}
|
|
218
|
+
function getPageImport(architecture) {
|
|
219
|
+
if (architecture === "feature") {
|
|
220
|
+
return "@/features/home/components/HomePage";
|
|
221
|
+
}
|
|
222
|
+
return "@/components/home/HomeView";
|
|
223
|
+
}
|
|
224
|
+
function getPageComponent(architecture) {
|
|
225
|
+
if (architecture === "feature") {
|
|
226
|
+
return "HomePage";
|
|
227
|
+
}
|
|
228
|
+
return "HomeView";
|
|
229
|
+
}
|
|
230
|
+
async function extendProjectFiles(architecture, structure) {
|
|
231
|
+
await extendLayout(structure, architecture);
|
|
232
|
+
await extendPage(structure, architecture);
|
|
233
|
+
}
|
|
234
|
+
async function extendLayout(structure, architecture) {
|
|
235
|
+
const layoutPath = getAppFilePath(structure, "layout.tsx");
|
|
236
|
+
if (!await fs4.pathExists(layoutPath)) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const content = await fs4.readFile(layoutPath, "utf-8");
|
|
240
|
+
const providersImport = getProvidersImport(architecture);
|
|
241
|
+
if (content.includes("<Providers>")) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
let updated = content;
|
|
245
|
+
if (!content.includes(providersImport) && !content.includes('from "@/providers"') && !content.includes("from '@/providers'")) {
|
|
246
|
+
const importLine = `import { Providers } from "${providersImport}";
|
|
247
|
+
`;
|
|
248
|
+
const lastImportIndex = updated.lastIndexOf("import ");
|
|
249
|
+
if (lastImportIndex !== -1) {
|
|
250
|
+
const insertAt = updated.indexOf("\n", lastImportIndex) + 1;
|
|
251
|
+
updated = updated.slice(0, insertAt) + importLine + updated.slice(insertAt);
|
|
252
|
+
} else {
|
|
253
|
+
updated = importLine + updated;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
updated = updated.replace(/(<body[^>]*>)/, "$1\n <Providers>");
|
|
257
|
+
updated = updated.replace("</body>", " </Providers>\n </body>");
|
|
258
|
+
if (updated !== content) {
|
|
259
|
+
await fs4.writeFile(layoutPath, updated);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
async function extendPage(structure, architecture) {
|
|
263
|
+
const pagePath = getAppFilePath(structure, "page.tsx");
|
|
264
|
+
if (!await fs4.pathExists(pagePath)) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const content = await fs4.readFile(pagePath, "utf-8");
|
|
268
|
+
const pageImport = getPageImport(architecture);
|
|
269
|
+
const pageComponent = getPageComponent(architecture);
|
|
270
|
+
if (content.includes(pageComponent)) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const isDefaultTemplate = content.includes("Get started by editing") || content.includes("next.svg") || content.includes("vercel.svg");
|
|
274
|
+
if (!isDefaultTemplate) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const updated = `import { ${pageComponent} } from "${pageImport}";
|
|
278
|
+
|
|
279
|
+
export default function Home() {
|
|
280
|
+
return <${pageComponent} />;
|
|
281
|
+
}
|
|
282
|
+
`;
|
|
283
|
+
await fs4.writeFile(pagePath, updated);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// src/utils/readModule.ts
|
|
287
|
+
import fs5 from "fs-extra";
|
|
288
|
+
import path7 from "path";
|
|
289
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
290
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
291
|
+
var __dirname2 = path7.dirname(__filename2);
|
|
292
|
+
async function readModule(moduleName) {
|
|
293
|
+
const modulePath = path7.resolve(
|
|
294
|
+
__dirname2,
|
|
295
|
+
"../templates/modules",
|
|
296
|
+
moduleName,
|
|
297
|
+
"module.ts"
|
|
298
|
+
);
|
|
299
|
+
const content = await fs5.readFile(modulePath, "utf-8");
|
|
300
|
+
const dataUrl = `data:text/javascript;charset=utf-8,${encodeURIComponent(content)}`;
|
|
301
|
+
const { default: moduleDef } = await import(dataUrl);
|
|
302
|
+
return moduleDef;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/utils/installDependencies.ts
|
|
306
|
+
import { execa } from "execa";
|
|
307
|
+
var installCommands = {
|
|
308
|
+
npm: { command: "npm", dependencies: ["install"], devDependencies: ["install", "-D"] },
|
|
309
|
+
pnpm: { command: "pnpm", dependencies: ["add"], devDependencies: ["add", "-D"] },
|
|
310
|
+
yarn: { command: "yarn", dependencies: ["add"], devDependencies: ["add", "-D"] },
|
|
311
|
+
bun: { command: "bun", dependencies: ["add"], devDependencies: ["add", "-d"] }
|
|
312
|
+
};
|
|
313
|
+
async function installDependencies(modules2, packageManager) {
|
|
314
|
+
const dependencies = [
|
|
315
|
+
...new Set(modules2.flatMap((module) => module.dependencies))
|
|
316
|
+
];
|
|
317
|
+
const devDependencies = [
|
|
318
|
+
...new Set(modules2.flatMap((module) => module.devDependencies))
|
|
319
|
+
];
|
|
320
|
+
const totalCount = dependencies.length + devDependencies.length;
|
|
321
|
+
if (totalCount === 0) {
|
|
322
|
+
return 0;
|
|
323
|
+
}
|
|
324
|
+
const { command, dependencies: depsArgs, devDependencies: devArgs } = installCommands[packageManager];
|
|
325
|
+
try {
|
|
326
|
+
if (dependencies.length > 0) {
|
|
327
|
+
await execa(command, [...depsArgs, ...dependencies], {
|
|
328
|
+
stdio: "inherit",
|
|
329
|
+
cwd: process.cwd()
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
if (devDependencies.length > 0) {
|
|
333
|
+
await execa(command, [...devArgs, ...devDependencies], {
|
|
334
|
+
stdio: "inherit",
|
|
335
|
+
cwd: process.cwd()
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
return totalCount;
|
|
339
|
+
} catch {
|
|
340
|
+
throw new Error(
|
|
341
|
+
"Failed to install dependencies.\nPlease resolve the issue and run the command again."
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/utils/detectPackageManager.ts
|
|
347
|
+
import fs6 from "fs";
|
|
348
|
+
import path8 from "path";
|
|
349
|
+
import { execa as execa2 } from "execa";
|
|
350
|
+
function detectPackageManager() {
|
|
351
|
+
const root = process.cwd();
|
|
352
|
+
if (fs6.existsSync(path8.join(root, "pnpm-lock.yaml"))) {
|
|
353
|
+
return "pnpm";
|
|
354
|
+
}
|
|
355
|
+
if (fs6.existsSync(path8.join(root, "yarn.lock"))) {
|
|
356
|
+
return "yarn";
|
|
357
|
+
}
|
|
358
|
+
if (fs6.existsSync(path8.join(root, "bun.lockb")) || fs6.existsSync(path8.join(root, "bun.lock"))) {
|
|
359
|
+
return "bun";
|
|
360
|
+
}
|
|
361
|
+
if (fs6.existsSync(path8.join(root, "package-lock.json"))) {
|
|
362
|
+
return "npm";
|
|
363
|
+
}
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
async function validatePackageManager(packageManager) {
|
|
367
|
+
const result = await execa2(packageManager, ["--version"], {
|
|
368
|
+
reject: false
|
|
369
|
+
});
|
|
370
|
+
if (result.exitCode !== 0) {
|
|
371
|
+
throw new Error(
|
|
372
|
+
`${packageManager} is not available.
|
|
373
|
+
Please install ${packageManager} and try again.`
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/utils/logger.ts
|
|
379
|
+
import pc from "picocolors";
|
|
380
|
+
var logger = {
|
|
381
|
+
success(message) {
|
|
382
|
+
console.log(`${pc.green("\u2713")} ${message}`);
|
|
383
|
+
},
|
|
384
|
+
info(message) {
|
|
385
|
+
console.log(`${pc.cyan("\u2139")} ${message}`);
|
|
386
|
+
},
|
|
387
|
+
warning(message) {
|
|
388
|
+
console.log(`${pc.yellow("\u26A0")} ${message}`);
|
|
389
|
+
},
|
|
390
|
+
error(message) {
|
|
391
|
+
for (const line of message.split("\n")) {
|
|
392
|
+
console.log(`${pc.red("\u2716")} ${line}`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// src/prompts/architecture.ts
|
|
398
|
+
import { select } from "@clack/prompts";
|
|
399
|
+
|
|
400
|
+
// src/config/architectures.ts
|
|
401
|
+
var architectures = [
|
|
402
|
+
{
|
|
403
|
+
value: "standard",
|
|
404
|
+
label: "Standard",
|
|
405
|
+
hint: "Simple folder structure"
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
value: "enterprise",
|
|
409
|
+
label: "Enterprise",
|
|
410
|
+
hint: "Scalable enterprise architecture"
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
value: "feature",
|
|
414
|
+
label: "Feature Based",
|
|
415
|
+
hint: "Organize code by feature"
|
|
416
|
+
}
|
|
417
|
+
];
|
|
418
|
+
|
|
419
|
+
// src/prompts/architecture.ts
|
|
420
|
+
async function selectArchitecture() {
|
|
421
|
+
return await select({
|
|
422
|
+
message: "Choose project architecture",
|
|
423
|
+
options: architectures.map((item) => ({
|
|
424
|
+
value: item.value,
|
|
425
|
+
label: item.label,
|
|
426
|
+
hint: item.hint
|
|
427
|
+
}))
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/prompts/modules.ts
|
|
432
|
+
import { multiselect } from "@clack/prompts";
|
|
433
|
+
|
|
434
|
+
// src/config/modules.ts
|
|
435
|
+
var modules = [
|
|
436
|
+
{
|
|
437
|
+
value: "core",
|
|
438
|
+
label: "Core",
|
|
439
|
+
hint: "Basic starter files"
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
value: "axios",
|
|
443
|
+
label: "Axios",
|
|
444
|
+
hint: "API Client"
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
value: "zustand",
|
|
448
|
+
label: "Zustand",
|
|
449
|
+
hint: "State Management"
|
|
450
|
+
}
|
|
451
|
+
];
|
|
452
|
+
|
|
453
|
+
// src/prompts/modules.ts
|
|
454
|
+
async function selectModules() {
|
|
455
|
+
return await multiselect({
|
|
456
|
+
message: "Select modules",
|
|
457
|
+
options: modules.map((item) => ({
|
|
458
|
+
value: item.value,
|
|
459
|
+
label: item.label,
|
|
460
|
+
hint: item.hint
|
|
461
|
+
})),
|
|
462
|
+
required: true
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// src/prompts/packageManager.ts
|
|
467
|
+
import { select as select2 } from "@clack/prompts";
|
|
468
|
+
async function selectPackageManager() {
|
|
469
|
+
return await select2({
|
|
470
|
+
message: "Choose package manager",
|
|
471
|
+
options: [
|
|
472
|
+
{ value: "npm", label: "npm" },
|
|
473
|
+
{ value: "pnpm", label: "pnpm" },
|
|
474
|
+
{ value: "yarn", label: "yarn" },
|
|
475
|
+
{ value: "bun", label: "bun" }
|
|
476
|
+
],
|
|
477
|
+
initialValue: "npm"
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// src/commands/init.ts
|
|
482
|
+
function getVersion() {
|
|
483
|
+
const dir = path9.dirname(fileURLToPath3(import.meta.url));
|
|
484
|
+
const pkg = JSON.parse(
|
|
485
|
+
fs7.readFileSync(path9.join(dir, "../package.json"), "utf-8")
|
|
486
|
+
);
|
|
487
|
+
return pkg.version;
|
|
488
|
+
}
|
|
489
|
+
function getArchitectureLabel(value) {
|
|
490
|
+
return architectures.find((item) => item.value === value)?.label ?? value;
|
|
491
|
+
}
|
|
492
|
+
function getModuleLabel(value) {
|
|
493
|
+
return modules.find((item) => item.value === value)?.label ?? value;
|
|
494
|
+
}
|
|
495
|
+
function printSummary(options) {
|
|
496
|
+
console.log("");
|
|
497
|
+
logger.success("Project initialized successfully.");
|
|
498
|
+
console.log("");
|
|
499
|
+
console.log("Framework:");
|
|
500
|
+
console.log("Next.js");
|
|
501
|
+
console.log("");
|
|
502
|
+
console.log("Architecture:");
|
|
503
|
+
console.log(getArchitectureLabel(options.architecture));
|
|
504
|
+
console.log("");
|
|
505
|
+
console.log("Modules:");
|
|
506
|
+
for (const module of options.selectedModules) {
|
|
507
|
+
console.log(getModuleLabel(module));
|
|
508
|
+
}
|
|
509
|
+
console.log("");
|
|
510
|
+
console.log("Package Manager:");
|
|
511
|
+
console.log(options.packageManager);
|
|
512
|
+
console.log("");
|
|
513
|
+
console.log("Dependencies Installed:");
|
|
514
|
+
console.log(options.dependenciesInstalled);
|
|
515
|
+
console.log("");
|
|
516
|
+
console.log("Files Created:");
|
|
517
|
+
console.log(options.filesCreated);
|
|
518
|
+
console.log("");
|
|
519
|
+
console.log("Files Skipped:");
|
|
520
|
+
console.log(options.filesSkipped);
|
|
521
|
+
console.log("");
|
|
522
|
+
console.log("Done.");
|
|
523
|
+
console.log("");
|
|
524
|
+
logger.success("Happy Coding!");
|
|
525
|
+
}
|
|
526
|
+
async function initCommand() {
|
|
527
|
+
intro("Frontend Init");
|
|
528
|
+
logger.info("Production Ready Frontend Starter");
|
|
529
|
+
logger.info(`Version ${getVersion()}`);
|
|
530
|
+
const detectSpinner = spinner();
|
|
531
|
+
detectSpinner.start("Detecting project...");
|
|
532
|
+
try {
|
|
533
|
+
validateProject();
|
|
534
|
+
detectSpinner.stop("Success");
|
|
535
|
+
} catch (error) {
|
|
536
|
+
detectSpinner.stop("Failed");
|
|
537
|
+
logger.error(
|
|
538
|
+
error instanceof Error ? error.message : "Project validation failed."
|
|
539
|
+
);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
const architectureSpinner = spinner();
|
|
543
|
+
architectureSpinner.start("Selecting architecture...");
|
|
544
|
+
const architecture = await selectArchitecture();
|
|
545
|
+
if (isCancel(architecture) || !architecture) {
|
|
546
|
+
architectureSpinner.stop("Failed");
|
|
547
|
+
cancel("Operation cancelled.");
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
architectureSpinner.stop("Success");
|
|
551
|
+
const modulesSpinner = spinner();
|
|
552
|
+
modulesSpinner.start("Selecting modules...");
|
|
553
|
+
const selectedModules = await selectModules();
|
|
554
|
+
if (isCancel(selectedModules) || selectedModules.length === 0) {
|
|
555
|
+
modulesSpinner.stop("Failed");
|
|
556
|
+
cancel("Operation cancelled.");
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
modulesSpinner.stop("Success");
|
|
560
|
+
const structure = detectProjectStructure();
|
|
561
|
+
const moduleDefinitions = await Promise.all(
|
|
562
|
+
selectedModules.map((module) => readModule(module))
|
|
563
|
+
);
|
|
564
|
+
const detected = detectPackageManager();
|
|
565
|
+
let packageManager;
|
|
566
|
+
if (detected) {
|
|
567
|
+
packageManager = detected;
|
|
568
|
+
logger.info(`Detected: ${packageManager}`);
|
|
569
|
+
} else {
|
|
570
|
+
const selected = await selectPackageManager();
|
|
571
|
+
if (isCancel(selected) || !selected) {
|
|
572
|
+
cancel("Operation cancelled.");
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
packageManager = selected;
|
|
576
|
+
logger.info(`Using ${packageManager}`);
|
|
577
|
+
}
|
|
578
|
+
try {
|
|
579
|
+
await validatePackageManager(packageManager);
|
|
580
|
+
} catch (error) {
|
|
581
|
+
logger.error(
|
|
582
|
+
error instanceof Error ? error.message : "Package manager not available."
|
|
583
|
+
);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
let dependenciesInstalled = 0;
|
|
587
|
+
const installSpinner = spinner();
|
|
588
|
+
installSpinner.start("Installing dependencies...");
|
|
589
|
+
try {
|
|
590
|
+
dependenciesInstalled = await installDependencies(
|
|
591
|
+
moduleDefinitions,
|
|
592
|
+
packageManager
|
|
593
|
+
);
|
|
594
|
+
installSpinner.stop("Success");
|
|
595
|
+
} catch (error) {
|
|
596
|
+
installSpinner.stop("Failed");
|
|
597
|
+
logger.error(
|
|
598
|
+
error instanceof Error ? error.message : "Failed to install dependencies.\nPlease resolve the issue and run the command again."
|
|
599
|
+
);
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
const architectureCopySpinner = spinner();
|
|
603
|
+
architectureCopySpinner.start("Copying architecture...");
|
|
604
|
+
let architectureStats;
|
|
605
|
+
try {
|
|
606
|
+
architectureStats = await copyArchitecture(
|
|
607
|
+
"next",
|
|
608
|
+
architecture,
|
|
609
|
+
structure
|
|
610
|
+
);
|
|
611
|
+
architectureCopySpinner.stop("Success");
|
|
612
|
+
} catch (error) {
|
|
613
|
+
architectureCopySpinner.stop("Failed");
|
|
614
|
+
logger.error(
|
|
615
|
+
error instanceof Error ? error.message : "Failed to copy architecture."
|
|
616
|
+
);
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
const modulesCopySpinner = spinner();
|
|
620
|
+
modulesCopySpinner.start("Copying modules...");
|
|
621
|
+
let moduleStats;
|
|
622
|
+
try {
|
|
623
|
+
moduleStats = await copyModules(
|
|
624
|
+
selectedModules,
|
|
625
|
+
structure,
|
|
626
|
+
architecture
|
|
627
|
+
);
|
|
628
|
+
modulesCopySpinner.stop("Success");
|
|
629
|
+
} catch (error) {
|
|
630
|
+
modulesCopySpinner.stop("Failed");
|
|
631
|
+
logger.error(
|
|
632
|
+
error instanceof Error ? error.message : "Failed to copy modules."
|
|
633
|
+
);
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
try {
|
|
637
|
+
await extendProjectFiles(architecture, structure);
|
|
638
|
+
} catch (error) {
|
|
639
|
+
logger.error(
|
|
640
|
+
error instanceof Error ? error.message : "Failed to extend project files."
|
|
641
|
+
);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
const finishingSpinner = spinner();
|
|
645
|
+
finishingSpinner.start("Finishing...");
|
|
646
|
+
printSummary({
|
|
647
|
+
architecture,
|
|
648
|
+
selectedModules,
|
|
649
|
+
packageManager,
|
|
650
|
+
dependenciesInstalled,
|
|
651
|
+
filesCreated: architectureStats.created + moduleStats.created,
|
|
652
|
+
filesSkipped: architectureStats.skipped + moduleStats.skipped
|
|
653
|
+
});
|
|
654
|
+
finishingSpinner.stop("Success");
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// src/cli.ts
|
|
658
|
+
var program = new Command();
|
|
659
|
+
program.name("frontend-init").description("Initialize frontend architecture").version("1.0.0");
|
|
660
|
+
program.command("init").action(initCommand);
|
|
661
|
+
program.parse();
|
|
662
|
+
//# sourceMappingURL=cli.js.map
|