fwinit 1.0.2 → 1.0.4

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 (2) hide show
  1. package/dist/index.js +169 -10
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11,31 +11,36 @@ var TEMPLATES = [
11
11
  name: "ASP.NET",
12
12
  alias: ["aspnet", "dotnet", "csharp"],
13
13
  description: "API REST con ASP.NET Core, Clean Architecture y C#",
14
- folder: "ASPNET"
14
+ folder: "ASPNET",
15
+ runtime: "dotnet"
15
16
  },
16
17
  {
17
18
  name: "Express",
18
19
  alias: ["express"],
19
20
  description: "API REST con Express, Prisma, TypeScript y Bun",
20
- folder: "EXPRESS"
21
+ folder: "EXPRESS",
22
+ runtime: "bun"
21
23
  },
22
24
  {
23
25
  name: "Fastify",
24
26
  alias: ["fastify"],
25
27
  description: "API REST con Fastify, Prisma, TypeScript y Bun",
26
- folder: "FASTIFY"
28
+ folder: "FASTIFY",
29
+ runtime: "bun"
27
30
  },
28
31
  {
29
32
  name: "Node.js Vanilla",
30
33
  alias: ["nodejs", "vanilla", "node"],
31
34
  description: "API con Node.js puro, Prisma, TypeScript y tsx",
32
- folder: "NODEJS-VANILLA"
35
+ folder: "NODEJS-VANILLA",
36
+ runtime: "node"
33
37
  },
34
38
  {
35
39
  name: "React Native",
36
40
  alias: ["react-native", "rn", "expo"],
37
41
  description: "App m\xF3vil con React Native, Expo y TypeScript",
38
- folder: "REACT-NATIVE"
42
+ folder: "REACT-NATIVE",
43
+ runtime: "node"
39
44
  }
40
45
  ];
41
46
  function findTemplate(input) {
@@ -117,6 +122,23 @@ async function substituteNodejs(dir, projectName) {
117
122
  await writeFile(packageJsonPath, JSON.stringify(pkg, null, 2) + "\n");
118
123
  } catch {
119
124
  }
125
+ const appJsonPath = join(dir, "app.json");
126
+ try {
127
+ const content = await readFile(appJsonPath, "utf-8");
128
+ const app = JSON.parse(content);
129
+ if (app.expo) {
130
+ app.expo.name = projectName;
131
+ app.expo.slug = projectName;
132
+ if (app.expo.scheme) {
133
+ app.expo.scheme = toScheme(projectName);
134
+ }
135
+ await writeFile(appJsonPath, JSON.stringify(app, null, 2) + "\n");
136
+ }
137
+ } catch {
138
+ }
139
+ }
140
+ function toScheme(str) {
141
+ return str.replace(/[-_\s]+/g, "").toLowerCase();
120
142
  }
121
143
  async function substituteAspNet(dir, projectName) {
122
144
  const pascalName = toPascalCase(projectName);
@@ -191,6 +213,22 @@ async function selectTemplate() {
191
213
  ]);
192
214
  return template;
193
215
  }
