create-bunli 0.7.0 → 0.8.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 (106) hide show
  1. package/README.md +18 -6
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +372 -195
  5. package/dist/create-project.d.ts +5 -4
  6. package/dist/create-project.d.ts.map +1 -0
  7. package/dist/create.d.ts +4 -3
  8. package/dist/create.d.ts.map +1 -0
  9. package/dist/index.d.ts +6 -3
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +344 -187
  12. package/dist/steps.d.ts +36 -0
  13. package/dist/steps.d.ts.map +1 -0
  14. package/dist/template-engine.d.ts +3 -2
  15. package/dist/template-engine.d.ts.map +1 -0
  16. package/dist/templates/advanced/README.md +8 -4
  17. package/dist/templates/advanced/bunli.config.ts +19 -19
  18. package/dist/templates/advanced/package.json +9 -4
  19. package/dist/templates/advanced/src/commands/config.ts +129 -118
  20. package/dist/templates/advanced/src/commands/init.ts +53 -59
  21. package/dist/templates/advanced/src/commands/serve.ts +77 -82
  22. package/dist/templates/advanced/src/commands/validate.ts +58 -64
  23. package/dist/templates/advanced/src/index.ts +30 -29
  24. package/dist/templates/advanced/src/utils/config.ts +48 -47
  25. package/dist/templates/advanced/src/utils/constants.ts +6 -6
  26. package/dist/templates/advanced/src/utils/glob.ts +29 -28
  27. package/dist/templates/advanced/src/utils/validator.ts +60 -61
  28. package/dist/templates/advanced/template.json +2 -6
  29. package/dist/templates/advanced/tsconfig.json +1 -1
  30. package/dist/templates/basic/README.md +1 -1
  31. package/dist/templates/basic/bunli.config.ts +17 -17
  32. package/dist/templates/basic/package.json +4 -3
  33. package/dist/templates/basic/src/commands/hello.ts +20 -25
  34. package/dist/templates/basic/src/index.ts +9 -8
  35. package/dist/templates/basic/template.json +2 -6
  36. package/dist/templates/basic/tsconfig.json +1 -1
  37. package/dist/templates/monorepo/README.md +1 -1
  38. package/dist/templates/monorepo/bunli.config.ts +21 -21
  39. package/dist/templates/monorepo/package.json +3 -2
  40. package/dist/templates/monorepo/packages/cli/package.json +10 -5
  41. package/dist/templates/monorepo/packages/cli/src/index.ts +13 -13
  42. package/dist/templates/monorepo/packages/cli/tsconfig.json +3 -6
  43. package/dist/templates/monorepo/packages/core/package.json +6 -5
  44. package/dist/templates/monorepo/packages/core/scripts/build.ts +10 -10
  45. package/dist/templates/monorepo/packages/core/src/commands/analyze.ts +58 -56
  46. package/dist/templates/monorepo/packages/core/src/commands/process.ts +39 -46
  47. package/dist/templates/monorepo/packages/core/src/index.ts +3 -3
  48. package/dist/templates/monorepo/packages/core/src/types.ts +15 -15
  49. package/dist/templates/monorepo/packages/core/tsconfig.json +3 -5
  50. package/dist/templates/monorepo/packages/utils/package.json +6 -5
  51. package/dist/templates/monorepo/packages/utils/scripts/build.ts +10 -10
  52. package/dist/templates/monorepo/packages/utils/src/format.ts +19 -19
  53. package/dist/templates/monorepo/packages/utils/src/index.ts +3 -3
  54. package/dist/templates/monorepo/packages/utils/src/json.ts +4 -4
  55. package/dist/templates/monorepo/packages/utils/src/logger.ts +9 -9
  56. package/dist/templates/monorepo/packages/utils/tsconfig.json +2 -2
  57. package/dist/templates/monorepo/template.json +2 -6
  58. package/dist/templates/monorepo/tsconfig.json +1 -1
  59. package/dist/templates/monorepo/turbo.json +1 -1
  60. package/dist/types.d.ts +2 -1
  61. package/dist/types.d.ts.map +1 -0
  62. package/package.json +35 -34
  63. package/templates/advanced/README.md +8 -4
  64. package/templates/advanced/bunli.config.ts +19 -19
  65. package/templates/advanced/package.json +9 -4
  66. package/templates/advanced/src/commands/config.ts +129 -118
  67. package/templates/advanced/src/commands/init.ts +53 -59
  68. package/templates/advanced/src/commands/serve.ts +77 -82
  69. package/templates/advanced/src/commands/validate.ts +58 -64
  70. package/templates/advanced/src/index.ts +30 -29
  71. package/templates/advanced/src/utils/config.ts +48 -47
  72. package/templates/advanced/src/utils/constants.ts +6 -6
  73. package/templates/advanced/src/utils/glob.ts +29 -28
  74. package/templates/advanced/src/utils/validator.ts +60 -61
  75. package/templates/advanced/template.json +2 -6
  76. package/templates/advanced/tsconfig.json +1 -1
  77. package/templates/basic/README.md +1 -1
  78. package/templates/basic/bunli.config.ts +17 -17
  79. package/templates/basic/package.json +4 -3
  80. package/templates/basic/src/commands/hello.ts +20 -25
  81. package/templates/basic/src/index.ts +9 -8
  82. package/templates/basic/template.json +2 -6
  83. package/templates/basic/tsconfig.json +1 -1
  84. package/templates/monorepo/README.md +1 -1
  85. package/templates/monorepo/bunli.config.ts +21 -21
  86. package/templates/monorepo/package.json +3 -2
  87. package/templates/monorepo/packages/cli/package.json +10 -5
  88. package/templates/monorepo/packages/cli/src/index.ts +13 -13
  89. package/templates/monorepo/packages/cli/tsconfig.json +3 -6
  90. package/templates/monorepo/packages/core/package.json +6 -5
  91. package/templates/monorepo/packages/core/scripts/build.ts +10 -10
  92. package/templates/monorepo/packages/core/src/commands/analyze.ts +58 -56
  93. package/templates/monorepo/packages/core/src/commands/process.ts +39 -46
  94. package/templates/monorepo/packages/core/src/index.ts +3 -3
  95. package/templates/monorepo/packages/core/src/types.ts +15 -15
  96. package/templates/monorepo/packages/core/tsconfig.json +3 -5
  97. package/templates/monorepo/packages/utils/package.json +6 -5
  98. package/templates/monorepo/packages/utils/scripts/build.ts +10 -10
  99. package/templates/monorepo/packages/utils/src/format.ts +19 -19
  100. package/templates/monorepo/packages/utils/src/index.ts +3 -3
  101. package/templates/monorepo/packages/utils/src/json.ts +4 -4
  102. package/templates/monorepo/packages/utils/src/logger.ts +9 -9
  103. package/templates/monorepo/packages/utils/tsconfig.json +2 -2
  104. package/templates/monorepo/template.json +2 -6
  105. package/templates/monorepo/tsconfig.json +1 -1
  106. package/templates/monorepo/turbo.json +1 -1
