@codefast/cli 0.3.1-canary.5 → 0.3.1-canary.8

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/dist/index.cjs CHANGED
@@ -26,55 +26,32 @@ var __webpack_require__ = {};
26
26
  })();
27
27
  var __webpack_exports__ = {};
28
28
  const external_commander_namespaceObject = require("commander");
29
- const external_node_child_process_namespaceObject = require("node:child_process");
30
29
  const external_node_fs_namespaceObject = require("node:fs");
31
30
  var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
32
31
  const external_node_path_namespaceObject = require("node:path");
33
32
  var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
33
+ const external_node_child_process_namespaceObject = require("node:child_process");
34
34
  const external_node_readline_namespaceObject = require("node:readline");
35
- const MAIN_DEPENDENCIES = [
36
- "@codefast/ui",
37
- "@codefast/hooks"
38
- ];
39
- const DEV_DEPENDENCIES = {
40
- commitLint: [
41
- "@commitlint/cli",
42
- "@commitlint/config-conventional",
43
- "@commitlint/types"
44
- ],
45
- gitTools: [
46
- "simple-git-hooks"
47
- ],
48
- formatting: [
49
- "prettier",
50
- "prettier-plugin-packagejson",
51
- "prettier-plugin-tailwindcss",
52
- "lint-staged"
53
- ],
54
- linting: [
55
- "@codefast/eslint-config"
56
- ]
57
- };
58
- const rl = external_node_readline_namespaceObject.createInterface({
35
+ var external_node_readline_default = /*#__PURE__*/ __webpack_require__.n(external_node_readline_namespaceObject);
36
+ const rl = external_node_readline_default().createInterface({
59
37
  input: process.stdin,
60
38
  output: process.stdout
61
39
  });
62
- function create_project_prompt(question) {
40
+ function utils_prompt(question) {
63
41
  return new Promise((resolve)=>{
64
42
  rl.question(question, (answer)=>{
65
43
  resolve(answer.trim());
66
44
  });
67
45
  });
68
46
  }
69
- function checkEnvironment() {
70
- try {
71
- (0, external_node_child_process_namespaceObject.execSync)("pnpm --version", {
72
- stdio: "ignore"
73
- });
74
- } catch {
75
- console.error("❌ PNPM chưa được cài đặt. Vui lòng cài đặt PNPM trước.");
76
- process.exit(1);
77
- }
47
+ function runCommand(command) {
48
+ (0, external_node_child_process_namespaceObject.execSync)(command, {
49
+ stdio: "inherit"
50
+ });
51
+ }
52
+ function writeConfigFile(filePath, content) {
53
+ external_node_fs_default().writeFileSync(filePath, content);
54
+ console.log(`📝 Created ${filePath}`);
78
55
  }
79
56
  function validateProjectName(name) {
80
57
  if (!name || /^\s*$/.test(name)) return {
@@ -99,13 +76,13 @@ function validateProjectName(name) {
99
76
  valid: false,
100
77
  error: `Project name "${name}" is a reserved system name.`
101
78
  };
102
- const fullPath = external_node_path_namespaceObject.resolve(process.cwd(), name);
103
- if (external_node_fs_namespaceObject.existsSync(fullPath)) return {
79
+ const fullPath = external_node_path_default().resolve(process.cwd(), name);
80
+ if (external_node_fs_default().existsSync(fullPath)) return {
104
81
  valid: false,
105
82
  error: `A directory named "${name}" already exists.`
106
83
  };
107
84
  try {
108
- external_node_fs_namespaceObject.accessSync(process.cwd(), external_node_fs_namespaceObject.constants.W_OK);
85
+ external_node_fs_default().accessSync(process.cwd(), external_node_fs_default().constants.W_OK);
109
86
  } catch {
110
87
  return {
111
88
  valid: false,
@@ -117,49 +94,139 @@ function validateProjectName(name) {
117
94
  };
118
95
  }
119
96
  async function getValidProjectName(initialName, attempts = 0, maxAttempts = 3) {
120
- const projectName = initialName ?? await create_project_prompt("Enter project name: ");
97
+ const projectName = initialName ?? await utils_prompt("Enter project name: ");
121
98
  const validation = validateProjectName(projectName);
122
99
  if (validation.valid) return projectName;
123
- if (attempts >= maxAttempts - 1) throw new Error("❌ Exceeded maximum attempts to enter a valid project name.");
100
+ if (attempts >= maxAttempts - 1) throw new Error(`❌ Exceeded maximum attempts to enter a valid project name.`);
124
101
  console.error(`❌ ${validation.error} (${maxAttempts - attempts - 1} attempts remaining)`);
125
102
  return getValidProjectName(void 0, attempts + 1, maxAttempts);
126
103
  }
127
- function runCommand(command) {
128
- (0, external_node_child_process_namespaceObject.execSync)(command, {
129
- stdio: "inherit"
130
- });
131
- }
132
- function writeConfigFile(filePath, content) {
133
- external_node_fs_namespaceObject.writeFileSync(filePath, content);
134
- console.log(`📝 Created ${filePath}`);
135
- }
136
- function updatePostcssConfig(projectDir) {
137
- const postcssConfigPath = external_node_path_namespaceObject.join(projectDir, "postcss.config.mjs");
138
- if (!external_node_fs_namespaceObject.existsSync(postcssConfigPath)) return void console.warn("⚠️ postcss.config.mjs not found, skipping update.");
139
- let content = external_node_fs_namespaceObject.readFileSync(postcssConfigPath, "utf8");
140
- const typeComment = `/** @type {import('postcss-load-config').Config} */\n`;
141
- if (content.includes(typeComment)) console.log("📝 postcss.config.mjs already contains type comment, no changes made.");
142
- else {
143
- content = content.replace("const config = {", `${typeComment}const config = {`);
144
- external_node_fs_namespaceObject.writeFileSync(postcssConfigPath, content);
145
- console.log("📝 Updated postcss.config.mjs with type comment.");
104
+ async function checkExistingProject(projectNameArg) {
105
+ const packageJsonPath = external_node_path_default().join(process.cwd(), "package.json");
106
+ const packageJsonExists = external_node_fs_default().existsSync(packageJsonPath);
107
+ if (packageJsonExists) {
108
+ console.log(`⚠️ A package.json file has been detected in the current directory.`);
109
+ const confirm = await utils_prompt("Do you want to continue configuring this existing project? [Y/n]: ");
110
+ if ("" === confirm || "y" === confirm.toLowerCase()) {
111
+ const packageJson = JSON.parse(external_node_fs_default().readFileSync(packageJsonPath, "utf8"));
112
+ const projectName = packageJson.name ?? await getValidProjectName(projectNameArg);
113
+ return {
114
+ projectName,
115
+ packageJsonExists
116
+ };
117
+ }
118
+ console.log("Aborting...");
119
+ throw new Error("User aborted the operation.");
146
120
  }
121
+ const projectName = await getValidProjectName(projectNameArg);
122
+ return {
123
+ projectName,
124
+ packageJsonExists
125
+ };
147
126
  }
148
- const configFiles = {
149
- "commitlint.config.mjs": `/**
150
- * @see https://commitlint.js.org/reference/configuration.html
151
- * @type {import('@commitlint/types').UserConfig}
152
- */
153
- const config = {
154
- extends: ["@commitlint/config-conventional"],
155
- rules: {
156
- "body-max-line-length": [2, "always", 350],
157
- },
127
+ const MAIN_DEPENDENCIES = [
128
+ "@codefast/ui",
129
+ "@codefast/hooks"
130
+ ];
131
+ const DEV_DEPENDENCIES = {
132
+ commitLint: [
133
+ "@commitlint/cli",
134
+ "@commitlint/config-conventional",
135
+ "@commitlint/types"
136
+ ],
137
+ gitTools: [
138
+ "simple-git-hooks"
139
+ ],
140
+ formatting: [
141
+ "prettier",
142
+ "prettier-plugin-packagejson",
143
+ "prettier-plugin-tailwindcss",
144
+ "lint-staged"
145
+ ],
146
+ linting: [
147
+ "@codefast/eslint-config"
148
+ ]
158
149
  };
159
-
160
- export default config;
150
+ const PACKAGES_TO_REMOVE = [
151
+ "@eslint/eslintrc"
152
+ ];
153
+ const configGroups = {
154
+ deployment: [
155
+ {
156
+ path: ".vercelignore",
157
+ content: `#-------------------------------------------------------------------------------
158
+ # Dependencies and package managers
159
+ #-------------------------------------------------------------------------------
160
+
161
+ # Node dependencies
162
+ node_modules
163
+ .pnpm-store
164
+ .yarn
165
+
166
+ #-------------------------------------------------------------------------------
167
+ # Development and environment
168
+ #-------------------------------------------------------------------------------
169
+
170
+ # Environment variables (except examples)
171
+ .env*
172
+ !.env.example
173
+
174
+ # Build artifacts
175
+ .next
176
+ build
177
+ dist
178
+ .git
179
+ .github
180
+ .turbo
181
+ .changeset
182
+ .eslintcache
183
+
184
+ # Debug logs
185
+ npm-debug.log*
186
+ yarn-debug.log*
187
+
188
+ #-------------------------------------------------------------------------------
189
+ # Testing
190
+ #-------------------------------------------------------------------------------
191
+
192
+ # Test coverage reports and config snapshots
193
+ coverage
194
+ *.test.*
195
+ *.e2e.*
196
+
197
+ #-------------------------------------------------------------------------------
198
+ # Editor and IDE configuration
199
+ #-------------------------------------------------------------------------------
200
+
201
+ # IDE project files
202
+ .idea
203
+ *.suo
204
+ *.ntvs*
205
+ *.njsproj
206
+ *.sln
207
+
208
+ #-------------------------------------------------------------------------------
209
+ # Operating system files
210
+ #-------------------------------------------------------------------------------
211
+
212
+ # System metadata files
213
+ .DS_Store
214
+ Thumbs.db
215
+
216
+ #-------------------------------------------------------------------------------
217
+ # Deployment
218
+ #-------------------------------------------------------------------------------
219
+
220
+ # Vercel deployment configuration
221
+ .vercel
161
222
  `,
162
- "prettier.config.mjs": `/**
223
+ description: "Ignore files that should not be deployed to Vercel"
224
+ }
225
+ ],
226
+ formatting: [
227
+ {
228
+ path: "prettier.config.mjs",
229
+ content: `/**
163
230
  * @see https://prettier.io/docs/en/configuration.html
164
231
  * @type {import('prettier').Config}
165
232
  */
@@ -172,8 +239,71 @@ const config = {
172
239
 
173
240
  export default config;
174
241
  `,
175
- "lint-staged.config.mjs": `import { relative } from "node:path";
242
+ description: "Prettier configuration for source code formatting"
243
+ },
244
+ {
245
+ path: ".editorconfig",
246
+ content: `# https://editorconfig.org
247
+ root = true
248
+
249
+ [*]
250
+ charset = utf-8
251
+ insert_final_newline = true
252
+
253
+ end_of_line = lf
254
+ indent_style = space
255
+ indent_size = 2
256
+ max_line_length = 120
257
+
258
+ trim_trailing_whitespace = true
259
+
260
+ [Makefile]
261
+ indent_style = tab
262
+
263
+ [{go.mod,go.sum,*.go}]
264
+ indent_style = tab
176
265
 
266
+ [*.rs]
267
+ # Keep in sync with rustfmt
268
+ indent_size = 4
269
+ `,
270
+ description: "Editor configuration for source code formatting"
271
+ }
272
+ ],
273
+ linting: [
274
+ {
275
+ path: "commitlint.config.mjs",
276
+ content: `/**
277
+ * @see https://commitlint.js.org/reference/configuration.html
278
+ * @type {import('@commitlint/types').UserConfig}
279
+ */
280
+ const config = {
281
+ extends: ["@commitlint/config-conventional"],
282
+ rules: {
283
+ "body-max-line-length": [2, "always", 350],
284
+ },
285
+ };
286
+
287
+ export default config;
288
+ `,
289
+ description: "Commitlint configuration"
290
+ },
291
+ {
292
+ path: "eslint.config.mjs",
293
+ content: `import { config } from "@codefast/eslint-config/next";
294
+ /**
295
+ * @see https://eslint.org/docs/latest/use/configure/configuration-files
296
+ * @type {import('eslint').Linter.Config[]}
297
+ */
298
+ const eslintConfig = [...config];
299
+
300
+ export default eslintConfig;
301
+ `,
302
+ description: "ESLint configuration for the project"
303
+ },
304
+ {
305
+ path: "lint-staged.config.mjs",
306
+ content: `import { relative } from "node:path";
177
307
  /**
178
308
  * Builds an ESLint command with the given filenames.
179
309
  *
@@ -197,16 +327,13 @@ const config = {
197
327
 
198
328
  export default config;
199
329
  `,
200
- "eslint.config.mjs": `import { config } from "@codefast/eslint-config/next";
201
- /**
202
- * @see https://eslint.org/docs/latest/use/configure/configuration-files
203
- * @type {import('eslint').Linter.Config[]}
204
- */
205
- const eslintConfig = [...config];
206
-
207
- export default eslintConfig;
208
- `,
209
- "src/lib/fonts.ts": `import { cn } from "@codefast/ui";
330
+ description: "Configure lint-staged for pre-commit checks"
331
+ }
332
+ ],
333
+ styles: [
334
+ {
335
+ path: "src/lib/fonts.ts",
336
+ content: `import { cn } from "@codefast/ui";
210
337
  import { Geist, Geist_Mono as GeistMono } from "next/font/google";
211
338
 
212
339
  const fontGeistSans = Geist({
@@ -221,7 +348,11 @@ const fontGeistMono = GeistMono({
221
348
 
222
349
  export const fontVariables = cn(fontGeistSans.variable, fontGeistMono.variable);
223
350
  `,
224
- "src/app/globals.css": `@import "tailwindcss";
351
+ description: "Font configuration for the application"
352
+ },
353
+ {
354
+ path: "src/app/globals.css",
355
+ content: `@import "tailwindcss";
225
356
 
226
357
  @import "@codefast/ui/styles.css";
227
358
 
@@ -229,103 +360,71 @@ export const fontVariables = cn(fontGeistSans.variable, fontGeistMono.variable);
229
360
 
230
361
  @custom-variant dark (&:where(.dark, .dark *));
231
362
 
232
- @theme inline {
233
- --font-sans: var(--font-geist-sans);
234
- --font-mono: var(--font-geist-mono);
235
- }
236
-
237
363
  @layer base {
238
364
  :root {
365
+ --font-sans: var(--font-geist-sans);
366
+ --font-mono: var(--font-geist-mono);
239
367
  --input: var(--color-neutral-200);
240
368
  --border: var(--color-neutral-200);
241
-
242
369
  --ring: var(--color-neutral-400);
243
-
244
370
  --background: var(--color-white);
245
371
  --foreground: var(--color-neutral-950);
246
-
247
372
  --primary: var(--color-sky-500);
248
373
  --primary-foreground: var(--color-neutral-50);
249
-
250
374
  --secondary: var(--color-neutral-100);
251
375
  --secondary-foreground: var(--color-neutral-900);
252
-
253
376
  --destructive: var(--color-red-600);
254
-
255
377
  --accent: var(--color-neutral-100);
256
378
  --accent-foreground: var(--color-neutral-900);
257
-
258
379
  --muted: var(--color-neutral-100);
259
380
  --muted-foreground: var(--color-neutral-500);
260
-
261
381
  --popover: var(--color-white);
262
382
  --popover-foreground: var(--color-neutral-950);
263
383
  --popover-overlay: --alpha(var(--color-neutral-900) / 20%);
264
-
265
384
  --card: var(--color-white);
266
385
  --card-foreground: var(--color-neutral-950);
267
-
268
386
  --sidebar: var(--color-neutral-50);
269
387
  --sidebar-foreground: var(--color-neutral-950);
270
-
271
388
  --sidebar-primary: var(--color-sky-500);
272
389
  --sidebar-primary-foreground: var(--color-neutral-50);
273
-
274
390
  --sidebar-accent: var(--color-neutral-100);
275
391
  --sidebar-accent-foreground: var(--color-neutral-900);
276
-
277
392
  --sidebar-border: var(--color-neutral-200);
278
393
  --sidebar-ring: var(--color-neutral-400);
279
-
280
394
  --chart-1: var(--color-orange-600);
281
395
  --chart-2: var(--color-teal-600);
282
396
  --chart-3: var(--color-cyan-600);
283
397
  --chart-4: var(--color-amber-600);
284
398
  --chart-5: var(--color-amber-500);
285
399
  }
286
-
287
400
  @variant dark {
288
401
  --input: var(--color-neutral-700);
289
402
  --border: --alpha(var(--color-neutral-700) / 50%);
290
-
291
403
  --ring: var(--color-neutral-500);
292
-
293
404
  --background: var(--color-neutral-950);
294
405
  --foreground: var(--color-neutral-50);
295
-
296
406
  --primary: var(--color-sky-700);
297
407
  --primary-foreground: var(--color-neutral-50);
298
-
299
408
  --secondary: var(--color-neutral-800);
300
409
  --secondary-foreground: var(--color-neutral-50);
301
-
302
410
  --destructive: var(--color-red-400);
303
-
304
411
  --accent: var(--color-neutral-700);
305
412
  --accent-foreground: var(--color-neutral-50);
306
-
307
413
  --muted: var(--color-neutral-800);
308
414
  --muted-foreground: var(--color-neutral-400);
309
-
310
415
  --popover: var(--color-neutral-800);
311
416
  --popover-foreground: var(--color-neutral-50);
312
417
  --popover-overlay: --alpha(var(--color-neutral-900) / 80%);
313
-
314
418
  --card: var(--color-neutral-900);
315
419
  --card-foreground: var(--color-neutral-50);
316
-
317
420
  --sidebar: var(--color-neutral-900);
318
421
  --sidebar-foreground: var(--color-neutral-50);
319
-
320
422
  --sidebar-primary: var(--color-sky-700);
321
423
  --sidebar-primary-foreground: var(--color-neutral-50);
322
-
323
424
  --sidebar-accent: var(--color-neutral-800);
324
425
  --sidebar-accent-foreground: var(--color-neutral-50);
325
-
326
426
  --sidebar-border: var(--color-neutral-800);
327
427
  --sidebar-ring: var(--color-neutral-600);
328
-
329
428
  --chart-1: var(--color-blue-700);
330
429
  --chart-2: var(--color-emerald-500);
331
430
  --chart-3: var(--color-amber-500);
@@ -333,82 +432,164 @@ export const fontVariables = cn(fontGeistSans.variable, fontGeistMono.variable);
333
432
  --chart-5: var(--color-rose-500);
334
433
  }
335
434
  }
336
- `
435
+ `,
436
+ description: "Global CSS variables for the application"
437
+ }
438
+ ]
337
439
  };
440
+ function updatePostcssConfig(projectDir) {
441
+ const postcssConfigPath = external_node_path_default().join(projectDir, "postcss.config.mjs");
442
+ if (!external_node_fs_default().existsSync(postcssConfigPath)) return void console.warn(`⚠️ postcss.config.mjs not found, skipping update.`);
443
+ let content = external_node_fs_default().readFileSync(postcssConfigPath, "utf8");
444
+ const typeComment = `/** @type {import('postcss-load-config').Config} */\n`;
445
+ if (content.includes(typeComment)) console.log(`📝 postcss.config.mjs already contains type comment, no changes made.`);
446
+ else {
447
+ content = content.replace("const config = {", `${typeComment}const config = {`);
448
+ external_node_fs_default().writeFileSync(postcssConfigPath, content);
449
+ console.log(`📝 Updated postcss.config.mjs with type comment.`);
450
+ }
451
+ }
452
+ function updateLayoutFile(projectDir) {
453
+ const layoutPath = external_node_path_default().join(projectDir, "src/app/layout.tsx");
454
+ if (!external_node_fs_default().existsSync(layoutPath)) return void console.warn(`⚠️ src/app/layout.tsx not found, skipping update.`);
455
+ let content = external_node_fs_default().readFileSync(layoutPath, "utf8");
456
+ if (content.includes('<html className={cn(fontVariables, "antialiased")} lang="en">')) return void console.log(`📝 src/app/layout.tsx already uses fontVariables in html tag, no changes made.`);
457
+ const newImports = `import type { Metadata } from "next";
458
+
459
+ import { cn } from "@codefast/ui";
460
+
461
+ import { fontVariables } from "@/lib/fonts";
462
+ import "@/app/globals.css";`;
463
+ content = content.replaceAll(/import\s+{[^}]+}\s+from\s+["']next\/font\/google["'];\s*/g, "");
464
+ content = content.replace(/const\s+geistSans\s*=[^;]+;\s*const\s+geistMono\s*=[^;]+;\s*/, "");
465
+ content = content.replace(/import\s+type\s+{[^}]+}\s+from\s+["']next["'];\s*import\s+["']\.\/globals\.css["'];\s*/, newImports);
466
+ const htmlRegex = /<html\s+lang="en"\s*>/;
467
+ const newHtml = '<html className={cn(fontVariables, "antialiased")} lang="en">';
468
+ content = content.replace(htmlRegex, newHtml);
469
+ const bodyRegex = /<body\s+className={`[^`]*`}\s*>/;
470
+ const newBody = "<body>";
471
+ content = content.replace(bodyRegex, newBody);
472
+ try {
473
+ external_node_fs_default().writeFileSync(layoutPath, content);
474
+ console.log(`📝 Updated src/app/layout.tsx to use fontVariables in html tag and removed className from body`);
475
+ } catch (error) {
476
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
477
+ console.warn(`⚠️ Failed to update src/app/layout.tsx: ${errorMessage}`);
478
+ }
479
+ }
338
480
  function updatePackageJson(projectDir) {
339
- const packageJsonPath = external_node_path_namespaceObject.join(projectDir, "package.json");
340
- const packageJson = JSON.parse(external_node_fs_namespaceObject.readFileSync(packageJsonPath, "utf8"));
481
+ const packageJsonPath = external_node_path_default().join(projectDir, "package.json");
482
+ const packageJson = JSON.parse(external_node_fs_default().readFileSync(packageJsonPath, "utf8"));
341
483
  packageJson.scripts = {
342
484
  ...packageJson.scripts,
485
+ "deploy:preview": "vercel deploy --archive=tgz",
486
+ "deploy:prod": "vercel deploy --archive=tgz --prod",
343
487
  format: 'prettier --write "**/*.{js,mjs,jsx,ts,tsx,json,css,md,mdx}"',
344
488
  "format:check": 'prettier --check "**/*.{js,mjs,jsx,ts,tsx,json,css,md,mdx}"',
345
- "lint:check": "next lint --max-warnings 0"
489
+ lint: "next lint --fix --max-warnings 0",
490
+ "lint:check": "next lint --max-warnings 0",
491
+ "package:install": "pnpm install",
492
+ "package:outdated": "pnpm outdated",
493
+ "package:upgrade": "pnpm update --latest",
494
+ preview: "pnpm build && pnpm start"
346
495
  };
347
496
  packageJson["simple-git-hooks"] = {
348
497
  "pre-commit": "pnpm lint-staged",
349
498
  "commit-msg": "pnpm commitlint --edit"
350
499
  };
351
- external_node_fs_namespaceObject.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
352
- console.log("📝 Updated package.json");
500
+ external_node_fs_default().writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
501
+ console.log(`📝 Updated package.json`);
353
502
  }
354
- function createNextProject(projectName) {
355
- console.log("📦 Creating Next.js project...");
356
- const flags = '--ts --tailwind --eslint --app --turbo --use-pnpm --src-dir --turbopack --import-alias "@/*"';
357
- runCommand(`npx create-next-app@latest ${projectName} ${flags}`);
503
+ function createConfigFiles(projectDir) {
504
+ console.log(`\n📝 Creating configuration files...`);
505
+ for (const [category, configs] of Object.entries(configGroups)){
506
+ console.log(`\n📂 Setting up ${category} configurations:`);
507
+ for (const { path: filePath, content, description } of configs){
508
+ const fullPath = external_node_path_default().join(projectDir, filePath);
509
+ const dir = external_node_path_default().dirname(fullPath);
510
+ if (!external_node_fs_default().existsSync(dir)) external_node_fs_default().mkdirSync(dir, {
511
+ recursive: true
512
+ });
513
+ writeConfigFile(fullPath, content);
514
+ if (description) console.log(` ℹ️ ${filePath}: ${description}`);
515
+ }
516
+ }
358
517
  }
359
518
  function installDependencies() {
360
- console.log("📚 Installing dependencies...");
519
+ console.log(`\n📚 Installing dependencies...`);
361
520
  if (MAIN_DEPENDENCIES.length > 0) runCommand(`pnpm add ${MAIN_DEPENDENCIES.join(" ")}`);
362
521
  const allDevDependencies = Object.values(DEV_DEPENDENCIES).flat().join(" ");
363
522
  if (allDevDependencies) runCommand(`pnpm add -D ${allDevDependencies}`);
364
523
  }
365
- function createConfigFiles(projectDir) {
366
- console.log(`\n📝 Creating configuration files...`);
367
- for (const [filePath, content] of Object.entries(configFiles)){
368
- const fullPath = external_node_path_namespaceObject.join(projectDir, filePath);
369
- const dir = external_node_path_namespaceObject.dirname(fullPath);
370
- if (!external_node_fs_namespaceObject.existsSync(dir)) external_node_fs_namespaceObject.mkdirSync(dir, {
371
- recursive: true
524
+ function cleanupPackages() {
525
+ console.log(`\n🧹 Cleaning up unnecessary packages...`);
526
+ const packageJsonPath = external_node_path_namespaceObject.join(process.cwd(), "package.json");
527
+ if (!external_node_fs_namespaceObject.existsSync(packageJsonPath)) return void console.warn(`⚠️ package.json not found, skipping cleanup.`);
528
+ let packageJson;
529
+ try {
530
+ packageJson = JSON.parse(external_node_fs_namespaceObject.readFileSync(packageJsonPath, "utf8"));
531
+ } catch (error) {
532
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
533
+ console.warn(`⚠️ Error reading package.json: ${errorMessage}, skipping cleanup.`);
534
+ return;
535
+ }
536
+ const dependencies = packageJson.dependencies ?? {};
537
+ const devDependencies = packageJson.devDependencies ?? {};
538
+ const packagesToRemove = PACKAGES_TO_REMOVE.filter((pkg)=>pkg in dependencies || pkg in devDependencies);
539
+ if (packagesToRemove.length > 0) runCommand(`pnpm remove ${packagesToRemove.join(" ")}`);
540
+ else console.log(`ℹ️ No unnecessary packages found in package.json to remove.`);
541
+ }
542
+ function checkEnvironment() {
543
+ try {
544
+ (0, external_node_child_process_namespaceObject.execSync)("pnpm --version", {
545
+ stdio: "ignore"
372
546
  });
373
- writeConfigFile(fullPath, content);
547
+ } catch {
548
+ console.error(`❌ PNPM chưa được cài đặt. Vui lòng cài đặt PNPM trước.`);
549
+ process.exit(1);
374
550
  }
375
551
  }
552
+ function createNextProject(projectName) {
553
+ console.log(`📦 Creating Next.js project...`);
554
+ const flags = '--ts --tailwind --eslint --app --turbo --use-pnpm --src-dir --turbopack --import-alias "@/*"';
555
+ runCommand(`npx create-next-app@latest ${projectName} ${flags}`);
556
+ }
376
557
  function createProjectCommand(program) {
377
558
  program.command("create-project [name]").description("Create a new Next.js project with recommended setup").action(async (projectNameArg)=>{
378
559
  try {
379
560
  checkEnvironment();
380
- const projectName = await getValidProjectName(projectNameArg);
381
- console.log(`\n🚀 Starting project creation for ${projectName}...\n`);
382
- const packageJsonExists = external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(process.cwd(), "package.json"));
383
- if (packageJsonExists) console.log(`📦 A package.json file has been detected. Skipping the Next.js project creation step.`);
384
- else {
561
+ const { projectName, packageJsonExists } = await checkExistingProject(projectNameArg);
562
+ if (!packageJsonExists) {
563
+ console.log(`\n🚀 Starting project creation for ${projectName}...\n`);
385
564
  createNextProject(projectName);
386
565
  process.chdir(projectName);
387
566
  console.log(`📂 Moved to ${projectName}`);
388
567
  }
568
+ updateLayoutFile(process.cwd());
389
569
  updatePostcssConfig(process.cwd());
390
570
  installDependencies();
571
+ cleanupPackages();
391
572
  createConfigFiles(process.cwd());
392
573
  updatePackageJson(process.cwd());
393
574
  console.log(`\n🔄 Activating git hooks...`);
394
575
  runCommand("pnpm simple-git-hooks");
395
576
  console.log(`\n📝 Formatting files with Prettier...`);
396
577
  runCommand("pnpm format");
397
- console.log(`\n✅ Project created successfully!`);
578
+ console.log(`\n✅ Project ${packageJsonExists ? "configured" : "created"} successfully!`);
398
579
  console.log(`- Project: ${projectName}`);
399
580
  console.log("- Next.js with TypeScript");
400
581
  console.log("- TailwindCSS and @codefast/ui");
401
582
  console.log("- ESLint, Prettier, Commitlint, and Git Hooks");
402
583
  console.log("- Lint-staged for pre-commit checks");
403
- console.log(`\n📁 Project directory: ${external_node_path_namespaceObject.resolve(process.cwd())}`);
584
+ console.log(`\n📁 Project directory: ${process.cwd()}`);
404
585
  console.log(`\n🚀 To start development:`);
405
586
  console.log(`cd ${projectName} && pnpm dev`);
406
- rl.close();
407
587
  } catch (error) {
408
588
  const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
409
589
  console.error(`\n❌ An error occurred: ${errorMessage}`);
410
- rl.close();
411
590
  process.exit(1);
591
+ } finally{
592
+ rl.close();
412
593
  }
413
594
  });
414
595
  }