create-better-t-stack 3.11.0-pr750.6c25d46 → 3.11.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 (118) hide show
  1. package/dist/cli.d.mts +1 -0
  2. package/dist/cli.mjs +8 -0
  3. package/dist/index.d.mts +351 -0
  4. package/dist/index.mjs +4 -0
  5. package/dist/src-Dc2OdxbP.mjs +7248 -0
  6. package/package.json +25 -35
  7. package/templates/addons/turborepo/turbo.json.hbs +5 -0
  8. package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +3 -5
  9. package/templates/auth/better-auth/convex/backend/convex/privateData.ts.hbs +13 -12
  10. package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-client.ts.hbs +2 -2
  11. package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-server.ts.hbs +1 -0
  12. package/templates/deploy/alchemy/alchemy.run.ts.hbs +2 -2
  13. package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +17 -29
  14. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +2 -4
  15. package/bin/create-better-t-stack +0 -98
  16. package/src/api.ts +0 -203
  17. package/src/cli.ts +0 -185
  18. package/src/constants.ts +0 -270
  19. package/src/helpers/addons/addons-setup.ts +0 -201
  20. package/src/helpers/addons/examples-setup.ts +0 -137
  21. package/src/helpers/addons/fumadocs-setup.ts +0 -99
  22. package/src/helpers/addons/oxlint-setup.ts +0 -36
  23. package/src/helpers/addons/ruler-setup.ts +0 -135
  24. package/src/helpers/addons/starlight-setup.ts +0 -45
  25. package/src/helpers/addons/tauri-setup.ts +0 -90
  26. package/src/helpers/addons/tui-setup.ts +0 -64
  27. package/src/helpers/addons/ultracite-setup.ts +0 -228
  28. package/src/helpers/addons/vite-pwa-setup.ts +0 -59
  29. package/src/helpers/addons/wxt-setup.ts +0 -86
  30. package/src/helpers/core/add-addons.ts +0 -85
  31. package/src/helpers/core/add-deployment.ts +0 -102
  32. package/src/helpers/core/api-setup.ts +0 -280
  33. package/src/helpers/core/auth-setup.ts +0 -203
  34. package/src/helpers/core/backend-setup.ts +0 -69
  35. package/src/helpers/core/command-handlers.ts +0 -354
  36. package/src/helpers/core/convex-codegen.ts +0 -14
  37. package/src/helpers/core/create-project.ts +0 -134
  38. package/src/helpers/core/create-readme.ts +0 -694
  39. package/src/helpers/core/db-setup.ts +0 -184
  40. package/src/helpers/core/detect-project-config.ts +0 -41
  41. package/src/helpers/core/env-setup.ts +0 -481
  42. package/src/helpers/core/git.ts +0 -23
  43. package/src/helpers/core/install-dependencies.ts +0 -29
  44. package/src/helpers/core/payments-setup.ts +0 -48
  45. package/src/helpers/core/post-installation.ts +0 -403
  46. package/src/helpers/core/project-config.ts +0 -250
  47. package/src/helpers/core/runtime-setup.ts +0 -76
  48. package/src/helpers/core/template-manager.ts +0 -917
  49. package/src/helpers/core/workspace-setup.ts +0 -184
  50. package/src/helpers/database-providers/d1-setup.ts +0 -28
  51. package/src/helpers/database-providers/docker-compose-setup.ts +0 -50
  52. package/src/helpers/database-providers/mongodb-atlas-setup.ts +0 -182
  53. package/src/helpers/database-providers/neon-setup.ts +0 -240
  54. package/src/helpers/database-providers/planetscale-setup.ts +0 -78
  55. package/src/helpers/database-providers/prisma-postgres-setup.ts +0 -193
  56. package/src/helpers/database-providers/supabase-setup.ts +0 -196
  57. package/src/helpers/database-providers/turso-setup.ts +0 -309
  58. package/src/helpers/deployment/alchemy/alchemy-combined-setup.ts +0 -80
  59. package/src/helpers/deployment/alchemy/alchemy-next-setup.ts +0 -52
  60. package/src/helpers/deployment/alchemy/alchemy-nuxt-setup.ts +0 -105
  61. package/src/helpers/deployment/alchemy/alchemy-react-router-setup.ts +0 -33
  62. package/src/helpers/deployment/alchemy/alchemy-solid-setup.ts +0 -33
  63. package/src/helpers/deployment/alchemy/alchemy-svelte-setup.ts +0 -99
  64. package/src/helpers/deployment/alchemy/alchemy-tanstack-router-setup.ts +0 -34
  65. package/src/helpers/deployment/alchemy/alchemy-tanstack-start-setup.ts +0 -99
  66. package/src/helpers/deployment/alchemy/env-dts-setup.ts +0 -76
  67. package/src/helpers/deployment/alchemy/index.ts +0 -7
  68. package/src/helpers/deployment/server-deploy-setup.ts +0 -55
  69. package/src/helpers/deployment/web-deploy-setup.ts +0 -58
  70. package/src/index.ts +0 -51
  71. package/src/prompts/addons.ts +0 -200
  72. package/src/prompts/api.ts +0 -49
  73. package/src/prompts/auth.ts +0 -84
  74. package/src/prompts/backend.ts +0 -83
  75. package/src/prompts/config-prompts.ts +0 -138
  76. package/src/prompts/database-setup.ts +0 -112
  77. package/src/prompts/database.ts +0 -57
  78. package/src/prompts/examples.ts +0 -60
  79. package/src/prompts/frontend.ts +0 -118
  80. package/src/prompts/git.ts +0 -16
  81. package/src/prompts/install.ts +0 -16
  82. package/src/prompts/orm.ts +0 -53
  83. package/src/prompts/package-manager.ts +0 -32
  84. package/src/prompts/payments.ts +0 -50
  85. package/src/prompts/project-name.ts +0 -86
  86. package/src/prompts/runtime.ts +0 -47
  87. package/src/prompts/server-deploy.ts +0 -91
  88. package/src/prompts/web-deploy.ts +0 -107
  89. package/src/tui/app.tsx +0 -1062
  90. package/src/types.ts +0 -70
  91. package/src/utils/add-package-deps.ts +0 -57
  92. package/src/utils/analytics.ts +0 -39
  93. package/src/utils/better-auth-plugin-setup.ts +0 -71
  94. package/src/utils/bts-config.ts +0 -122
  95. package/src/utils/command-exists.ts +0 -16
  96. package/src/utils/compatibility-rules.ts +0 -337
  97. package/src/utils/compatibility.ts +0 -11
  98. package/src/utils/config-processing.ts +0 -130
  99. package/src/utils/config-validation.ts +0 -470
  100. package/src/utils/display-config.ts +0 -96
  101. package/src/utils/docker-utils.ts +0 -70
  102. package/src/utils/errors.ts +0 -30
  103. package/src/utils/file-formatter.ts +0 -11
  104. package/src/utils/generate-reproducible-command.ts +0 -53
  105. package/src/utils/get-latest-cli-version.ts +0 -27
  106. package/src/utils/get-package-manager.ts +0 -13
  107. package/src/utils/open-url.ts +0 -18
  108. package/src/utils/package-runner.ts +0 -23
  109. package/src/utils/project-directory.ts +0 -102
  110. package/src/utils/project-name-validation.ts +0 -43
  111. package/src/utils/render-title.ts +0 -48
  112. package/src/utils/setup-catalogs.ts +0 -192
  113. package/src/utils/sponsors.ts +0 -101
  114. package/src/utils/telemetry.ts +0 -19
  115. package/src/utils/template-processor.ts +0 -64
  116. package/src/utils/templates.ts +0 -94
  117. package/src/utils/ts-morph.ts +0 -26
  118. package/src/validation.ts +0 -117
