create-better-t-stack 3.13.1-pr788.c579ceb → 3.13.2-dev.6a9e17a

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/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { o as createBtsCli } from "./src-DtAyzhjL.mjs";
2
+ import { o as createBtsCli } from "./src-CCKoHHi-.mjs";
3
3
 
4
4
  //#region src/cli.ts
5
5
  createBtsCli().run();
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { a as create, c as docs, d as sponsors, i as builder, l as generateVirtualProject, n as TEMPLATE_COUNT, o as createBtsCli, r as VirtualFileSystem, s as createVirtual, t as EMBEDDED_TEMPLATES, u as router } from "./src-DtAyzhjL.mjs";
2
+ import { a as create, c as docs, d as sponsors, i as builder, l as generateVirtualProject, n as TEMPLATE_COUNT, o as createBtsCli, r as VirtualFileSystem, s as createVirtual, t as EMBEDDED_TEMPLATES, u as router } from "./src-CCKoHHi-.mjs";
3
3
 
4
4
  export { EMBEDDED_TEMPLATES, TEMPLATE_COUNT, VirtualFileSystem, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, router, sponsors };
@@ -10,15 +10,14 @@ import fs from "fs-extra";
10
10
  import path from "node:path";
11
11
  import { fileURLToPath } from "node:url";
12
12
  import { EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, TEMPLATE_COUNT, VirtualFileSystem, dependencyVersionMap, generateVirtualProject, generateVirtualProject as generateVirtualProject$1 } from "@better-t-stack/template-generator";
13
- import { ConfirmPrompt, GroupMultiSelectPrompt, MultiSelectPrompt, SelectPrompt, isCancel as isCancel$1 } from "@clack/core";
14
13
  import { AsyncLocalStorage } from "node:async_hooks";
14
+ import { ConfirmPrompt, GroupMultiSelectPrompt, MultiSelectPrompt, SelectPrompt, isCancel as isCancel$1 } from "@clack/core";
15
15
  import gradient from "gradient-string";
16
16
  import { writeTreeToFilesystem } from "@better-t-stack/template-generator/fs-writer";
17
17
  import { $, execa } from "execa";
18
18
  import { IndentationText, Node, Project, QuoteKind, SyntaxKind } from "ts-morph";
19
19
  import * as JSONC from "jsonc-parser";
20
20
  import { format } from "oxfmt";
21
- import { glob } from "tinyglobby";
22
21
  import os$1 from "node:os";
23
22
 
24
23
  //#region src/utils/get-package-manager.ts
@@ -92,6 +91,59 @@ const ADDON_COMPATIBILITY = {
92
91
  none: []
93
92
  };
94
93
 
94
+ //#endregion
95
+ //#region src/utils/context.ts
96
+ const cliStorage = new AsyncLocalStorage();
97
+ function defaultContext() {
98
+ return {
99
+ navigation: {
100
+ isFirstPrompt: false,
101
+ lastPromptShownUI: false
102
+ },
103
+ silent: false,
104
+ verbose: false
105
+ };
106
+ }
107
+ function getContext() {
108
+ const ctx = cliStorage.getStore();
109
+ if (!ctx) return defaultContext();
110
+ return ctx;
111
+ }
112
+ function tryGetContext() {
113
+ return cliStorage.getStore();
114
+ }
115
+ function isSilent() {
116
+ return getContext().silent;
117
+ }
118
+ function isFirstPrompt() {
119
+ return getContext().navigation.isFirstPrompt;
120
+ }
121
+ function didLastPromptShowUI() {
122
+ return getContext().navigation.lastPromptShownUI;
123
+ }
124
+ function setIsFirstPrompt$1(value) {
125
+ const ctx = tryGetContext();
126
+ if (ctx) ctx.navigation.isFirstPrompt = value;
127
+ }
128
+ function setLastPromptShownUI(value) {
129
+ const ctx = tryGetContext();
130
+ if (ctx) ctx.navigation.lastPromptShownUI = value;
131
+ }
132
+ async function runWithContextAsync(options, fn) {
133
+ const ctx = {
134
+ navigation: {
135
+ isFirstPrompt: false,
136
+ lastPromptShownUI: false
137
+ },
138
+ silent: options.silent ?? false,
139
+ verbose: options.verbose ?? false,
140
+ projectDir: options.projectDir,
141
+ projectName: options.projectName,
142
+ packageManager: options.packageManager
143
+ };
144
+ return cliStorage.run(ctx, fn);
145
+ }
146
+
95
147
  //#endregion
