initkit 1.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 +844 -0
- package/bin/index.js +8 -0
- package/package.json +79 -0
- package/src/cli.js +264 -0
- package/src/commands/create.js +264 -0
- package/src/index.js +9 -0
- package/src/prompts/questions.js +358 -0
- package/src/templates/express.js +915 -0
- package/src/templates/fullstack.js +1236 -0
- package/src/templates/nextjs.js +620 -0
- package/src/templates/react.js +586 -0
- package/src/templates/vue.js +545 -0
- package/src/utils/errorHandler.js +275 -0
- package/src/utils/git.js +69 -0
- package/src/utils/packageManager.js +90 -0
- package/src/utils/templateGenerator.js +365 -0
- package/src/utils/validation.js +186 -0
- package/src/utils/versionFetcher.js +128 -0
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { getLatestVersion } from '../utils/versionFetcher.js';
|
|
6
|
+
|
|
7
|
+
async function fetchVersion(packageName, fallback = 'latest') {
|
|
8
|
+
try {
|
|
9
|
+
const version = await getLatestVersion(packageName);
|
|
10
|
+
return `^${version}`;
|
|
11
|
+
} catch {
|
|
12
|
+
return fallback;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generate Next.js project with App Router or Pages Router structure
|
|
18
|
+
*
|
|
19
|
+
* Creates a modern Next.js project with:
|
|
20
|
+
* - Folder structure based on user preference (feature-based, type-based, or pages-router)
|
|
21
|
+
* - TypeScript or JavaScript configuration
|
|
22
|
+
* - App Router (default) or Pages Router architecture
|
|
23
|
+
* - Package.json with Next.js 14+ dependencies
|
|
24
|
+
* - README with getting started instructions
|
|
25
|
+
*
|
|
26
|
+
* @param {string} projectPath - Absolute path to the project directory
|
|
27
|
+
* @param {Object} config - User configuration object
|
|
28
|
+
* @param {string} config.projectName - Name of the project
|
|
29
|
+
* @param {string} config.language - Programming language ('typescript'|'javascript')
|
|
30
|
+
* @param {string} [config.folderStructure='feature-based'] - Folder organization pattern
|
|
31
|
+
* - 'feature-based': Organize by features/modules (recommended for large apps)
|
|
32
|
+
* - 'type-based': Organize by file type (components, hooks, utils)
|
|
33
|
+
* - 'pages-router': Use legacy Pages Router instead of App Router
|
|
34
|
+
* @param {string} config.packageManager - Package manager to use
|
|
35
|
+
*
|
|
36
|
+
* @returns {Promise<void>}
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // Create Next.js project with App Router and feature-based structure
|
|
40
|
+
* await generateNextjsTemplate('/path/to/project', {
|
|
41
|
+
* projectName: 'my-nextjs-app',
|
|
42
|
+
* language: 'typescript',
|
|
43
|
+
* folderStructure: 'feature-based',
|
|
44
|
+
* packageManager: 'npm'
|
|
45
|
+
* });
|
|
46
|
+
*/
|
|
47
|
+
export async function generateNextjsTemplate(projectPath, config) {
|
|
48
|
+
// Create folder structure only
|
|
49
|
+
await createNextjsFolderStructure(projectPath, config);
|
|
50
|
+
|
|
51
|
+
// Generate package.json
|
|
52
|
+
await generateNextjsPackageJson(projectPath, config);
|
|
53
|
+
|
|
54
|
+
// Generate essential files (layout, page, etc.)
|
|
55
|
+
await generateNextjsEssentialFiles(projectPath, config);
|
|
56
|
+
|
|
57
|
+
// Generate README
|
|
58
|
+
await generateNextjsReadme(projectPath, config);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function createNextjsFolderStructure(projectPath, config) {
|
|
62
|
+
const srcPath = path.join(projectPath, 'src');
|
|
63
|
+
const folderStructure = config.folderStructure || 'feature-based';
|
|
64
|
+
const useAppRouter = folderStructure !== 'pages-router';
|
|
65
|
+
|
|
66
|
+
if (folderStructure === 'feature-based') {
|
|
67
|
+
// Feature-based structure
|
|
68
|
+
const features = ['auth', 'dashboard', 'users'];
|
|
69
|
+
for (const feature of features) {
|
|
70
|
+
await fs.ensureDir(path.join(srcPath, 'features', feature, 'components'));
|
|
71
|
+
await fs.ensureDir(path.join(srcPath, 'features', feature, 'hooks'));
|
|
72
|
+
await fs.ensureDir(path.join(srcPath, 'features', feature, 'services'));
|
|
73
|
+
await fs.ensureDir(path.join(srcPath, 'features', feature, 'types'));
|
|
74
|
+
|
|
75
|
+
// Create barrel export
|
|
76
|
+
await fs.writeFile(
|
|
77
|
+
path.join(srcPath, 'features', feature, 'index.ts'),
|
|
78
|
+
generateBarrelExport(feature)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Shared directory
|
|
83
|
+
await fs.ensureDir(path.join(srcPath, 'shared', 'components', 'ui'));
|
|
84
|
+
await fs.ensureDir(path.join(srcPath, 'shared', 'components', 'layout'));
|
|
85
|
+
await fs.ensureDir(path.join(srcPath, 'shared', 'hooks'));
|
|
86
|
+
await fs.ensureDir(path.join(srcPath, 'shared', 'utils'));
|
|
87
|
+
await fs.ensureDir(path.join(srcPath, 'shared', 'types'));
|
|
88
|
+
|
|
89
|
+
await fs.writeFile(
|
|
90
|
+
path.join(srcPath, 'shared', 'components', 'index.ts'),
|
|
91
|
+
`// Export shared components\n// TODO: Add your shared components here\n`
|
|
92
|
+
);
|
|
93
|
+
} else if (folderStructure === 'component-based') {
|
|
94
|
+
// Component-based structure
|
|
95
|
+
await fs.ensureDir(path.join(srcPath, 'components', 'ui'));
|
|
96
|
+
await fs.ensureDir(path.join(srcPath, 'components', 'layout'));
|
|
97
|
+
await fs.ensureDir(path.join(srcPath, 'components', 'forms'));
|
|
98
|
+
await fs.ensureDir(path.join(srcPath, 'hooks'));
|
|
99
|
+
await fs.ensureDir(path.join(srcPath, 'services'));
|
|
100
|
+
await fs.ensureDir(path.join(srcPath, 'utils'));
|
|
101
|
+
await fs.ensureDir(path.join(srcPath, 'types'));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// App or pages directory
|
|
105
|
+
if (useAppRouter) {
|
|
106
|
+
await fs.ensureDir(path.join(srcPath, 'app'));
|
|
107
|
+
await fs.ensureDir(path.join(srcPath, 'app', 'api'));
|
|
108
|
+
} else {
|
|
109
|
+
await fs.ensureDir(path.join(srcPath, 'pages'));
|
|
110
|
+
await fs.ensureDir(path.join(srcPath, 'pages', 'api'));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Common directories
|
|
114
|
+
await fs.ensureDir(path.join(srcPath, 'lib'));
|
|
115
|
+
await fs.ensureDir(path.join(projectPath, 'public'));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function generateNextjsPackageJson(projectPath, config) {
|
|
119
|
+
const { language, styling, additionalLibraries = [] } = config;
|
|
120
|
+
const isTypeScript = language === 'typescript';
|
|
121
|
+
|
|
122
|
+
const spinner = ora('Fetching latest package versions...').start();
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
// Fetch core dependencies
|
|
126
|
+
const [nextVer, reactVer, reactDomVer] = await Promise.all([
|
|
127
|
+
fetchVersion('next'),
|
|
128
|
+
fetchVersion('react'),
|
|
129
|
+
fetchVersion('react-dom'),
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
const dependencies = {
|
|
133
|
+
next: nextVer,
|
|
134
|
+
react: reactVer,
|
|
135
|
+
'react-dom': reactDomVer,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const devDependencies = {};
|
|
139
|
+
|
|
140
|
+
// TypeScript dependencies
|
|
141
|
+
if (isTypeScript) {
|
|
142
|
+
const [tsVer, typesNodeVer, typesReactVer, typesReactDomVer] = await Promise.all([
|
|
143
|
+
fetchVersion('typescript'),
|
|
144
|
+
fetchVersion('@types/node'),
|
|
145
|
+
fetchVersion('@types/react'),
|
|
146
|
+
fetchVersion('@types/react-dom'),
|
|
147
|
+
]);
|
|
148
|
+
devDependencies['typescript'] = tsVer;
|
|
149
|
+
devDependencies['@types/node'] = typesNodeVer;
|
|
150
|
+
devDependencies['@types/react'] = typesReactVer;
|
|
151
|
+
devDependencies['@types/react-dom'] = typesReactDomVer;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Add Tailwind
|
|
155
|
+
if (styling === 'tailwind') {
|
|
156
|
+
devDependencies['tailwindcss'] = await fetchVersion('tailwindcss');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Add libraries
|
|
160
|
+
if (additionalLibraries.includes('tanstack-query')) {
|
|
161
|
+
dependencies['@tanstack/react-query'] = await fetchVersion('@tanstack/react-query');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (additionalLibraries.includes('redux-toolkit')) {
|
|
165
|
+
const [reduxVer, reactReduxVer] = await Promise.all([
|
|
166
|
+
fetchVersion('@reduxjs/toolkit'),
|
|
167
|
+
fetchVersion('react-redux'),
|
|
168
|
+
]);
|
|
169
|
+
dependencies['@reduxjs/toolkit'] = reduxVer;
|
|
170
|
+
dependencies['react-redux'] = reactReduxVer;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (additionalLibraries.includes('zustand')) {
|
|
174
|
+
dependencies['zustand'] = await fetchVersion('zustand');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (additionalLibraries.includes('jotai')) {
|
|
178
|
+
dependencies['jotai'] = await fetchVersion('jotai');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (additionalLibraries.includes('axios')) {
|
|
182
|
+
dependencies['axios'] = await fetchVersion('axios');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (additionalLibraries.includes('zod')) {
|
|
186
|
+
dependencies['zod'] = await fetchVersion('zod');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (additionalLibraries.includes('react-hook-form')) {
|
|
190
|
+
const [formVer, resolversVer] = await Promise.all([
|
|
191
|
+
fetchVersion('react-hook-form'),
|
|
192
|
+
fetchVersion('@hookform/resolvers'),
|
|
193
|
+
]);
|
|
194
|
+
dependencies['react-hook-form'] = formVer;
|
|
195
|
+
dependencies['@hookform/resolvers'] = resolversVer;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (additionalLibraries.includes('framer-motion')) {
|
|
199
|
+
dependencies['framer-motion'] = await fetchVersion('framer-motion');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (additionalLibraries.includes('react-icons')) {
|
|
203
|
+
dependencies['react-icons'] = await fetchVersion('react-icons');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (additionalLibraries.includes('radix-ui')) {
|
|
207
|
+
const [dialogVer, dropdownVer, selectVer, slotVer] = await Promise.all([
|
|
208
|
+
fetchVersion('@radix-ui/react-dialog'),
|
|
209
|
+
fetchVersion('@radix-ui/react-dropdown-menu'),
|
|
210
|
+
fetchVersion('@radix-ui/react-select'),
|
|
211
|
+
fetchVersion('@radix-ui/react-slot'),
|
|
212
|
+
]);
|
|
213
|
+
Object.assign(dependencies, {
|
|
214
|
+
'@radix-ui/react-dialog': dialogVer,
|
|
215
|
+
'@radix-ui/react-dropdown-menu': dropdownVer,
|
|
216
|
+
'@radix-ui/react-select': selectVer,
|
|
217
|
+
'@radix-ui/react-slot': slotVer,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
spinner.succeed(chalk.green('Fetched latest versions'));
|
|
222
|
+
|
|
223
|
+
const packageJson = {
|
|
224
|
+
name: config.projectName,
|
|
225
|
+
version: '0.1.0',
|
|
226
|
+
private: true,
|
|
227
|
+
scripts: {
|
|
228
|
+
dev: 'next dev',
|
|
229
|
+
build: 'next build',
|
|
230
|
+
start: 'next start',
|
|
231
|
+
lint: 'next lint',
|
|
232
|
+
},
|
|
233
|
+
dependencies,
|
|
234
|
+
devDependencies,
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
await fs.writeJSON(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
|
|
238
|
+
} catch (error) {
|
|
239
|
+
spinner.fail(chalk.yellow('Could not fetch versions, using fallbacks'));
|
|
240
|
+
|
|
241
|
+
// Fallback with latest tag
|
|
242
|
+
const dependencies = { next: 'latest', react: 'latest', 'react-dom': 'latest' };
|
|
243
|
+
const devDependencies = isTypeScript
|
|
244
|
+
? {
|
|
245
|
+
typescript: 'latest',
|
|
246
|
+
'@types/node': 'latest',
|
|
247
|
+
'@types/react': 'latest',
|
|
248
|
+
'@types/react-dom': 'latest',
|
|
249
|
+
}
|
|
250
|
+
: {};
|
|
251
|
+
|
|
252
|
+
if (styling === 'tailwind') devDependencies['tailwindcss'] = 'latest';
|
|
253
|
+
if (additionalLibraries.includes('tanstack-query'))
|
|
254
|
+
dependencies['@tanstack/react-query'] = 'latest';
|
|
255
|
+
if (additionalLibraries.includes('redux-toolkit')) {
|
|
256
|
+
dependencies['@reduxjs/toolkit'] = 'latest';
|
|
257
|
+
dependencies['react-redux'] = 'latest';
|
|
258
|
+
}
|
|
259
|
+
if (additionalLibraries.includes('zustand')) dependencies['zustand'] = 'latest';
|
|
260
|
+
if (additionalLibraries.includes('jotai')) dependencies['jotai'] = 'latest';
|
|
261
|
+
if (additionalLibraries.includes('axios')) dependencies['axios'] = 'latest';
|
|
262
|
+
if (additionalLibraries.includes('zod')) dependencies['zod'] = 'latest';
|
|
263
|
+
if (additionalLibraries.includes('react-hook-form')) {
|
|
264
|
+
dependencies['react-hook-form'] = 'latest';
|
|
265
|
+
dependencies['@hookform/resolvers'] = 'latest';
|
|
266
|
+
}
|
|
267
|
+
if (additionalLibraries.includes('framer-motion')) dependencies['framer-motion'] = 'latest';
|
|
268
|
+
if (additionalLibraries.includes('react-icons')) dependencies['react-icons'] = 'latest';
|
|
269
|
+
if (additionalLibraries.includes('radix-ui')) {
|
|
270
|
+
Object.assign(dependencies, {
|
|
271
|
+
'@radix-ui/react-dialog': 'latest',
|
|
272
|
+
'@radix-ui/react-dropdown-menu': 'latest',
|
|
273
|
+
'@radix-ui/react-select': 'latest',
|
|
274
|
+
'@radix-ui/react-slot': 'latest',
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const packageJson = {
|
|
279
|
+
name: config.projectName,
|
|
280
|
+
version: '0.1.0',
|
|
281
|
+
private: true,
|
|
282
|
+
scripts: {
|
|
283
|
+
dev: 'next dev',
|
|
284
|
+
build: 'next build',
|
|
285
|
+
start: 'next start',
|
|
286
|
+
lint: 'next lint',
|
|
287
|
+
},
|
|
288
|
+
dependencies,
|
|
289
|
+
devDependencies,
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
await fs.writeJSON(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function generateNextjsEssentialFiles(projectPath, config) {
|
|
297
|
+
const { language, folderStructure } = config;
|
|
298
|
+
const isTypeScript = language === 'typescript';
|
|
299
|
+
const ext = isTypeScript ? 'tsx' : 'jsx';
|
|
300
|
+
const useAppRouter = folderStructure !== 'pages-router';
|
|
301
|
+
const appPath = path.join(projectPath, 'src', useAppRouter ? 'app' : 'pages');
|
|
302
|
+
|
|
303
|
+
if (useAppRouter) {
|
|
304
|
+
// Create layout file for App Router
|
|
305
|
+
const layoutContent = `${isTypeScript ? "import type { Metadata } from 'next'\n" : ''}import './globals.css'
|
|
306
|
+
|
|
307
|
+
${
|
|
308
|
+
isTypeScript
|
|
309
|
+
? `export const metadata: Metadata = {
|
|
310
|
+
title: '${config.projectName}',
|
|
311
|
+
description: 'Generated by InitKit CLI',
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
`
|
|
315
|
+
: ''
|
|
316
|
+
}export default function RootLayout(${
|
|
317
|
+
isTypeScript
|
|
318
|
+
? `{
|
|
319
|
+
children,
|
|
320
|
+
}: Readonly<{
|
|
321
|
+
children: React.ReactNode
|
|
322
|
+
}>`
|
|
323
|
+
: '{ children }'
|
|
324
|
+
}) {
|
|
325
|
+
return (
|
|
326
|
+
<html lang="en">
|
|
327
|
+
<body>{children}</body>
|
|
328
|
+
</html>
|
|
329
|
+
)
|
|
330
|
+
}
|
|
331
|
+
`;
|
|
332
|
+
|
|
333
|
+
await fs.writeFile(path.join(appPath, `layout.${ext}`), layoutContent);
|
|
334
|
+
|
|
335
|
+
// Create page file for App Router
|
|
336
|
+
const pageContent = `export default function Home() {
|
|
337
|
+
return (
|
|
338
|
+
<div style={{ padding: '2rem', fontFamily: 'system-ui, sans-serif' }}>
|
|
339
|
+
<h1>Welcome to ${config.projectName}</h1>
|
|
340
|
+
<p>Get started by editing <code>src/app/page.${ext}</code></p>
|
|
341
|
+
</div>
|
|
342
|
+
)
|
|
343
|
+
}
|
|
344
|
+
`;
|
|
345
|
+
|
|
346
|
+
await fs.writeFile(path.join(appPath, `page.${ext}`), pageContent);
|
|
347
|
+
|
|
348
|
+
// Create globals.css
|
|
349
|
+
const globalsCssContent = `:root {
|
|
350
|
+
--foreground-rgb: 0, 0, 0;
|
|
351
|
+
--background-start-rgb: 214, 219, 220;
|
|
352
|
+
--background-end-rgb: 255, 255, 255;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
@media (prefers-color-scheme: dark) {
|
|
356
|
+
:root {
|
|
357
|
+
--foreground-rgb: 255, 255, 255;
|
|
358
|
+
--background-start-rgb: 0, 0, 0;
|
|
359
|
+
--background-end-rgb: 0, 0, 0;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
body {
|
|
364
|
+
color: rgb(var(--foreground-rgb));
|
|
365
|
+
background: linear-gradient(
|
|
366
|
+
to bottom,
|
|
367
|
+
transparent,
|
|
368
|
+
rgb(var(--background-end-rgb))
|
|
369
|
+
)
|
|
370
|
+
rgb(var(--background-start-rgb));
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
* {
|
|
374
|
+
box-sizing: border-box;
|
|
375
|
+
padding: 0;
|
|
376
|
+
margin: 0;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
html,
|
|
380
|
+
body {
|
|
381
|
+
max-width: 100vw;
|
|
382
|
+
overflow-x: hidden;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
a {
|
|
386
|
+
color: inherit;
|
|
387
|
+
text-decoration: none;
|
|
388
|
+
}
|
|
389
|
+
`;
|
|
390
|
+
|
|
391
|
+
await fs.writeFile(path.join(appPath, 'globals.css'), globalsCssContent);
|
|
392
|
+
} else {
|
|
393
|
+
// Create _app file for Pages Router
|
|
394
|
+
const appPageContent = `${isTypeScript ? "import type { AppProps } from 'next/app'\n" : ''}import '../styles/globals.css'
|
|
395
|
+
|
|
396
|
+
export default function App(${isTypeScript ? '{ Component, pageProps }: AppProps' : '{ Component, pageProps }'}) {
|
|
397
|
+
return <Component {...pageProps} />
|
|
398
|
+
}
|
|
399
|
+
`;
|
|
400
|
+
|
|
401
|
+
await fs.writeFile(path.join(appPath, `_app.${ext}`), appPageContent);
|
|
402
|
+
|
|
403
|
+
// Create index page for Pages Router
|
|
404
|
+
const indexPageContent = `export default function Home() {
|
|
405
|
+
return (
|
|
406
|
+
<div style={{ padding: '2rem', fontFamily: 'system-ui, sans-serif' }}>
|
|
407
|
+
<h1>Welcome to ${config.projectName}</h1>
|
|
408
|
+
<p>Get started by editing <code>src/pages/index.${ext}</code></p>
|
|
409
|
+
</div>
|
|
410
|
+
)
|
|
411
|
+
}
|
|
412
|
+
`;
|
|
413
|
+
|
|
414
|
+
await fs.writeFile(path.join(appPath, `index.${ext}`), indexPageContent);
|
|
415
|
+
|
|
416
|
+
// Create styles directory
|
|
417
|
+
const stylesPath = path.join(projectPath, 'src', 'styles');
|
|
418
|
+
await fs.ensureDir(stylesPath);
|
|
419
|
+
await fs.writeFile(
|
|
420
|
+
path.join(stylesPath, 'globals.css'),
|
|
421
|
+
`:root {
|
|
422
|
+
--foreground-rgb: 0, 0, 0;
|
|
423
|
+
--background-start-rgb: 214, 219, 220;
|
|
424
|
+
--background-end-rgb: 255, 255, 255;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
body {
|
|
428
|
+
color: rgb(var(--foreground-rgb));
|
|
429
|
+
background: linear-gradient(
|
|
430
|
+
to bottom,
|
|
431
|
+
transparent,
|
|
432
|
+
rgb(var(--background-end-rgb))
|
|
433
|
+
)
|
|
434
|
+
rgb(var(--background-start-rgb));
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
* {
|
|
438
|
+
box-sizing: border-box;
|
|
439
|
+
padding: 0;
|
|
440
|
+
margin: 0;
|
|
441
|
+
}
|
|
442
|
+
`
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Create next.config file
|
|
447
|
+
const nextConfigContent = isTypeScript
|
|
448
|
+
? `import type { NextConfig } from 'next'
|
|
449
|
+
|
|
450
|
+
const nextConfig: NextConfig = {
|
|
451
|
+
/* config options here */
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export default nextConfig
|
|
455
|
+
`
|
|
456
|
+
: `/** @type {import('next').NextConfig} */
|
|
457
|
+
const nextConfig = {
|
|
458
|
+
/* config options here */
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export default nextConfig
|
|
462
|
+
`;
|
|
463
|
+
|
|
464
|
+
await fs.writeFile(
|
|
465
|
+
path.join(projectPath, isTypeScript ? 'next.config.ts' : 'next.config.js'),
|
|
466
|
+
nextConfigContent
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
// Create .gitignore
|
|
470
|
+
const gitignoreContent = `# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
471
|
+
|
|
472
|
+
# dependencies
|
|
473
|
+
/node_modules
|
|
474
|
+
/.pnp
|
|
475
|
+
.pnp.js
|
|
476
|
+
.yarn/install-state.gz
|
|
477
|
+
|
|
478
|
+
# testing
|
|
479
|
+
/coverage
|
|
480
|
+
|
|
481
|
+
# next.js
|
|
482
|
+
/.next/
|
|
483
|
+
/out/
|
|
484
|
+
|
|
485
|
+
# production
|
|
486
|
+
/build
|
|
487
|
+
|
|
488
|
+
# misc
|
|
489
|
+
.DS_Store
|
|
490
|
+
*.pem
|
|
491
|
+
|
|
492
|
+
# debug
|
|
493
|
+
npm-debug.log*
|
|
494
|
+
yarn-debug.log*
|
|
495
|
+
yarn-error.log*
|
|
496
|
+
|
|
497
|
+
# local env files
|
|
498
|
+
.env*.local
|
|
499
|
+
|
|
500
|
+
# vercel
|
|
501
|
+
.vercel
|
|
502
|
+
|
|
503
|
+
# typescript
|
|
504
|
+
*.tsbuildinfo
|
|
505
|
+
next-env.d.ts
|
|
506
|
+
`;
|
|
507
|
+
|
|
508
|
+
await fs.writeFile(path.join(projectPath, '.gitignore'), gitignoreContent);
|
|
509
|
+
|
|
510
|
+
// Create tsconfig.json if TypeScript
|
|
511
|
+
if (isTypeScript) {
|
|
512
|
+
const tsconfigContent = {
|
|
513
|
+
compilerOptions: {
|
|
514
|
+
target: 'ES2017',
|
|
515
|
+
lib: ['dom', 'dom.iterable', 'esnext'],
|
|
516
|
+
allowJs: true,
|
|
517
|
+
skipLibCheck: true,
|
|
518
|
+
strict: true,
|
|
519
|
+
noEmit: true,
|
|
520
|
+
esModuleInterop: true,
|
|
521
|
+
module: 'esnext',
|
|
522
|
+
moduleResolution: 'bundler',
|
|
523
|
+
resolveJsonModule: true,
|
|
524
|
+
isolatedModules: true,
|
|
525
|
+
jsx: 'preserve',
|
|
526
|
+
incremental: true,
|
|
527
|
+
plugins: [
|
|
528
|
+
{
|
|
529
|
+
name: 'next',
|
|
530
|
+
},
|
|
531
|
+
],
|
|
532
|
+
paths: {
|
|
533
|
+
'@/*': ['./src/*'],
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
include: ['next-env.d.ts', 'src/**/*.ts', 'src/**/*.tsx', '.next/types/**/*.ts'],
|
|
537
|
+
exclude: ['node_modules'],
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
await fs.writeJSON(path.join(projectPath, 'tsconfig.json'), tsconfigContent, { spaces: 2 });
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
async function generateNextjsReadme(projectPath, config) {
|
|
545
|
+
const { projectName, folderStructure, language, styling, packageManager } = config;
|
|
546
|
+
|
|
547
|
+
const readme = `# ${projectName}
|
|
548
|
+
|
|
549
|
+
Created with InitKit CLI
|
|
550
|
+
|
|
551
|
+
## Setup
|
|
552
|
+
|
|
553
|
+
1. Install dependencies:
|
|
554
|
+
\`\`\`bash
|
|
555
|
+
${packageManager} install
|
|
556
|
+
\`\`\`
|
|
557
|
+
|
|
558
|
+
2. Run the development server:
|
|
559
|
+
\`\`\`bash
|
|
560
|
+
${packageManager} ${packageManager === 'npm' ? 'run ' : ''}dev
|
|
561
|
+
\`\`\`
|
|
562
|
+
|
|
563
|
+
3. Open [http://localhost:3000](http://localhost:3000)
|
|
564
|
+
|
|
565
|
+
## Tech Stack
|
|
566
|
+
|
|
567
|
+
- **Next.js 15** - React framework
|
|
568
|
+
- **React 19** - UI library${language === 'typescript' ? '\n- **TypeScript** - Type safety' : ''}${styling === 'tailwind' ? '\n- **Tailwind CSS v4** - Styling' : ''}
|
|
569
|
+
|
|
570
|
+
## Folder Structure
|
|
571
|
+
|
|
572
|
+
\`\`\`
|
|
573
|
+
src/
|
|
574
|
+
${
|
|
575
|
+
folderStructure === 'feature-based'
|
|
576
|
+
? `├── features/ # Feature modules
|
|
577
|
+
│ ├── auth/ # Authentication
|
|
578
|
+
│ ├── dashboard/ # Dashboard
|
|
579
|
+
│ └── users/ # Users
|
|
580
|
+
├── shared/ # Shared code
|
|
581
|
+
├── app/ # Next.js App Router`
|
|
582
|
+
: `├── components/ # React components
|
|
583
|
+
├── hooks/ # Custom hooks
|
|
584
|
+
├── services/ # API services
|
|
585
|
+
├── app/ # Next.js App Router`
|
|
586
|
+
}
|
|
587
|
+
├── lib/ # Utilities
|
|
588
|
+
└── public/ # Static files
|
|
589
|
+
\`\`\`
|
|
590
|
+
|
|
591
|
+
## Next Steps
|
|
592
|
+
|
|
593
|
+
1. Run \`npx create-next-app@latest . --use-${packageManager}\` to initialize Next.js${styling === 'tailwind' ? '\n2. Install Tailwind v4: `' + packageManager + (packageManager === 'npm' ? ' install' : ' add') + ' tailwindcss@next`' : ''}
|
|
594
|
+
3. Start building in \`src/features/\`
|
|
595
|
+
4. Add environment variables in \`.env.local\`
|
|
596
|
+
|
|
597
|
+
---
|
|
598
|
+
|
|
599
|
+
Built with InitKit
|
|
600
|
+
`;
|
|
601
|
+
|
|
602
|
+
await fs.writeFile(path.join(projectPath, 'README.md'), readme);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function generateBarrelExport(featureName) {
|
|
606
|
+
return `// ${featureName.toUpperCase()} Feature
|
|
607
|
+
|
|
608
|
+
// TODO: Export your components
|
|
609
|
+
// export { default as ${featureName.charAt(0).toUpperCase() + featureName.slice(1)}Component } from './components/${featureName.charAt(0).toUpperCase() + featureName.slice(1)}Component';
|
|
610
|
+
|
|
611
|
+
// TODO: Export your hooks
|
|
612
|
+
// export { use${featureName.charAt(0).toUpperCase() + featureName.slice(1)} } from './hooks/use${featureName.charAt(0).toUpperCase() + featureName.slice(1)}';
|
|
613
|
+
|
|
614
|
+
// TODO: Export your services
|
|
615
|
+
// export * from './services/${featureName}Service';
|
|
616
|
+
|
|
617
|
+
// TODO: Export your types
|
|
618
|
+
// export type * from './types/${featureName}.types';
|
|
619
|
+
`;
|
|
620
|
+
}
|