@tanstack/cta-engine 0.10.0-alpha.6 → 0.11.0

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/src/mcp.ts CHANGED
@@ -8,10 +8,7 @@ import { createApp } from './create-app.js'
8
8
  import { finalizeAddOns } from './add-ons.js'
9
9
  import { createDefaultEnvironment } from './environment.js'
10
10
 
11
- const server = new McpServer({
12
- name: 'Demo',
13
- version: '1.0.0',
14
- })
11
+ import type { TemplateOptions } from './types.js'
15
12
 
16
13
  const tanStackReactAddOns = [
17
14
  {
@@ -57,78 +54,6 @@ const tanStackReactAddOns = [
57
54
  },
58
55
  ]
59
56
 
60
- server.tool('listTanStackReactAddOns', {}, () => {
61
- return {
62
- content: [{ type: 'text', text: JSON.stringify(tanStackReactAddOns) }],
63
- }
64
- })
65
-
66
- server.tool(
67
- 'createTanStackReactApplication',
68
- {
69
- projectName: z
70
- .string()
71
- .describe(
72
- 'The package.json module name of the application (will also be the directory name)',
73
- ),
74
- cwd: z.string().describe('The directory to create the application in'),
75
- addOns: z
76
- .array(
77
- z.enum([
78
- 'clerk',
79
- 'convex',
80
- 'form',
81
- 'netlify',
82
- 'sentry',
83
- 'shadcn',
84
- 'start',
85
- 'store',
86
- 'tanstack-query',
87
- 'tanchat',
88
- ]),
89
- )
90
- .describe('The IDs of the add-ons to install'),
91
- },
92
- async ({ projectName, addOns, cwd }) => {
93
- try {
94
- process.chdir(cwd)
95
- const chosenAddOns = await finalizeAddOns(
96
- 'react',
97
- 'file-router',
98
- addOns as unknown as Array<string>,
99
- )
100
- await createApp(
101
- {
102
- projectName: projectName.replace(/^\//, './'),
103
- framework: 'react',
104
- typescript: true,
105
- tailwind: true,
106
- packageManager: 'pnpm',
107
- toolchain: 'none',
108
- mode: 'file-router',
109
- addOns: true,
110
- chosenAddOns,
111
- git: true,
112
- variableValues: {},
113
- },
114
- {
115
- silent: true,
116
- environment: createDefaultEnvironment(),
117
- },
118
- )
119
- return {
120
- content: [{ type: 'text', text: 'Application created successfully' }],
121
- }
122
- } catch (error) {
123
- return {
124
- content: [
125
- { type: 'text', text: `Error creating application: ${error}` },
126
- ],
127
- }
128
- }
129
- },
130
- )
131
-
132
57
  const tanStackSolidAddOns = [
133
58
  {
134
59
  id: 'solid-ui',
@@ -146,6 +71,11 @@ const tanStackSolidAddOns = [
146
71
  id: 'store',
147
72
  description: 'Enable the TanStack Store state management library',
148
73
  },
74
+ {
75
+ id: 'start',
76
+ description:
77
+ 'Set this if you want a TanStack Start application that supports server functions or APIs',
78
+ },
149
79
  {
150
80
  id: 'tanstack-query',
151
81
  description: 'Enable TanStack Query for data fetching',
@@ -156,80 +86,200 @@ const tanStackSolidAddOns = [
156
86
  },
157
87
  ]
158
88
 
159
- server.tool('listTanStackSolidAddOns', {}, () => {
160
- return {
161
- content: [{ type: 'text', text: JSON.stringify(tanStackSolidAddOns) }],
162
- }
163
- })
89
+ function createServer({
90
+ appName,
91
+ forcedAddOns = [],
92
+ name,
93
+ }: {
94
+ appName?: string
95
+ forcedAddOns?: Array<string>
96
+ name?: string
97
+ }) {
98
+ const server = new McpServer({
99
+ name: `${appName} Application Builder`,
100
+ version: '1.0.0',
101
+ })
164
102
 
165
- server.tool(
166
- 'createTanStackSolidApplication',
167
- {
168
- projectName: z
169
- .string()
170
- .describe(
171
- 'The package.json module name of the application (will also be the directory name)',
172
- ),
173
- cwd: z.string().describe('The directory to create the application in'),
174
- addOns: z
175
- .array(
176
- z.enum([
177
- 'solid-ui',
178
- 'form',
179
- 'sentry',
180
- 'store',
181
- 'tanstack-query',
182
- 'tanchat',
183
- ]),
184
- )
185
- .describe('The IDs of the add-ons to install'),
186
- },
187
- async ({ projectName, addOns, cwd }) => {
188
- try {
189
- process.chdir(cwd)
190
- const chosenAddOns = await finalizeAddOns(
191
- 'solid',
192
- 'file-router',
193
- addOns as unknown as Array<string>,
194
- )
195
- await createApp(
196
- {
197
- projectName: projectName.replace(/^\//, './'),
198
- framework: 'solid',
199
- typescript: true,
200
- tailwind: true,
201
- packageManager: 'pnpm',
202
- toolchain: 'none',
203
- mode: 'file-router',
204
- addOns: true,
205
- chosenAddOns,
206
- git: true,
207
- variableValues: {},
208
- },
209
- {
210
- silent: true,
211
- environment: createDefaultEnvironment(),
212
- },
213
- )
214
- return {
215
- content: [{ type: 'text', text: 'Application created successfully' }],
216
- }
217
- } catch (error) {
218
- return {
219
- content: [
220
- { type: 'text', text: `Error creating application: ${error}` },
221
- ],
103
+ server.tool('listTanStackReactAddOns', {}, () => {
104
+ return {
105
+ content: [{ type: 'text', text: JSON.stringify(tanStackReactAddOns) }],
106
+ }
107
+ })
108
+
109
+ server.tool(
110
+ 'createTanStackReactApplication',
111
+ {
112
+ projectName: z
113
+ .string()
114
+ .describe(
115
+ 'The package.json module name of the application (will also be the directory name)',
116
+ ),
117
+ cwd: z.string().describe('The directory to create the application in'),
118
+ addOns: z
119
+ .array(
120
+ z.enum([
121
+ 'clerk',
122
+ 'convex',
123
+ 'form',
124
+ 'netlify',
125
+ 'sentry',
126
+ 'shadcn',
127
+ 'start',
128
+ 'store',
129
+ 'tanstack-query',
130
+ 'tanchat',
131
+ ]),
132
+ )
133
+ .describe('The IDs of the add-ons to install'),
134
+ targetDir: z
135
+ .string()
136
+ .describe(
137
+ 'The directory to create the application in. Use the absolute path of the directory you want the application to be created in',
138
+ ),
139
+ },
140
+ async ({ projectName, addOns, cwd, targetDir }) => {
141
+ try {
142
+ process.chdir(cwd)
143
+ const chosenAddOns = await finalizeAddOns(
144
+ 'react',
145
+ 'file-router',
146
+ Array.from(
147
+ new Set([...(addOns as unknown as Array<string>), ...forcedAddOns]),
148
+ ),
149
+ )
150
+ await createApp(
151
+ {
152
+ projectName: projectName.replace(/^\//, './'),
153
+ framework: 'react',
154
+ typescript: true,
155
+ tailwind: true,
156
+ packageManager: 'pnpm',
157
+ toolchain: 'none',
158
+ mode: 'file-router',
159
+ addOns: true,
160
+ chosenAddOns,
161
+ git: true,
162
+ variableValues: {},
163
+ },
164
+ {
165
+ silent: true,
166
+ environment: createDefaultEnvironment(),
167
+ name,
168
+ cwd: targetDir,
169
+ },
170
+ )
171
+ return {
172
+ content: [{ type: 'text', text: 'Application created successfully' }],
173
+ }
174
+ } catch (error) {
175
+ return {
176
+ content: [
177
+ { type: 'text', text: `Error creating application: ${error}` },
178
+ ],
179
+ }
222
180
  }
181
+ },
182
+ )
183
+
184
+ server.tool('listTanStackSolidAddOns', {}, () => {
185
+ return {
186
+ content: [{ type: 'text', text: JSON.stringify(tanStackSolidAddOns) }],
223
187
  }
188
+ })
189
+
190
+ server.tool(
191
+ 'createTanStackSolidApplication',
192
+ {
193
+ projectName: z
194
+ .string()
195
+ .describe(
196
+ 'The package.json module name of the application (will also be the directory name)',
197
+ ),
198
+ cwd: z.string().describe('The directory to create the application in'),
199
+ addOns: z
200
+ .array(
201
+ z.enum([
202
+ 'solid-ui',
203
+ 'form',
204
+ 'sentry',
205
+ 'store',
206
+ 'tanstack-query',
207
+ 'tanchat',
208
+ ]),
209
+ )
210
+ .describe('The IDs of the add-ons to install'),
211
+ targetDir: z
212
+ .string()
213
+ .describe(
214
+ 'The directory to create the application in. Use the absolute path of the directory you want the application to be created in',
215
+ ),
216
+ },
217
+ async ({ projectName, addOns, cwd, targetDir }) => {
218
+ try {
219
+ process.chdir(cwd)
220
+ const chosenAddOns = await finalizeAddOns(
221
+ 'solid',
222
+ 'file-router',
223
+ Array.from(
224
+ new Set([...(addOns as unknown as Array<string>), ...forcedAddOns]),
225
+ ),
226
+ )
227
+ await createApp(
228
+ {
229
+ projectName: projectName.replace(/^\//, './'),
230
+ framework: 'solid',
231
+ typescript: true,
232
+ tailwind: true,
233
+ packageManager: 'pnpm',
234
+ toolchain: 'none',
235
+ mode: 'file-router',
236
+ addOns: true,
237
+ chosenAddOns,
238
+ git: true,
239
+ variableValues: {},
240
+ },
241
+ {
242
+ silent: true,
243
+ environment: createDefaultEnvironment(),
244
+ name,
245
+ cwd: targetDir,
246
+ },
247
+ )
248
+ return {
249
+ content: [{ type: 'text', text: 'Application created successfully' }],
250
+ }
251
+ } catch (error) {
252
+ return {
253
+ content: [
254
+ { type: 'text', text: `Error creating application: ${error}` },
255
+ ],
256
+ }
257
+ }
258
+ },
259
+ )
260
+
261
+ return server
262
+ }
263
+
264
+ export default async function runServer(
265
+ sse: boolean,
266
+ {
267
+ forcedAddOns,
268
+ appName,
269
+ name,
270
+ }: {
271
+ forcedMode?: TemplateOptions
272
+ forcedAddOns?: Array<string>
273
+ appName?: string
274
+ name?: string
224
275
  },
225
- )
276
+ ) {
277
+ let transport: SSEServerTransport | null = null
226
278
 
227
- export default async function runServer(sse: boolean) {
279
+ const server = createServer({ appName, forcedAddOns, name })
228
280
  if (sse) {
229
281
  const app = express()
230
282
 
231
- let transport: SSEServerTransport | null = null
232
-
233
283
  app.get('/sse', (req, res) => {
234
284
  transport = new SSEServerTransport('/messages', res)
235
285
  server.connect(transport)
package/src/options.ts CHANGED
@@ -21,6 +21,7 @@ import type { AddOn, CliOptions, Options, Overlay, Variable } from './types.js'
21
21
  // If all CLI options are provided, use them directly
22
22
  export async function normalizeOptions(
23
23
  cliOptions: CliOptions,
24
+ forcedAddOns?: Array<string>,
24
25
  ): Promise<Options | undefined> {
25
26
  // in some cases, if you use windows/powershell, the argument for addons
26
27
  // if sepparated by comma is not really passed as an array, but as a string
@@ -31,6 +32,7 @@ export async function normalizeOptions(
31
32
  cliOptions.addOns = parseSeparatedArgs
32
33
  }
33
34
  }
35
+
34
36
  if (cliOptions.projectName) {
35
37
  let typescript =
36
38
  cliOptions.template === 'typescript' ||
@@ -58,11 +60,23 @@ export async function normalizeOptions(
58
60
 
59
61
  let addOns = false
60
62
  let chosenAddOns: Array<AddOn> = []
61
- if (Array.isArray(cliOptions.addOns) || overlay?.dependsOn) {
63
+ if (
64
+ Array.isArray(cliOptions.addOns) ||
65
+ overlay?.dependsOn ||
66
+ forcedAddOns
67
+ ) {
62
68
  addOns = true
63
- let finalAddOns = [...(overlay?.dependsOn || [])]
69
+ let finalAddOns = Array.from(
70
+ new Set([...(overlay?.dependsOn || []), ...(forcedAddOns || [])]),
71
+ )
64
72
  if (cliOptions.addOns && Array.isArray(cliOptions.addOns)) {
65
- finalAddOns = [...finalAddOns, ...cliOptions.addOns]
73
+ finalAddOns = Array.from(
74
+ new Set([
75
+ ...(forcedAddOns || []),
76
+ ...finalAddOns,
77
+ ...cliOptions.addOns,
78
+ ]),
79
+ )
66
80
  }
67
81
  chosenAddOns = await finalizeAddOns(
68
82
  cliOptions.framework || DEFAULT_FRAMEWORK,
@@ -135,6 +149,13 @@ async function collectVariables(
135
149
 
136
150
  export async function promptForOptions(
137
151
  cliOptions: CliOptions,
152
+ {
153
+ forcedAddOns = [],
154
+ forcedMode,
155
+ }: {
156
+ forcedAddOns?: Array<string>
157
+ forcedMode?: 'typescript' | 'javascript' | 'file-router'
158
+ },
138
159
  ): Promise<Required<Options>> {
139
160
  const options = {} as Required<Options>
140
161
 
@@ -166,7 +187,7 @@ export async function promptForOptions(
166
187
  }
167
188
 
168
189
  // Router type selection
169
- if (!cliOptions.template) {
190
+ if (!cliOptions.template && !forcedMode) {
170
191
  const routerType = await select({
171
192
  message: 'Select the router type:',
172
193
  options: [
@@ -186,6 +207,9 @@ export async function promptForOptions(
186
207
  process.exit(0)
187
208
  }
188
209
  options.mode = routerType as typeof CODE_ROUTER | typeof FILE_ROUTER
210
+ } else if (forcedMode) {
211
+ options.mode = forcedMode === 'file-router' ? FILE_ROUTER : CODE_ROUTER
212
+ options.typescript = options.mode === FILE_ROUTER
189
213
  } else {
190
214
  options.mode =
191
215
  cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER
@@ -274,7 +298,7 @@ export async function promptForOptions(
274
298
  options.chosenAddOns = await finalizeAddOns(
275
299
  options.framework,
276
300
  options.mode,
277
- cliOptions.addOns,
301
+ Array.from(new Set([...cliOptions.addOns, ...forcedAddOns])),
278
302
  )
279
303
  options.tailwind = true
280
304
  } else if (cliOptions.addOns) {
@@ -285,11 +309,13 @@ export async function promptForOptions(
285
309
  if (options.typescript && addOns.length > 0) {
286
310
  const value = await multiselect({
287
311
  message: 'What add-ons would you like for your project:',
288
- options: addOns.map((addOn) => ({
289
- value: addOn.id,
290
- label: addOn.name,
291
- hint: addOn.description,
292
- })),
312
+ options: addOns
313
+ .filter((addOn) => !forcedAddOns.includes(addOn.id))
314
+ .map((addOn) => ({
315
+ value: addOn.id,
316
+ label: addOn.name,
317
+ hint: addOn.description,
318
+ })),
293
319
  required: false,
294
320
  })
295
321
 
@@ -306,11 +332,13 @@ export async function promptForOptions(
306
332
  if (options.typescript && examples.length > 0) {
307
333
  const value = await multiselect({
308
334
  message: 'Would you like any examples?',
309
- options: examples.map((addOn) => ({
310
- value: addOn.id,
311
- label: addOn.name,
312
- hint: addOn.description,
313
- })),
335
+ options: examples
336
+ .filter((addOn) => !forcedAddOns.includes(addOn.id))
337
+ .map((addOn) => ({
338
+ value: addOn.id,
339
+ label: addOn.name,
340
+ hint: addOn.description,
341
+ })),
314
342
  required: false,
315
343
  })
316
344
 
@@ -321,14 +349,26 @@ export async function promptForOptions(
321
349
  selectedExamples = value
322
350
  }
323
351
 
324
- if (selectedAddOns.length > 0 || selectedExamples.length > 0) {
352
+ if (
353
+ selectedAddOns.length > 0 ||
354
+ selectedExamples.length > 0 ||
355
+ forcedAddOns.length > 0
356
+ ) {
325
357
  options.chosenAddOns = await finalizeAddOns(
326
358
  options.framework,
327
359
  options.mode,
328
- [...selectedAddOns, ...selectedExamples],
360
+ Array.from(
361
+ new Set([...selectedAddOns, ...selectedExamples, ...forcedAddOns]),
362
+ ),
329
363
  )
330
364
  options.tailwind = true
331
365
  }
366
+ } else if (forcedAddOns.length > 0) {
367
+ options.chosenAddOns = await finalizeAddOns(
368
+ options.framework,
369
+ options.mode,
370
+ forcedAddOns,
371
+ )
332
372
  }
333
373
 
334
374
  // Collect variables
package/src/types.ts CHANGED
@@ -4,6 +4,8 @@ import type { ToolChain } from './toolchain.js'
4
4
 
5
5
  export type Framework = 'solid' | 'react'
6
6
 
7
+ export type TemplateOptions = 'typescript' | 'javascript' | 'file-router'
8
+
7
9
  export interface Options {
8
10
  framework: Framework
9
11
  projectName: string
@@ -20,7 +22,7 @@ export interface Options {
20
22
  }
21
23
 
22
24
  export interface CliOptions {
23
- template?: 'typescript' | 'javascript' | 'file-router'
25
+ template?: TemplateOptions
24
26
  framework?: Framework
25
27
  tailwind?: boolean
26
28
  packageManager?: PackageManager
package/tsconfig.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
- "extends": "../../tsconfig.json",
3
2
  "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ES2020",
4
5
  "outDir": "./dist",
5
6
  "rootDir": "./src",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "moduleResolution": "node",
6
12
  "declaration": true,
7
13
  "declarationDir": "./dist/types"
8
14
  },