96
148
  //#region src/utils/errors.ts
97
149
  var UserCancelledError = class extends Error {
@@ -107,17 +159,20 @@ var CLIError = class extends Error {
107
159
  }
108
160
  };
109
161
  function exitWithError(message) {
162
+ if (isSilent()) throw new CLIError(message);
110
163
  consola.error(pc.red(message));
111
- throw new CLIError(message);
164
+ process.exit(1);
112
165
  }
113
166
  function exitCancelled(message = "Operation cancelled") {
167
+ if (isSilent()) throw new UserCancelledError(message);
114
168
  cancel(pc.red(message));
115
- throw new UserCancelledError(message);
169
+ process.exit(1);
116
170
  }
117
171
  function handleError(error, fallbackMessage) {
118
172
  const message = error instanceof Error ? error.message : fallbackMessage || String(error);
173
+ if (isSilent()) throw error instanceof Error ? error : new Error(message);
119
174
  consola.error(pc.red(message));
120
- throw error instanceof Error ? error : new Error(message);
175
+ process.exit(1);
121
176
  }
122
177
 
123
178
  //#endregion
@@ -263,64 +318,8 @@ function validateExamplesCompatibility(examples, backend, database, frontend, ap
263
318
  }
264
319
  }
265
320
 
266
- //#endregion
267
- //#region src/utils/context.ts
268
- const cliStorage = new AsyncLocalStorage();
269
- function defaultContext() {
270
- return {
271
- navigation: {
272
- isFirstPrompt: false,
273
- lastPromptShownUI: false
274
- },
275
- silent: false,
276
- verbose: false
277
- };
278
- }
279
- function getContext() {
280
- const ctx = cliStorage.getStore();
281
- if (!ctx) return defaultContext();
282
- return ctx;
283
- }
284
- function tryGetContext() {
285
- return cliStorage.getStore();
286
- }
287
- function isSilent() {
288
- return getContext().silent;
289
- }
290
- function isFirstPrompt() {
291
- return getContext().navigation.isFirstPrompt;
292
- }
293
- function didLastPromptShowUI() {
294
- return getContext().navigation.lastPromptShownUI;
295
- }
296
- function setIsFirstPrompt$1(value) {
297
- const ctx = tryGetContext();
298
- if (ctx) ctx.navigation.isFirstPrompt = value;
299
- }
300
- function setLastPromptShownUI(value) {
301
- const ctx = tryGetContext();
302
- if (ctx) ctx.navigation.lastPromptShownUI = value;
303
- }
304
- async function runWithContextAsync(options, fn) {
305
- const ctx = {
306
- navigation: {
307
- isFirstPrompt: false,
308
- lastPromptShownUI: false
309
- },
310
- silent: options.silent ?? false,
311
- verbose: options.verbose ?? false,
312
- projectDir: options.projectDir,
313
- projectName: options.projectName,
314
- packageManager: options.packageManager
315
- };
316
- return cliStorage.run(ctx, fn);
317
- }
318
-
319
321
  //#endregion
320
322
  //#region src/utils/navigation.ts
321
- /**
322
- * Navigation symbols and utilities for prompt navigation
323
- */
324
323
  const GO_BACK_SYMBOL = Symbol("clack:goBack");
