@create-lft-app/cli 1.0.14 → 1.1.1

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.
Files changed (61) hide show
  1. package/dist/bin/cli.js +150 -171
  2. package/dist/bin/cli.js.map +1 -1
  3. package/dist/src/index.js +148 -169
  4. package/dist/src/index.js.map +1 -1
  5. package/package.json +1 -1
  6. package/templates/app/auth/login/page.tsx +0 -153
  7. package/templates/app/dashboard/page.tsx +0 -102
  8. package/templates/app/globals.css +0 -249
  9. package/templates/app/layout.tsx +0 -40
  10. package/templates/app/page.tsx +0 -5
  11. package/templates/components/dashboard/widget.tsx +0 -113
  12. package/templates/components/layout/admin-midday-sidebar.tsx +0 -247
  13. package/templates/components/layout/admin-sidebar.tsx +0 -146
  14. package/templates/components/layout/header.tsx +0 -71
  15. package/templates/components/layout/main-content.tsx +0 -28
  16. package/templates/components/layout/midday-sidebar.tsx +0 -381
  17. package/templates/components/layout/nav-user.tsx +0 -108
  18. package/templates/components/layout/page-header.tsx +0 -95
  19. package/templates/components/layout/sidebar-context.tsx +0 -33
  20. package/templates/components/layout/sidebar.tsx +0 -194
  21. package/templates/components/layout/suspension-banner.tsx +0 -21
  22. package/templates/components/ui/accordion.tsx +0 -58
  23. package/templates/components/ui/alert-dialog.tsx +0 -165
  24. package/templates/components/ui/alert.tsx +0 -66
  25. package/templates/components/ui/avatar.tsx +0 -55
  26. package/templates/components/ui/badge.tsx +0 -50
  27. package/templates/components/ui/button.tsx +0 -89
  28. package/templates/components/ui/calendar.tsx +0 -220
  29. package/templates/components/ui/card.tsx +0 -89
  30. package/templates/components/ui/checkbox.tsx +0 -38
  31. package/templates/components/ui/collapsible.tsx +0 -33
  32. package/templates/components/ui/command.tsx +0 -196
  33. package/templates/components/ui/dialog.tsx +0 -153
  34. package/templates/components/ui/dropdown-menu.tsx +0 -280
  35. package/templates/components/ui/form.tsx +0 -171
  36. package/templates/components/ui/icons.tsx +0 -167
  37. package/templates/components/ui/input.tsx +0 -28
  38. package/templates/components/ui/label.tsx +0 -25
  39. package/templates/components/ui/popover.tsx +0 -59
  40. package/templates/components/ui/progress.tsx +0 -32
  41. package/templates/components/ui/radio-group.tsx +0 -45
  42. package/templates/components/ui/scroll-area.tsx +0 -63
  43. package/templates/components/ui/select.tsx +0 -208
  44. package/templates/components/ui/separator.tsx +0 -28
  45. package/templates/components/ui/sheet.tsx +0 -146
  46. package/templates/components/ui/sidebar.tsx +0 -726
  47. package/templates/components/ui/skeleton.tsx +0 -15
  48. package/templates/components/ui/slider.tsx +0 -58
  49. package/templates/components/ui/sonner.tsx +0 -47
  50. package/templates/components/ui/spinner.tsx +0 -27
  51. package/templates/components/ui/submit-button.tsx +0 -47
  52. package/templates/components/ui/switch.tsx +0 -31
  53. package/templates/components/ui/table.tsx +0 -120
  54. package/templates/components/ui/tabs.tsx +0 -75
  55. package/templates/components/ui/textarea.tsx +0 -26
  56. package/templates/components/ui/tooltip.tsx +0 -70
  57. package/templates/hooks/use-mobile.ts +0 -21
  58. package/templates/lib/supabase/client.ts +0 -8
  59. package/templates/lib/supabase/server.ts +0 -29
  60. package/templates/lib/utils.ts +0 -6
  61. package/templates/modules/auth/actions/auth-actions.ts +0 -12
package/dist/bin/cli.js CHANGED
@@ -4,15 +4,16 @@
4
4
  import { program } from "commander";
5
5
 
6
6
  // src/index.ts
7
- import path4 from "path";
7
+ import path3 from "path";
8
+ import { existsSync as existsSync3 } from "fs";
8
9
  import { confirm } from "@inquirer/prompts";
9
10
 
