create-better-t-stack 2.35.3 → 2.35.5-canary.449f72a5
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/README.md +34 -0
- package/dist/cli.js +420 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/{src-dID--AYy.js → src-BN1_H4yj.js} +49 -25
- package/package.json +2 -1
- package/templates/backend/server/server-base/tsconfig.json.hbs +9 -14
- package/templates/frontend/react/tanstack-start/package.json.hbs +1 -1
- package/templates/frontend/react/tanstack-start/{vite.config.ts → vite.config.ts.hbs} +7 -1
package/README.md
CHANGED
|
@@ -45,6 +45,40 @@ Follow the prompts to configure your project or use the `--yes` flag for default
|
|
|
45
45
|
| **Examples** | • Todo app<br>• AI Chat interface (using Vercel AI SDK) |
|
|
46
46
|
| **Developer Experience** | • Automatic Git initialization<br>• Package manager choice (npm, pnpm, bun)<br>• Automatic dependency installation |
|
|
47
47
|
|
|
48
|
+
## MCP (Model Context Protocol) Support
|
|
49
|
+
|
|
50
|
+
The CLI now supports the Model Context Protocol, allowing AI assistants to scaffold projects programmatically. This enables seamless integration with AI coding assistants like Claude, Cursor, and others.
|
|
51
|
+
|
|
52
|
+
### Quick Start with MCP
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Start the MCP server
|
|
56
|
+
npx create-better-t-stack --mcp
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### MCP Client Configuration
|
|
60
|
+
|
|
61
|
+
Add to your MCP client configuration:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"mcpServers": {
|
|
66
|
+
"better-t-stack": {
|
|
67
|
+
"command": "npx",
|
|
68
|
+
"args": ["-y", "create-better-t-stack", "--mcp"]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Available MCP Tools
|
|
75
|
+
|
|
76
|
+
- **`create_project`** - Create a new Better-T Stack project with specified configuration
|
|
77
|
+
- **`list_configurations`** - List all available configuration options
|
|
78
|
+
- **`get_project_info`** - Get information about an existing project
|
|
79
|
+
|
|
80
|
+
For detailed MCP documentation, see [MCP_README.md](./MCP_README.md).
|
|
81
|
+
|
|
48
82
|
## Usage
|
|
49
83
|
|
|
50
84
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,426 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createBtsCli } from "./src-
|
|
2
|
+
import { createBtsCli, init } from "./src-BN1_H4yj.js";
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
3
6
|
|
|
7
|
+
//#region src/mcp-server.ts
|
|
8
|
+
const server = new Server({
|
|
9
|
+
name: "create-better-t-stack",
|
|
10
|
+
version: "1.0.0"
|
|
11
|
+
});
|
|
12
|
+
const createProjectTool = {
|
|
13
|
+
name: "create_project",
|
|
14
|
+
description: "Create a new Better-T Stack project with the specified configuration",
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: "object",
|
|
17
|
+
properties: {
|
|
18
|
+
projectName: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "Name of the project to create"
|
|
21
|
+
},
|
|
22
|
+
database: {
|
|
23
|
+
type: "string",
|
|
24
|
+
enum: [
|
|
25
|
+
"none",
|
|
26
|
+
"sqlite",
|
|
27
|
+
"postgres",
|
|
28
|
+
"mysql",
|
|
29
|
+
"mongodb"
|
|
30
|
+
],
|
|
31
|
+
description: "Database type to use"
|
|
32
|
+
},
|
|
33
|
+
orm: {
|
|
34
|
+
type: "string",
|
|
35
|
+
enum: [
|
|
36
|
+
"drizzle",
|
|
37
|
+
"prisma",
|
|
38
|
+
"mongoose",
|
|
39
|
+
"none"
|
|
40
|
+
],
|
|
41
|
+
description: "ORM to use"
|
|
42
|
+
},
|
|
43
|
+
backend: {
|
|
44
|
+
type: "string",
|
|
45
|
+
enum: [
|
|
46
|
+
"hono",
|
|
47
|
+
"express",
|
|
48
|
+
"fastify",
|
|
49
|
+
"next",
|
|
50
|
+
"elysia",
|
|
51
|
+
"convex",
|
|
52
|
+
"none"
|
|
53
|
+
],
|
|
54
|
+
description: "Backend framework to use"
|
|
55
|
+
},
|
|
56
|
+
runtime: {
|
|
57
|
+
type: "string",
|
|
58
|
+
enum: [
|
|
59
|
+
"bun",
|
|
60
|
+
"node",
|
|
61
|
+
"workers",
|
|
62
|
+
"none"
|
|
63
|
+
],
|
|
64
|
+
description: "Runtime environment"
|
|
65
|
+
},
|
|
66
|
+
frontend: {
|
|
67
|
+
type: "array",
|
|
68
|
+
items: {
|
|
69
|
+
type: "string",
|
|
70
|
+
enum: [
|
|
71
|
+
"tanstack-router",
|
|
72
|
+
"react-router",
|
|
73
|
+
"tanstack-start",
|
|
74
|
+
"next",
|
|
75
|
+
"nuxt",
|
|
76
|
+
"native-nativewind",
|
|
77
|
+
"native-unistyles",
|
|
78
|
+
"svelte",
|
|
79
|
+
"solid",
|
|
80
|
+
"none"
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
description: "Frontend frameworks to use"
|
|
84
|
+
},
|
|
85
|
+
addons: {
|
|
86
|
+
type: "array",
|
|
87
|
+
items: {
|
|
88
|
+
type: "string",
|
|
89
|
+
enum: [
|
|
90
|
+
"pwa",
|
|
91
|
+
"tauri",
|
|
92
|
+
"starlight",
|
|
93
|
+
"biome",
|
|
94
|
+
"husky",
|
|
95
|
+
"ruler",
|
|
96
|
+
"turborepo",
|
|
97
|
+
"fumadocs",
|
|
98
|
+
"ultracite",
|
|
99
|
+
"oxlint",
|
|
100
|
+
"none"
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
description: "Addons to include"
|
|
104
|
+
},
|
|
105
|
+
examples: {
|
|
106
|
+
type: "array",
|
|
107
|
+
items: {
|
|
108
|
+
type: "string",
|
|
109
|
+
enum: [
|
|
110
|
+
"todo",
|
|
111
|
+
"ai",
|
|
112
|
+
"none"
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
description: "Example templates to include"
|
|
116
|
+
},
|
|
117
|
+
auth: {
|
|
118
|
+
type: "boolean",
|
|
119
|
+
description: "Whether to include authentication"
|
|
120
|
+
},
|
|
121
|
+
git: {
|
|
122
|
+
type: "boolean",
|
|
123
|
+
description: "Whether to initialize git repository"
|
|
124
|
+
},
|
|
125
|
+
packageManager: {
|
|
126
|
+
type: "string",
|
|
127
|
+
enum: [
|
|
128
|
+
"npm",
|
|
129
|
+
"pnpm",
|
|
130
|
+
"bun"
|
|
131
|
+
],
|
|
132
|
+
description: "Package manager to use"
|
|
133
|
+
},
|
|
134
|
+
install: {
|
|
135
|
+
type: "boolean",
|
|
136
|
+
description: "Whether to install dependencies"
|
|
137
|
+
},
|
|
138
|
+
dbSetup: {
|
|
139
|
+
type: "string",
|
|
140
|
+
enum: [
|
|
141
|
+
"turso",
|
|
142
|
+
"neon",
|
|
143
|
+
"prisma-postgres",
|
|
144
|
+
"mongodb-atlas",
|
|
145
|
+
"supabase",
|
|
146
|
+
"d1",
|
|
147
|
+
"docker",
|
|
148
|
+
"none"
|
|
149
|
+
],
|
|
150
|
+
description: "Database setup configuration"
|
|
151
|
+
},
|
|
152
|
+
api: {
|
|
153
|
+
type: "string",
|
|
154
|
+
enum: [
|
|
155
|
+
"trpc",
|
|
156
|
+
"orpc",
|
|
157
|
+
"none"
|
|
158
|
+
],
|
|
159
|
+
description: "API type to use"
|
|
160
|
+
},
|
|
161
|
+
webDeploy: {
|
|
162
|
+
type: "string",
|
|
163
|
+
enum: [
|
|
164
|
+
"vercel",
|
|
165
|
+
"netlify",
|
|
166
|
+
"wrangler",
|
|
167
|
+
"alchemy",
|
|
168
|
+
"none"
|
|
169
|
+
],
|
|
170
|
+
description: "Web deployment platform"
|
|
171
|
+
},
|
|
172
|
+
serverDeploy: {
|
|
173
|
+
type: "string",
|
|
174
|
+
enum: [
|
|
175
|
+
"wrangler",
|
|
176
|
+
"alchemy",
|
|
177
|
+
"none"
|
|
178
|
+
],
|
|
179
|
+
description: "Server deployment platform"
|
|
180
|
+
},
|
|
181
|
+
directoryConflict: {
|
|
182
|
+
type: "string",
|
|
183
|
+
enum: [
|
|
184
|
+
"merge",
|
|
185
|
+
"overwrite",
|
|
186
|
+
"increment",
|
|
187
|
+
"error"
|
|
188
|
+
],
|
|
189
|
+
description: "How to handle directory conflicts"
|
|
190
|
+
},
|
|
191
|
+
yes: {
|
|
192
|
+
type: "boolean",
|
|
193
|
+
description: "Use default configuration without prompts"
|
|
194
|
+
},
|
|
195
|
+
yolo: {
|
|
196
|
+
type: "boolean",
|
|
197
|
+
description: "Bypass validations and compatibility checks"
|
|
198
|
+
},
|
|
199
|
+
verbose: {
|
|
200
|
+
type: "boolean",
|
|
201
|
+
description: "Show detailed result information"
|
|
202
|
+
},
|
|
203
|
+
disableAnalytics: {
|
|
204
|
+
type: "boolean",
|
|
205
|
+
description: "Disable analytics"
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
required: ["projectName"]
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const listConfigurationsTool = {
|
|
212
|
+
name: "list_configurations",
|
|
213
|
+
description: "List available configurations for Better-T Stack projects",
|
|
214
|
+
inputSchema: {
|
|
215
|
+
type: "object",
|
|
216
|
+
properties: {}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
const getProjectInfoTool = {
|
|
220
|
+
name: "get_project_info",
|
|
221
|
+
description: "Get information about a Better-T Stack project",
|
|
222
|
+
inputSchema: {
|
|
223
|
+
type: "object",
|
|
224
|
+
properties: { projectPath: {
|
|
225
|
+
type: "string",
|
|
226
|
+
description: "Path to the project directory"
|
|
227
|
+
} },
|
|
228
|
+
required: ["projectPath"]
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
232
|
+
return { tools: [
|
|
233
|
+
createProjectTool,
|
|
234
|
+
listConfigurationsTool,
|
|
235
|
+
getProjectInfoTool
|
|
236
|
+
] };
|
|
237
|
+
});
|
|
238
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
239
|
+
const { name, arguments: args } = request.params;
|
|
240
|
+
switch (name) {
|
|
241
|
+
case "create_project": try {
|
|
242
|
+
const projectConfig = {
|
|
243
|
+
projectName: args?.projectName,
|
|
244
|
+
database: args?.database,
|
|
245
|
+
orm: args?.orm,
|
|
246
|
+
backend: args?.backend,
|
|
247
|
+
runtime: args?.runtime,
|
|
248
|
+
frontend: args?.frontend,
|
|
249
|
+
addons: args?.addons,
|
|
250
|
+
examples: args?.examples,
|
|
251
|
+
auth: args?.auth,
|
|
252
|
+
git: args?.git,
|
|
253
|
+
packageManager: args?.packageManager,
|
|
254
|
+
install: args?.install,
|
|
255
|
+
dbSetup: args?.dbSetup,
|
|
256
|
+
api: args?.api,
|
|
257
|
+
webDeploy: args?.webDeploy,
|
|
258
|
+
serverDeploy: args?.serverDeploy,
|
|
259
|
+
directoryConflict: args?.directoryConflict,
|
|
260
|
+
yes: args?.yes,
|
|
261
|
+
yolo: args?.yolo,
|
|
262
|
+
verbose: args?.verbose,
|
|
263
|
+
disableAnalytics: args?.disableAnalytics
|
|
264
|
+
};
|
|
265
|
+
const result = await init(args?.projectName, projectConfig);
|
|
266
|
+
return { content: [{
|
|
267
|
+
type: "text",
|
|
268
|
+
text: JSON.stringify(result, null, 2)
|
|
269
|
+
}] };
|
|
270
|
+
} catch (error) {
|
|
271
|
+
return {
|
|
272
|
+
content: [{
|
|
273
|
+
type: "text",
|
|
274
|
+
text: `Error creating project: ${error instanceof Error ? error.message : String(error)}`
|
|
275
|
+
}],
|
|
276
|
+
isError: true
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
case "list_configurations": {
|
|
280
|
+
const configurations = {
|
|
281
|
+
databases: [
|
|
282
|
+
"none",
|
|
283
|
+
"sqlite",
|
|
284
|
+
"postgres",
|
|
285
|
+
"mysql",
|
|
286
|
+
"mongodb"
|
|
287
|
+
],
|
|
288
|
+
orms: [
|
|
289
|
+
"drizzle",
|
|
290
|
+
"prisma",
|
|
291
|
+
"mongoose",
|
|
292
|
+
"none"
|
|
293
|
+
],
|
|
294
|
+
backends: [
|
|
295
|
+
"hono",
|
|
296
|
+
"express",
|
|
297
|
+
"fastify",
|
|
298
|
+
"next",
|
|
299
|
+
"elysia",
|
|
300
|
+
"convex",
|
|
301
|
+
"none"
|
|
302
|
+
],
|
|
303
|
+
runtimes: [
|
|
304
|
+
"bun",
|
|
305
|
+
"node",
|
|
306
|
+
"workers",
|
|
307
|
+
"none"
|
|
308
|
+
],
|
|
309
|
+
frontends: [
|
|
310
|
+
"tanstack-router",
|
|
311
|
+
"react-router",
|
|
312
|
+
"tanstack-start",
|
|
313
|
+
"next",
|
|
314
|
+
"nuxt",
|
|
315
|
+
"native-nativewind",
|
|
316
|
+
"native-unistyles",
|
|
317
|
+
"svelte",
|
|
318
|
+
"solid",
|
|
319
|
+
"none"
|
|
320
|
+
],
|
|
321
|
+
addons: [
|
|
322
|
+
"pwa",
|
|
323
|
+
"tauri",
|
|
324
|
+
"starlight",
|
|
325
|
+
"biome",
|
|
326
|
+
"husky",
|
|
327
|
+
"ruler",
|
|
328
|
+
"turborepo",
|
|
329
|
+
"fumadocs",
|
|
330
|
+
"ultracite",
|
|
331
|
+
"oxlint",
|
|
332
|
+
"none"
|
|
333
|
+
],
|
|
334
|
+
examples: [
|
|
335
|
+
"todo",
|
|
336
|
+
"ai",
|
|
337
|
+
"none"
|
|
338
|
+
],
|
|
339
|
+
packageManagers: [
|
|
340
|
+
"npm",
|
|
341
|
+
"pnpm",
|
|
342
|
+
"bun"
|
|
343
|
+
],
|
|
344
|
+
dbSetups: [
|
|
345
|
+
"turso",
|
|
346
|
+
"neon",
|
|
347
|
+
"prisma-postgres",
|
|
348
|
+
"mongodb-atlas",
|
|
349
|
+
"supabase",
|
|
350
|
+
"d1",
|
|
351
|
+
"docker",
|
|
352
|
+
"none"
|
|
353
|
+
],
|
|
354
|
+
apis: [
|
|
355
|
+
"trpc",
|
|
356
|
+
"orpc",
|
|
357
|
+
"none"
|
|
358
|
+
],
|
|
359
|
+
webDeploys: [
|
|
360
|
+
"vercel",
|
|
361
|
+
"netlify",
|
|
362
|
+
"wrangler",
|
|
363
|
+
"alchemy",
|
|
364
|
+
"none"
|
|
365
|
+
],
|
|
366
|
+
serverDeploys: [
|
|
367
|
+
"wrangler",
|
|
368
|
+
"alchemy",
|
|
369
|
+
"none"
|
|
370
|
+
],
|
|
371
|
+
directoryConflicts: [
|
|
372
|
+
"merge",
|
|
373
|
+
"overwrite",
|
|
374
|
+
"increment",
|
|
375
|
+
"error"
|
|
376
|
+
]
|
|
377
|
+
};
|
|
378
|
+
return { content: [{
|
|
379
|
+
type: "text",
|
|
380
|
+
text: JSON.stringify(configurations, null, 2)
|
|
381
|
+
}] };
|
|
382
|
+
}
|
|
383
|
+
case "get_project_info": try {
|
|
384
|
+
return { content: [{
|
|
385
|
+
type: "text",
|
|
386
|
+
text: "Project info functionality not yet implemented"
|
|
387
|
+
}] };
|
|
388
|
+
} catch (error) {
|
|
389
|
+
return {
|
|
390
|
+
content: [{
|
|
391
|
+
type: "text",
|
|
392
|
+
text: `Error getting project info: ${error instanceof Error ? error.message : String(error)}`
|
|
393
|
+
}],
|
|
394
|
+
isError: true
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
default: throw new Error(`Unknown tool: ${name}`);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
async function startMCPServer() {
|
|
401
|
+
const transport = new StdioServerTransport();
|
|
402
|
+
await server.connect(transport);
|
|
403
|
+
console.error("Better-T Stack MCP server started");
|
|
404
|
+
}
|
|
405
|
+
async function main$1() {
|
|
406
|
+
await startMCPServer();
|
|
407
|
+
}
|
|
408
|
+
if (import.meta.url === `file://${process.argv[1]}`) main$1().catch((error) => {
|
|
409
|
+
console.error("MCP server error:", error);
|
|
410
|
+
process.exit(1);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
//#endregion
|
|
4
414
|
//#region src/cli.ts
|
|
5
|
-
|
|
415
|
+
async function main() {
|
|
416
|
+
const args = process.argv.slice(2);
|
|
417
|
+
if (args.includes("--mcp")) await startMCPServer();
|
|
418
|
+
else createBtsCli().run();
|
|
419
|
+
}
|
|
420
|
+
main().catch((error) => {
|
|
421
|
+
console.error("CLI error:", error);
|
|
422
|
+
process.exit(1);
|
|
423
|
+
});
|
|
6
424
|
|
|
7
425
|
//#endregion
|
|
8
426
|
export { };
|
package/dist/index.d.ts
CHANGED
|
@@ -134,6 +134,7 @@ type CreateInput = {
|
|
|
134
134
|
directoryConflict?: DirectoryConflict;
|
|
135
135
|
renderTitle?: boolean;
|
|
136
136
|
disableAnalytics?: boolean;
|
|
137
|
+
mcp?: boolean;
|
|
137
138
|
};
|
|
138
139
|
type AddInput = {
|
|
139
140
|
addons?: Addons[];
|
|
@@ -221,6 +222,7 @@ declare const router: trpcServer.TRPCBuiltRouter<{
|
|
|
221
222
|
directoryConflict?: "error" | "merge" | "overwrite" | "increment" | undefined;
|
|
222
223
|
renderTitle?: boolean | undefined;
|
|
223
224
|
disableAnalytics?: boolean | undefined;
|
|
225
|
+
mcp?: boolean | undefined;
|
|
224
226
|
}];
|
|
225
227
|
output: InitResult | undefined;
|
|
226
228
|
meta: object;
|
package/dist/index.js
CHANGED
|
@@ -1080,7 +1080,8 @@ async function getServerDeploymentChoice(deployment, runtime, backend, webDeploy
|
|
|
1080
1080
|
if (backend === "none" || backend === "convex") return "none";
|
|
1081
1081
|
if (backend !== "hono") return "none";
|
|
1082
1082
|
const options = [];
|
|
1083
|
-
if (runtime
|
|
1083
|
+
if (runtime !== "workers") return "none";
|
|
1084
|
+
["alchemy", "wrangler"].forEach((deploy) => {
|
|
1084
1085
|
const { label, hint } = getDeploymentDisplay$1(deploy);
|
|
1085
1086
|
options.unshift({
|
|
1086
1087
|
value: deploy,
|
|
@@ -1088,11 +1089,6 @@ async function getServerDeploymentChoice(deployment, runtime, backend, webDeploy
|
|
|
1088
1089
|
hint
|
|
1089
1090
|
});
|
|
1090
1091
|
});
|
|
1091
|
-
else options.push({
|
|
1092
|
-
value: "none",
|
|
1093
|
-
label: "None",
|
|
1094
|
-
hint: "Manual setup"
|
|
1095
|
-
});
|
|
1096
1092
|
const response = await select({
|
|
1097
1093
|
message: "Select server deployment",
|
|
1098
1094
|
options,
|
|
@@ -1123,11 +1119,7 @@ async function getServerDeploymentToAdd(runtime, existingDeployment, backend) {
|
|
|
1123
1119
|
}
|
|
1124
1120
|
}
|
|
1125
1121
|
if (existingDeployment && existingDeployment !== "none") return "none";
|
|
1126
|
-
if (options.length > 0)
|
|
1127
|
-
value: "none",
|
|
1128
|
-
label: "None",
|
|
1129
|
-
hint: "Skip deployment setup"
|
|
1130
|
-
});
|
|
1122
|
+
if (options.length > 0) {}
|
|
1131
1123
|
if (options.length === 0) return "none";
|
|
1132
1124
|
const response = await select({
|
|
1133
1125
|
message: "Select server deployment",
|
|
@@ -1353,7 +1345,7 @@ const getLatestCLIVersion = () => {
|
|
|
1353
1345
|
*/
|
|
1354
1346
|
function isTelemetryEnabled() {
|
|
1355
1347
|
const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
|
|
1356
|
-
const BTS_TELEMETRY = "
|
|
1348
|
+
const BTS_TELEMETRY = "0";
|
|
1357
1349
|
if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
|
|
1358
1350
|
if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
|
|
1359
1351
|
return true;
|
|
@@ -1361,8 +1353,8 @@ function isTelemetryEnabled() {
|
|
|
1361
1353
|
|
|
1362
1354
|
//#endregion
|
|
1363
1355
|
//#region src/utils/analytics.ts
|
|
1364
|
-
const POSTHOG_API_KEY = "
|
|
1365
|
-
const POSTHOG_HOST = "
|
|
1356
|
+
const POSTHOG_API_KEY = "random";
|
|
1357
|
+
const POSTHOG_HOST = "random";
|
|
1366
1358
|
function generateSessionId() {
|
|
1367
1359
|
const rand = Math.random().toString(36).slice(2);
|
|
1368
1360
|
const now = Date.now().toString(36);
|
|
@@ -1740,6 +1732,7 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
1740
1732
|
validateApiConstraints(config, options);
|
|
1741
1733
|
validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
|
|
1742
1734
|
validateWorkersCompatibility(providedFlags, options, config);
|
|
1735
|
+
if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'wrangler' or 'alchemy' for --server-deploy.");
|
|
1743
1736
|
if (config.addons && config.addons.length > 0) {
|
|
1744
1737
|
validateAddonsAgainstFrontends(config.addons, config.frontend);
|
|
1745
1738
|
config.addons = [...new Set(config.addons)];
|
|
@@ -2983,11 +2976,7 @@ async function setupWorkersServerDeploy(serverDir, _packageManager) {
|
|
|
2983
2976
|
};
|
|
2984
2977
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
2985
2978
|
await addPackageDependency({
|
|
2986
|
-
devDependencies: [
|
|
2987
|
-
"wrangler",
|
|
2988
|
-
"@types/node",
|
|
2989
|
-
"@cloudflare/workers-types"
|
|
2990
|
-
],
|
|
2979
|
+
devDependencies: ["wrangler", "@types/node"],
|
|
2991
2980
|
projectDir: serverDir
|
|
2992
2981
|
});
|
|
2993
2982
|
}
|
|
@@ -3012,7 +3001,6 @@ async function setupAlchemyServerDeploy(serverDir, _packageManager) {
|
|
|
3012
3001
|
"alchemy",
|
|
3013
3002
|
"wrangler",
|
|
3014
3003
|
"@types/node",
|
|
3015
|
-
"@cloudflare/workers-types",
|
|
3016
3004
|
"dotenv"
|
|
3017
3005
|
],
|
|
3018
3006
|
projectDir: serverDir
|
|
@@ -3380,6 +3368,17 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager) {
|
|
|
3380
3368
|
defaultImport: "alchemy"
|
|
3381
3369
|
});
|
|
3382
3370
|
else alchemyImport.setModuleSpecifier("alchemy/cloudflare/tanstack-start");
|
|
3371
|
+
const reactImport = sourceFile.getImportDeclaration("@vitejs/plugin-react");
|
|
3372
|
+
let reactPluginIdentifier = "viteReact";
|
|
3373
|
+
if (!reactImport) sourceFile.addImportDeclaration({
|
|
3374
|
+
moduleSpecifier: "@vitejs/plugin-react",
|
|
3375
|
+
defaultImport: "viteReact"
|
|
3376
|
+
});
|
|
3377
|
+
else {
|
|
3378
|
+
const defaultImport = reactImport.getDefaultImport();
|
|
3379
|
+
if (defaultImport) reactPluginIdentifier = defaultImport.getText();
|
|
3380
|
+
else reactImport.setDefaultImport("viteReact");
|
|
3381
|
+
}
|
|
3383
3382
|
const exportAssignment = sourceFile.getExportAssignment((d) => !d.isExportEquals());
|
|
3384
3383
|
if (!exportAssignment) return;
|
|
3385
3384
|
const defineConfigCall = exportAssignment.getExpression();
|
|
@@ -3403,26 +3402,33 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager) {
|
|
|
3403
3402
|
const hasShim = initializer.getElements().some((el) => el.getText().includes("alchemy"));
|
|
3404
3403
|
if (!hasShim) initializer.addElement("alchemy()");
|
|
3405
3404
|
const tanstackElements = initializer.getElements().filter((el) => el.getText().includes("tanstackStart"));
|
|
3405
|
+
let needsReactPlugin = false;
|
|
3406
3406
|
tanstackElements.forEach((element) => {
|
|
3407
3407
|
if (Node.isCallExpression(element)) {
|
|
3408
3408
|
const args = element.getArguments();
|
|
3409
|
-
if (args.length === 0)
|
|
3409
|
+
if (args.length === 0) {
|
|
3410
|
+
element.addArgument(`{
|
|
3410
3411
|
target: "cloudflare-module",
|
|
3411
3412
|
customViteReactPlugin: true,
|
|
3412
3413
|
}`);
|
|
3413
|
-
|
|
3414
|
+
needsReactPlugin = true;
|
|
3415
|
+
} else if (args.length === 1 && Node.isObjectLiteralExpression(args[0])) {
|
|
3414
3416
|
const configObj = args[0];
|
|
3415
3417
|
if (!configObj.getProperty("target")) configObj.addPropertyAssignment({
|
|
3416
3418
|
name: "target",
|
|
3417
3419
|
initializer: "\"cloudflare-module\""
|
|
3418
3420
|
});
|
|
3419
|
-
|
|
3421
|
+
const hasCustomViteReactPlugin = !!configObj.getProperty("customViteReactPlugin");
|
|
3422
|
+
if (!hasCustomViteReactPlugin) configObj.addPropertyAssignment({
|
|
3420
3423
|
name: "customViteReactPlugin",
|
|
3421
3424
|
initializer: "true"
|
|
3422
3425
|
});
|
|
3426
|
+
needsReactPlugin = true;
|
|
3423
3427
|
}
|
|
3424
3428
|
}
|
|
3425
3429
|
});
|
|
3430
|
+
const hasReactPlugin = initializer.getElements().some((el) => Node.isCallExpression(el) && el.getExpression().getText() === reactPluginIdentifier);
|
|
3431
|
+
if (needsReactPlugin && !hasReactPlugin) initializer.addElement(`${reactPluginIdentifier}()`);
|
|
3426
3432
|
}
|
|
3427
3433
|
} else configObject.addPropertyAssignment({
|
|
3428
3434
|
name: "plugins",
|
|
@@ -3631,6 +3637,17 @@ async function setupTanstackStartWorkersDeploy(projectDir, packageManager) {
|
|
|
3631
3637
|
if (!await fs.pathExists(viteConfigPath)) return;
|
|
3632
3638
|
const sourceFile = tsProject.addSourceFileAtPathIfExists(viteConfigPath);
|
|
3633
3639
|
if (!sourceFile) return;
|
|
3640
|
+
const reactImport = sourceFile.getImportDeclaration("@vitejs/plugin-react");
|
|
3641
|
+
let reactPluginIdentifier = "viteReact";
|
|
3642
|
+
if (!reactImport) sourceFile.addImportDeclaration({
|
|
3643
|
+
moduleSpecifier: "@vitejs/plugin-react",
|
|
3644
|
+
defaultImport: "viteReact"
|
|
3645
|
+
});
|
|
3646
|
+
else {
|
|
3647
|
+
const defaultImport = reactImport.getDefaultImport();
|
|
3648
|
+
if (defaultImport) reactPluginIdentifier = defaultImport.getText();
|
|
3649
|
+
else reactImport.setDefaultImport("viteReact");
|
|
3650
|
+
}
|
|
3634
3651
|
const defineCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((expr) => {
|
|
3635
3652
|
const expression = expr.getExpression();
|
|
3636
3653
|
return Node.isIdentifier(expression) && expression.getText() === "defineConfig";
|
|
@@ -3640,9 +3657,15 @@ async function setupTanstackStartWorkersDeploy(projectDir, packageManager) {
|
|
|
3640
3657
|
if (!configObj) return;
|
|
3641
3658
|
const pluginsArray = ensureArrayProperty(configObj, "plugins");
|
|
3642
3659
|
const tanstackPluginIndex = pluginsArray.getElements().findIndex((el) => el.getText().includes("tanstackStart("));
|
|
3643
|
-
const tanstackPluginText = "tanstackStart({ target: \"cloudflare-module\" })";
|
|
3660
|
+
const tanstackPluginText = "tanstackStart({ target: \"cloudflare-module\", customViteReactPlugin: true })";
|
|
3644
3661
|
if (tanstackPluginIndex === -1) pluginsArray.addElement(tanstackPluginText);
|
|
3645
3662
|
else pluginsArray.getElements()[tanstackPluginIndex].replaceWithText(tanstackPluginText);
|
|
3663
|
+
const hasReactPlugin = pluginsArray.getElements().some((el) => Node.isCallExpression(el) && el.getExpression().getText() === reactPluginIdentifier);
|
|
3664
|
+
if (!hasReactPlugin) {
|
|
3665
|
+
const nextIndex = pluginsArray.getElements().findIndex((el) => el.getText().includes("tanstackStart(")) + 1;
|
|
3666
|
+
if (nextIndex > 0) pluginsArray.insertElement(nextIndex, `${reactPluginIdentifier}()`);
|
|
3667
|
+
else pluginsArray.addElement(`${reactPluginIdentifier}()`);
|
|
3668
|
+
}
|
|
3646
3669
|
await tsProject.save();
|
|
3647
3670
|
}
|
|
3648
3671
|
|
|
@@ -6297,7 +6320,8 @@ const router = t.router({
|
|
|
6297
6320
|
serverDeploy: ServerDeploySchema.optional(),
|
|
6298
6321
|
directoryConflict: DirectoryConflictSchema.optional(),
|
|
6299
6322
|
renderTitle: z.boolean().optional(),
|
|
6300
|
-
disableAnalytics: z.boolean().optional().default(false).describe("Disable analytics")
|
|
6323
|
+
disableAnalytics: z.boolean().optional().default(false).describe("Disable analytics"),
|
|
6324
|
+
mcp: z.boolean().optional().default(false).describe("Start as MCP server")
|
|
6301
6325
|
})])).mutation(async ({ input }) => {
|
|
6302
6326
|
const [projectName, options] = input;
|
|
6303
6327
|
const combinedInput = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "2.35.
|
|
3
|
+
"version": "2.35.5-canary.449f72a5",
|
|
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",
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"@biomejs/js-api": "^3.0.0",
|
|
68
68
|
"@biomejs/wasm-nodejs": "^2.2.0",
|
|
69
69
|
"@clack/prompts": "^0.11.0",
|
|
70
|
+
"@modelcontextprotocol/sdk": "^0.4.0",
|
|
70
71
|
"consola": "^3.4.2",
|
|
71
72
|
"execa": "^9.6.0",
|
|
72
73
|
"fs-extra": "^11.3.1",
|
|
@@ -8,28 +8,23 @@
|
|
|
8
8
|
"skipLibCheck": true,
|
|
9
9
|
"baseUrl": "./",
|
|
10
10
|
"paths": {
|
|
11
|
-
"@/*": ["./src/*"]
|
|
12
|
-
|
|
13
|
-
"prisma": ["node_modules/prisma"]
|
|
14
|
-
{{/if}}
|
|
11
|
+
"@/*": ["./src/*"]{{#if (eq orm "prisma")}},
|
|
12
|
+
"prisma": ["node_modules/prisma"]{{/if}}
|
|
15
13
|
},
|
|
16
14
|
"outDir": "./dist",
|
|
17
15
|
"types": [
|
|
18
16
|
{{#if (eq runtime "node")}}
|
|
19
|
-
|
|
17
|
+
"node"
|
|
20
18
|
{{else if (eq runtime "bun")}}
|
|
21
|
-
|
|
19
|
+
"bun"
|
|
22
20
|
{{else if (eq runtime "workers")}}
|
|
23
|
-
|
|
24
|
-
"node"
|
|
21
|
+
"node"
|
|
25
22
|
{{else}}
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
"node",
|
|
24
|
+
"bun"
|
|
28
25
|
{{/if}}
|
|
29
|
-
],
|
|
30
|
-
{{
|
|
31
|
-
"composite": true,
|
|
32
|
-
{{/unless}}
|
|
26
|
+
]{{#unless (or (eq backend "convex") (eq backend "none"))}},
|
|
27
|
+
"composite": true{{/unless}},
|
|
33
28
|
"jsx": "react-jsx"{{#if (eq backend "hono")}},
|
|
34
29
|
"jsxImportSource": "hono/jsx"{{/if}}
|
|
35
30
|
}
|
|
@@ -2,7 +2,13 @@ import { defineConfig } from "vite";
|
|
|
2
2
|
import tsconfigPaths from "vite-tsconfig-paths";
|
|
3
3
|
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
4
4
|
import tailwindcss from "@tailwindcss/vite";
|
|
5
|
+
import viteReact from "@vitejs/plugin-react";
|
|
5
6
|
|
|
6
7
|
export default defineConfig({
|
|
7
|
-
plugins: [
|
|
8
|
+
plugins: [
|
|
9
|
+
tsconfigPaths(),
|
|
10
|
+
tailwindcss(),
|
|
11
|
+
tanstackStart({ customViteReactPlugin: true }),
|
|
12
|
+
viteReact(),
|
|
13
|
+
],
|
|
8
14
|
});
|