fwinit 1.3.1 → 1.3.3

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/README.md CHANGED
@@ -97,6 +97,8 @@ Igual para frontend: `fwinit react-native mi-app` pregunta por `frontend/`.
97
97
  | `--no-backend` | Dejar el código en la raíz del proyecto (saltea la pregunta) |
98
98
  | `-b, --backend-template <tpl>` | Backend a usar en fullstack (saltea la pregunta) |
99
99
  | `-f, --frontend-template <tpl>` | Frontend a usar en fullstack (saltea la pregunta) |
100
+ | `--git` | Inicializar un repositorio git (saltea la pregunta) |
101
+ | `--no-git` | No inicializar git (saltea la pregunta) |
100
102
 
101
103
  ## Templates disponibles
102
104
 
@@ -106,6 +108,7 @@ Igual para frontend: `fwinit react-native mi-app` pregunta por `frontend/`.
106
108
  | `express` | backend | API REST con Express, Prisma, TypeScript y Bun | bun |
107
109
  | `fastify` | backend | API REST con Fastify, Prisma, TypeScript y Bun | bun |
108
110
  | `nodejs` | backend | API con Node.js puro, Prisma, TypeScript y tsx | node |
111
+ | `astro` | frontend | Landing estática con Astro 7 y TypeScript | node |
109
112
  | `react-native` | frontend | App móvil con React Native, Expo y TypeScript | node |
110
113
 
111
114
  La capa es metadata de cada template (`layer` en `templates/<TEMPLATE>/template.json`): para sumar un frontend nuevo (Astro, Next.js...), alcanza con agregar la carpeta del template y su `layer`. Los templates se clasifican por capa en la metadata, no por estructura de carpetas.
@@ -120,9 +123,10 @@ El runtime `bun` de un template se porta automáticamente a `node` (npm/pnpm) si
120
123
  4. Si corresponde, porta el template de runtime bun a node (npm/pnpm)
121
124
  5. Instala `.opencode/` con la skill `create-specs` y el comando `/create-specs`
122
125
  6. Limpia los lock files (cada proyecto instala los suyos)
123
- 7. Te muestra los próximos pasos (install y dev de cada carpeta)
126
+ 7. Si lo elegís (o pasás `--git`), inicializa un repositorio git en la raíz
127
+ 8. Te muestra los próximos pasos (install y dev de cada carpeta)
124
128
 
125
- En modo fullstack, además crea `.gitignore` y `README.md` en la raíz del monorepo.
129
+ En modo fullstack (y single con layout de capa) crea además `.gitignore` y `README.md` en la raíz: el `.gitignore` protege el estado de AI de desarrollo (`.atl/`, `odd` y `.opencode/`), que viven fuera de la carpeta del template.
126
130
 
127
131
  ## Desarrollo
128
132
 
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import ora from "ora";
6
6
  import chalk from "chalk";
7
7
  import { readFileSync } from "fs";
8
8
  import { mkdir as mkdir2, cp as cp2, access, readFile as readFile2 } from "fs/promises";
9
- import { join as join2 } from "path";
9
+ import { join as join2, basename } from "path";
10
10
 
11
11
  // src/templates.ts
12
12
  var TEMPLATES = [
@@ -42,6 +42,14 @@ var TEMPLATES = [
42
42
  runtime: "node",
43
43
  layer: "backend"
44
44
  },
45
+ {
46
+ name: "Astro",
47
+ alias: ["astro"],
48
+ description: "Landing est\xE1tica con Astro 7 y TypeScript",
49
+ folder: "ASTRO",
50
+ runtime: "node",
51
+ layer: "frontend"
52
+ },
45
53
  {
46
54
  name: "React Native",
47
55
  alias: ["react-native", "rn", "expo"],
@@ -243,6 +251,17 @@ async function selectTemplateFromLayer(layer) {
243
251
  ]);
244
252
  return template;
245
253
  }
