@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/src/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import path4 from "path";
4
+ import path3 from "path";
5
+ import { existsSync as existsSync3 } from "fs";
5
6
  import { confirm } from "@inquirer/prompts";
6
7
 
7
8
  // src/config/index.ts
@@ -186,6 +187,20 @@ async function createGitHubRepo(projectName, config) {
186
187
  `GitHub: ${owner}/${projectName}`
187
188
  );
188
189
  }
190
+ async function checkGitHubRepoExists(projectName, config) {
191
+ const octokit = new Octokit({ auth: config.credentials.github.token });
192
+ const org = config.defaults.githubOrg;
193
+ const owner = org || config.credentials.github.username;
194
+ try {
195
+ const existing = await octokit.rest.repos.get({
196
+ owner,
197
+ repo: projectName
198
+ });
199
+ return { exists: true, url: existing.data.html_url };
200
+ } catch {
201
+ return { exists: false };
202
+ }
203
+ }
189
204
 
190
205
  // src/services/supabase.ts
191
206
  var SUPABASE_API_URL = "https://api.supabase.com/v1";
@@ -287,6 +302,23 @@ function generateSecurePassword() {
287
302
  }
288
303
  return password;
289
304
  }
305
+ async function checkSupabaseProjectExists(projectName, config) {
306
+ const token = config.credentials.supabase.accessToken;
307
+ const existing = await findExistingProject(projectName, token);
308
+ if (existing) {
309
+ return { exists: true, url: `https://${existing.id}.supabase.co`, projectId: existing.id };
310
+ }
311
+ return { exists: false };
312
+ }
313
+ async function getExistingSupabaseKeys(projectId, config) {
314
+ const token = config.credentials.supabase.accessToken;
315
+ const { anonKey, serviceKey } = await getProjectApiKeys(projectId, token);
316
+ return {
317
+ url: `https://${projectId}.supabase.co`,
318
+ anonKey,
319
+ serviceKey
320
+ };
321
+ }
290
322
 
291
323
  // src/utils/validation.ts
292
324
  function validateProjectName(name) {
@@ -402,6 +434,15 @@ async function createJiraProject(projectName, config) {
402
434
  `Proyecto Jira: ${projectKey}`
403
435
  );
404
436
  }
437
+ async function checkJiraProjectExists(projectName, config) {
438
+ const { email, apiToken, domain } = config.credentials.jira;
439
+ const auth = Buffer.from(`${email}:${apiToken}`).toString("base64");
440
+ const existing = await findExistingJiraProject(projectName, auth, domain);
441
+ if (existing) {
442
+ return { exists: true, url: `https://${domain}/browse/${existing.key}` };
443
+ }
444
+ return { exists: false };
445
+ }
405
446
 
406
447
  // src/steps/scaffold-nextjs.ts
407
448
  import { execa } from "execa";
@@ -414,21 +455,13 @@ async function scaffoldNextJs(projectName, projectPath) {
414
455
  return;
415
456
  }