10
11
  // src/config/index.ts
11
12
  import { existsSync, readFileSync } from "fs";
12
13
  var configFilePath = "";
13
14
  var loadedConfig = null;
14
- function setConfigPath(path5) {
15
- configFilePath = path5;
15
+ function setConfigPath(path4) {
16
+ configFilePath = path4;
16
17
  loadedConfig = null;
17
18
  }
18
19
  function getConfigPath() {
@@ -196,6 +197,20 @@ async function createGitHubRepo(projectName, config) {
196
197
  `GitHub: ${owner}/${projectName}`
197
198
  );
198
199
  }
200
+ async function checkGitHubRepoExists(projectName, config) {
201
+ const octokit = new Octokit({ auth: config.credentials.github.token });
202
+ const org = config.defaults.githubOrg;
203
+ const owner = org || config.credentials.github.username;
204
+ try {
205
+ const existing = await octokit.rest.repos.get({
206
+ owner,
207
+ repo: projectName
208
+ });
209
+ return { exists: true, url: existing.data.html_url };
210
+ } catch {
211
+ return { exists: false };
212
+ }
213
+ }
199
214
 
200
215
  // src/services/supabase.ts
201
216
  var SUPABASE_API_URL = "https://api.supabase.com/v1";
@@ -297,6 +312,23 @@ function generateSecurePassword() {
297
312
  }
298
313
  return password;
299
314
  }
315
+ async function checkSupabaseProjectExists(projectName, config) {
316
+ const token = config.credentials.supabase.accessToken;
317
+ const existing = await findExistingProject(projectName, token);
318
+ if (existing) {
319
+ return { exists: true, url: `https://${existing.id}.supabase.co`, projectId: existing.id };
320
+ }
321
+ return { exists: false };
322
+ }
323
+ async function getExistingSupabaseKeys(projectId, config) {
324
+ const token = config.credentials.supabase.accessToken;
325
+ const { anonKey, serviceKey } = await getProjectApiKeys(projectId, token);
326
+ return {
327
+ url: `https://${projectId}.supabase.co`,
328
+ anonKey,
329
+ serviceKey
330
+ };
331
+ }
300
332
 
301
333
  // src/utils/validation.ts
302
334
  function validateProjectName(name) {
@@ -412,6 +444,15 @@ async function createJiraProject(projectName, config) {
412
444
  `Proyecto Jira: ${projectKey}`
413
445
  );
414
446
  }
447
+ async function checkJiraProjectExists(projectName, config) {
448
+ const { email, apiToken, domain } = config.credentials.jira;
449
+ const auth = Buffer.from(`${email}:${apiToken}`).toString("base64");
450
+ const existing = await findExistingJiraProject(projectName, auth, domain);
451
+ if (existing) {
452
+ return { exists: true, url: `https://${domain}/browse/${existing.key}` };
453
+ }
454
+ return { exists: false };
455
+ }
415
456
 
416
457
  // src/steps/scaffold-nextjs.ts
417
458
  import { execa } from "execa";
@@ -424,21 +465,13 @@ async function scaffoldNextJs(projectName, projectPath) {
424
465
  return;
425
466
  }