@@ -1,99 +0,0 @@
1
- import path from "node:path";
2
- import fs from "fs-extra";
3
- import { IndentationText, Node, Project, QuoteKind } from "ts-morph";
4
- import type { PackageManager } from "../../../types";
5
- import { addPackageDependency } from "../../../utils/add-package-deps";
6
-
7
- export async function setupSvelteAlchemyDeploy(
8
- projectDir: string,
9
- _packageManager: PackageManager,
10
- options?: { skipAppScripts?: boolean },
11
- ) {
12
- const webAppDir = path.join(projectDir, "apps/web");
13
- if (!(await fs.pathExists(webAppDir))) return;
14
-
15
- await addPackageDependency({
16
- devDependencies: ["alchemy", "@sveltejs/adapter-cloudflare"],
17
- projectDir: webAppDir,
18
- });
19
-
20
- const pkgPath = path.join(webAppDir, "package.json");
21
- if (await fs.pathExists(pkgPath)) {
22
- const pkg = await fs.readJson(pkgPath);
23
-
24
- if (!options?.skipAppScripts) {
25
- pkg.scripts = {
26
- ...pkg.scripts,
27
- dev: "alchemy dev",
28
- deploy: "alchemy deploy",
29
- destroy: "alchemy destroy",
30
- };
31
- }
32
-
33
- await fs.writeJson(pkgPath, pkg, { spaces: 2 });
34
- }
35
-
36
- const svelteConfigPath = path.join(webAppDir, "svelte.config.js");
37
- if (!(await fs.pathExists(svelteConfigPath))) return;
38
-
39
- try {
40
- const project = new Project({
41
- manipulationSettings: {
42
- indentationText: IndentationText.TwoSpaces,
43
- quoteKind: QuoteKind.Single,
44
- },
45
- });
46
-
47
- project.addSourceFileAtPath(svelteConfigPath);
48
- const sourceFile = project.getSourceFileOrThrow(svelteConfigPath);
49
-
50
- const importDeclarations = sourceFile.getImportDeclarations();
51
- const adapterImport = importDeclarations.find((imp) =>
52
- imp.getModuleSpecifierValue().includes("@sveltejs/adapter"),
53
- );
54
-
55
- if (adapterImport) {
56
- adapterImport.setModuleSpecifier("alchemy/cloudflare/sveltekit");
57
- adapterImport.removeDefaultImport();
58
- adapterImport.setDefaultImport("alchemy");
59
- } else {
60
- sourceFile.insertImportDeclaration(0, {
61
- moduleSpecifier: "alchemy/cloudflare/sveltekit",
62
- defaultImport: "alchemy",
63
- });
64
- }
65
-
66
- const configVariable = sourceFile.getVariableDeclaration("config");
67
- if (configVariable) {
68
- const initializer = configVariable.getInitializer();
69
- if (Node.isObjectLiteralExpression(initializer)) {
70
- updateAdapterInConfig(initializer);
71
- }
72
- }
73
-
74
- await project.save();
75
- } catch (error) {
76
- console.warn("Failed to update svelte.config.js:", error);
77
- }
78
- }
79
-
80
- function updateAdapterInConfig(configObject: Node) {
81
- if (!Node.isObjectLiteralExpression(configObject)) return;
82
-
83
- const kitProperty = configObject.getProperty("kit");
84
- if (kitProperty && Node.isPropertyAssignment(kitProperty)) {
85
- const kitInitializer = kitProperty.getInitializer();
86
- if (Node.isObjectLiteralExpression(kitInitializer)) {
87
- const adapterProperty = kitInitializer.getProperty("adapter");
88
- if (adapterProperty && Node.isPropertyAssignment(adapterProperty)) {
89
- const initializer = adapterProperty.getInitializer();
90
- if (Node.isCallExpression(initializer)) {
91
- const expression = initializer.getExpression();
92
- if (Node.isIdentifier(expression) && expression.getText() === "adapter") {
93
- expression.replaceWithText("alchemy");
94
- }
95
- }
96
- }
97
- }
98
- }
99
- }
@@ -1,34 +0,0 @@
1
- import path from "node:path";
2
- import fs from "fs-extra";
3
- import type { PackageManager } from "../../../types";
4
- import { addPackageDependency } from "../../../utils/add-package-deps";
5
-
6
- export async function setupTanStackRouterAlchemyDeploy(
7
- projectDir: string,
8
- _packageManager: PackageManager,
9
- options?: { skipAppScripts?: boolean },
10
- ) {
11
- const webAppDir = path.join(projectDir, "apps/web");
12
- if (!(await fs.pathExists(webAppDir))) return;
13
-
14
- await addPackageDependency({
15
- devDependencies: ["alchemy"],
16
- projectDir: webAppDir,
17
- });
18
-
19
- const pkgPath = path.join(webAppDir, "package.json");
20
- if (await fs.pathExists(pkgPath)) {
21
- const pkg = await fs.readJson(pkgPath);
22
-
23
- if (!options?.skipAppScripts) {
24
- pkg.scripts = {
25
- ...pkg.scripts,
26
- dev: "alchemy dev",
27
- deploy: "alchemy deploy",
28
- destroy: "alchemy destroy",
29
- };
30
- }
31
-
32
- await fs.writeJson(pkgPath, pkg, { spaces: 2 });
33
- }
34
- }
@@ -1,99 +0,0 @@
1
- import path from "node:path";
2
- import fs from "fs-extra";
3
- import { IndentationText, Node, Project, QuoteKind } from "ts-morph";
4
- import type { PackageManager } from "../../../types";
5
- import { addPackageDependency } from "../../../utils/add-package-deps";
6
-
7
- export async function setupTanStackStartAlchemyDeploy(
8
- projectDir: string,
9
- _packageManager: PackageManager,
10
- options?: { skipAppScripts?: boolean },
11
- ) {
12
- const webAppDir = path.join(projectDir, "apps/web");
13
- if (!(await fs.pathExists(webAppDir))) return;
14
-
15
- await addPackageDependency({
16
- devDependencies: ["alchemy", "@cloudflare/vite-plugin"],
17
- projectDir: webAppDir,
18
- });
19
-
20
- const pkgPath = path.join(webAppDir, "package.json");
21
- if (await fs.pathExists(pkgPath)) {
22
- const pkg = await fs.readJson(pkgPath);
23
-
24
- if (!options?.skipAppScripts) {
25
- pkg.scripts = {
26
- ...pkg.scripts,
27
- dev: "alchemy dev",
28
- deploy: "alchemy deploy",
29
- destroy: "alchemy destroy",
30
- };
31
- }
32
-
33
- await fs.writeJson(pkgPath, pkg, { spaces: 2 });
34
- }
35
-
36
- const viteConfigPath = path.join(webAppDir, "vite.config.ts");
37
- if (await fs.pathExists(viteConfigPath)) {
38
- try {
39
- const project = new Project({
40
- manipulationSettings: {
41
- indentationText: IndentationText.TwoSpaces,
42
- quoteKind: QuoteKind.Double,
43
- },
44
- });
45
-
46
- project.addSourceFileAtPath(viteConfigPath);
47
- const sourceFile = project.getSourceFileOrThrow(viteConfigPath);
48
-
49
- const alchemyImport = sourceFile.getImportDeclaration("alchemy/cloudflare/tanstack-start");
50
- if (!alchemyImport) {
51
- sourceFile.addImportDeclaration({
52
- moduleSpecifier: "alchemy/cloudflare/tanstack-start",
53
- defaultImport: "alchemy",
54
- });
55
- } else {
56
- alchemyImport.setModuleSpecifier("alchemy/cloudflare/tanstack-start");
57
- }
58
-
59
- const exportAssignment = sourceFile.getExportAssignment((d) => !d.isExportEquals());
60
- if (!exportAssignment) return;
61
-
62
- const defineConfigCall = exportAssignment.getExpression();
63
- if (
64
- !Node.isCallExpression(defineConfigCall) ||
65
- defineConfigCall.getExpression().getText() !== "defineConfig"
66
- )
67
- return;
68
-
69
- let configObject = defineConfigCall.getArguments()[0];
70
- if (!configObject) {
71
- configObject = defineConfigCall.addArgument("{}");
72
- }
73
-
74
- if (Node.isObjectLiteralExpression(configObject)) {
75
- const pluginsProperty = configObject.getProperty("plugins");
76
- if (pluginsProperty && Node.isPropertyAssignment(pluginsProperty)) {
77
- const initializer = pluginsProperty.getInitializer();
78
- if (Node.isArrayLiteralExpression(initializer)) {
79
- const hasAlchemy = initializer
80
- .getElements()
81
- .some((el) => el.getText().includes("alchemy("));
82
- if (!hasAlchemy) {
83
- initializer.addElement("alchemy()");
84
- }
85
- }
86
- } else {
87
- configObject.addPropertyAssignment({
88
- name: "plugins",
89
- initializer: "[alchemy()]",
90
- });
91
- }
92
- }
93
-
94
- await project.save();
95
- } catch (error) {
96
- console.warn("Failed to update vite.config.ts:", error);
97
- }
98
- }
99
- }
@@ -1,76 +0,0 @@
1
- import path from "node:path";
2
- import fs from "fs-extra";
3
- import { Project } from "ts-morph";
4
- import type { ProjectConfig } from "../../../types";
5
-
6
- const tsProject = new Project({
7
- useInMemoryFileSystem: false,
8
- skipAddingFilesFromTsConfig: true,
9
- });
10
-
11
- function determineImportPath(
12
- envDtsPath: string,
13
- projectDir: string,
14
- config: ProjectConfig,
15
- ): string {
16
- const { webDeploy, serverDeploy, backend } = config;
17
- const isBackendSelf = backend === "self";
18
-
19
- let alchemyRunPath: string;
20
-
21
- if (webDeploy === "alchemy" && (serverDeploy === "alchemy" || isBackendSelf)) {
22
- // Both web and server are alchemy, or web + backend=self
23
- if (isBackendSelf) {
24
- alchemyRunPath = path.join(projectDir, "apps/web/alchemy.run.ts");
25
- } else {
26
- alchemyRunPath = path.join(projectDir, "alchemy.run.ts");
27
- }
28
- } else if (webDeploy === "alchemy") {
29
- // Only web is alchemy
30
- alchemyRunPath = path.join(projectDir, "apps/web/alchemy.run.ts");
31
- } else if (serverDeploy === "alchemy") {
32
- // Only server is alchemy
33
- alchemyRunPath = path.join(projectDir, "apps/server/alchemy.run.ts");
34
- } else {
35
- // Should not happen, but fallback
36
- alchemyRunPath = path.join(projectDir, "alchemy.run.ts");
37
- }
38
-
39
- // Calculate relative path from env.d.ts to alchemy.run.ts
40
- const relativePath = path.relative(path.dirname(envDtsPath), alchemyRunPath.replace(/\.ts$/, ""));
41
-
42
- // Normalize the path for imports (use forward slashes, handle relative paths)
43
- const importPath = relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
44
-
45
- return importPath.replace(/\\/g, "/");
46
- }
47
-
48
- export async function setupEnvDtsImport(
49
- envDtsPath: string,
50
- projectDir: string,
51
- config: ProjectConfig,
52
- ) {
53
- if (!(await fs.pathExists(envDtsPath))) {
54
- return;
55
- }
56
-
57
- const importPath = determineImportPath(envDtsPath, projectDir, config);
58
-
59
- const sourceFile = tsProject.addSourceFileAtPath(envDtsPath);
60
-
61
- const existingImports = sourceFile.getImportDeclarations();
62
- const alreadyHasImport = existingImports.some(
63
- (imp) =>
64
- imp.getModuleSpecifierValue() === importPath &&
65
- imp.getNamedImports().some((named) => named.getName() === "server"),
66
- );
67
-
68
- if (!alreadyHasImport) {
69
- sourceFile.insertImportDeclaration(0, {
70
- moduleSpecifier: importPath,
71
- namedImports: [{ name: "server", isTypeOnly: true }],
72
- });
73
- }
74
-
75
- await sourceFile.save();
76
- }
@@ -1,7 +0,0 @@
1
- export { setupNextAlchemyDeploy } from "./alchemy-next-setup";
2
- export { setupNuxtAlchemyDeploy } from "./alchemy-nuxt-setup";
3
- export { setupReactRouterAlchemyDeploy } from "./alchemy-react-router-setup";
4
- export { setupSolidAlchemyDeploy } from "./alchemy-solid-setup";
5
- export { setupSvelteAlchemyDeploy } from "./alchemy-svelte-setup";
6
- export { setupTanStackRouterAlchemyDeploy } from "./alchemy-tanstack-router-setup";
7
- export { setupTanStackStartAlchemyDeploy } from "./alchemy-tanstack-start-setup";
@@ -1,55 +0,0 @@
1
- import path from "node:path";
2
- import fs from "fs-extra";
3
- import type { ProjectConfig } from "../../types";
4
- import { addPackageDependency } from "../../utils/add-package-deps";
5
-
6
- export async function setupServerDeploy(config: ProjectConfig) {
7
- const { serverDeploy, webDeploy, projectDir } = config;
8
-
9
- if (serverDeploy === "none") return;
10
-
11
- if (serverDeploy === "alchemy" && webDeploy === "alchemy") {
12
- return;
13
- }
14
-
15
- const serverDir = path.join(projectDir, "apps/server");
16
- if (!(await fs.pathExists(serverDir))) return;
17
-
18
- if (serverDeploy === "alchemy") {
19
- await setupAlchemyServerDeploy(serverDir, projectDir);
20
- }
21
- }
22
-
23
- export async function setupAlchemyServerDeploy(serverDir: string, projectDir?: string) {
24
- if (!(await fs.pathExists(serverDir))) return;
25
-
26
- await addPackageDependency({
27
- devDependencies: ["alchemy", "wrangler", "@types/node", "@cloudflare/workers-types"],
28
- projectDir: serverDir,
29
- });
30
-
31
- if (projectDir) {
32
- await addAlchemyPackagesDependencies(projectDir);
33
- }
34
-
35
- const packageJsonPath = path.join(serverDir, "package.json");
36
- if (await fs.pathExists(packageJsonPath)) {
37
- const packageJson = await fs.readJson(packageJsonPath);
38
-
39
- packageJson.scripts = {
40
- ...packageJson.scripts,
41
- dev: "alchemy dev",
42
- deploy: "alchemy deploy",
43
- destroy: "alchemy destroy",
44
- };
45
-
46
- await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
47
- }
48
- }
49
-
50
- async function addAlchemyPackagesDependencies(projectDir: string) {
51
- await addPackageDependency({
52
- devDependencies: ["@cloudflare/workers-types"],
53
- projectDir,
54
- });
55
- }
@@ -1,58 +0,0 @@
1
- import type { ProjectConfig } from "../../types";
2
- import { addPackageDependency } from "../../utils/add-package-deps";
3
- import { setupCombinedAlchemyDeploy } from "./alchemy/alchemy-combined-setup";
4
- import { setupNextAlchemyDeploy } from "./alchemy/alchemy-next-setup";
5
- import { setupNuxtAlchemyDeploy } from "./alchemy/alchemy-nuxt-setup";
6
- import { setupReactRouterAlchemyDeploy } from "./alchemy/alchemy-react-router-setup";
7
- import { setupSolidAlchemyDeploy } from "./alchemy/alchemy-solid-setup";
8
- import { setupSvelteAlchemyDeploy } from "./alchemy/alchemy-svelte-setup";
9
- import { setupTanStackRouterAlchemyDeploy } from "./alchemy/alchemy-tanstack-router-setup";
10
- import { setupTanStackStartAlchemyDeploy } from "./alchemy/alchemy-tanstack-start-setup";
11
-
12
- export async function setupWebDeploy(config: ProjectConfig) {
13
- const { webDeploy, serverDeploy, frontend, projectDir } = config;
14
- const { packageManager } = config;
15
-
16
- if (webDeploy === "none") return;
17
-
18
- if (webDeploy !== "alchemy") return;
19
-
20
- if (webDeploy === "alchemy" && serverDeploy === "alchemy") {
21
- await setupCombinedAlchemyDeploy(projectDir, packageManager, config);
22
- await addAlchemyPackagesDependencies(projectDir);
23
- return;
24
- }
25
-
26
- const isNext = frontend.includes("next");
27
- const isNuxt = frontend.includes("nuxt");
28
- const isSvelte = frontend.includes("svelte");
29
- const isTanstackRouter = frontend.includes("tanstack-router");
30
- const isTanstackStart = frontend.includes("tanstack-start");
31
- const isReactRouter = frontend.includes("react-router");
32
- const isSolid = frontend.includes("solid");
33
-
34
- if (isNext) {
35
- await setupNextAlchemyDeploy(projectDir, packageManager);
36
- } else if (isNuxt) {
37
- await setupNuxtAlchemyDeploy(projectDir, packageManager);
38
- } else if (isSvelte) {
39
- await setupSvelteAlchemyDeploy(projectDir, packageManager);
40
- } else if (isTanstackStart) {
41
- await setupTanStackStartAlchemyDeploy(projectDir, packageManager);
42
- } else if (isTanstackRouter) {
43
- await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
44
- } else if (isReactRouter) {
45
- await setupReactRouterAlchemyDeploy(projectDir, packageManager);
46
- } else if (isSolid) {
47
- await setupSolidAlchemyDeploy(projectDir, packageManager);
48
- }
49
-
50
- await addAlchemyPackagesDependencies(projectDir);
51
- }
52
-
53
- async function addAlchemyPackagesDependencies(projectDir: string) {
54
- await addPackageDependency({
55
- devDependencies: ["@cloudflare/workers-types"],
56
- projectDir,
57
- });
58
- }
package/src/index.ts DELETED
@@ -1,51 +0,0 @@
1
- /**
2
- * create-better-t-stack
3
- *
4
- * A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects.
5
- *
6
- * @example Programmatic usage
7
- * ```typescript
8
- * import { create } from "create-better-t-stack";
9
- *
10
- * const result = await create({
11
- * projectName: "my-app",
12
- * frontend: ["tanstack-router"],
13
- * backend: "hono",
14
- * database: "sqlite",
15
- * orm: "drizzle",
16
- * defaults: true,
17
- * });
18
- *
19
- * if (result.success) {
20
- * console.log(`Created at: ${result.projectDirectory}`);
21
- * }
22
- * ```
23
- */
24
-
25
- // Main programmatic API
26
- export { create, type CreateOptions, type CreateResult } from "./api";
27
-
28
- // Re-export types for type-safe usage
29
- export type {
30
- Addons,
31
- API,
32
- Auth,
33
- Backend,
34
- Database,
35
- DatabaseSetup,
36
- Examples,
37
- Frontend,
38
- ORM,
39
- PackageManager,
40
- Payments,
41
- ProjectConfig,
42
- Runtime,
43
- ServerDeploy,
44
- WebDeploy,
45
- DirectoryConflict,
46
- Template,
47
- BetterTStackConfig,
48
- } from "./types";
49
-
50
- // Legacy exports for backwards compatibility (deprecated)
51
- export { create as init } from "./api";
@@ -1,200 +0,0 @@
1
- import { groupMultiselect, isCancel } from "@clack/prompts";
2
- import { DEFAULT_CONFIG } from "../constants";
3
- import { type Addons, AddonsSchema, type Auth, type Frontend } from "../types";
4
- import { getCompatibleAddons, validateAddonCompatibility } from "../utils/compatibility-rules";
5
- import { exitCancelled } from "../utils/errors";
6
-
7
- type AddonOption = {
8
- value: Addons;
9
- label: string;
10
- hint: string;
11
- };
12
-
13
- function getAddonDisplay(addon: Addons): { label: string; hint: string } {
14
- let label: string;
15
- let hint: string;
16
-
17
- switch (addon) {
18
- case "turborepo":
19
- label = "Turborepo";
20
- hint = "High-performance build system";
21
- break;
22
- case "pwa":
23
- label = "PWA";
24
- hint = "Make your app installable and work offline";
25
- break;
26
- case "tauri":
27
- label = "Tauri";
28
- hint = "Build native desktop apps from your web frontend";
29
- break;
30
- case "biome":
31
- label = "Biome";
32
- hint = "Format, lint, and more";
33
- break;
34
- case "oxlint":
35
- label = "Oxlint";
36
- hint = "Oxlint + Oxfmt (linting & formatting)";
37
- break;
38
- case "ultracite":
39
- label = "Ultracite";
40
- hint = "Zero-config Biome preset with AI integration";
41
- break;
42
- case "ruler":
43
- label = "Ruler";
44
- hint = "Centralize your AI rules";
45
- break;
46
- case "husky":
47
- label = "Husky";
48
- hint = "Modern native Git hooks made easy";
49
- break;
50
- case "starlight":
51
- label = "Starlight";
52
- hint = "Build stellar docs with astro";
53
- break;
54
- case "fumadocs":
55
- label = "Fumadocs";
56
- hint = "Build excellent documentation site";
57
- break;
58
- case "opentui":
59
- label = "OpenTUI";
60
- hint = "Build terminal user interfaces";
61
- break;
62
- case "wxt":
63
- label = "WXT";
64
- hint = "Build browser extensions";
65
- break;
66
- default:
67
- label = addon;
68
- hint = `Add ${addon}`;
69
- }
70
-
71
- return { label, hint };
72
- }
73
-
74
- const ADDON_GROUPS = {
75
- Documentation: ["starlight", "fumadocs"],
76
- Linting: ["biome", "oxlint", "ultracite"],
77
- Other: ["ruler", "pwa", "tauri", "husky", "opentui", "wxt", "turborepo"],
78
- };
79
-
80
- export async function getAddonsChoice(addons?: Addons[], frontends?: Frontend[], auth?: Auth) {
81
- if (addons !== undefined) return addons;
82
-
83
- const allAddons = AddonsSchema.options.filter((addon) => addon !== "none");
84
- const groupedOptions: Record<string, AddonOption[]> = {
85
- Documentation: [],
86
- Linting: [],
87
- Other: [],
88
- };
89
-
90
- const frontendsArray = frontends || [];
91
-
92
- for (const addon of allAddons) {
93
- const { isCompatible } = validateAddonCompatibility(addon, frontendsArray, auth);
94
- if (!isCompatible) continue;
95
-
96
- const { label, hint } = getAddonDisplay(addon);
97
- const option = { value: addon, label, hint };
98
-
99
- if (ADDON_GROUPS.Documentation.includes(addon)) {
100
- groupedOptions.Documentation.push(option);
101
- } else if (ADDON_GROUPS.Linting.includes(addon)) {
102
- groupedOptions.Linting.push(option);
103
- } else if (ADDON_GROUPS.Other.includes(addon)) {
104
- groupedOptions.Other.push(option);
105
- }
106
- }
107
-
108
- Object.keys(groupedOptions).forEach((group) => {
109
- if (groupedOptions[group].length === 0) {
110
- delete groupedOptions[group];
111
- } else {
112
- const groupOrder = ADDON_GROUPS[group as keyof typeof ADDON_GROUPS] || [];
113
- groupedOptions[group].sort((a, b) => {
114
- const indexA = groupOrder.indexOf(a.value);
115
- const indexB = groupOrder.indexOf(b.value);
116
- return indexA - indexB;
117
- });
118
- }
119
- });
120
-
121
- const initialValues = DEFAULT_CONFIG.addons.filter((addonValue) =>
122
- Object.values(groupedOptions).some((options) =>
123
- options.some((opt) => opt.value === addonValue),
124
- ),
125
- );
126
-
127
- const response = await groupMultiselect<Addons>({
128
- message: "Select addons",
129
- options: groupedOptions,
130
- initialValues: initialValues,
131
- required: false,
132
- selectableGroups: false,
133
- });
134
-
135
- if (isCancel(response)) return exitCancelled("Operation cancelled");
136
-
137
- return response;
138
- }
139
-
140
- export async function getAddonsToAdd(
141
- frontend: Frontend[],
142
- existingAddons: Addons[] = [],
143
- auth?: Auth,
144
- ) {
145
- const groupedOptions: Record<string, AddonOption[]> = {
146
- Documentation: [],
147
- Linting: [],
148
- Other: [],
149
- };
150
-
151
- const frontendArray = frontend || [];
152
-
153
- const compatibleAddons = getCompatibleAddons(
154
- AddonsSchema.options.filter((addon) => addon !== "none"),
155
- frontendArray,
156
- existingAddons,
157
- auth,
158
- );
159
-
160
- for (const addon of compatibleAddons) {
161
- const { label, hint } = getAddonDisplay(addon);
162
- const option = { value: addon, label, hint };
163
-
164
- if (ADDON_GROUPS.Documentation.includes(addon)) {
165
- groupedOptions.Documentation.push(option);
166
- } else if (ADDON_GROUPS.Linting.includes(addon)) {
167
- groupedOptions.Linting.push(option);
168
- } else if (ADDON_GROUPS.Other.includes(addon)) {
169
- groupedOptions.Other.push(option);
170
- }
171
- }
172
-
173
- Object.keys(groupedOptions).forEach((group) => {
174
- if (groupedOptions[group].length === 0) {
175
- delete groupedOptions[group];
176
- } else {
177
- const groupOrder = ADDON_GROUPS[group as keyof typeof ADDON_GROUPS] || [];
178
- groupedOptions[group].sort((a, b) => {
179
- const indexA = groupOrder.indexOf(a.value);
180
- const indexB = groupOrder.indexOf(b.value);
181
- return indexA - indexB;
182
- });
183
- }
184
- });
185
-
186
- if (Object.keys(groupedOptions).length === 0) {
187
- return [];
188
- }
189
-
190
- const response = await groupMultiselect<Addons>({
191
- message: "Select addons to add",
192
- options: groupedOptions,
193
- required: false,
194
- selectableGroups: false,
195
- });
196
-
197
- if (isCancel(response)) return exitCancelled("Operation cancelled");
198
-
199
- return response;
200
- }