325
324
  function isGoBack(value) {
326
325
  return value === GO_BACK_SYMBOL;
@@ -2148,7 +2147,7 @@ const formatOptions = {
2148
2147
  experimentalSortPackageJson: true,
2149
2148
  experimentalSortImports: { order: "asc" }
2150
2149
  };
2151
- async function formatFile(filePath, content) {
2150
+ async function formatCode(filePath, content) {
2152
2151
  try {
2153
2152
  const result = await format(path.basename(filePath), content, formatOptions);
2154
2153
  if (result.errors && result.errors.length > 0) return null;
@@ -2157,26 +2156,20 @@ async function formatFile(filePath, content) {
2157
2156
  return null;
2158
2157
  }
2159
2158
  }
2160
- /**
2161
- * Format all files in a project directory using oxfmt
2162
- */
2163
- async function formatProjectFiles(projectDir) {
2164
- const files = await glob(["**/*.{ts,tsx,js,jsx,json,mjs,cjs}"], {
2165
- cwd: projectDir,
2166
- absolute: true,
2167
- ignore: [
2168
- "**/node_modules/**",
2169
- "**/dist/**",
2170
- "**/.git/**"
2171
- ]
2172
- });
2173
- await Promise.all(files.map(async (filePath) => {
2174
- try {
2175
- const content = await fs.readFile(filePath, "utf-8");
2176
- const formatted = await formatFile(filePath, content);
2177
- if (formatted && formatted !== content) await fs.writeFile(filePath, formatted, "utf-8");
2178
- } catch {}
2179
- }));
2159
+ async function formatProject(projectDir) {
2160
+ async function formatDirectory(dir) {
2161
+ const entries = await fs.readdir(dir, { withFileTypes: true });
2162
+ await Promise.all(entries.map(async (entry) => {
2163
+ const fullPath = path.join(dir, entry.name);
2164
+ if (entry.isDirectory()) await formatDirectory(fullPath);
2165
+ else if (entry.isFile()) try {
2166
+ const content = await fs.readFile(fullPath, "utf-8");
2167
+ const formatted = await formatCode(fullPath, content);
2168
+ if (formatted && formatted !== content) await fs.writeFile(fullPath, formatted, "utf-8");
2169
+ } catch {}
2170
+ }));
2171
+ }
2172
+ await formatDirectory(projectDir);
2180
2173
  }
2181
2174
 
2182
2175
  //#endregion
@@ -2310,17 +2303,17 @@ async function setupFumadocs(config) {
2310
2303
  * NOTE: Dependencies are handled by template-generator's addons-deps.ts processor
2311
2304
  * This file only handles external CLI initialization (oxlint --init, oxfmt --init)
2312
2305
  */
2313
- async function setupOxlint(projectDir, packageManager) {
2306
+ async function setupOxlint(config) {
2314
2307
  const s = spinner();
2315
2308
  s.start("Initializing oxlint and oxfmt...");
2316
- const oxlintArgs = getPackageExecutionArgs(packageManager, "oxlint@latest --init");
2309
+ const oxlintArgs = getPackageExecutionArgs(config.packageManager, "oxlint@latest --init");
2317
2310
  await $({
2318
- cwd: projectDir,
2311
+ cwd: config.projectDir,
2319
2312
  env: { CI: "true" }
2320
2313
  })`${oxlintArgs}`;
2321
- const oxfmtArgs = getPackageExecutionArgs(packageManager, "oxfmt@latest --init");
2314
+ const oxfmtArgs = getPackageExecutionArgs(config.packageManager, "oxfmt@latest --init");
2322
2315
  await $({
2323
- cwd: projectDir,
2316
+ cwd: config.projectDir,
2324
2317
  env: { CI: "true" }
2325
2318
  })`${oxfmtArgs}`;
2326
2319
  s.stop("oxlint and oxfmt initialized successfully!");
@@ -2535,32 +2528,47 @@ async function setupTui(config) {
2535
2528
  * This file handles interactive prompts and external CLI initialization
2536
2529
  */
2537
2530
  const EDITORS = {
2538
- vscode: { label: "VSCode / Cursor / Windsurf" },
2531
+ vscode: { label: "VS Code" },
2532
+ cursor: { label: "Cursor" },
2533
+ windsurf: { label: "Windsurf" },
2534
+ antigravity: { label: "Antigravity" },
2535
+ kiro: { label: "Kiro" },
2536
+ trae: { label: "Trae" },
2537
+ void: { label: "Void" },
2539
2538
  zed: { label: "Zed" }
2540
2539
  };
2541
2540
  const AGENTS = {
2542
- "vscode-copilot": { label: "VS Code Copilot" },
2543
- cursor: { label: "Cursor" },
2544
- windsurf: { label: "Windsurf" },
2545
- zed: { label: "Zed" },
2546
2541
  claude: { label: "Claude" },
2547
2542
  codex: { label: "Codex" },
2548
- kiro: { label: "Kiro" },
2543
+ jules: { label: "Jules" },
2544
+ copilot: { label: "GitHub Copilot" },
2549
2545
  cline: { label: "Cline" },
2550
2546
  amp: { label: "Amp" },
2551
2547
  aider: { label: "Aider" },
2552
2548
  "firebase-studio": { label: "Firebase Studio" },
2553
2549
  "open-hands": { label: "Open Hands" },
2554
- "gemini-cli": { label: "Gemini CLI" },
2550
+ gemini: { label: "Gemini" },
2555
2551
  junie: { label: "Junie" },
2556
2552
  augmentcode: { label: "AugmentCode" },
2557
2553
  "kilo-code": { label: "Kilo Code" },
2558
2554
  goose: { label: "Goose" },
2559
- "roo-code": { label: "Roo Code" }
2555
+ "roo-code": { label: "Roo Code" },
2556
+ warp: { label: "Warp" },
2557
+ droid: { label: "Droid" },
2558
+ opencode: { label: "OpenCode" },
2559
+ crush: { label: "Crush" },
2560
+ qwen: { label: "Qwen" },
2561
+ "amazon-q-cli": { label: "Amazon Q CLI" },
2562
+ firebender: { label: "Firebender" }
2560
2563
  };
2561
2564
  const HOOKS = {
2562
2565
  cursor: { label: "Cursor" },
2563
- claude: { label: "Claude" }
2566
+ windsurf: { label: "Windsurf" }
2567
+ };
2568
+ const LINTERS = {
2569
+ biome: { label: "Biome (recommended)" },
2570
+ oxlint: { label: "OxLint" },
2571
+ eslint: { label: "ESLint" }
2564
2572
  };
2565
2573
  function getFrameworksFromFrontend(frontend) {
2566
2574
  const frameworkMap = {
@@ -2584,6 +2592,14 @@ async function setupUltracite(config, hasHusky) {
2584
2592
  try {
2585
2593
  log.info("Setting up Ultracite...");
2586
2594
  const result = await group({
2595
+ linter: () => select({
2596
+ message: "Choose linter/formatter",
2597
+ options: Object.entries(LINTERS).map(([key, linter$1]) => ({
2598
+ value: key,
2599
+ label: linter$1.label
2600
+ })),
2601
+ initialValue: "biome"
2602
+ }),
2587
2603
  editors: () => multiselect({
2588
2604
  message: "Choose editors",
2589
2605
  options: Object.entries(EDITORS).map(([key, editor]) => ({
@@ -2601,7 +2617,7 @@ async function setupUltracite(config, hasHusky) {
2601
2617
  required: true
2602
2618
  }),
2603
2619
  hooks: () => autocompleteMultiselect({
2604
- message: "Choose hooks",
2620
+ message: "Choose hooks (optional)",
2605
2621
  options: Object.entries(HOOKS).map(([key, hook]) => ({
2606
2622
  value: key,
2607
2623
  label: hook.label
@@ -2610,6 +2626,7 @@ async function setupUltracite(config, hasHusky) {
2610
2626
  }, { onCancel: () => {
2611
2627
  exitCancelled("Operation cancelled");
2612
2628
  } });
2629
+ const linter = result.linter;
2613
2630
  const editors = result.editors;
2614
2631
  const agents = result.agents;
2615
2632
  const hooks = result.hooks;
@@ -2617,7 +2634,9 @@ async function setupUltracite(config, hasHusky) {
2617
2634
  const ultraciteArgs = [
2618
2635
  "init",
2619
2636
  "--pm",
2620
- packageManager
2637
+ packageManager,
2638
+ "--linter",
2639
+ linter
2621
2640
  ];
2622
2641
  if (frameworks.length > 0) ultraciteArgs.push("--frameworks", ...frameworks);
2623
2642
  if (editors.length > 0) ultraciteArgs.push("--editors", ...editors);
@@ -2702,37 +2721,25 @@ async function setupWxt(config) {
2702
2721
 
2703
2722
  //#endregion
2704
2723
  //#region src/helpers/addons/addons-setup.ts
2705
- async function setupAddons(config, isAddCommand = false) {
2706
- const { addons, frontend, projectDir, packageManager } = config;
2724
+ async function setupAddons(config) {
2725
+ const { addons, frontend } = config;
2707
2726
  const hasReactWebFrontend = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next");
2708
2727
  const hasNuxtFrontend = frontend.includes("nuxt");
2709
2728
  const hasSvelteFrontend = frontend.includes("svelte");
2710
2729
  const hasSolidFrontend = frontend.includes("solid");
2711
2730
  const hasNextFrontend = frontend.includes("next");
2712
- if (addons.includes("turborepo") && isAddCommand) log.info(`${pc.yellow("Update your package.json scripts:")}
2713
-
2714
- ${pc.dim("Replace:")} ${pc.yellow("\"pnpm -r dev\"")} ${pc.dim("→")} ${pc.green("\"turbo dev\"")}
2715
- ${pc.dim("Replace:")} ${pc.yellow("\"pnpm --filter web dev\"")} ${pc.dim("→")} ${pc.green("\"turbo -F web dev\"")}
2716
-
2717
- ${pc.cyan("Docs:")} ${pc.underline("https://turborepo.com/docs")}
2718
- `);
2719
2731
  if (addons.includes("tauri") && (hasReactWebFrontend || hasNuxtFrontend || hasSvelteFrontend || hasSolidFrontend || hasNextFrontend)) await setupTauri(config);
2720
2732
  const hasUltracite = addons.includes("ultracite");
2721
2733
  const hasHusky = addons.includes("husky");
2722
- const hasOxlint = addons.includes("oxlint");
2723
- if (hasUltracite) await setupUltracite(config, hasHusky);
2724
- if (hasOxlint) await setupOxlint(projectDir, packageManager);
2734
+ if (addons.includes("oxlint")) await setupOxlint(config);
2725
2735
  if (addons.includes("starlight")) await setupStarlight(config);
2726
2736
  if (addons.includes("ruler")) await setupRuler(config);
2727
2737
  if (addons.includes("fumadocs")) await setupFumadocs(config);
2728
2738
  if (addons.includes("opentui")) await setupTui(config);
2729
2739
  if (addons.includes("wxt")) await setupWxt(config);
2740
+ if (hasUltracite) await setupUltracite(config, hasHusky);
2730
2741
  }
2731
2742
 
2732
- //#endregion
2733
- //#region src/helpers/addons/examples-setup.ts
2734
- async function setupExamples(_config) {}
2735
-
2736
2743
  //#endregion
2737
2744
  //#region src/utils/add-package-deps.ts
2738
2745
  const addPackageDependency = async (opts) => {
@@ -4582,10 +4589,8 @@ async function createProject(options, cliInput = {}) {
4582
4589
  });
4583
4590
  if (!result.success || !result.tree) throw new Error(result.error || "Failed to generate project templates");
4584
4591
  await writeTreeToFilesystem(result.tree, projectDir);
4585
- await formatProjectFiles(projectDir);
4586
4592
  await setPackageManagerVersion(projectDir, options.packageManager);
4587
4593
  if (!isConvex && options.database !== "none") await setupDatabase(options, cliInput);
4588
- if (options.examples.length > 0 && options.examples[0] !== "none") await /* @__PURE__ */ setupExamples(options);
4589
4594
  if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
4590
4595
  if (options.auth === "better-auth" && !isConvex) {
4591
4596
  const authPackageDir = `${projectDir}/packages/auth`;
@@ -4595,6 +4600,7 @@ async function createProject(options, cliInput = {}) {
4595
4600
  await setupWebDeploy(options);
4596
4601
  await setupServerDeploy(options);
4597
4602
  await writeBtsConfig(options);
4603
+ await formatProject(projectDir);
4598
4604
  if (!isSilent()) log.success("Project template successfully scaffolded!");
4599
4605
  if (options.install) await installDependencies({
4600
4606
  projectDir,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "3.13.1-pr788.c579ceb",
3
+ "version": "3.13.2-dev.6a9e17a",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "keywords": [
6
6
  "better-auth",
@@ -70,8 +70,8 @@
70
70
  "prepublishOnly": "npm run build"
71
71
  },
72
72
  "dependencies": {
73
- "@better-t-stack/template-generator": "0.0.1",
74
- "@better-t-stack/types": "3.13.1-pr788.c579ceb",
73
+ "@better-t-stack/template-generator": "3.13.2-dev.6a9e17a",
74
+ "@better-t-stack/types": "3.13.2-dev.6a9e17a",
75
75
  "@clack/core": "^0.5.0",
76
76
  "@clack/prompts": "^1.0.0-alpha.8",
77
77
  "@orpc/server": "^1.13.0",