package/dist/cli.js CHANGED
@@ -3,162 +3,8 @@
3
3
 
4
4
  // src/cli.ts
5
5
  import { createCLI, defineCommand, option } from "@bunli/core";
6
- import { z } from "zod";
7
-
8
- // src/template-engine.ts
9
- import { downloadTemplate } from "giget";
10
- import { readdir } from "fs/promises";
11
- import { join } from "path";
12
- async function processTemplate(options) {
13
- const { source, dir, offline, variables = {} } = options;
14
- let templateDir;
15
- if (source.startsWith("/") || source.startsWith("./") || source.startsWith("../")) {
16
- const sourceDir = source.startsWith("/") ? source : join(process.cwd(), source);
17
- const copyExitCode = await Bun.spawn(["cp", "-r", sourceDir + "/.", dir], {
18
- stdout: "inherit",
19
- stderr: "inherit"
20
- }).exited;
21
- if (copyExitCode !== 0) {
22
- throw new Error(`Failed to copy local template from ${sourceDir}`);
23
- }
24
- templateDir = dir;
25
- } else {
26
- const result = await downloadTemplate(source, {
27
- dir,
28
- offline,
29
- preferOffline: true,
30
- force: true
31
- });
32
- templateDir = result.dir;
33
- }
34
- const manifest = await loadTemplateManifest(templateDir);
35
- if (manifest?.files || Object.keys(variables).length > 0) {
36
- await processTemplateFiles(templateDir, variables, manifest);
37
- }
38
- if (manifest?.hooks?.postInstall) {
39
- await runPostInstallHooks(templateDir, manifest.hooks.postInstall);
40
- }
41
- return { dir: templateDir, manifest };
42
- }
43
- async function loadTemplateManifest(dir) {
44
- const possiblePaths = [
45
- join(dir, "template.json"),
46
- join(dir, ".template.json"),
47
- join(dir, "template.yaml"),
48
- join(dir, ".template.yaml")
49
- ];
50
- for (const path of possiblePaths) {
51
- const file = Bun.file(path);
52
- if (await file.exists()) {
53
- const content = await file.text();
54
- if (path.endsWith(".json")) {
55
- const manifest = JSON.parse(content);
56
- const removeExitCode = await Bun.spawn(["rm", "-f", path], {
57
- stdout: "ignore",
58
- stderr: "ignore"
59
- }).exited;
60
- if (removeExitCode !== 0) {
61
- console.warn(`Warning: failed to remove template manifest ${path}`);
62
- }
63
- return manifest;
64
- }
65
- }
66
- }
67
- return null;
68
- }
69
- async function processTemplateFiles(dir, variables, manifest) {
70
- const files = await getFilesToProcess(dir, manifest);
71
- for (const file of files) {
72
- const filePath = join(dir, file);
73
- const content = await Bun.file(filePath).text();
74
- let processedContent = content;
75
- for (const [key, value] of Object.entries(variables)) {
76
- processedContent = processedContent.replaceAll(`{{${key}}}`, value).replaceAll(`<%= ${key} %>`, value).replaceAll(`$${key}`, value).replaceAll(`__${key}__`, value);
77
- }
78
- let newFilePath = filePath;
79
- for (const [key, value] of Object.entries(variables)) {
80
- newFilePath = newFilePath.replaceAll(`__${key}__`, value);
81
- }
82
- await Bun.write(newFilePath, processedContent);
83
- if (newFilePath !== filePath) {
84
- await Bun.spawn(["rm", filePath]).exited;
85
- }
86
- }
87
- }
88
- async function getFilesToProcess(dir, manifest) {
89
- if (manifest?.files?.include) {
90
- return manifest.files.include;
91
- }
92
- const files = [];
93
- async function walk(currentDir, prefix = "") {
94
- const entries = await readdir(currentDir, { withFileTypes: true });
95
- for (const entry of entries) {
96
- const path = join(prefix, entry.name);
97
- if (entry.isDirectory()) {
98
- if (!["node_modules", ".git", ".next", "dist", "build"].includes(entry.name)) {
99
- await walk(join(currentDir, entry.name), path);
100
- }
101
- } else {
102
- if (!path.match(/^(template\.json|\.template\.json|\.DS_Store|Thumbs\.db)$/) || path === ".gitignore") {
103
- files.push(path);
104
- }
105
- }
106
- }
107
- }
108
- await walk(dir);
109
- if (manifest?.files?.exclude) {
110
- return files.filter((file) => {
111
- return !manifest.files.exclude.some((pattern) => {
112
- const regexPattern = pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]");
113
- const regex = new RegExp(`^${regexPattern}$`);
114
- return regex.test(file);
115
- });
116
- });
117
- }
118
- return files;
119
- }
120
- async function runPostInstallHooks(dir, hooks) {
121
- for (const hook of hooks) {
122
- const proc = Bun.spawn(hook.split(" "), {
123
- cwd: dir,
124
- stdout: "inherit",
125
- stderr: "inherit"
126
- });
127
- const exitCode = await proc.exited;
128
- if (exitCode !== 0) {
129
- throw new Error(`Post-install hook failed: ${hook}`);
130
- }
131
- }
132
- }
133
- function resolveTemplateSource(template) {
134
- const specialTemplates = {
135
- basic: "github:bunli/templates/basic",
136
- advanced: "github:bunli/templates/advanced",
137
- monorepo: "github:bunli/templates/monorepo"
138
- };
139
- if (specialTemplates[template]) {
140
- return specialTemplates[template];
141
- }
142
- if (template.startsWith("npm:")) {
143
- return template.replace("npm:", "npm:/");
144
- }
145
- if (template.includes("/") && !template.includes(":")) {
146
- return `github:${template}`;
147
- }
148
- return template;
149
- }
150
- function getBundledTemplatePath(name) {
151
- return join(import.meta.dir, "..", "templates", name);
152
- }
153
- async function isLocalTemplate(template) {
154
- if (template.startsWith("file:") || template.startsWith("./") || template.startsWith("../")) {
155
- return true;
156
- }
157
- const bundledPath = getBundledTemplatePath(template);
158
- return await Bun.file(join(bundledPath, "package.json")).exists();
159
- }
160
6
 
