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

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @codefast/cli
2
2
 
3
+ ## 0.3.1-canary.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [`ed2ba2c`](https://github.com/codefastlabs/codefast/commit/ed2ba2cd81cfbf5764fe4631b9de9a5dcaa6ddfa) Thanks [@thevuong](https://github.com/thevuong)! - feat(cli): add cleanup step to remove unnecessary packages
8
+
9
+ - [`ee7c42f`](https://github.com/codefastlabs/codefast/commit/ee7c42f2ad157288c11e2cfbc2d2dbb52be8a6b5) Thanks [@thevuong](https://github.com/thevuong)! - feat(cli): update layout.tsx with fontVariables support
10
+
11
+ - [`53b416f`](https://github.com/codefastlabs/codefast/commit/53b416f7cb45c7d6d5834aa853c59c38ca1fbba8) Thanks [@thevuong](https://github.com/thevuong)! - feat(cli): modularize project creation functionalities
12
+
13
+ - [`24a767b`](https://github.com/codefastlabs/codefast/commit/24a767b8be3bb0cbe63a3ab3b13ab64130341218) Thanks [@thevuong](https://github.com/thevuong)! - refactor(cli): improve config file handling in project setup
14
+
3
15
  ## 0.3.1-canary.5
4
16
 
5
17
  ### Patch Changes
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,36 +94,67 @@ 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": `/**
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
+ ]
149
+ };
150
+ const PACKAGES_TO_REMOVE = [
151
+ "@eslint/eslintrc"
152
+ ];
153
+ const configGroups = {
154
+ linting: [
155
+ {
156
+ path: "commitlint.config.mjs",
157
+ content: `/**
150
158
  * @see https://commitlint.js.org/reference/configuration.html
151
159
  * @type {import('@commitlint/types').UserConfig}
152
160
  */
@@ -156,24 +164,25 @@ const config = {
156
164
  "body-max-line-length": [2, "always", 350],
157
165
  },
158
166
  };
159
-
160
167
  export default config;
161
168
  `,
162
- "prettier.config.mjs": `/**
163
- * @see https://prettier.io/docs/en/configuration.html
164
- * @type {import('prettier').Config}
169
+ description: "Commitlint configuration"
170
+ },
171
+ {
172
+ path: "eslint.config.mjs",
173
+ content: `import { config } from "@codefast/eslint-config/next";
174
+ /**
175
+ * @see https://eslint.org/docs/latest/use/configure/configuration-files
176
+ * @type {import('eslint').Linter.Config[]}
165
177
  */
166
- const config = {
167
- plugins: ["prettier-plugin-packagejson", "prettier-plugin-tailwindcss"],
168
- printWidth: 100,
169
- tailwindAttributes: ["classNames"],
170
- tailwindFunctions: ["tv"],
171
- };
172
-
173
- export default config;
178
+ const eslintConfig = [...config];
179
+ export default eslintConfig;
174
180
  `,
175
- "lint-staged.config.mjs": `import { relative } from "node:path";
176
-
181
+ description: "ESLint configuration for the project"
182
+ },
183
+ {
184
+ path: "lint-staged.config.mjs",
185
+ content: `import { relative } from "node:path";
177
186
  /**
178
187
  * Builds an ESLint command with the given filenames.
179
188
  *
@@ -185,7 +194,6 @@ function buildEslintCommand(filenames) {
185
194
  return \`next lint --fix --max-warnings 0 --file \${filenames.map((file) => relative(import.meta.dirname, file)).
186
195
  join(" --file ")}\`;
187
196
  }
188
-
189
197
  /**
190
198
  * @see https://www.npmjs.com/package/lint-staged
191
199
  * @type {import('lint-staged').Configuration}
@@ -194,138 +202,117 @@ const config = {
194
202
  "*.{js,mjs,jsx,ts,tsx}": [buildEslintCommand, "prettier --list-different --write"],
195
203
  "*.{json,css,md,mdx}": ["prettier --list-different --write"],
196
204
  };
197
-
198
205
  export default config;
199
206
  `,
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[]}
207
+ description: "Configure lint-staged for pre-commit checks"
208
+ }
209
+ ],
210
+ formatting: [
211
+ {
212
+ path: "prettier.config.mjs",
213
+ content: `/**
214
+ * @see https://prettier.io/docs/en/configuration.html
215
+ * @type {import('prettier').Config}
204
216
  */
205
- const eslintConfig = [...config];
206
-
207
- export default eslintConfig;
217
+ const config = {
218
+ plugins: ["prettier-plugin-packagejson", "prettier-plugin-tailwindcss"],
219
+ printWidth: 100,
220
+ tailwindAttributes: ["classNames"],
221
+ tailwindFunctions: ["tv"],
222
+ };
223
+ export default config;
208
224
  `,
209
- "src/lib/fonts.ts": `import { cn } from "@codefast/ui";
225
+ description: "Prettier configuration for source code formatting"
226
+ }
227
+ ],
228
+ styles: [
229
+ {
230
+ path: "src/lib/fonts.ts",
231
+ content: `import { cn } from "@codefast/ui";
210
232
  import { Geist, Geist_Mono as GeistMono } from "next/font/google";
211
-
212
233
  const fontGeistSans = Geist({
213
234
  subsets: ["latin"],
214
235
  variable: "--font-geist-sans",
215
236
  });
216
-
217
237
  const fontGeistMono = GeistMono({
218
238
  subsets: ["latin"],
219
239
  variable: "--font-geist-mono",
220
240
  });
221
-
222
241
  export const fontVariables = cn(fontGeistSans.variable, fontGeistMono.variable);
223
242
  `,
224
- "src/app/globals.css": `@import "tailwindcss";
225
-
243
+ description: "Font configuration for the application"
244
+ },
245
+ {
246
+ path: "src/app/globals.css",
247
+ content: `@import "tailwindcss";
226
248
  @import "@codefast/ui/styles.css";
227
-
228
249
  @source '../../node_modules/@codefast/ui';
229
-
230
250
  @custom-variant dark (&:where(.dark, .dark *));
231
-
232
- @theme inline {
233
- --font-sans: var(--font-geist-sans);
234
- --font-mono: var(--font-geist-mono);
235
- }
236
-
237
251
  @layer base {
238
252
  :root {
253
+ --font-sans: var(--font-geist-sans);
254
+ --font-mono: var(--font-geist-mono);
239
255
  --input: var(--color-neutral-200);
240
256
  --border: var(--color-neutral-200);
241
-
242
257
  --ring: var(--color-neutral-400);
243
-
244
258
  --background: var(--color-white);
245
259
  --foreground: var(--color-neutral-950);
246
-
247
260
  --primary: var(--color-sky-500);
248
261
  --primary-foreground: var(--color-neutral-50);
249
-
250
262
  --secondary: var(--color-neutral-100);
251
263
  --secondary-foreground: var(--color-neutral-900);
252
-
253
264
  --destructive: var(--color-red-600);
254
-
255
265
  --accent: var(--color-neutral-100);
256
266
  --accent-foreground: var(--color-neutral-900);
257
-
258
267
  --muted: var(--color-neutral-100);
259
268
  --muted-foreground: var(--color-neutral-500);
260
-
261
269
  --popover: var(--color-white);
262
270
  --popover-foreground: var(--color-neutral-950);
263
271
  --popover-overlay: --alpha(var(--color-neutral-900) / 20%);
264
-
265
272
  --card: var(--color-white);
266
273
  --card-foreground: var(--color-neutral-950);
267
-
268
274
  --sidebar: var(--color-neutral-50);
269
275
  --sidebar-foreground: var(--color-neutral-950);
270
-
271
276
  --sidebar-primary: var(--color-sky-500);
272
277
  --sidebar-primary-foreground: var(--color-neutral-50);
273
-
274
278
  --sidebar-accent: var(--color-neutral-100);
275
279
  --sidebar-accent-foreground: var(--color-neutral-900);
276
-
277
280
  --sidebar-border: var(--color-neutral-200);
278
281
  --sidebar-ring: var(--color-neutral-400);
279
-
280
282
  --chart-1: var(--color-orange-600);
281
283
  --chart-2: var(--color-teal-600);
282
284
  --chart-3: var(--color-cyan-600);
283
285
  --chart-4: var(--color-amber-600);
284
286
  --chart-5: var(--color-amber-500);
285
287
  }
286
-
287
288
  @variant dark {
288
289
  --input: var(--color-neutral-700);
289
290
  --border: --alpha(var(--color-neutral-700) / 50%);
290
-
291
291
  --ring: var(--color-neutral-500);
292
-
293
292
  --background: var(--color-neutral-950);
294
293
  --foreground: var(--color-neutral-50);
295
-
296
294
  --primary: var(--color-sky-700);
297
295
  --primary-foreground: var(--color-neutral-50);
298
-
299
296
  --secondary: var(--color-neutral-800);
300
297
  --secondary-foreground: var(--color-neutral-50);
301
-
302
298
  --destructive: var(--color-red-400);
303
-
304
299
  --accent: var(--color-neutral-700);
305
300
  --accent-foreground: var(--color-neutral-50);
306
-
307
301
  --muted: var(--color-neutral-800);
308
302
  --muted-foreground: var(--color-neutral-400);
309
-
310
303
  --popover: var(--color-neutral-800);
311
304
  --popover-foreground: var(--color-neutral-50);
312
305
  --popover-overlay: --alpha(var(--color-neutral-900) / 80%);
313
-
314
306
  --card: var(--color-neutral-900);
315
307
  --card-foreground: var(--color-neutral-50);
316
-
317
308
  --sidebar: var(--color-neutral-900);
318
309
  --sidebar-foreground: var(--color-neutral-50);
319
-
320
310
  --sidebar-primary: var(--color-sky-700);
321
311
  --sidebar-primary-foreground: var(--color-neutral-50);
322
-
323
312
  --sidebar-accent: var(--color-neutral-800);
324
313
  --sidebar-accent-foreground: var(--color-neutral-50);
325
-
326
314
  --sidebar-border: var(--color-neutral-800);
327
315
  --sidebar-ring: var(--color-neutral-600);
328
-
329
316
  --chart-1: var(--color-blue-700);
330
317
  --chart-2: var(--color-emerald-500);
331
318
  --chart-3: var(--color-amber-500);
@@ -333,11 +320,54 @@ export const fontVariables = cn(fontGeistSans.variable, fontGeistMono.variable);
333
320
  --chart-5: var(--color-rose-500);
334
321
  }
335
322
  }
336
- `
323
+ `,
324
+ description: "Global CSS variables for the application"
325
+ }
326
+ ]
337
327
  };
328
+ function updatePostcssConfig(projectDir) {
329
+ const postcssConfigPath = external_node_path_default().join(projectDir, "postcss.config.mjs");
330
+ if (!external_node_fs_default().existsSync(postcssConfigPath)) return void console.warn(`⚠️ postcss.config.mjs not found, skipping update.`);
331
+ let content = external_node_fs_default().readFileSync(postcssConfigPath, "utf8");
332
+ const typeComment = `/** @type {import('postcss-load-config').Config} */\n`;
333
+ if (content.includes(typeComment)) console.log(`📝 postcss.config.mjs already contains type comment, no changes made.`);
334
+ else {
335
+ content = content.replace("const config = {", `${typeComment}const config = {`);
336
+ external_node_fs_default().writeFileSync(postcssConfigPath, content);
337
+ console.log(`📝 Updated postcss.config.mjs with type comment.`);
338
+ }
339
+ }
340
+ function updateLayoutFile(projectDir) {
341
+ const layoutPath = external_node_path_default().join(projectDir, "src/app/layout.tsx");
342
+ if (!external_node_fs_default().existsSync(layoutPath)) return void console.warn(`⚠️ src/app/layout.tsx not found, skipping update.`);
343
+ let content = external_node_fs_default().readFileSync(layoutPath, "utf8");
344
+ 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.`);
345
+ const newImports = `import type { Metadata } from "next";
346
+
347
+ import { cn } from "@codefast/ui";
348
+
349
+ import { fontVariables } from "@/lib/fonts";
350
+ import "@/app/globals.css";`;
351
+ content = content.replaceAll(/import\s+{[^}]+}\s+from\s+["']next\/font\/google["'];\s*/g, "");
352
+ content = content.replace(/const\s+geistSans\s*=[^;]+;\s*const\s+geistMono\s*=[^;]+;\s*/, "");
353
+ content = content.replace(/import\s+type\s+{[^}]+}\s+from\s+["']next["'];\s*import\s+["']\.\/globals\.css["'];\s*/, newImports);
354
+ const htmlRegex = /<html\s+lang="en"\s*>/;
355
+ const newHtml = '<html className={cn(fontVariables, "antialiased")} lang="en">';
356
+ content = content.replace(htmlRegex, newHtml);
357
+ const bodyRegex = /<body\s+className={`[^`]*`}\s*>/;
358
+ const newBody = "<body>";
359
+ content = content.replace(bodyRegex, newBody);
360
+ try {
361
+ external_node_fs_default().writeFileSync(layoutPath, content);
362
+ console.log(`📝 Updated src/app/layout.tsx to use fontVariables in html tag and removed className from body`);
363
+ } catch (error) {
364
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
365
+ console.warn(`⚠️ Failed to update src/app/layout.tsx: ${errorMessage}`);
366
+ }
367
+ }
338
368
  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"));
369
+ const packageJsonPath = external_node_path_default().join(projectDir, "package.json");
370
+ const packageJson = JSON.parse(external_node_fs_default().readFileSync(packageJsonPath, "utf8"));
341
371
  packageJson.scripts = {
342
372
  ...packageJson.scripts,
343
373
  format: 'prettier --write "**/*.{js,mjs,jsx,ts,tsx,json,css,md,mdx}"',
@@ -348,67 +378,99 @@ function updatePackageJson(projectDir) {
348
378
  "pre-commit": "pnpm lint-staged",
349
379
  "commit-msg": "pnpm commitlint --edit"
350
380
  };
351
- external_node_fs_namespaceObject.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
352
- console.log("📝 Updated package.json");
381
+ external_node_fs_default().writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
382
+ console.log(`📝 Updated package.json`);
353
383
  }
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}`);
384
+ function createConfigFiles(projectDir) {
385
+ console.log(`\n📝 Creating configuration files...`);
386
+ for (const [category, configs] of Object.entries(configGroups)){
387
+ console.log(`\n📂 Setting up ${category} configurations:`);
388
+ for (const { path: filePath, content, description } of configs){
389
+ const fullPath = external_node_path_default().join(projectDir, filePath);
390
+ const dir = external_node_path_default().dirname(fullPath);
391
+ if (!external_node_fs_default().existsSync(dir)) external_node_fs_default().mkdirSync(dir, {
392
+ recursive: true
393
+ });
394
+ writeConfigFile(fullPath, content);
395
+ if (description) console.log(` ℹ️ ${filePath}: ${description}`);
396
+ }
397
+ }
358
398
  }
359
399
  function installDependencies() {
360
- console.log("📚 Installing dependencies...");
400
+ console.log(`\n📚 Installing dependencies...`);
361
401
  if (MAIN_DEPENDENCIES.length > 0) runCommand(`pnpm add ${MAIN_DEPENDENCIES.join(" ")}`);
362
402
  const allDevDependencies = Object.values(DEV_DEPENDENCIES).flat().join(" ");
363
403
  if (allDevDependencies) runCommand(`pnpm add -D ${allDevDependencies}`);
364
404
  }
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
405
+ function cleanupPackages() {
406
+ console.log(`\n🧹 Cleaning up unnecessary packages...`);
407
+ const packageJsonPath = external_node_path_namespaceObject.join(process.cwd(), "package.json");
408
+ if (!external_node_fs_namespaceObject.existsSync(packageJsonPath)) return void console.warn(`⚠️ package.json not found, skipping cleanup.`);
409
+ let packageJson;
410
+ try {
411
+ packageJson = JSON.parse(external_node_fs_namespaceObject.readFileSync(packageJsonPath, "utf8"));
412
+ } catch (error) {
413
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
414
+ console.warn(`⚠️ Error reading package.json: ${errorMessage}, skipping cleanup.`);
415
+ return;
416
+ }
417
+ const dependencies = packageJson.dependencies ?? {};
418
+ const devDependencies = packageJson.devDependencies ?? {};
419
+ const packagesToRemove = PACKAGES_TO_REMOVE.filter((pkg)=>pkg in dependencies || pkg in devDependencies);
420
+ if (packagesToRemove.length > 0) runCommand(`pnpm remove ${packagesToRemove.join(" ")}`);
421
+ else console.log(`ℹ️ No unnecessary packages found in package.json to remove.`);
422
+ }
423
+ function checkEnvironment() {
424
+ try {
425
+ (0, external_node_child_process_namespaceObject.execSync)("pnpm --version", {
426
+ stdio: "ignore"
372
427
  });
373
- writeConfigFile(fullPath, content);
428
+ } catch {
429
+ console.error(`❌ PNPM chưa được cài đặt. Vui lòng cài đặt PNPM trước.`);
430
+ process.exit(1);
374
431
  }
375
432
  }
433
+ function createNextProject(projectName) {
434
+ console.log(`📦 Creating Next.js project...`);
435
+ const flags = '--ts --tailwind --eslint --app --turbo --use-pnpm --src-dir --turbopack --import-alias "@/*"';
436
+ runCommand(`npx create-next-app@latest ${projectName} ${flags}`);
437
+ }
376
438
  function createProjectCommand(program) {
377
439
  program.command("create-project [name]").description("Create a new Next.js project with recommended setup").action(async (projectNameArg)=>{
378
440
  try {
379
441
  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 {
442
+ const { projectName, packageJsonExists } = await checkExistingProject(projectNameArg);
443
+ if (!packageJsonExists) {
444
+ console.log(`\n🚀 Starting project creation for ${projectName}...\n`);
385
445
  createNextProject(projectName);
386
446
  process.chdir(projectName);
387
447
  console.log(`📂 Moved to ${projectName}`);
388
448
  }
449
+ updateLayoutFile(process.cwd());
389
450
  updatePostcssConfig(process.cwd());
390
451
  installDependencies();
452
+ cleanupPackages();
391
453
  createConfigFiles(process.cwd());
392
454
  updatePackageJson(process.cwd());
393
455
  console.log(`\n🔄 Activating git hooks...`);
394
456
  runCommand("pnpm simple-git-hooks");
395
457
  console.log(`\n📝 Formatting files with Prettier...`);
396
458
  runCommand("pnpm format");
397
- console.log(`\n✅ Project created successfully!`);
459
+ console.log(`\n✅ Project ${packageJsonExists ? "configured" : "created"} successfully!`);
398
460
  console.log(`- Project: ${projectName}`);
399
461
  console.log("- Next.js with TypeScript");
400
462
  console.log("- TailwindCSS and @codefast/ui");
401
463
  console.log("- ESLint, Prettier, Commitlint, and Git Hooks");
402
464
  console.log("- Lint-staged for pre-commit checks");
403
- console.log(`\n📁 Project directory: ${external_node_path_namespaceObject.resolve(process.cwd())}`);
465
+ console.log(`\n📁 Project directory: ${process.cwd()}`);
404
466
  console.log(`\n🚀 To start development:`);
405
467
  console.log(`cd ${projectName} && pnpm dev`);
406
- rl.close();
407
468
  } catch (error) {
408
469
  const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
409
470
  console.error(`\n❌ An error occurred: ${errorMessage}`);
410
- rl.close();
411
471
  process.exit(1);
472
+ } finally{
473
+ rl.close();
412
474
  }
413
475
  });
414
476
  }