@codefast/cli 0.3.1-canary.11 → 0.3.1-canary.12

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.12
4
+
5
+ ### Patch Changes
6
+
7
+ - [`0841266`](https://github.com/codefastlabs/codefast/commit/0841266536570b017099fd32b6f0b732cc562932) Thanks [@thevuong](https://github.com/thevuong)! - fix(cli): remove redundant default action from CLI
8
+
9
+ - [`5d33972`](https://github.com/codefastlabs/codefast/commit/5d339720048479b37ce3ded253d5182364df6762) Thanks [@thevuong](https://github.com/thevuong)! - refactor(cli): update styles and constants for project creation
10
+
11
+ - [`109a2a5`](https://github.com/codefastlabs/codefast/commit/109a2a5cf4a205d4c5ceb5c3ac7afa95ca69a51d) Thanks [@thevuong](https://github.com/thevuong)! - feat(cli): improve ReactNode handling and RootLayout typings
12
+
13
+ - [`e873ddc`](https://github.com/codefastlabs/codefast/commit/e873ddcdabb110655e39aa7a69a0850c9050186e) Thanks [@thevuong](https://github.com/thevuong)! - refactor(cli): centralize file operations and error handling
14
+
3
15
  ## 0.3.1-canary.11
4
16
 
5
17
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -49,9 +49,38 @@ function runCommand(command) {
49
49
  stdio: "inherit"
50
50
  });
51
51
  }
52
- function writeConfigFile(filePath, content) {
53
- external_node_fs_default().writeFileSync(filePath, content);
54
- console.log(`šŸ“ Created ${filePath}`);
52
+ const FileUtils = {
53
+ exists (filePath) {
54
+ return external_node_fs_default().existsSync(filePath);
55
+ },
56
+ readFile (filePath) {
57
+ try {
58
+ return external_node_fs_default().readFileSync(filePath, "utf8");
59
+ } catch (error) {
60
+ throw new Error(`Failed to read file ${filePath}: ${getErrorMessage(error)}`);
61
+ }
62
+ },
63
+ writeFile (filePath, content) {
64
+ try {
65
+ external_node_fs_default().writeFileSync(filePath, content);
66
+ } catch (error) {
67
+ throw new Error(`Failed to write to file ${filePath}: ${getErrorMessage(error)}`);
68
+ }
69
+ },
70
+ writeConfigFile (filePath, content) {
71
+ external_node_fs_default().writeFileSync(filePath, content);
72
+ console.log(`šŸ“ Created ${filePath}`);
73
+ },
74
+ validatePath (filePath, filename) {
75
+ if (!this.exists(filePath)) {
76
+ console.warn(`āš ļø ${filename} not found, skipping update.`);
77
+ return false;
78
+ }
79
+ return true;
80
+ }
81
+ };
82
+ function getErrorMessage(error) {
83
+ return error instanceof Error ? error.message : "Unknown error occurred";
55
84
  }
56
85
  function validateProjectName(name) {
57
86
  if (!name || /^\s*$/.test(name)) return {
@@ -290,7 +319,6 @@ yarn-error.log*
290
319
  package-lock.json
291
320
  yarn.lock
292
321
  pnpm-lock.yaml
293
- src/app/globals.css
294
322
  `,
295
323
  description: ""
296
324
  }
@@ -316,6 +344,7 @@ export default config;
316
344
  {
317
345
  path: "eslint.config.mjs",
318
346
  content: `import { config } from "@codefast/eslint-config/next";
347
+
319
348
  /**
320
349
  * @see https://eslint.org/docs/latest/use/configure/configuration-files
321
350
  * @type {import('eslint').Linter.Config[]}
@@ -390,6 +419,7 @@ export const fontVariables = cn(fontGeistSans.variable, fontGeistMono.variable);
390
419
  :root {
391
420
  --font-sans: var(--font-geist-sans);
392
421
  --font-mono: var(--font-geist-mono);
422
+
393
423
  --input: var(--color-neutral-200);
394
424
  --border: var(--color-neutral-200);
395
425
  --ring: var(--color-neutral-400);
@@ -423,6 +453,7 @@ export const fontVariables = cn(fontGeistSans.variable, fontGeistMono.variable);
423
453
  --chart-4: var(--color-amber-600);
424
454
  --chart-5: var(--color-amber-500);
425
455
  }
456
+
426
457
  .dark {
427
458
  --input: var(--color-neutral-700);
428
459
  --border: --alpha(var(--color-neutral-700) / 50%);
@@ -463,87 +494,86 @@ export const fontVariables = cn(fontGeistSans.variable, fontGeistMono.variable);
463
494
  }
464
495
  ]
465
496
  };
497
+ function updateFileIfNeeded(filePath, filename, checkFn, updateFn, successMessage, alreadyUpdatedMessage) {
498
+ if (!FileUtils.validatePath(filePath, filename)) return;
499
+ const content = FileUtils.readFile(filePath);
500
+ if (checkFn(content)) return void console.log(`šŸ“ ${alreadyUpdatedMessage}`);
501
+ try {
502
+ const updatedContent = updateFn(content);
503
+ FileUtils.writeFile(filePath, updatedContent);
504
+ console.log(`šŸ“ ${successMessage}`);
505
+ } catch (error) {
506
+ console.warn(`āš ļø Failed to update ${filename}: ${getErrorMessage(error)}`);
507
+ }
508
+ }
466
509
  function updatePostcssConfig(projectDir) {
467
510
  const postcssConfigPath = external_node_path_default().join(projectDir, "postcss.config.mjs");
468
- if (!external_node_fs_default().existsSync(postcssConfigPath)) return void console.warn(`āš ļø postcss.config.mjs not found, skipping update.`);
469
- let content = external_node_fs_default().readFileSync(postcssConfigPath, "utf8");
470
511
  const typeComment = `/** @type {import('postcss-load-config').Config} */\n`;
471
- if (content.includes(typeComment)) console.log(`šŸ“ postcss.config.mjs already contains type comment, no changes made.`);
472
- else {
473
- content = content.replace("const config = {", `${typeComment}const config = {`);
474
- external_node_fs_default().writeFileSync(postcssConfigPath, content);
475
- console.log(`šŸ“ Updated postcss.config.mjs with type comment.`);
476
- }
512
+ updateFileIfNeeded(postcssConfigPath, "postcss.config.mjs", (content)=>content.includes(typeComment), (content)=>content.replace("const config = {", `${typeComment}const config = {`), "Updated postcss.config.mjs with type comment.", "postcss.config.mjs already contains type comment, no changes made.");
477
513
  }
478
514
  function updateLayoutFile(projectDir) {
479
515
  const layoutPath = external_node_path_default().join(projectDir, "src/app/layout.tsx");
480
- if (!external_node_fs_default().existsSync(layoutPath)) return void console.warn(`āš ļø src/app/layout.tsx not found, skipping update.`);
481
- let content = external_node_fs_default().readFileSync(layoutPath, "utf8");
482
- 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.`);
483
- const newImports = `import type { Metadata } from "next";
484
-
516
+ const htmlTarget = '<html className={cn(fontVariables, "antialiased")} lang="en">';
517
+ updateFileIfNeeded(layoutPath, "src/app/layout.tsx", (content)=>content.includes(htmlTarget), (content)=>{
518
+ const newImports = `import type { Metadata } from "next";
519
+ import type { JSX, ReactNode } from "react";
485
520
  import { cn } from "@codefast/ui";
486
-
487
521
  import { fontVariables } from "@/lib/fonts";
488
522
  import "@/app/globals.css";`;
489
- content = content.replaceAll(/import\s+{[^}]+}\s+from\s+["']next\/font\/google["'];\s*/g, "");
490
- content = content.replace(/const\s+geistSans\s*=[^;]+;\s*const\s+geistMono\s*=[^;]+;\s*/, "");
491
- content = content.replace(/import\s+type\s+{[^}]+}\s+from\s+["']next["'];\s*import\s+["']\.\/globals\.css["'];\s*/, newImports);
492
- const htmlRegex = /<html\s+lang="en"\s*>/;
493
- const newHtml = '<html className={cn(fontVariables, "antialiased")} lang="en">';
494
- content = content.replace(htmlRegex, newHtml);
495
- const bodyRegex = /<body\s+className={`[^`]*`}\s*>/;
496
- const newBody = "<body>";
497
- content = content.replace(bodyRegex, newBody);
498
- try {
499
- external_node_fs_default().writeFileSync(layoutPath, content);
500
- console.log(`šŸ“ Updated src/app/layout.tsx to use fontVariables in html tag and removed className from body`);
501
- } catch (error) {
502
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
503
- console.warn(`āš ļø Failed to update src/app/layout.tsx: ${errorMessage}`);
504
- }
523
+ let result = content;
524
+ result = result.replaceAll(/import\s+{[^}]+}\s+from\s+["']next\/font\/google["'];\s*/g, "");
525
+ result = result.replace(/const\s+geistSans\s*=[^;]+;\s*const\s+geistMono\s*=[^;]+;\s*/, "");
526
+ result = result.replace(/import\s+type\s+{[^}]+}\s+from\s+["']next["'];\s*import\s+["']\.\/globals\.css["'];\s*/, newImports);
527
+ result = result.replace(/export default function RootLayout\({\s*children,\s*}\s*:\s*Readonly<{\s*children:\s*React\.ReactNode;\s*}>\)\s*{/, "export default function RootLayout({ children }: Readonly<{ children: ReactNode }>): JSX.Element {");
528
+ result = result.replace(/<html\s+lang="en"\s*>/, htmlTarget);
529
+ result = result.replace(/<body\s+className={`[^`]*`}\s*>/, "<body>");
530
+ return result;
531
+ }, "Updated src/app/layout.tsx to use fontVariables in html tag, removed className from body, modified ReactNode import, and added return type to RootLayout", "src/app/layout.tsx already uses fontVariables in html tag, no changes made.");
505
532
  }
506
533
  function updatePackageJson(projectDir) {
507
534
  const packageJsonPath = external_node_path_default().join(projectDir, "package.json");
508
- const packageJson = JSON.parse(external_node_fs_default().readFileSync(packageJsonPath, "utf8"));
509
- packageJson.scripts = {
510
- ...packageJson.scripts,
511
- "deploy:preview": "vercel deploy --archive=tgz",
512
- "deploy:prod": "vercel deploy --archive=tgz --prod",
513
- format: "prettier --write --ignore-unknown .",
514
- "format:check": "prettier --check --ignore-unknown .",
515
- lint: "next lint --fix --max-warnings 0",
516
- "lint:check": "next lint --max-warnings 0",
517
- "package:install": "pnpm install",
518
- "package:outdated": "pnpm outdated",
519
- "package:upgrade": "pnpm update --latest",
520
- preview: "pnpm build && pnpm start"
521
- };
522
- packageJson["simple-git-hooks"] = {
523
- "pre-commit": "pnpm lint-staged",
524
- "commit-msg": "pnpm commitlint --edit"
525
- };
526
- external_node_fs_default().writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
527
- console.log(`šŸ“ Updated package.json`);
535
+ if (!FileUtils.validatePath(packageJsonPath, "package.json")) return;
536
+ try {
537
+ const packageJson = JSON.parse(FileUtils.readFile(packageJsonPath));
538
+ packageJson.scripts = {
539
+ ...packageJson.scripts,
540
+ "deploy:preview": "vercel deploy --archive=tgz",
541
+ "deploy:prod": "vercel deploy --archive=tgz --prod",
542
+ format: "prettier --write --ignore-unknown .",
543
+ "format:check": "prettier --check --ignore-unknown .",
544
+ lint: "next lint --fix --max-warnings 0",
545
+ "lint:check": "next lint --max-warnings 0",
546
+ "package:install": "pnpm install",
547
+ "package:outdated": "pnpm outdated",
548
+ "package:upgrade": "pnpm update --latest",
549
+ preview: "pnpm build && pnpm start"
550
+ };
551
+ packageJson["simple-git-hooks"] = {
552
+ "pre-commit": "pnpm lint-staged",
553
+ "commit-msg": "pnpm commitlint --edit"
554
+ };
555
+ FileUtils.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
556
+ console.log(`šŸ“ Updated package.json`);
557
+ } catch (error) {
558
+ console.warn(`āš ļø Failed to update package.json: ${getErrorMessage(error)}`);
559
+ }
528
560
  }
529
561
  function updateNextConfig(projectDir) {
530
562
  const nextConfigPath = external_node_path_default().join(projectDir, "next.config.ts");
531
- if (!external_node_fs_default().existsSync(nextConfigPath)) return void console.warn(`āš ļø next.config.ts not found, skipping update.`);
532
- let content = external_node_fs_default().readFileSync(nextConfigPath, "utf8");
533
- if (content.includes('optimizePackageImports: ["@codefast/ui"]')) return void console.log(`šŸ“ next.config.ts already contains optimizePackageImports configuration, no changes made.`);
534
- const configPattern = /const nextConfig: NextConfig = \{/;
535
- const experimentalConfig = `const nextConfig: NextConfig = {
563
+ const optimizePackagesConfig = 'optimizePackageImports: ["@codefast/ui"]';
564
+ updateFileIfNeeded(nextConfigPath, "next.config.ts", (content)=>content.includes(optimizePackagesConfig), (content)=>content.replace(/const nextConfig: NextConfig = \{/, `const nextConfig: NextConfig = {
536
565
  experimental: {
537
566
  optimizePackageImports: ["@codefast/ui"],
538
- },`;
539
- content = content.replace(configPattern, experimentalConfig);
540
- try {
541
- external_node_fs_default().writeFileSync(nextConfigPath, content);
542
- console.log(`šŸ“ Updated next.config.ts with optimizePackageImports configuration.`);
543
- } catch (error) {
544
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
545
- console.warn(`āš ļø Failed to update next.config.ts: ${errorMessage}`);
546
- }
567
+ },`), "Updated next.config.ts with optimizePackageImports configuration.", "next.config.ts already contains optimizePackageImports configuration, no changes made.");
568
+ }
569
+ function updatePageFile(projectDir) {
570
+ const pagePath = external_node_path_default().join(projectDir, "src/app/page.tsx");
571
+ updateFileIfNeeded(pagePath, "src/app/page.tsx", (content)=>content.includes("): JSX.Element"), (content)=>{
572
+ let result = content;
573
+ if (!result.includes("import type { JSX }")) result = result.includes("import type {") ? result.replace(/import type {(?<types>[^}]+)} from "react";/, 'import type {$1, JSX } from "react";') : result.includes("import {") ? result.replace(/import {/, 'import type { JSX } from "react";\n\nimport {') : `import type { JSX } from "react";\n\n${result}`;
574
+ result = result.replace(/export default function Home\(\) {/, "export default function Home(): JSX.Element {");
575
+ return result;
576
+ }, "Updated src/app/page.tsx to add JSX.Element return type to Home component", "src/app/page.tsx already has JSX.Element return type, no changes made.");
547
577
  }
548
578
  function createConfigFiles(projectDir) {
549
579
  console.log(`\nšŸ“ Creating configuration files...`);
@@ -552,10 +582,10 @@ function createConfigFiles(projectDir) {
552
582
  for (const { path: filePath, content, description } of configs){
553
583
  const fullPath = external_node_path_default().join(projectDir, filePath);
554
584
  const dir = external_node_path_default().dirname(fullPath);
555
- if (!external_node_fs_default().existsSync(dir)) external_node_fs_default().mkdirSync(dir, {
585
+ if (!FileUtils.exists(dir)) external_node_fs_default().mkdirSync(dir, {
556
586
  recursive: true
557
587
  });
558
- writeConfigFile(fullPath, content);
588
+ FileUtils.writeConfigFile(fullPath, content);
559
589
  if (description) console.log(` ā„¹ļø ${filePath}: ${description}`);
560
590
  }
561
591
  }
@@ -574,8 +604,7 @@ function cleanupPackages() {
574
604
  try {
575
605
  packageJson = JSON.parse(external_node_fs_namespaceObject.readFileSync(packageJsonPath, "utf8"));
576
606
  } catch (error) {
577
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
578
- console.warn(`āš ļø Error reading package.json: ${errorMessage}, skipping cleanup.`);
607
+ console.warn(`āš ļø Error reading package.json: ${getErrorMessage(error)}, skipping cleanup.`);
579
608
  return;
580
609
  }
581
610
  const dependencies = packageJson.dependencies ?? {};
@@ -611,6 +640,7 @@ function createProjectCommand(program) {
611
640
  console.log(`šŸ“‚ Moved to ${projectName}`);
612
641
  }
613
642
  updateLayoutFile(process.cwd());
643
+ updatePageFile(process.cwd());
614
644
  updatePostcssConfig(process.cwd());
615
645
  installDependencies();
616
646
  cleanupPackages();
@@ -621,6 +651,8 @@ function createProjectCommand(program) {
621
651
  runCommand("pnpm simple-git-hooks");
622
652
  console.log(`\nšŸ“ Formatting files with Prettier...`);
623
653
  runCommand("pnpm format");
654
+ console.log(`\nšŸ” Running ESLint to check code style...`);
655
+ runCommand("pnpm lint");
624
656
  console.log(`\nāœ… Project ${packageJsonExists ? "configured" : "created"} successfully!`);
625
657
  console.log(`- Project: ${projectName}`);
626
658
  console.log("- Next.js with TypeScript");
@@ -631,8 +663,7 @@ function createProjectCommand(program) {
631
663
  console.log(`\nšŸš€ To start development:`);
632
664
  console.log(`cd ${projectName} && pnpm dev`);
633
665
  } catch (error) {
634
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
635
- console.error(`\nāŒ An error occurred: ${errorMessage}`);
666
+ console.error(`\nāŒ An error occurred: ${getErrorMessage(error)}`);
636
667
  process.exit(1);
637
668
  } finally{
638
669
  rl.close();
@@ -771,7 +802,7 @@ const EXPORTS_PREVIEW_FILE = "exports-preview.json";
771
802
  const EXPORTS_ANALYSIS_FILE = "exports-analysis.json";
772
803
  const ENCODING = "utf8";
773
804
  const JSON_INDENT = 2;
774
- const FileUtils = {
805
+ const file_utils_FileUtils = {
775
806
  exists (filePath) {
776
807
  return external_node_fs_default().existsSync(filePath);
777
808
  },
@@ -809,19 +840,19 @@ const FileUtils = {
809
840
  }
810
841
  };
811
842
  function fileExists(filePath) {
812
- return FileUtils.exists(filePath);
843
+ return file_utils_FileUtils.exists(filePath);
813
844
  }
814
845
  function readPackageJson(filePath) {
815
- return FileUtils.readJsonFile(filePath);
846
+ return file_utils_FileUtils.readJsonFile(filePath);
816
847
  }
817
848
  function backupPackageJson(filePath) {
818
849
  try {
819
850
  const content = external_node_fs_default().readFileSync(filePath, ENCODING);
820
851
  const dir = external_node_path_default().dirname(filePath);
821
852
  const backupDir = external_node_path_default().join(dir, EXPORTS_ANALYSIS_DIR);
822
- FileUtils.ensureDirectoryExists(backupDir);
853
+ file_utils_FileUtils.ensureDirectoryExists(backupDir);
823
854
  const baseName = external_node_path_default().basename(filePath);
824
- const backupPath = external_node_path_default().join(backupDir, FileUtils.createBackupFilename(baseName));
855
+ const backupPath = external_node_path_default().join(backupDir, file_utils_FileUtils.createBackupFilename(baseName));
825
856
  external_node_fs_default().writeFileSync(backupPath, content, ENCODING);
826
857
  return true;
827
858
  } catch (error) {
@@ -830,11 +861,11 @@ function backupPackageJson(filePath) {
830
861
  }
831
862
  }
832
863
  function savePackageJson(filePath, content) {
833
- return FileUtils.writeJsonFile(filePath, content);
864
+ return file_utils_FileUtils.writeJsonFile(filePath, content);
834
865
  }
835
866
  function saveJsonToFile(packageDir, fileName, content) {
836
- const outputDir = FileUtils.getAnalysisDir(packageDir);
837
- FileUtils.writeJsonFile(external_node_path_default().join(outputDir, fileName), content);
867
+ const outputDir = file_utils_FileUtils.getAnalysisDir(packageDir);
868
+ file_utils_FileUtils.writeJsonFile(external_node_path_default().join(outputDir, fileName), content);
838
869
  }
839
870
  function saveExportsAnalysis(packageDir, analysis) {
840
871
  saveJsonToFile(packageDir, EXPORTS_ANALYSIS_FILE, analysis);
@@ -994,12 +1025,6 @@ function main() {
994
1025
  program.name("codefast").description("CodeFast CLI - A development toolkit for CodeFast.").version(getPackageVersion(), "-v, --version", "display CLI version");
995
1026
  createUpdateExportsCommand(program);
996
1027
  createProjectCommand(program);
997
- program.action(()=>{
998
- console.log('Use "codefast create-project" to create a new Next.js project.');
999
- console.log('Use "codefast update-exports" to update exports.');
1000
- console.log('Run "codefast --help" to see all available commands.');
1001
- program.help();
1002
- });
1003
1028
  program.parse(process.argv);
1004
1029
  }
1005
1030
  main();