161
- // ../../node_modules/better-result/dist/index.mjs
7
+ // ../../node_modules/.bun/better-result@2.7.0/node_modules/better-result/dist/index.mjs
162
8
  function dual(arity, body) {
163
9
  if (arity === 2)
164
10
  return (...args) => {
@@ -581,8 +427,331 @@ var Result = {
581
427
  flatten
582
428
  };
583
429
 
430
+ // src/cli.ts
431
+ import { z } from "zod";
432
+
433
+ // src/create.ts
434
+ import path from "path";
435
+
436
+ // src/steps.ts
437
+ import { existsSync } from "fs";
438
+ import { join } from "path";
439
+ var LOCKFILE_MAP = [
440
+ ["bun.lock", "bun"],
441
+ ["bun.lockb", "bun"],
442
+ ["pnpm-lock.yaml", "pnpm"],
443
+ ["yarn.lock", "yarn"],
444
+ ["package-lock.json", "npm"]
445
+ ];
446
+ function detectPackageManager(cwd) {
447
+ const dir = cwd ?? process.cwd();
448
+ for (const [lockfile, manager] of LOCKFILE_MAP) {
449
+ if (existsSync(join(dir, lockfile))) {
450
+ return manager;
451
+ }
452
+ }
453
+ const userAgent = process.env.npm_config_user_agent;
454
+ if (userAgent) {
455
+ if (userAgent.startsWith("bun"))
456
+ return "bun";
457
+ if (userAgent.startsWith("pnpm"))
458
+ return "pnpm";
459
+ if (userAgent.startsWith("yarn"))
460
+ return "yarn";
461
+ if (userAgent.startsWith("npm"))
462
+ return "npm";
463
+ }
464
+ return "bun";
465
+ }
466
+ function isInGitRepo(cwd) {
467
+ try {
468
+ const result = Bun.spawnSync(["git", "rev-parse", "--is-inside-work-tree"], {
469
+ cwd: cwd ?? process.cwd(),
470
+ stdout: "ignore",
471
+ stderr: "ignore"
472
+ });
473
+ return result.exitCode === 0;
474
+ } catch {
475
+ return false;
476
+ }
477
+ }
478
+ async function runSteps(dir, steps) {
479
+ for (const step of steps) {
480
+ switch (step.type) {
481
+ case "install":
482
+ await runInstall(dir);
483
+ break;
484
+ case "git-init":
485
+ await runGitInit(dir, step.commit);
486
+ break;
487
+ case "open-editor":
488
+ await runOpenEditor(dir);
489
+ break;
490
+ case "command":
491
+ await runCommand(step.cmd, step.cwd ?? dir);
492
+ break;
493
+ }
494
+ }
495
+ }
496
+ async function runInstall(cwd) {
497
+ const pm = detectPackageManager(cwd);
498
+ const proc = Bun.spawn([pm, "install"], {
499
+ cwd,
500
+ stdout: "pipe",
501
+ stderr: "pipe"
502
+ });
503
+ const exitCode = await proc.exited;
504
+ if (exitCode !== 0) {
505
+ const stderr = await new Response(proc.stderr).text();
506
+ throw new Error(`"${pm} install" exited with code ${exitCode}${stderr ? `: ${stderr.trim()}` : ""}`);
507
+ }
508
+ }
509
+ async function runGitInit(cwd, commit) {
510
+ await spawnChecked(["git", "init"], cwd, "git init");
511
+ if (commit) {
512
+ await ensureGitIdentity(cwd);
513
+ await spawnChecked(["git", "add", "."], cwd, "git add");
514
+ await spawnChecked(["git", "commit", "-m", commit], cwd, "git commit");
515
+ }
516
+ }
517
+ async function runOpenEditor(cwd) {
518
+ const editor = process.env.EDITOR || "code";
519
+ try {
520
+ const proc = Bun.spawn([editor, cwd], {
521
+ stdout: "ignore",
522
+ stderr: "ignore"
523
+ });
524
+ const raceResult = await Promise.race([
525
+ proc.exited.then((code) => ({ kind: "exited", code })),
526
+ new Promise((resolve) => setTimeout(() => resolve({ kind: "timeout" }), 500))
527
+ ]);
528
+ if (raceResult.kind === "exited" && raceResult.code !== 0) {
529
+ console.warn(`Warning: could not open editor "${editor}" (exit code ${raceResult.code})`);
530
+ }
531
+ } catch {
532
+ console.warn(`Warning: could not open editor "${editor}"`);
533
+ }
534
+ }
535
+ function getCommandSpawnArgs(cmd, platform = process.platform) {
536
+ return platform === "win32" ? ["cmd", "/d", "/s", "/c", cmd] : ["sh", "-c", cmd];
537
+ }
538
+ async function runCommand(cmd, cwd) {
539
+ const proc = Bun.spawn(getCommandSpawnArgs(cmd), {
540
+ cwd,
541
+ stdout: "inherit",
542
+ stderr: "inherit"
543
+ });
544
+ const exitCode = await proc.exited;
545
+ if (exitCode !== 0) {
546
+ throw new Error(`Command "${cmd}" exited with code ${exitCode}`);
547
+ }
548
+ }
549
+ async function ensureGitIdentity(cwd) {
550
+ const hasName = Bun.spawnSync(["git", "config", "user.name"], { cwd }).exitCode === 0;
551
+ const hasEmail = Bun.spawnSync(["git", "config", "user.email"], { cwd }).exitCode === 0;
552
+ if (!hasName) {
553
+ await spawnChecked(["git", "config", "user.name", "Bunli"], cwd, "git config user.name");
554
+ }
555
+ if (!hasEmail) {
556
+ await spawnChecked(["git", "config", "user.email", "bunli@scaffolded.project"], cwd, "git config user.email");
557
+ }
558
+ }
559
+ async function spawnChecked(cmd, cwd, label) {
560
+ const proc = Bun.spawn(cmd, {
561
+ cwd,
562
+ stdout: "ignore",
563
+ stderr: "pipe"
564
+ });
565
+ const exitCode = await proc.exited;
566
+ if (exitCode !== 0) {
567
+ const stderr = await new Response(proc.stderr).text();
568
+ throw new Error(`"${label}" failed with exit code ${exitCode}${stderr ? `: ${stderr.trim()}` : ""}`);
569
+ }
570
+ }
571
+
572
+ // src/template-engine.ts
573
+ import { readdir } from "fs/promises";
574
+ import { join as join2 } from "path";
575
+ import { downloadTemplate } from "giget";
576
+ async function processTemplate(options) {
577
+ const { source, dir, offline, variables = {} } = options;
578
+ let templateDir;
579
+ if (source.startsWith("/") || source.startsWith("./") || source.startsWith("../")) {
580
+ const sourceDir = source.startsWith("/") ? source : join2(process.cwd(), source);
581
+ const copyExitCode = await Bun.spawn(["cp", "-r", sourceDir + "/.", dir], {
582
+ stdout: "inherit",
583
+ stderr: "inherit"
584
+ }).exited;
585
+ if (copyExitCode !== 0) {
586
+ throw new Error(`Failed to copy local template from ${sourceDir}`);
587
+ }
588
+ templateDir = dir;
589
+ } else {
590
+ const result = await downloadTemplate(source, {
591
+ dir,
592
+ offline,
593
+ preferOffline: true,
594
+ force: true
595
+ });
596
+ templateDir = result.dir;
597
+ }
598
+ const manifest = await loadTemplateManifest(templateDir);
599
+ if (manifest?.files || Object.keys(variables).length > 0) {
600
+ await processTemplateFiles(templateDir, variables, manifest);
601
+ }
602
+ if (manifest?.hooks?.postInstall) {
603
+ await runPostInstallHooks(templateDir, manifest.hooks.postInstall);
604
+ }
605
+ return { dir: templateDir, manifest };
606
+ }
607
+ async function loadTemplateManifest(dir) {
608
+ const possiblePaths = [
609
+ join2(dir, "template.json"),
610
+ join2(dir, ".template.json"),
611
+ join2(dir, "template.yaml"),
612
+ join2(dir, ".template.yaml")
613
+ ];
614
+ for (const path of possiblePaths) {
615
+ const file = Bun.file(path);
616
+ if (await file.exists()) {
617
+ const content = await file.text();
618
+ if (path.endsWith(".json")) {
619
+ const manifest = JSON.parse(content);
620
+ const removeExitCode = await Bun.spawn(["rm", "-f", path], {
621
+ stdout: "ignore",
622
+ stderr: "ignore"
623
+ }).exited;
624
+ if (removeExitCode !== 0) {
625
+ console.warn(`Warning: failed to remove template manifest ${path}`);
626
+ }
627
+ return manifest;
628
+ }
629
+ }
630
+ }
631
+ return null;
632
+ }
633
+ async function processTemplateFiles(dir, variables, manifest) {
634
+ const files = await getFilesToProcess(dir, manifest);
635
+ for (const file of files) {
636
+ const filePath = join2(dir, file);
637
+ const content = await Bun.file(filePath).text();
638
+ let processedContent = content;
639
+ for (const [key, value] of Object.entries(variables)) {
640
+ processedContent = processedContent.replaceAll(`{{${key}}}`, value).replaceAll(`<%= ${key} %>`, value).replaceAll(`$${key}`, value).replaceAll(`__${key}__`, value);
641
+ }
642
+ let newFilePath = filePath;
643
+ for (const [key, value] of Object.entries(variables)) {
644
+ newFilePath = newFilePath.replaceAll(`__${key}__`, value);
645
+ }
646
+ await Bun.write(newFilePath, processedContent);
647
+ if (newFilePath !== filePath) {
648
+ await Bun.spawn(["rm", filePath]).exited;
649
+ }
650
+ }
651
+ }
652
+ async function getFilesToProcess(dir, manifest) {
653
+ if (manifest?.files?.include) {
654
+ return manifest.files.include;
655
+ }
656
+ const files = [];
657
+ async function walk(currentDir, prefix = "") {
658
+ const entries = await readdir(currentDir, { withFileTypes: true });
659
+ for (const entry of entries) {
660
+ const path = join2(prefix, entry.name);
661
+ if (entry.isDirectory()) {
662
+ if (!["node_modules", ".git", ".next", "dist", "build"].includes(entry.name)) {
663
+ await walk(join2(currentDir, entry.name), path);
664
+ }
665
+ } else {
666
+ if (!path.match(/^(template\.json|\.template\.json|\.DS_Store|Thumbs\.db)$/) || path === ".gitignore") {
667
+ files.push(path);
668
+ }
669
+ }
670
+ }
671
+ }
672
+ await walk(dir);
673
+ if (manifest?.files?.exclude) {
674
+ return files.filter((file) => {
675
+ return !manifest.files.exclude.some((pattern) => {
676
+ const regexPattern = pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]");
677
+ const regex = new RegExp(`^${regexPattern}$`);
678
+ return regex.test(file);
679
+ });
680
+ });
681
+ }
682
+ return files;
683
+ }
684
+ async function runPostInstallHooks(dir, hooks) {
685
+ for (const hook of hooks) {
686
+ const proc = Bun.spawn(hook.split(" "), {
687
+ cwd: dir,
688
+ stdout: "inherit",
689
+ stderr: "inherit"
690
+ });
691
+ const exitCode = await proc.exited;
692
+ if (exitCode !== 0) {
693
+ throw new Error(`Post-install hook failed: ${hook}`);
694
+ }
695
+ }
696
+ }
697
+ function resolveTemplateSource(template) {
698
+ const specialTemplates = {
699
+ basic: "github:bunli/templates/basic",
700
+ advanced: "github:bunli/templates/advanced",
701
+ monorepo: "github:bunli/templates/monorepo"
702
+ };
703
+ if (specialTemplates[template]) {
704
+ return specialTemplates[template];
705
+ }
706
+ if (template.startsWith("npm:")) {
707
+ return template.replace("npm:", "npm:/");
708
+ }
709
+ if (template.includes("/") && !template.includes(":")) {
710
+ return `github:${template}`;
711
+ }
712
+ return template;
713
+ }
714
+ function getBundledTemplatePath(name) {
715
+ return join2(import.meta.dir, "..", "templates", name);
716
+ }
717
+ async function isLocalTemplate(template) {
718
+ if (template.startsWith("file:") || template.startsWith("./") || template.startsWith("../")) {
719
+ return true;
720
+ }
721
+ const bundledPath = getBundledTemplatePath(template);
722
+ return await Bun.file(join2(bundledPath, "package.json")).exists();
723
+ }
724
+
584
725
  // src/create-project.ts
585
726
  var toErrorMessage = (error) => error instanceof Error ? error.message : String(error);
727
+ function stepLabel(step) {
728
+ switch (step.type) {
729
+ case "install":
730
+ return {
731
+ running: "Installing dependencies...",
732
+ done: "Dependencies installed",
733
+ failed: "Failed to install dependencies"
734
+ };
735
+ case "git-init":
736
+ return {
737
+ running: "Initializing git repository...",
738
+ done: "Git repository initialized",
739
+ failed: "Failed to initialize git repository"
740
+ };
741
+ case "open-editor":
742
+ return {
743
+ running: "Opening editor...",
744
+ done: "Editor opened",
745
+ failed: "Failed to open editor"
746
+ };
747
+ case "command":
748
+ return {
749
+ running: `Running ${step.cmd}...`,
750
+ done: `Completed ${step.cmd}`,
751
+ failed: `Failed to run ${step.cmd}`
752
+ };
753
+ }
754
+ }
586
755
  var tryAsync = (fn, mapError2) => Result.tryPromise({ try: fn, catch: mapError2 });
587
756
 
588
757
  class UserCancelledError extends TaggedError("UserCancelledError")() {
@@ -610,7 +779,9 @@ async function createProject(options) {
610
779
  const { name, dir, template, git, install, prompt, spinner, colors, shell, offline } = options;
611
780
  const directoryCheck = await shell`test -d ${dir}`.nothrow();
612
781
  if (directoryCheck.exitCode === 0) {
613
- const overwrite = await prompt.confirm(`Directory ${dir} already exists. Overwrite?`, { default: false });
782
+ const overwrite = await prompt.confirm(`Directory ${dir} already exists. Overwrite?`, {
783
+ default: false
784
+ });
614
785
  if (!overwrite) {
615
786
  return Result.err(new UserCancelledError("Cancelled"));
616
787
  }
@@ -655,40 +826,28 @@ async function createProject(options) {
655
826
  return templateResult;
656
827
  }
657
828
  spin.succeed("Project structure created");
829
+ const postSteps = [];
830
+ if (install) {
831
+ postSteps.push({ type: "install" });
832
+ }
658
833
  if (git) {
659
- const gitSpin = spinner("Initializing git repository...");
660
- gitSpin.start();
661
- const gitInit = await shell`cd ${dir} && git init`.nothrow();
662
- const gitAdd = await shell`cd ${dir} && git add .`.nothrow();
663
- const gitCommit = await shell`cd ${dir} && git commit -m "feat: initialize ${name} CLI project with Bunli
664
-
665
- - Generated using create-bunli template
666
- - Includes basic CLI structure with commands directory
667
- - Configured with Bunli build system and TypeScript
668
- - Ready for development with bun run dev"`.nothrow();
669
- if (gitInit.exitCode === 0 && gitAdd.exitCode === 0 && gitCommit.exitCode === 0) {
670
- gitSpin.succeed("Git repository initialized");
671
- } else {
672
- gitSpin.fail("Failed to initialize git repository");
673
- const output = [gitInit.stderr, gitAdd.stderr, gitCommit.stderr].map((value) => value.toString().trim()).filter(Boolean).join(`
674
- `);
675
- if (output) {
676
- console.error(colors.dim(` ${output}`));
677
- }
678
- }
834
+ postSteps.push({
835
+ type: "git-init",
836
+ commit: `feat: initialize ${name} CLI project with Bunli`
837
+ });
679
838
  }
680
- if (install) {
681
- const installSpin = spinner("Installing dependencies...");
682
- installSpin.start();
683
- const installResult = await shell`cd ${dir} && bun install`.nothrow();
684
- if (installResult.exitCode === 0) {
685
- installSpin.succeed("Dependencies installed");
686
- } else {
687
- installSpin.fail("Failed to install dependencies");
688
- console.error(colors.dim(" You can install them manually by running: bun install"));
689
- const errorOutput = installResult.stderr.toString().trim();
690
- if (errorOutput) {
691
- console.error(colors.dim(` ${errorOutput}`));
839
+ for (const step of postSteps) {
840
+ const label = stepLabel(step);
841
+ const stepSpin = spinner(label.running);
842
+ stepSpin.start();
843
+ try {
844
+ await runSteps(dir, [step]);
845
+ stepSpin.succeed(label.done);
846
+ } catch (error) {
847
+ stepSpin.fail(label.failed);
848
+ const message = error instanceof Error ? error.message : String(error);
849
+ if (message) {
850
+ console.error(colors.dim(` ${message}`));
692
851
  }
693
852
  }
694
853
  }
@@ -696,10 +855,11 @@ async function createProject(options) {
696
855
  }
697
856
 
698
857
  // src/create.ts
699
- import path from "path";
700
858
  class InvalidProjectNameError extends TaggedError("InvalidProjectNameError")() {
701
859
  constructor(name) {
702
- super({ message: `Project name "${name}" must only contain lowercase letters, numbers, and hyphens` });
860
+ super({
861
+ message: `Project name "${name}" must only contain lowercase letters, numbers, and hyphens`
862
+ });
703
863
  }
704
864
  }
705
865
 
@@ -793,11 +953,28 @@ async function run() {
793
953
  description: "Create a new Bunli CLI project",
794
954
  options: {
795
955
  name: option(z.string().optional(), { description: "Project name" }),
796
- template: option(z.string().default("basic"), { short: "t", description: "Project template (basic, advanced, monorepo, or github:user/repo)" }),
797
- dir: option(z.string().optional(), { short: "d", description: "Directory to create project in" }),
798
- git: option(z.boolean().default(true), { short: "g", description: "Initialize git repository" }),
799
- install: option(z.boolean().default(true), { short: "i", description: "Install dependencies" }),
800
- offline: option(z.boolean().default(false), { description: "Use cached templates when available" })
956
+ template: option(z.string().default("basic"), {
957
+ short: "t",
958
+ description: "Project template (basic, advanced, monorepo, or github:user/repo)"
959
+ }),
960
+ dir: option(z.string().optional(), {
961
+ short: "d",
962
+ description: "Directory to create project in"
963
+ }),
964
+ git: option(z.boolean().default(true), {
965
+ short: "g",
966
+ description: "Initialize git repository",
967
+ argumentKind: "flag"
968
+ }),
969
+ install: option(z.boolean().default(true), {
970
+ short: "i",
971
+ description: "Install dependencies",
972
+ argumentKind: "flag"
973
+ }),
974
+ offline: option(z.boolean().default(false), {
975
+ description: "Use cached templates when available",
976
+ argumentKind: "flag"
977
+ })
801
978
  },
802
979
  handler: async (context) => {
803
980
  const result = await create(context);
@@ -1,7 +1,7 @@
1
- import type { PromptApi, PromptSpinnerFactory } from '@bunli/core';
2
- import type { Colors } from '@bunli/utils';
3
- import type { CreateOptions } from './types.js';
4
- import { Result } from 'better-result';
1
+ import type { PromptApi, PromptSpinnerFactory } from "@bunli/core";
2
+ import type { Colors } from "@bunli/utils";
3
+ import { Result } from "better-result";
4
+ import type { CreateOptions } from "./types.js";
5
5
  interface CreateProjectOptions extends CreateOptions {
6
6
  name: string;
7
7
  dir: string;
@@ -35,3 +35,4 @@ declare class TemplateProcessingError extends TemplateProcessingError_base {
35
35
  export type CreateProjectError = UserCancelledError | ShellCommandError | TemplateProcessingError;
36
36
  export declare function createProject(options: CreateProjectOptions): Promise<Result<void, CreateProjectError>>;
37
37
  export {};
38
+ //# sourceMappingURL=create-project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-project.d.ts","sourceRoot":"","sources":["../src/create-project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAUpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAuChD,UAAU,oBAAqB,SAAQ,aAAa;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;CACrB;;aAGU,MAAM;;AADjB,qBAAa,kBAAmB,SAAQ,uBAEpC;IACF,YAAY,OAAO,EAAE,MAAM,EAE1B;CACF;;aAGU,MAAM;aACN,MAAM;YACP,MAAM;;AAHhB,cAAM,iBAAkB,SAAQ,sBAI5B;IACF,YAAY,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAM1C;CACF;;aAGU,MAAM;WACR,OAAO;;AAFhB,cAAM,uBAAwB,SAAQ,4BAGlC;IACF,YAAY,KAAK,EAAE,OAAO,EAEzB;CACF;AAED,MAAM,MAAM,kBAAkB,GAAG,kBAAkB,GAAG,iBAAiB,GAAG,uBAAuB,CAAC;AAElG,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAuG3C"}
package/dist/create.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { HandlerArgs } from '@bunli/core';
2
- import { type CreateProjectError } from './create-project.js';
3
- import { Result } from 'better-result';
1
+ import type { HandlerArgs } from "@bunli/core";
2
+ import { Result } from "better-result";
3
+ import { type CreateProjectError } from "./create-project.js";
4
4
  interface CreateOptions {
5
5
  name?: string;
6
6
  template: string;
@@ -24,3 +24,4 @@ export declare class UserCancelledError extends UserCancelledError_base {
24
24
  export type CreateCommandError = InvalidProjectNameError | UserCancelledError | CreateProjectError;
25
25
  export declare function create(context: HandlerArgs<CreateOptions>): Promise<Result<void, CreateCommandError>>;
26
26
  export {};
27
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAEpD,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;AAE7B,UAAU,aAAa;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;;aAGU,MAAM;;AADjB,cAAM,uBAAwB,SAAQ,4BAElC;IACF,YAAY,IAAI,EAAE,MAAM,EAIvB;CACF;;aAGU,MAAM;;AADjB,qBAAa,kBAAmB,SAAQ,uBAEpC;IACF,YAAY,OAAO,EAAE,MAAM,EAE1B;CACF;AAED,MAAM,MAAM,kBAAkB,GAAG,uBAAuB,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAEnG,wBAAsB,MAAM,CAC1B,OAAO,EAAE,WAAW,CAAC,aAAa,CAAC,GAClC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAgF3C"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
- export { createProject } from './create-project.js';
2
- export { processTemplate, resolveTemplateSource, isLocalTemplate } from './template-engine.js';
3
- export type { CreateOptions, ProjectConfig, TemplateManifest, TemplateVariable } from './types.js';
1
+ export { createProject } from "./create-project.js";
2
+ export { processTemplate, resolveTemplateSource, isLocalTemplate } from "./template-engine.js";
3
+ export { runSteps, detectPackageManager, isInGitRepo } from "./steps.js";
4
+ export type { Step, PackageManager } from "./steps.js";
5
+ export type { CreateOptions, ProjectConfig, TemplateManifest, TemplateVariable } from "./types.js";
4
6
  export declare const version = "0.1.0";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC/F,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnG,eAAO,MAAM,OAAO,UAAU,CAAC"}