254
+ async function askGitInit() {
255
+ const { initGit } = await inquirer.prompt([
256
+ {
257
+ type: "confirm",
258
+ name: "initGit",
259
+ message: "\xBFInicializar un repositorio git?",
260
+ default: true
261
+ }
262
+ ]);
263
+ return initGit;
264
+ }
246
265
  async function selectPackageManager() {
247
266
  const { pm } = await inquirer.prompt([
248
267
  {
@@ -431,10 +450,9 @@ async function adaptToNodeRuntime(projectPath) {
431
450
  } catch {
432
451
  }
433
452
  }
434
- async function writeRootFiles(info) {
453
+ async function writeRootGitignore(projectPath) {
435
454
  const { writeFile: writeFile2, access: access2 } = await import("fs/promises");
436
455
  const { join: join3 } = await import("path");
437
- const projectPath = resolve(process.cwd(), info.projectName);
438
456
  const gitignorePath = join3(projectPath, ".gitignore");
439
457
  try {
440
458
  await access2(gitignorePath);
@@ -444,6 +462,28 @@ async function writeRootFiles(info) {
444
462
  "# AI dev state\n.atl/\nodd\n.opencode/\n"
445
463
  );
446
464
  }
465
+ }
466
+ async function gitInit(projectPath) {
467
+ const { execFile } = await import("child_process");
468
+ try {
469
+ await new Promise((resolve2, reject) => {
470
+ execFile(
471
+ "git",
472
+ ["init"],
473
+ { cwd: projectPath },
474
+ (err) => err ? reject(err) : resolve2()
475
+ );
476
+ });
477
+ return true;
478
+ } catch {
479
+ return false;
480
+ }
481
+ }
482
+ async function writeRootFiles(info) {
483
+ const { writeFile: writeFile2 } = await import("fs/promises");
484
+ const { join: join3 } = await import("path");
485
+ const projectPath = resolve(process.cwd(), info.projectName);
486
+ await writeRootGitignore(projectPath);
447
487
  const installB = info.backend.postInit?.install ? resolveCommand(info.backend.postInit.install, info.pm) : `${info.pm} install`;
448
488
  const devB = info.backend.postInit?.dev ? resolveCommand(info.backend.postInit.dev, info.pm) : `${info.pm} run dev`;
449
489
  const installF = info.frontend.postInit?.install ? resolveCommand(info.frontend.postInit.install, info.pm) : `${info.pm} install`;
@@ -526,9 +566,9 @@ async function readTemplatePostInit(dir) {
526
566
  return void 0;
527
567
  }
528
568
  }
529
- async function createFullStackProject(projectName, backendTpl, frontendTpl, pm) {
569
+ async function createFullStackProject(projectPath, backendTpl, frontendTpl, pm) {
530
570
  const { tempDir, repoRoot } = await downloadAndExtractRepo();
531
- const projectPath = getProjectPath(projectName);
571
+ const projectName = basename(projectPath);
532
572
  const backendPath = join2(projectPath, "backend");
533
573
  const frontendPath = join2(projectPath, "frontend");
534
574
  await copyTemplate(
@@ -595,10 +635,10 @@ program.command("list").description("Mostrar templates disponibles").action(() =
595
635
  console.log();
596
636
  }
597
637
  });
598
- program.argument("[template]", "Template a usar (o 'fullstack' para backend + frontend)").argument("[project-name]", "Nombre del proyecto").option("-p, --pm <package-manager>", "Package manager a usar: npm, pnpm o bun").option("--backend", "Empaquetar el c\xF3digo en backend/ (saltea la pregunta)").option("--no-backend", "Dejar el c\xF3digo en la ra\xEDz del proyecto (saltea la pregunta)").option("-b, --backend-template <tpl>", "Backend a usar en fullstack (saltea la pregunta)").option("-f, --frontend-template <tpl>", "Frontend a usar en fullstack (saltea la pregunta)").action(
638
+ program.argument("[template]", "Template a usar (o 'fullstack' para backend + frontend)").argument("[project-name]", "Nombre del proyecto").option("-p, --pm <package-manager>", "Package manager a usar: npm, pnpm o bun").option("--backend", "Empaquetar el c\xF3digo en backend/ (saltea la pregunta)").option("--no-backend", "Dejar el c\xF3digo en la ra\xEDz del proyecto (saltea la pregunta)").option("-b, --backend-template <tpl>", "Backend a usar en fullstack (saltea la pregunta)").option("-f, --frontend-template <tpl>", "Frontend a usar en fullstack (saltea la pregunta)").option("--git", "Inicializar un repositorio git (saltea la pregunta)").option("--no-git", "No inicializar git (saltea la pregunta)").action(
599
639
  async (templateArg, projectNameArg) => {
600
640
  try {
601
- const { pm: pmArg, backend, backendTemplate, frontendTemplate } = program.opts();
641
+ const { pm: pmArg, backend, backendTemplate, frontendTemplate, git } = program.opts();
602
642
  if (pmArg && !PACKAGE_MANAGERS.includes(pmArg)) {
603
643
  console.error(
604
644
  chalk.red(
@@ -688,6 +728,7 @@ program.argument("[template]", "Template a usar (o 'fullstack' para backend + fr
688
728
  if (!pm && needsPm) {
689
729
  pm = await selectPackageManager();
690
730
  }
731
+ const wantsGit = git === void 0 ? await askGitInit() : git;
691
732
  if (projectExists(projectName)) {
692
733
  console.error(
693
734
  chalk.red(
@@ -697,21 +738,29 @@ program.argument("[template]", "Template a usar (o 'fullstack' para backend + fr
697
738
  );
698
739
  process.exit(1);
699
740
  }
741
+ const projectPath = getProjectPath(projectName);
700
742
  const spinner = ora(
701
743
  isFullStack ? "Descargando templates..." : `Descargando template ${template.name}...`
702
744
  ).start();
703
745
  if (isFullStack) {
704
746
  spinner.text = "Creando proyecto...";
705
- await createFullStackProject(projectName, backendTpl, frontendTpl, pm ?? "npm");
747
+ await createFullStackProject(
748
+ projectPath,
749
+ backendTpl,
750
+ frontendTpl,
751
+ pm ?? "npm"
752
+ );
706
753
  spinner.succeed("Template descargado");
707
754
  } else {
708
755
  const { tempDir, templatePath, repoRoot } = await downloadAndExtract(template.folder);
709
- const projectPath = getProjectPath(projectName);
710
756
  const codePath = useLayerLayout ? join2(projectPath, layer) : projectPath;
711
757
  spinner.text = "Creando proyecto...";
712
758
  await copyTemplate(templatePath, codePath);
713
759
  await substituteTemplate(codePath, projectName, template.folder);
714
760
  await installOpenCodeFiles(repoRoot, projectPath);
761
+ if (useLayerLayout) {
762
+ await writeRootGitignore(projectPath);
763
+ }
715
764
  if (template.runtime === "bun" && pm && pm !== "bun") {
716
765
  await adaptToNodeRuntime(codePath);
717
766
  }
@@ -733,6 +782,20 @@ program.argument("[template]", "Template a usar (o 'fullstack' para backend + fr
733
782
  );
734
783
  }
735
784
  }
785
+ if (wantsGit) {
786
+ const ok = await gitInit(projectPath);
787
+ if (ok) {
788
+ console.log(
789
+ chalk.dim(" Git: repositorio inicializado\n")
790
+ );
791
+ } else {
792
+ console.log(
793
+ chalk.yellow(
794
+ " \u26A0 No se pudo inicializar git (\xBFest\xE1 instalado?)\n"
795
+ )
796
+ );
797
+ }
798
+ }
736
799
  } catch (error) {
737
800
  console.error(
738
801
  chalk.red(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fwinit",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "CLI para crear proyectos desde templates",
5
5
  "type": "module",
6
6
  "bin": {