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 +6 -2
- package/dist/index.js +72 -9
- package/package.json +1 -1
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.
|
|
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
|
|
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
|
|
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(
|
|
569
|
+
async function createFullStackProject(projectPath, backendTpl, frontendTpl, pm) {
|
|
530
570
|
const { tempDir, repoRoot } = await downloadAndExtractRepo();
|
|
531
|
-
const
|
|
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(
|
|
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(
|