216
+ async function selectPackageManager() {
217
+ const { pm } = await inquirer.prompt([
218
+ {
219
+ type: "list",
220
+ name: "pm",
221
+ message: "\xBFQu\xE9 package manager quer\xE9s usar?",
222
+ choices: [
223
+ { name: "pnpm (recomendado)", value: "pnpm" },
224
+ { name: "npm", value: "npm" },
225
+ { name: "bun", value: "bun" }
226
+ ],
227
+ default: "pnpm"
228
+ }
229
+ ]);
230
+ return pm;
231
+ }
194
232
  async function askProjectName() {
195
233
  const { name } = await inquirer.prompt([
196
234
  {
@@ -212,15 +250,67 @@ async function askProjectName() {
212
250
  // src/utils.ts
213
251
  import { existsSync } from "fs";
214
252
  import { resolve } from "path";
253
+ var PM_RUNNER = {
254
+ npm: "npm",
255
+ pnpm: "pnpm",
256
+ bun: "bun"
257
+ };
258
+ var PM_EXEC = {
259
+ npm: "npx",
260
+ pnpm: "pnpm exec",
261
+ bun: "bunx"
262
+ };
263
+ var BUN_SUBCOMMANDS = /* @__PURE__ */ new Set([
264
+ "install",
265
+ "add",
266
+ "remove",
267
+ "update",
268
+ "upgrade",
269
+ "run",
270
+ "x",
271
+ "link",
272
+ "unlink",
273
+ "init",
274
+ "create",
275
+ "pm",
276
+ "outdated",
277
+ "why",
278
+ "publish",
279
+ "prune",
280
+ "rebuild"
281
+ ]);
215
282
  function getProjectPath(name) {
216
283
  return resolve(process.cwd(), name);
217
284
  }
218
285
  function projectExists(name) {
219
286
  return existsSync(getProjectPath(name));
220
287
  }
221
- function printSuccess(projectName, templateName, postInit) {
222
- const installCmd = postInit?.install || "npm install";
223
- const devCmd = postInit?.dev || "npm run dev";
288
+ function resolveCommand(cmd, pm) {
289
+ const tokens = cmd.trim().split(/\s+/);
290
+ const [runner, ...rest] = tokens;
291
+ switch (runner) {
292
+ case "pnpm":
293
+ return [PM_RUNNER[pm], ...rest].join(" ");
294
+ case "npm":
295
+ return [PM_RUNNER[pm], ...rest].join(" ");
296
+ case "bun": {
297
+ if (pm === "bun") return cmd;
298
+ const sub = rest[0];
299
+ if (sub && !BUN_SUBCOMMANDS.has(sub)) {
300
+ return [PM_RUNNER[pm], "run", ...rest].join(" ");
301
+ }
302
+ return [PM_RUNNER[pm], ...rest].join(" ");
303
+ }
304
+ case "npx":
305
+ case "bunx":
306
+ return [PM_EXEC[pm], ...rest].join(" ");
307
+ default:
308
+ return cmd;
309
+ }
310
+ }
311
+ function printSuccess(projectName, templateName, pm, postInit) {
312
+ const installCmd = postInit?.install ? resolveCommand(postInit.install, pm) : `${pm} install`;
313
+ const devCmd = postInit?.dev ? resolveCommand(postInit.dev, pm) : `${pm} run dev`;
224
314
  console.log(`
225
315
  \x1B[32m\u2714\x1B[0m Template descargado
226
316
  \x1B[32m\u2714\x1B[0m Proyecto creado en ./${projectName}
@@ -232,9 +322,60 @@ function printSuccess(projectName, templateName, postInit) {
232
322
  \x1B[36m${devCmd}\x1B[0m
233
323
  `);
234
324
  }
325
+ function adaptScriptToNode(script) {
326
+ let s = script.trim();
327
+ if (/^bun test\b/.test(s)) {
328
+ s = s.replace(/^bun test\b/, "vitest run");
329
+ }
330
+ s = s.replace(/^bun --hot /, "tsx watch ").replace(/^bunx /, "").replace(/^bun /, "tsx ");
331
+ return s;
332
+ }
333
+ async function adaptToNodeRuntime(projectPath) {
334
+ const { readFile: readFile2, writeFile: writeFile2, readdir: readdir2 } = await import("fs/promises");
335
+ const { join: join2 } = await import("path");
336
+ const pkgPath = join2(projectPath, "package.json");
337
+ const pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
338
+ for (const key of Object.keys(pkg.scripts ?? {})) {
339
+ pkg.scripts[key] = adaptScriptToNode(pkg.scripts[key]);
340
+ }
341
+ pkg.devDependencies ??= {};
342
+ pkg.devDependencies.tsx = "^4.0.0";
343
+ pkg.devDependencies.vitest = "^3.0.0";
344
+ delete pkg.devDependencies["bun-types"];
345
+ await writeFile2(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
346
+ async function walk(dir) {
347
+ const entries = await readdir2(dir, { withFileTypes: true });
348
+ for (const entry of entries) {
349
+ const full = join2(dir, entry.name);
350
+ if (entry.isDirectory()) {
351
+ if (entry.name !== "node_modules") await walk(full);
352
+ } else if (entry.name.endsWith(".test.ts")) {
353
+ const content = await readFile2(full, "utf-8");
354
+ if (content.includes("bun:test")) {
355
+ await writeFile2(full, content.replaceAll('from "bun:test"', 'from "vitest"'));
356
+ }
357
+ }
358
+ }
359
+ }
360
+ const srcDir = join2(projectPath, "src");
361
+ try {
362
+ await walk(srcDir);
363
+ } catch {
364
+ }
365
+ const tsconfigPath = join2(projectPath, "tsconfig.json");
366
+ try {
367
+ const tsconfig = JSON.parse(await readFile2(tsconfigPath, "utf-8"));
368
+ if (tsconfig.compilerOptions?.types?.includes("bun-types")) {
369
+ delete tsconfig.compilerOptions.types;
370
+ await writeFile2(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n");
371
+ }
372
+ } catch {
373
+ }
374
+ }
235
375
 
236
376
  // src/index.ts
237
377
  var program = new Command();
378
+ var PACKAGE_MANAGERS = ["npm", "pnpm", "bun"];
238
379
  program.name("fwinit").description("CLI para crear proyectos desde templates").version("1.0.0");
239
380
  program.command("list").description("Mostrar templates disponibles").action(() => {
240
381
  console.log(
@@ -247,11 +388,23 @@ program.command("list").description("Mostrar templates disponibles").action(() =
247
388
  });
248
389
  console.log();
249
390
  });
250
- program.argument("[template]", "Template a usar").argument("[project-name]", "Nombre del proyecto").action(
391
+ program.argument("[template]", "Template a usar").argument("[project-name]", "Nombre del proyecto").option("-p, --pm <package-manager>", "Package manager a usar: npm, pnpm o bun").action(
251
392
  async (templateArg, projectNameArg) => {
252
393
  try {
394
+ const { pm: pmArg } = program.opts();
395
+ if (pmArg && !PACKAGE_MANAGERS.includes(pmArg)) {
396
+ console.error(
397
+ chalk.red(
398
+ `
399
+ \u2716 Package manager "${pmArg}" inv\xE1lido. Us\xE1: npm, pnpm o bun.
400
+ `
401
+ )
402
+ );
403
+ process.exit(1);
404
+ }
253
405
  let template = templateArg ? findTemplate(templateArg) : void 0;
254
406
  let projectName = projectNameArg;
407
+ let pm = pmArg;
255
408
  if (templateArg && !template) {
256
409
  console.error(
257
410
  chalk.red(
@@ -272,6 +425,9 @@ program.argument("[template]", "Template a usar").argument("[project-name]", "No
272
425
  if (!projectName) {
273
426
  projectName = await askProjectName();
274
427
  }
428
+ if (!pm && template.runtime !== "dotnet") {
429
+ pm = await selectPackageManager();
430
+ }
275
431
  if (projectExists(projectName)) {
276
432
  console.error(
277
433
  chalk.red(
@@ -295,6 +451,9 @@ program.argument("[template]", "Template a usar").argument("[project-name]", "No
295
451
  projectName,
296
452
  template.folder
297
453
  );
454
+ if (template.runtime === "bun" && pm && pm !== "bun") {
455
+ await adaptToNodeRuntime(getProjectPath(projectName));
456
+ }
298
457
  await cleanup(tempDir);
299
458
  spinner.succeed("Template descargado");
300
459
  let postInit;
@@ -310,7 +469,7 @@ program.argument("[template]", "Template a usar").argument("[project-name]", "No
310
469
  postInit = tj.postInit;
311
470
  } catch {
312
471
  }
313
- printSuccess(projectName, template.name, postInit);
472
+ printSuccess(projectName, template.name, pm ?? "npm", postInit);
314
473
  } catch (error) {
315
474
  console.error(
316
475
  chalk.red(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fwinit",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "CLI para crear proyectos desde templates",
5
5
  "type": "module",
6
6
  "bin": {