426
467
  await withSpinner(
427
- "Inicializando proyecto Next.js...",
468
+ "Inicializando proyecto Next.js con template LFT...",
428
469
  async () => {
429
470
  await execa("npx", [
430
- "create-next-app@latest",
471
+ "@create-lft-app/nextjs@latest",
431
472
  projectName,
432
- "--typescript",
433
- "--tailwind",
434
- "--eslint",
435
- "--app",
436
- "--turbopack",
437
- "--src-dir",
438
- "--import-alias",
439
- "@/*",
440
- "--use-npm",
441
- "--yes"
473
+ "--cwd",
474
+ process.cwd()
442
475
  ], {
443
476
  cwd: process.cwd(),
444
477
  stdio: "pipe"
@@ -448,137 +481,13 @@ async function scaffoldNextJs(projectName, projectPath) {
448
481
  );
449
482
  }
450
483
 
451
- // src/steps/copy-template.ts
452
- import { cp, mkdir } from "fs/promises";
453
- import path2 from "path";
454
- import { fileURLToPath } from "url";
455
- var __filename2 = fileURLToPath(import.meta.url);
456
- var __dirname2 = path2.dirname(__filename2);
457
- async function copyTemplate(projectPath) {
458
- await withSpinner(
459
- "Copiando template LFT...",
460
- async () => {
461
- const templatesDir = path2.join(__dirname2, "..", "..", "templates");
462
- const srcDir = path2.join(projectPath, "src");
463
- await cp(
464
- path2.join(templatesDir, "components", "ui"),
465
- path2.join(srcDir, "components", "ui"),
466
- { recursive: true }
467
- );
468
- await cp(
469
- path2.join(templatesDir, "components", "layout"),
470
- path2.join(srcDir, "components", "layout"),
471
- { recursive: true }
472
- );
473
- await cp(
474
- path2.join(templatesDir, "components", "dashboard"),
475
- path2.join(srcDir, "components", "dashboard"),
476
- { recursive: true }
477
- );
478
- await cp(
479
- path2.join(templatesDir, "lib"),
480
- path2.join(srcDir, "lib"),
481
- { recursive: true }
482
- );
483
- await cp(
484
- path2.join(templatesDir, "modules"),
485
- path2.join(srcDir, "modules"),
486
- { recursive: true }
487
- );
488
- await mkdir(path2.join(srcDir, "hooks"), { recursive: true });
489
- await cp(
490
- path2.join(templatesDir, "hooks"),
491
- path2.join(srcDir, "hooks"),
492
- { recursive: true }
493
- );
494
- await cp(
495
- path2.join(templatesDir, "app", "layout.tsx"),
496
- path2.join(srcDir, "app", "layout.tsx")
497
- );
498
- await cp(
499
- path2.join(templatesDir, "app", "page.tsx"),
500
- path2.join(srcDir, "app", "page.tsx")
501
- );
502
- await mkdir(path2.join(srcDir, "app", "dashboard"), { recursive: true });
503
- await cp(
504
- path2.join(templatesDir, "app", "dashboard", "page.tsx"),
505
- path2.join(srcDir, "app", "dashboard", "page.tsx")
506
- );
507
- await mkdir(path2.join(srcDir, "app", "auth", "login"), { recursive: true });
508
- await cp(
509
- path2.join(templatesDir, "app", "auth", "login", "page.tsx"),
510
- path2.join(srcDir, "app", "auth", "login", "page.tsx")
511
- );
512
- await cp(
513
- path2.join(templatesDir, "app", "globals.css"),
514
- path2.join(srcDir, "app", "globals.css")
515
- );
516
- },
517
- "Template LFT copiado (47 componentes + p\xE1ginas)"
518
- );
519
- }
520
-
521
484
  // src/steps/install-deps.ts
522
485
  import { execa as execa2 } from "execa";
523
- var TEMPLATE_DEPENDENCIES = [
524
- // Radix UI primitives
525
- "@radix-ui/react-accordion",
526
- "@radix-ui/react-alert-dialog",
527
- "@radix-ui/react-avatar",
528
- "@radix-ui/react-checkbox",
529
- "@radix-ui/react-collapsible",
530
- "@radix-ui/react-dialog",
531
- "@radix-ui/react-dropdown-menu",
532
- "@radix-ui/react-label",
533
- "@radix-ui/react-popover",
534
- "@radix-ui/react-progress",
535
- "@radix-ui/react-radio-group",
536
- "@radix-ui/react-scroll-area",
537
- "@radix-ui/react-select",
538
- "@radix-ui/react-separator",
539
- "@radix-ui/react-slider",
540
- "@radix-ui/react-slot",
541
- "@radix-ui/react-switch",
542
- "@radix-ui/react-tabs",
543
- "@radix-ui/react-tooltip",
544
- // UI Utilities
545
- "class-variance-authority",
546
- "clsx",
547
- "tailwind-merge",
548
- // Icons
549
- "lucide-react",
550
- // Form handling
551
- "react-hook-form",
552
- "@hookform/resolvers",
553
- // Command menu
554
- "cmdk",
555
- // Date picker
556
- "react-day-picker",
557
- "date-fns",
558
- // Toast notifications
559
- "sonner",
560
- // Theme
561
- "next-themes",
562
- // Animations
563
- "tw-animate-css",
564
- // Validation
565
- "zod",
566
- // Supabase client
567
- "@supabase/supabase-js",
568
- "@supabase/ssr"
569
- ];
570
- var TEMPLATE_DEV_DEPENDENCIES = [
571
- "tailwindcss-animate"
572
- ];
573
486
  async function installDependencies(projectPath) {
574
487
  await withSpinner(
575
- `Instalando dependencias (${TEMPLATE_DEPENDENCIES.length} paquetes)...`,
488
+ "Instalando dependencias...",
576
489
  async () => {
577
- await execa2("npm", ["install", ...TEMPLATE_DEPENDENCIES], {
578
- cwd: projectPath,
579
- stdio: "pipe"
580
- });
581
- await execa2("npm", ["install", "-D", ...TEMPLATE_DEV_DEPENDENCIES], {
490
+ await execa2("npm", ["install"], {
582
491
  cwd: projectPath,
583
492
  stdio: "pipe"
584
493
  });
@@ -589,7 +498,7 @@ async function installDependencies(projectPath) {
589
498
 
590
499
  // src/steps/create-env.ts
591
500
  import { writeFile, readFile, appendFile } from "fs/promises";
592
- import path3 from "path";
501
+ import path2 from "path";
593
502
  async function createEnvFile(projectPath, supabaseKeys) {
594
503
  await withSpinner(
595
504
  "Creando archivo .env.local...",
@@ -600,10 +509,10 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=${supabaseKeys.anonKey}
600
509
  SUPABASE_SERVICE_ROLE_KEY=${supabaseKeys.serviceKey}
601
510
  `;
602
511
  await writeFile(
603
- path3.join(projectPath, ".env.local"),
512
+ path2.join(projectPath, ".env.local"),
604
513
  envContent
605
514
  );
606
- const gitignorePath = path3.join(projectPath, ".gitignore");
515
+ const gitignorePath = path2.join(projectPath, ".gitignore");
607
516
  try {
608
517
  const gitignore = await readFile(gitignorePath, "utf-8");
609
518
  if (!gitignore.includes(".env.local")) {
@@ -701,31 +610,78 @@ async function createProject(projectName, options = {}) {
701
610
  if (!validation.valid) {
702
611
  throw new Error(validation.error);
703
612
  }
704
- const projectPath = path4.resolve(process.cwd(), projectName);
613
+ const projectPath = path3.resolve(process.cwd(), projectName);
705
614
  if (!await hasConfig()) {
706
615
  logger.warning('No se encontr\xF3 configuraci\xF3n. Ejecuta "create-lft-app config" primero.');
707
616
  throw new Error("Configuraci\xF3n no encontrada");
708
617
  }
709
618
  const config = await loadConfig();
619
+ logger.info("Verificando recursos existentes...");
620
+ const status = {
621
+ github: { exists: false },
622
+ supabase: { exists: false },
623
+ jira: { exists: false },
624
+ nextjs: { exists: existsSync3(projectPath) }
625
+ };
626
+ const checks = [];
627
+ if (!options.skipGithub) {
628
+ checks.push(
629
+ checkGitHubRepoExists(projectName, config).then((result) => {
630
+ status.github = result;
631
+ })
632
+ );
633
+ }
634
+ if (!options.skipSupabase) {
635
+ checks.push(
636
+ checkSupabaseProjectExists(projectName, config).then((result) => {
637
+ status.supabase = result;
638
+ })
639
+ );
640
+ }
641
+ if (!options.skipJira) {
642
+ checks.push(
643
+ checkJiraProjectExists(projectName, config).then((result) => {
644
+ status.jira = result;
645
+ })
646
+ );
647
+ }
648
+ await Promise.all(checks);
710
649
  logger.newLine();
711
- logger.title("Resumen de recursos a crear:");
650
+ logger.title("Resumen de recursos:");
712
651
  logger.newLine();
713
652
  const resources = [];
653
+ const owner = config.defaults.githubOrg || config.credentials.github.username;
714
654
  if (!options.skipGithub) {
715
- resources.push({ label: "GitHub", value: `${config.defaults.githubOrg || config.credentials.github.username}/${projectName} (privado)` });
655
+ const githubStatus = status.github.exists ? "\u2713 ya existe" : "se crear\xE1";
656
+ resources.push({
657
+ label: "GitHub",
658
+ value: `${owner}/${projectName} (${githubStatus})`
659
+ });
716
660
  }
717
661
  if (!options.skipSupabase) {
718
- resources.push({ label: "Supabase", value: `${projectName} en ${config.defaults.supabaseRegion}` });
662
+ const supabaseStatus = status.supabase.exists ? "\u2713 ya existe" : "se crear\xE1";
663
+ resources.push({
664
+ label: "Supabase",
665
+ value: `${projectName} en ${config.defaults.supabaseRegion} (${supabaseStatus})`
666
+ });
719
667
  }
720
668
  if (!options.skipJira) {
721
- resources.push({ label: "Jira", value: `Proyecto "${projectName}" en ${config.credentials.jira.domain}` });
669
+ const jiraStatus = status.jira.exists ? "\u2713 ya existe" : "se crear\xE1";
670
+ resources.push({
671
+ label: "Jira",
672
+ value: `Proyecto "${projectName}" (${jiraStatus})`
673
+ });
722
674
  }
723
- resources.push({ label: "Next.js", value: "App Router + TypeScript + Tailwind + Dashboard" });
675
+ const nextjsStatus = status.nextjs.exists ? "\u2713 ya existe" : "se crear\xE1";
676
+ resources.push({
677
+ label: "Next.js",
678
+ value: `App Router + TypeScript + Tailwind (${nextjsStatus})`
679
+ });
724
680
  logger.table(resources);
725
681
  logger.newLine();
726
682
  if (!options.autoConfirm) {
727
683
  const shouldContinue = await confirm({
728
- message: "\xBFContinuar con la creaci\xF3n?",
684
+ message: "\xBFContinuar?",
729
685
  default: true
730
686
  });
731
687
  if (!shouldContinue) {
@@ -740,35 +696,58 @@ async function createProject(projectName, options = {}) {
740
696
  let supabaseKeys;
741
697
  const externalTasks = [];
742
698
  if (!options.skipGithub) {
743
- externalTasks.push(
744
- createGitHubRepo(projectName, config).then((url) => {
745
- urls.github = url;
746
- })
747
- );
699
+ if (status.github.exists) {
700
+ urls.github = status.github.url;
701
+ logger.success(`GitHub: ${owner}/${projectName} (ya existe)`);
702
+ } else {
703
+ externalTasks.push(
704
+ createGitHubRepo(projectName, config).then((url) => {
705
+ urls.github = url;
706
+ })
707
+ );
708
+ }
748
709
  }
749
710
  if (!options.skipSupabase) {
750
- externalTasks.push(
751
- createSupabaseProject(projectName, config).then((result) => {
752
- urls.supabase = result.url;
753
- supabaseKeys = result;
754
- })
755
- );
711
+ if (status.supabase.exists && status.supabase.projectId) {
712
+ externalTasks.push(
713
+ getExistingSupabaseKeys(status.supabase.projectId, config).then((result) => {
714
+ urls.supabase = result.url;
715
+ supabaseKeys = result;
716
+ logger.success(`Supabase: ${projectName} (ya existe)`);
717
+ })
718
+ );
719
+ } else {
720
+ externalTasks.push(
721
+ createSupabaseProject(projectName, config).then((result) => {
722
+ urls.supabase = result.url;
723
+ supabaseKeys = result;
724
+ })
725
+ );
726
+ }
756
727
  }
757
728
  if (!options.skipJira) {
758
- externalTasks.push(
759
- createJiraProject(projectName, config).then((url) => {
760
- urls.jira = url;
761
- })
762
- );
729
+ if (status.jira.exists) {
730
+ urls.jira = status.jira.url;
731
+ logger.success(`Jira: ${projectName} (ya existe)`);
732
+ } else {
733
+ externalTasks.push(
734
+ createJiraProject(projectName, config).then((url) => {
735
+ urls.jira = url;
736
+ })
737
+ );
738
+ }
763
739
  }
764
740
  await Promise.all(externalTasks);
765
- await scaffoldNextJs(projectName, projectPath);
766
- await copyTemplate(projectPath);
767
- await installDependencies(projectPath);
741
+ if (!status.nextjs.exists) {
742
+ await scaffoldNextJs(projectName, projectPath);
743
+ await installDependencies(projectPath);
744
+ } else {
745
+ logger.success(`Next.js: ${projectName} (ya existe)`);
746
+ }
768
747
  if (supabaseKeys) {
769
748
  await createEnvFile(projectPath, supabaseKeys);
770
749
  }
771
- if (!options.skipGit && urls.github) {
750
+ if (!options.skipGit && urls.github && !status.nextjs.exists) {
772
751
  await setupGit(projectPath, urls.github);
773
752
  }
774
753
  logger.newLine();