416
457
  await withSpinner(
417
- "Inicializando proyecto Next.js...",
458
+ "Inicializando proyecto Next.js con template LFT...",
418
459
  async () => {
419
460
  await execa("npx", [
420
- "create-next-app@latest",
461
+ "@create-lft-app/nextjs@latest",
421
462
  projectName,
422
- "--typescript",
423
- "--tailwind",
424
- "--eslint",
425
- "--app",
426
- "--turbopack",
427
- "--src-dir",
428
- "--import-alias",
429
- "@/*",
430
- "--use-npm",
431
- "--yes"
463
+ "--cwd",
464
+ process.cwd()
432
465
  ], {
433
466
  cwd: process.cwd(),
434
467
  stdio: "pipe"
@@ -438,137 +471,13 @@ async function scaffoldNextJs(projectName, projectPath) {
438
471
  );
439
472
  }
440
473
 
441
- // src/steps/copy-template.ts
442
- import { cp, mkdir } from "fs/promises";
443
- import path2 from "path";
444
- import { fileURLToPath } from "url";
445
- var __filename2 = fileURLToPath(import.meta.url);
446
- var __dirname2 = path2.dirname(__filename2);
447
- async function copyTemplate(projectPath) {
448
- await withSpinner(
449
- "Copiando template LFT...",
450
- async () => {
451
- const templatesDir = path2.join(__dirname2, "..", "..", "templates");
452
- const srcDir = path2.join(projectPath, "src");
453
- await cp(
454
- path2.join(templatesDir, "components", "ui"),
455
- path2.join(srcDir, "components", "ui"),
456
- { recursive: true }
457
- );
458
- await cp(
459
- path2.join(templatesDir, "components", "layout"),
460
- path2.join(srcDir, "components", "layout"),
461
- { recursive: true }
462
- );
463
- await cp(
464
- path2.join(templatesDir, "components", "dashboard"),
465
- path2.join(srcDir, "components", "dashboard"),
466
- { recursive: true }
467
- );
468
- await cp(
469
- path2.join(templatesDir, "lib"),
470
- path2.join(srcDir, "lib"),
471
- { recursive: true }
472
- );
473
- await cp(
474
- path2.join(templatesDir, "modules"),
475
- path2.join(srcDir, "modules"),
476
- { recursive: true }
477
- );
478
- await mkdir(path2.join(srcDir, "hooks"), { recursive: true });
479
- await cp(
480
- path2.join(templatesDir, "hooks"),
481
- path2.join(srcDir, "hooks"),
482
- { recursive: true }
483
- );
484
- await cp(
485
- path2.join(templatesDir, "app", "layout.tsx"),
486
- path2.join(srcDir, "app", "layout.tsx")
487
- );
488
- await cp(
489
- path2.join(templatesDir, "app", "page.tsx"),
490
- path2.join(srcDir, "app", "page.tsx")
491
- );
492
- await mkdir(path2.join(srcDir, "app", "dashboard"), { recursive: true });
493
- await cp(
494
- path2.join(templatesDir, "app", "dashboard", "page.tsx"),
495
- path2.join(srcDir, "app", "dashboard", "page.tsx")
496
- );
497
- await mkdir(path2.join(srcDir, "app", "auth", "login"), { recursive: true });
498
- await cp(
499
- path2.join(templatesDir, "app", "auth", "login", "page.tsx"),
500
- path2.join(srcDir, "app", "auth", "login", "page.tsx")
501
- );
502
- await cp(
503
- path2.join(templatesDir, "app", "globals.css"),
504
- path2.join(srcDir, "app", "globals.css")
505
- );
506
- },
507
- "Template LFT copiado (47 componentes + p\xE1ginas)"
508
- );
509
- }
510
-
511
474
  // src/steps/install-deps.ts
512
475
  import { execa as execa2 } from "execa";
513
- var TEMPLATE_DEPENDENCIES = [
514
- // Radix UI primitives
515
- "@radix-ui/react-accordion",
516
- "@radix-ui/react-alert-dialog",
517
- "@radix-ui/react-avatar",
518
- "@radix-ui/react-checkbox",
519
- "@radix-ui/react-collapsible",
520
- "@radix-ui/react-dialog",
521
- "@radix-ui/react-dropdown-menu",
522
- "@radix-ui/react-label",
523
- "@radix-ui/react-popover",
524
- "@radix-ui/react-progress",
525
- "@radix-ui/react-radio-group",
526
- "@radix-ui/react-scroll-area",
527
- "@radix-ui/react-select",
528
- "@radix-ui/react-separator",
529
- "@radix-ui/react-slider",
530
- "@radix-ui/react-slot",
531
- "@radix-ui/react-switch",
532
- "@radix-ui/react-tabs",
533
- "@radix-ui/react-tooltip",
534
- // UI Utilities
535
- "class-variance-authority",
536
- "clsx",
537
- "tailwind-merge",
538
- // Icons
539
- "lucide-react",
540
- // Form handling
541
- "react-hook-form",
542
- "@hookform/resolvers",
543
- // Command menu
544
- "cmdk",
545
- // Date picker
546
- "react-day-picker",
547
- "date-fns",
548
- // Toast notifications
549
- "sonner",
550
- // Theme
551
- "next-themes",
552
- // Animations
553
- "tw-animate-css",
554
- // Validation
555
- "zod",
556
- // Supabase client
557
- "@supabase/supabase-js",
558
- "@supabase/ssr"
559
- ];
560
- var TEMPLATE_DEV_DEPENDENCIES = [
561
- "tailwindcss-animate"
562
- ];
563
476
  async function installDependencies(projectPath) {
564
477
  await withSpinner(
565
- `Instalando dependencias (${TEMPLATE_DEPENDENCIES.length} paquetes)...`,
478
+ "Instalando dependencias...",
566
479
  async () => {
567
- await execa2("npm", ["install", ...TEMPLATE_DEPENDENCIES], {
568
- cwd: projectPath,
569
- stdio: "pipe"
570
- });
571
- await execa2("npm", ["install", "-D", ...TEMPLATE_DEV_DEPENDENCIES], {
480
+ await execa2("npm", ["install"], {
572
481
  cwd: projectPath,
573
482
  stdio: "pipe"
574
483
  });
@@ -579,7 +488,7 @@ async function installDependencies(projectPath) {
579
488
 
580
489
  // src/steps/create-env.ts
581
490
  import { writeFile, readFile, appendFile } from "fs/promises";
582
- import path3 from "path";
491
+ import path2 from "path";
583
492
  async function createEnvFile(projectPath, supabaseKeys) {
584
493
  await withSpinner(
585
494
  "Creando archivo .env.local...",
@@ -590,10 +499,10 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=${supabaseKeys.anonKey}
590
499
  SUPABASE_SERVICE_ROLE_KEY=${supabaseKeys.serviceKey}
591
500
  `;
592
501
  await writeFile(
593
- path3.join(projectPath, ".env.local"),
502
+ path2.join(projectPath, ".env.local"),
594
503
  envContent
595
504
  );
596
- const gitignorePath = path3.join(projectPath, ".gitignore");
505
+ const gitignorePath = path2.join(projectPath, ".gitignore");
597
506
  try {
598
507
  const gitignore = await readFile(gitignorePath, "utf-8");
599
508
  if (!gitignore.includes(".env.local")) {
@@ -678,31 +587,78 @@ async function createProject(projectName, options = {}) {
678
587
  if (!validation.valid) {
679
588
  throw new Error(validation.error);
680
589
  }
681
- const projectPath = path4.resolve(process.cwd(), projectName);
590
+ const projectPath = path3.resolve(process.cwd(), projectName);
682
591
  if (!await hasConfig()) {
683
592
  logger.warning('No se encontr\xF3 configuraci\xF3n. Ejecuta "create-lft-app config" primero.');
684
593
  throw new Error("Configuraci\xF3n no encontrada");
685
594
  }
686
595
  const config = await loadConfig();
596
+ logger.info("Verificando recursos existentes...");
597
+ const status = {
598
+ github: { exists: false },
599
+ supabase: { exists: false },
600
+ jira: { exists: false },
601
+ nextjs: { exists: existsSync3(projectPath) }
602
+ };
603
+ const checks = [];
604
+ if (!options.skipGithub) {
605
+ checks.push(
606
+ checkGitHubRepoExists(projectName, config).then((result) => {
607
+ status.github = result;
608
+ })
609
+ );
610
+ }
611
+ if (!options.skipSupabase) {
612
+ checks.push(
613
+ checkSupabaseProjectExists(projectName, config).then((result) => {
614
+ status.supabase = result;
615
+ })
616
+ );
617
+ }
618
+ if (!options.skipJira) {
619
+ checks.push(
620
+ checkJiraProjectExists(projectName, config).then((result) => {
621
+ status.jira = result;
622
+ })
623
+ );
624
+ }
625
+ await Promise.all(checks);
687
626
  logger.newLine();
688
- logger.title("Resumen de recursos a crear:");
627
+ logger.title("Resumen de recursos:");
689
628
  logger.newLine();
690
629
  const resources = [];
630
+ const owner = config.defaults.githubOrg || config.credentials.github.username;
691
631
  if (!options.skipGithub) {
692
- resources.push({ label: "GitHub", value: `${config.defaults.githubOrg || config.credentials.github.username}/${projectName} (privado)` });
632
+ const githubStatus = status.github.exists ? "\u2713 ya existe" : "se crear\xE1";
633
+ resources.push({
634
+ label: "GitHub",
635
+ value: `${owner}/${projectName} (${githubStatus})`
636
+ });
693
637
  }
694
638
  if (!options.skipSupabase) {
695
- resources.push({ label: "Supabase", value: `${projectName} en ${config.defaults.supabaseRegion}` });
639
+ const supabaseStatus = status.supabase.exists ? "\u2713 ya existe" : "se crear\xE1";
640
+ resources.push({
641
+ label: "Supabase",
642
+ value: `${projectName} en ${config.defaults.supabaseRegion} (${supabaseStatus})`
643
+ });
696
644
  }
697
645
  if (!options.skipJira) {
698
- resources.push({ label: "Jira", value: `Proyecto "${projectName}" en ${config.credentials.jira.domain}` });
646
+ const jiraStatus = status.jira.exists ? "\u2713 ya existe" : "se crear\xE1";
647
+ resources.push({
648
+ label: "Jira",
649
+ value: `Proyecto "${projectName}" (${jiraStatus})`
650
+ });
699
651
  }
700
- resources.push({ label: "Next.js", value: "App Router + TypeScript + Tailwind + Dashboard" });
652
+ const nextjsStatus = status.nextjs.exists ? "\u2713 ya existe" : "se crear\xE1";
653
+ resources.push({
654
+ label: "Next.js",
655
+ value: `App Router + TypeScript + Tailwind (${nextjsStatus})`
656
+ });
701
657
  logger.table(resources);
702
658
  logger.newLine();
703
659
  if (!options.autoConfirm) {
704
660
  const shouldContinue = await confirm({
705
- message: "\xBFContinuar con la creaci\xF3n?",
661
+ message: "\xBFContinuar?",
706
662
  default: true
707
663
  });
708
664
  if (!shouldContinue) {
@@ -717,35 +673,58 @@ async function createProject(projectName, options = {}) {
717
673
  let supabaseKeys;
718
674
  const externalTasks = [];
719
675
  if (!options.skipGithub) {
720
- externalTasks.push(
721
- createGitHubRepo(projectName, config).then((url) => {
722
- urls.github = url;
723
- })
724
- );
676
+ if (status.github.exists) {
677
+ urls.github = status.github.url;
678
+ logger.success(`GitHub: ${owner}/${projectName} (ya existe)`);
679
+ } else {
680
+ externalTasks.push(
681
+ createGitHubRepo(projectName, config).then((url) => {
682
+ urls.github = url;
683
+ })
684
+ );
685
+ }
725
686
  }
726
687
  if (!options.skipSupabase) {
727
- externalTasks.push(
728
- createSupabaseProject(projectName, config).then((result) => {
729
- urls.supabase = result.url;
730
- supabaseKeys = result;
731
- })
732
- );
688
+ if (status.supabase.exists && status.supabase.projectId) {
689
+ externalTasks.push(
690
+ getExistingSupabaseKeys(status.supabase.projectId, config).then((result) => {
691
+ urls.supabase = result.url;
692
+ supabaseKeys = result;
693
+ logger.success(`Supabase: ${projectName} (ya existe)`);
694
+ })
695
+ );
696
+ } else {
697
+ externalTasks.push(
698
+ createSupabaseProject(projectName, config).then((result) => {
699
+ urls.supabase = result.url;
700
+ supabaseKeys = result;
701
+ })
702
+ );
703
+ }
733
704
  }
734
705
  if (!options.skipJira) {
735
- externalTasks.push(
736
- createJiraProject(projectName, config).then((url) => {
737
- urls.jira = url;
738
- })
739
- );
706
+ if (status.jira.exists) {
707
+ urls.jira = status.jira.url;
708
+ logger.success(`Jira: ${projectName} (ya existe)`);
709
+ } else {
710
+ externalTasks.push(
711
+ createJiraProject(projectName, config).then((url) => {
712
+ urls.jira = url;
713
+ })
714
+ );
715
+ }
740
716
  }
741
717
  await Promise.all(externalTasks);
742
- await scaffoldNextJs(projectName, projectPath);
743
- await copyTemplate(projectPath);
744
- await installDependencies(projectPath);
718
+ if (!status.nextjs.exists) {
719
+ await scaffoldNextJs(projectName, projectPath);
720
+ await installDependencies(projectPath);
721
+ } else {
722
+ logger.success(`Next.js: ${projectName} (ya existe)`);
723
+ }
745
724
  if (supabaseKeys) {
746
725
  await createEnvFile(projectPath, supabaseKeys);
747
726
  }
748
- if (!options.skipGit && urls.github) {
727
+ if (!options.skipGit && urls.github && !status.nextjs.exists) {
749
728
  await setupGit(projectPath, urls.github);
750
729
  }
751
730
  logger.newLine();