create-better-t-stack 2.23.1 → 2.24.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.
- package/dist/index.js +65 -33
- package/package.json +1 -1
- package/templates/auth/server/base/src/lib/auth.ts.hbs +1 -1
- package/templates/deploy/web/react/tanstack-start/wrangler.jsonc.hbs +20 -0
- package/templates/frontend/native/unistyles/_gitignore +1 -1
- package/templates/frontend/native/unistyles/package.json.hbs +19 -19
- package/templates/frontend/react/web-base/_gitignore +2 -0
package/dist/index.js
CHANGED
|
@@ -130,6 +130,15 @@ const ADDON_COMPATIBILITY = {
|
|
|
130
130
|
starlight: [],
|
|
131
131
|
none: []
|
|
132
132
|
};
|
|
133
|
+
const WEB_FRAMEWORKS = [
|
|
134
|
+
"tanstack-router",
|
|
135
|
+
"react-router",
|
|
136
|
+
"tanstack-start",
|
|
137
|
+
"next",
|
|
138
|
+
"nuxt",
|
|
139
|
+
"svelte",
|
|
140
|
+
"solid"
|
|
141
|
+
];
|
|
133
142
|
|
|
134
143
|
//#endregion
|
|
135
144
|
//#region src/types.ts
|
|
@@ -834,14 +843,9 @@ async function getRuntimeChoice(runtime, backend) {
|
|
|
834
843
|
|
|
835
844
|
//#endregion
|
|
836
845
|
//#region src/prompts/web-deploy.ts
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
"solid",
|
|
841
|
-
"next",
|
|
842
|
-
"nuxt",
|
|
843
|
-
"svelte"
|
|
844
|
-
];
|
|
846
|
+
function hasWebFrontend(frontends) {
|
|
847
|
+
return frontends.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
848
|
+
}
|
|
845
849
|
function getDeploymentDisplay(deployment) {
|
|
846
850
|
if (deployment === "workers") return {
|
|
847
851
|
label: "Cloudflare Workers",
|
|
@@ -854,8 +858,7 @@ function getDeploymentDisplay(deployment) {
|
|
|
854
858
|
}
|
|
855
859
|
async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []) {
|
|
856
860
|
if (deployment !== void 0) return deployment;
|
|
857
|
-
|
|
858
|
-
if (!hasCompatibleFrontend) return "none";
|
|
861
|
+
if (!hasWebFrontend(frontend)) return "none";
|
|
859
862
|
const options = [{
|
|
860
863
|
value: "workers",
|
|
861
864
|
label: "Cloudflare Workers",
|
|
@@ -877,8 +880,9 @@ async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []
|
|
|
877
880
|
return response;
|
|
878
881
|
}
|
|
879
882
|
async function getDeploymentToAdd(frontend, existingDeployment) {
|
|
883
|
+
if (!hasWebFrontend(frontend)) return "none";
|
|
880
884
|
const options = [];
|
|
881
|
-
if (
|
|
885
|
+
if (existingDeployment !== "workers") {
|
|
882
886
|
const { label, hint } = getDeploymentDisplay("workers");
|
|
883
887
|
options.push({
|
|
884
888
|
value: "workers",
|
|
@@ -1322,7 +1326,7 @@ function processAndValidateFlags(options, providedFlags, projectName) {
|
|
|
1322
1326
|
config.frontend = [];
|
|
1323
1327
|
} else {
|
|
1324
1328
|
const validOptions = options.frontend.filter((f) => f !== "none");
|
|
1325
|
-
const webFrontends = validOptions.filter((f) => f
|
|
1329
|
+
const webFrontends = validOptions.filter((f) => WEB_FRAMEWORKS.includes(f));
|
|
1326
1330
|
const nativeFrontends = validOptions.filter((f) => f === "native-nativewind" || f === "native-unistyles");
|
|
1327
1331
|
if (webFrontends.length > 1) {
|
|
1328
1332
|
consola$1.fatal("Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid");
|
|
@@ -1483,12 +1487,10 @@ function processAndValidateFlags(options, providedFlags, projectName) {
|
|
|
1483
1487
|
consola$1.fatal("MongoDB database is not compatible with Cloudflare Workers runtime. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.");
|
|
1484
1488
|
process.exit(1);
|
|
1485
1489
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
process.exit(1);
|
|
1491
|
-
}
|
|
1490
|
+
const hasWebFrontendFlag = (config.frontend ?? []).some((f) => WEB_FRAMEWORKS.includes(f));
|
|
1491
|
+
if (config.webDeploy && config.webDeploy !== "none" && !hasWebFrontendFlag) {
|
|
1492
|
+
consola$1.fatal("'--web-deploy' requires a web frontend. Please select a web frontend or set '--web-deploy none'.");
|
|
1493
|
+
process.exit(1);
|
|
1492
1494
|
}
|
|
1493
1495
|
return config;
|
|
1494
1496
|
}
|
|
@@ -2283,6 +2285,7 @@ async function setupDeploymentTemplates(projectDir, context) {
|
|
|
2283
2285
|
const frontends = context.frontend;
|
|
2284
2286
|
const templateMap = {
|
|
2285
2287
|
"tanstack-router": "react/tanstack-router",
|
|
2288
|
+
"tanstack-start": "react/tanstack-start",
|
|
2286
2289
|
"react-router": "react/react-router",
|
|
2287
2290
|
solid: "solid",
|
|
2288
2291
|
next: "react/next",
|
|
@@ -2450,6 +2453,44 @@ async function setupSvelteWorkersDeploy(projectDir, packageManager) {
|
|
|
2450
2453
|
}
|
|
2451
2454
|
}
|
|
2452
2455
|
|
|
2456
|
+
//#endregion
|
|
2457
|
+
//#region src/helpers/setup/workers-tanstack-start-setup.ts
|
|
2458
|
+
async function setupTanstackStartWorkersDeploy(projectDir, packageManager) {
|
|
2459
|
+
const webAppDir = path.join(projectDir, "apps/web");
|
|
2460
|
+
if (!await fs.pathExists(webAppDir)) return;
|
|
2461
|
+
await addPackageDependency({
|
|
2462
|
+
devDependencies: ["wrangler"],
|
|
2463
|
+
projectDir: webAppDir
|
|
2464
|
+
});
|
|
2465
|
+
const pkgPath = path.join(webAppDir, "package.json");
|
|
2466
|
+
if (await fs.pathExists(pkgPath)) {
|
|
2467
|
+
const pkg = await fs.readJson(pkgPath);
|
|
2468
|
+
pkg.scripts = {
|
|
2469
|
+
...pkg.scripts,
|
|
2470
|
+
deploy: `${packageManager} run build && wrangler deploy`,
|
|
2471
|
+
"cf-typegen": "wrangler types --env-interface Env"
|
|
2472
|
+
};
|
|
2473
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
2474
|
+
}
|
|
2475
|
+
const viteConfigPath = path.join(webAppDir, "vite.config.ts");
|
|
2476
|
+
if (!await fs.pathExists(viteConfigPath)) return;
|
|
2477
|
+
const sourceFile = tsProject.addSourceFileAtPathIfExists(viteConfigPath);
|
|
2478
|
+
if (!sourceFile) return;
|
|
2479
|
+
const defineCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((expr) => {
|
|
2480
|
+
const expression = expr.getExpression();
|
|
2481
|
+
return Node.isIdentifier(expression) && expression.getText() === "defineConfig";
|
|
2482
|
+
});
|
|
2483
|
+
if (!defineCall) return;
|
|
2484
|
+
const configObj = defineCall.getArguments()[0];
|
|
2485
|
+
if (!configObj) return;
|
|
2486
|
+
const pluginsArray = ensureArrayProperty(configObj, "plugins");
|
|
2487
|
+
const tanstackPluginIndex = pluginsArray.getElements().findIndex((el) => el.getText().includes("tanstackStart("));
|
|
2488
|
+
const tanstackPluginText = "tanstackStart({ target: \"cloudflare-module\" })";
|
|
2489
|
+
if (tanstackPluginIndex === -1) pluginsArray.addElement(tanstackPluginText);
|
|
2490
|
+
else pluginsArray.getElements()[tanstackPluginIndex].replaceWithText(tanstackPluginText);
|
|
2491
|
+
await tsProject.save();
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2453
2494
|
//#endregion
|
|
2454
2495
|
//#region src/helpers/setup/workers-vite-setup.ts
|
|
2455
2496
|
async function setupWorkersVitePlugin(projectDir) {
|
|
@@ -2492,11 +2533,13 @@ async function setupWebDeploy(config) {
|
|
|
2492
2533
|
const isNuxt = frontend.includes("nuxt");
|
|
2493
2534
|
const isSvelte = frontend.includes("svelte");
|
|
2494
2535
|
const isTanstackRouter = frontend.includes("tanstack-router");
|
|
2536
|
+
const isTanstackStart = frontend.includes("tanstack-start");
|
|
2495
2537
|
const isReactRouter = frontend.includes("react-router");
|
|
2496
2538
|
const isSolid = frontend.includes("solid");
|
|
2497
2539
|
if (isNext) await setupNextWorkersDeploy(projectDir, packageManager);
|
|
2498
2540
|
else if (isNuxt) await setupNuxtWorkersDeploy(projectDir, packageManager);
|
|
2499
2541
|
else if (isSvelte) await setupSvelteWorkersDeploy(projectDir, packageManager);
|
|
2542
|
+
else if (isTanstackStart) await setupTanstackStartWorkersDeploy(projectDir, packageManager);
|
|
2500
2543
|
else if (isTanstackRouter || isReactRouter || isSolid) await setupWorkersWebDeploy(projectDir, packageManager);
|
|
2501
2544
|
}
|
|
2502
2545
|
async function setupWorkersWebDeploy(projectDir, pkgManager) {
|
|
@@ -2550,17 +2593,6 @@ async function addDeploymentToProject(input) {
|
|
|
2550
2593
|
const detectedConfig = await detectProjectConfig(projectDir);
|
|
2551
2594
|
if (!detectedConfig) exitWithError("Could not detect the project configuration. Please ensure this is a valid Better-T Stack project.");
|
|
2552
2595
|
if (detectedConfig.webDeploy === input.webDeploy) exitWithError(`${input.webDeploy} deployment is already configured for this project.`);
|
|
2553
|
-
if (input.webDeploy === "workers") {
|
|
2554
|
-
const compatibleFrontends = [
|
|
2555
|
-
"tanstack-router",
|
|
2556
|
-
"react-router",
|
|
2557
|
-
"solid",
|
|
2558
|
-
"next",
|
|
2559
|
-
"svelte"
|
|
2560
|
-
];
|
|
2561
|
-
const hasCompatible = detectedConfig.frontend?.some((f) => compatibleFrontends.includes(f));
|
|
2562
|
-
if (!hasCompatible) exitWithError("Cloudflare Workers deployment requires a compatible web frontend (tanstack-router, react-router, solid, next, or svelte).");
|
|
2563
|
-
}
|
|
2564
2596
|
const config = {
|
|
2565
2597
|
projectName: detectedConfig.projectName || path.basename(projectDir),
|
|
2566
2598
|
projectDir,
|
|
@@ -2814,7 +2846,7 @@ async function setupAuth(config) {
|
|
|
2814
2846
|
dependencies: ["better-auth"],
|
|
2815
2847
|
projectDir: serverDir
|
|
2816
2848
|
});
|
|
2817
|
-
const hasWebFrontend = frontend.some((f) => [
|
|
2849
|
+
const hasWebFrontend$1 = frontend.some((f) => [
|
|
2818
2850
|
"react-router",
|
|
2819
2851
|
"tanstack-router",
|
|
2820
2852
|
"tanstack-start",
|
|
@@ -2823,7 +2855,7 @@ async function setupAuth(config) {
|
|
|
2823
2855
|
"svelte",
|
|
2824
2856
|
"solid"
|
|
2825
2857
|
].includes(f));
|
|
2826
|
-
if (hasWebFrontend && clientDirExists) await addPackageDependency({
|
|
2858
|
+
if (hasWebFrontend$1 && clientDirExists) await addPackageDependency({
|
|
2827
2859
|
dependencies: ["better-auth"],
|
|
2828
2860
|
projectDir: clientDir
|
|
2829
2861
|
});
|
|
@@ -2946,8 +2978,8 @@ async function setupEnvironmentVariables(config) {
|
|
|
2946
2978
|
const hasNuxt = frontend.includes("nuxt");
|
|
2947
2979
|
const hasSvelte = frontend.includes("svelte");
|
|
2948
2980
|
const hasSolid = frontend.includes("solid");
|
|
2949
|
-
const hasWebFrontend = hasReactRouter || hasTanStackRouter || hasTanStackStart || hasNextJs || hasNuxt || hasSolid || hasSvelte;
|
|
2950
|
-
if (hasWebFrontend) {
|
|
2981
|
+
const hasWebFrontend$1 = hasReactRouter || hasTanStackRouter || hasTanStackStart || hasNextJs || hasNuxt || hasSolid || hasSvelte;
|
|
2982
|
+
if (hasWebFrontend$1) {
|
|
2951
2983
|
const clientDir = path.join(projectDir, "apps/web");
|
|
2952
2984
|
if (await fs.pathExists(clientDir)) {
|
|
2953
2985
|
let envVarName = "VITE_SERVER_URL";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.24.1",
|
|
4
4
|
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -68,7 +68,7 @@ import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
|
68
68
|
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
69
69
|
import { expo } from "@better-auth/expo";
|
|
70
70
|
{{/if}}
|
|
71
|
-
import { db } from "
|
|
71
|
+
import { db } from "../db";
|
|
72
72
|
import * as schema from "../db/schema/auth";
|
|
73
73
|
import { env } from "cloudflare:workers";
|
|
74
74
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../node_modules/wrangler/config-schema.json",
|
|
3
|
+
"name": "{{projectName}}",
|
|
4
|
+
"main": ".output/server/index.mjs",
|
|
5
|
+
"compatibility_date": "2025-07-05",
|
|
6
|
+
"compatibility_flags": ["nodejs_compat"],
|
|
7
|
+
"assets": {
|
|
8
|
+
"directory": ".output/public",
|
|
9
|
+
},
|
|
10
|
+
"observability": {
|
|
11
|
+
"enabled": true,
|
|
12
|
+
},
|
|
13
|
+
// "kv_namespaces": [
|
|
14
|
+
// {
|
|
15
|
+
// "binding": "CACHE",
|
|
16
|
+
// "id": "<Your KV ID>",
|
|
17
|
+
// },
|
|
18
|
+
// ],
|
|
19
|
+
}
|
|
20
|
+
|
|
@@ -9,40 +9,40 @@
|
|
|
9
9
|
"web": "expo start --web"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@expo/vector-icons": "^14.
|
|
13
|
-
"@react-navigation/bottom-tabs": "^7.
|
|
14
|
-
"@react-navigation/drawer": "^7.
|
|
15
|
-
"@react-navigation/native": "^7.
|
|
12
|
+
"@expo/vector-icons": "^14.1.0",
|
|
13
|
+
"@react-navigation/bottom-tabs": "^7.3.10",
|
|
14
|
+
"@react-navigation/drawer": "^7.3.9",
|
|
15
|
+
"@react-navigation/native": "^7.1.6",
|
|
16
16
|
{{#if (includes examples "ai")}}
|
|
17
17
|
"@stardazed/streams-text-encoding": "^1.0.2",
|
|
18
18
|
"@ungap/structured-clone": "^1.3.0",
|
|
19
19
|
{{/if}}
|
|
20
|
-
"@tanstack/react-form": "^1.0
|
|
21
|
-
"babel-plugin-react-compiler": "^19.
|
|
22
|
-
"expo": "^53.0.
|
|
23
|
-
"expo-constants": "~17.1.
|
|
24
|
-
"expo-linking": "~7.1.
|
|
25
|
-
"expo-router": "~5.
|
|
20
|
+
"@tanstack/react-form": "^1.14.0",
|
|
21
|
+
"babel-plugin-react-compiler": "^19.1.0-rc.2",
|
|
22
|
+
"expo": "^53.0.17",
|
|
23
|
+
"expo-constants": "~17.1.7",
|
|
24
|
+
"expo-linking": "~7.1.7",
|
|
25
|
+
"expo-router": "~5.1.3",
|
|
26
26
|
"expo-secure-store": "~14.2.3",
|
|
27
27
|
"expo-status-bar": "~2.2.3",
|
|
28
|
-
"expo-system-ui": "~5.0.
|
|
29
|
-
"expo-dev-client": "~5.
|
|
30
|
-
"expo-web-browser": "~14.
|
|
28
|
+
"expo-system-ui": "~5.0.10",
|
|
29
|
+
"expo-dev-client": "~5.2.4",
|
|
30
|
+
"expo-web-browser": "~14.2.0",
|
|
31
31
|
"react": "19.0.0",
|
|
32
32
|
"react-dom": "19.0.0",
|
|
33
|
-
"react-native": "0.79.
|
|
33
|
+
"react-native": "0.79.5",
|
|
34
34
|
"react-native-edge-to-edge": "1.6.0",
|
|
35
35
|
"react-native-gesture-handler": "~2.24.0",
|
|
36
|
-
"react-native-nitro-modules": "0.
|
|
36
|
+
"react-native-nitro-modules": "0.26.3",
|
|
37
37
|
"react-native-reanimated": "~3.17.4",
|
|
38
38
|
"react-native-safe-area-context": "5.4.0",
|
|
39
|
-
"react-native-screens": "~4.
|
|
40
|
-
"react-native-unistyles": "^3.0.0
|
|
39
|
+
"react-native-screens": "~4.11.1",
|
|
40
|
+
"react-native-unistyles": "^3.0.0",
|
|
41
41
|
"react-native-web": "^0.20.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"ajv": "^8.
|
|
45
|
-
"@babel/core": "^7.
|
|
44
|
+
"ajv": "^8.17.1",
|
|
45
|
+
"@babel/core": "^7.28.0",
|
|
46
46
|
"@types/react": "~19.0.10",
|
|
47
47
|
"typescript": "~5.8.3"
|
|
48
48
|
},
|