@tanstack/cta-cli 0.10.0-alpha.18 → 0.10.0-alpha.20
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.js +90 -34
- package/dist/command-line.js +75 -0
- package/dist/mcp.js +170 -0
- package/dist/options.js +40 -328
- package/dist/types/cli.d.ts +3 -1
- package/dist/types/command-line.d.ts +3 -0
- package/dist/types/mcp.d.ts +7 -0
- package/dist/types/options.d.ts +3 -4
- package/dist/types/types.d.ts +4 -1
- package/dist/types/ui-environment.d.ts +1 -1
- package/dist/types/ui-prompts.d.ts +10 -0
- package/dist/types/utils.d.ts +3 -0
- package/dist/ui-environment.js +45 -42
- package/dist/ui-prompts.js +140 -0
- package/dist/utils.js +7 -0
- package/package.json +10 -6
- package/src/cli.ts +115 -50
- package/src/command-line.ts +111 -0
- package/src/mcp.ts +247 -0
- package/src/options.ts +70 -394
- package/src/types.ts +5 -1
- package/src/ui-environment.ts +52 -44
- package/src/ui-prompts.ts +187 -0
- package/src/utils.ts +11 -0
- package/tests/command-line.test.ts +205 -0
- package/tests/index.test.ts +9 -0
- package/tests/options.test.ts +287 -0
- package/tests/setupVitest.ts +6 -0
- package/tests/ui-environment.test.ts +97 -0
- package/tests/ui-prompts.test.ts +233 -0
- package/vitest.config.js +7 -0
package/src/options.ts
CHANGED
|
@@ -1,174 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
cancel,
|
|
3
|
-
confirm,
|
|
4
|
-
isCancel,
|
|
5
|
-
multiselect,
|
|
6
|
-
select,
|
|
7
|
-
text,
|
|
8
|
-
} from '@clack/prompts'
|
|
9
|
-
|
|
10
1
|
import {
|
|
11
2
|
CODE_ROUTER,
|
|
12
|
-
DEFAULT_PACKAGE_MANAGER,
|
|
13
3
|
FILE_ROUTER,
|
|
14
|
-
SUPPORTED_PACKAGE_MANAGERS,
|
|
15
4
|
finalizeAddOns,
|
|
16
|
-
getAllAddOns,
|
|
17
5
|
getFrameworkById,
|
|
18
6
|
getPackageManager,
|
|
19
|
-
loadRemoteAddOn,
|
|
20
7
|
} from '@tanstack/cta-engine'
|
|
21
8
|
|
|
22
|
-
import
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
9
|
+
import {
|
|
10
|
+
getProjectName,
|
|
11
|
+
selectAddOns,
|
|
12
|
+
selectGit,
|
|
13
|
+
selectPackageManager,
|
|
14
|
+
selectRouterType,
|
|
15
|
+
selectTailwind,
|
|
16
|
+
selectToolchain,
|
|
17
|
+
selectTypescript,
|
|
18
|
+
} from './ui-prompts.js'
|
|
19
|
+
|
|
20
|
+
import type { Mode, Options } from '@tanstack/cta-engine'
|
|
30
21
|
|
|
31
22
|
import type { CliOptions } from './types.js'
|
|
32
23
|
|
|
33
|
-
// If all CLI options are provided, use them directly
|
|
34
|
-
export async function normalizeOptions(
|
|
35
|
-
cliOptions: CliOptions,
|
|
36
|
-
forcedMode?: Mode,
|
|
37
|
-
forcedAddOns?: Array<string>,
|
|
38
|
-
): Promise<Options | undefined> {
|
|
39
|
-
// in some cases, if you use windows/powershell, the argument for addons
|
|
40
|
-
// if sepparated by comma is not really passed as an array, but as a string
|
|
41
|
-
// with spaces, We need to normalize this edge case.
|
|
42
|
-
if (Array.isArray(cliOptions.addOns) && cliOptions.addOns.length === 1) {
|
|
43
|
-
const parseSeparatedArgs = cliOptions.addOns[0].split(' ')
|
|
44
|
-
if (parseSeparatedArgs.length > 1) {
|
|
45
|
-
cliOptions.addOns = parseSeparatedArgs
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (cliOptions.projectName) {
|
|
50
|
-
let typescript =
|
|
51
|
-
cliOptions.template === 'typescript' ||
|
|
52
|
-
cliOptions.template === 'file-router' ||
|
|
53
|
-
cliOptions.framework === 'solid'
|
|
54
|
-
|
|
55
|
-
let tailwind = !!cliOptions.tailwind
|
|
56
|
-
if (cliOptions.framework === 'solid') {
|
|
57
|
-
tailwind = true
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let mode: typeof FILE_ROUTER | typeof CODE_ROUTER =
|
|
61
|
-
cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER
|
|
62
|
-
|
|
63
|
-
const starter = cliOptions.starter
|
|
64
|
-
? ((await loadRemoteAddOn(cliOptions.starter)) as Starter)
|
|
65
|
-
: undefined
|
|
66
|
-
|
|
67
|
-
if (starter) {
|
|
68
|
-
tailwind = starter.tailwind
|
|
69
|
-
typescript = starter.typescript
|
|
70
|
-
cliOptions.framework = starter.framework
|
|
71
|
-
mode = starter.mode
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
let addOns = false
|
|
75
|
-
let chosenAddOns: Array<AddOn> = []
|
|
76
|
-
if (
|
|
77
|
-
Array.isArray(cliOptions.addOns) ||
|
|
78
|
-
starter?.dependsOn ||
|
|
79
|
-
forcedAddOns ||
|
|
80
|
-
cliOptions.toolchain
|
|
81
|
-
) {
|
|
82
|
-
addOns = true
|
|
83
|
-
let finalAddOns = Array.from(
|
|
84
|
-
new Set([...(starter?.dependsOn || []), ...(forcedAddOns || [])]),
|
|
85
|
-
)
|
|
86
|
-
if (cliOptions.addOns && Array.isArray(cliOptions.addOns)) {
|
|
87
|
-
finalAddOns = Array.from(
|
|
88
|
-
new Set([
|
|
89
|
-
...(forcedAddOns || []),
|
|
90
|
-
...finalAddOns,
|
|
91
|
-
...cliOptions.addOns,
|
|
92
|
-
]),
|
|
93
|
-
)
|
|
94
|
-
}
|
|
95
|
-
const framework = getFrameworkById(cliOptions.framework || 'react-cra')!
|
|
96
|
-
|
|
97
|
-
if (cliOptions.toolchain) {
|
|
98
|
-
finalAddOns.push(cliOptions.toolchain)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
chosenAddOns = await finalizeAddOns(
|
|
102
|
-
framework,
|
|
103
|
-
forcedMode || cliOptions.template === 'file-router'
|
|
104
|
-
? FILE_ROUTER
|
|
105
|
-
: CODE_ROUTER,
|
|
106
|
-
finalAddOns,
|
|
107
|
-
)
|
|
108
|
-
tailwind = true
|
|
109
|
-
typescript = true
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return {
|
|
113
|
-
// TODO: This is a bit to fix the default framework
|
|
114
|
-
framework: getFrameworkById(cliOptions.framework || 'react-cra')!,
|
|
115
|
-
projectName: cliOptions.projectName,
|
|
116
|
-
typescript,
|
|
117
|
-
tailwind,
|
|
118
|
-
packageManager:
|
|
119
|
-
cliOptions.packageManager ||
|
|
120
|
-
getPackageManager() ||
|
|
121
|
-
DEFAULT_PACKAGE_MANAGER,
|
|
122
|
-
mode,
|
|
123
|
-
git: !!cliOptions.git,
|
|
124
|
-
addOns,
|
|
125
|
-
chosenAddOns,
|
|
126
|
-
variableValues: {},
|
|
127
|
-
starter,
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async function collectVariables(
|
|
133
|
-
variables: Array<Variable>,
|
|
134
|
-
): Promise<Record<string, string | number | boolean>> {
|
|
135
|
-
const responses: Record<string, string | number | boolean> = {}
|
|
136
|
-
for (const variable of variables) {
|
|
137
|
-
if (variable.type === 'string') {
|
|
138
|
-
const response = await text({
|
|
139
|
-
message: variable.description,
|
|
140
|
-
initialValue: variable.default,
|
|
141
|
-
})
|
|
142
|
-
if (isCancel(response)) {
|
|
143
|
-
cancel('Operation cancelled.')
|
|
144
|
-
process.exit(0)
|
|
145
|
-
}
|
|
146
|
-
responses[variable.name] = response
|
|
147
|
-
} else if (variable.type === 'number') {
|
|
148
|
-
const response = await text({
|
|
149
|
-
message: variable.description,
|
|
150
|
-
initialValue: variable.default.toString(),
|
|
151
|
-
})
|
|
152
|
-
if (isCancel(response)) {
|
|
153
|
-
cancel('Operation cancelled.')
|
|
154
|
-
process.exit(0)
|
|
155
|
-
}
|
|
156
|
-
responses[variable.name] = Number(response)
|
|
157
|
-
} else {
|
|
158
|
-
const response = await confirm({
|
|
159
|
-
message: variable.description,
|
|
160
|
-
initialValue: variable.default === true,
|
|
161
|
-
})
|
|
162
|
-
if (isCancel(response)) {
|
|
163
|
-
cancel('Operation cancelled.')
|
|
164
|
-
process.exit(0)
|
|
165
|
-
}
|
|
166
|
-
responses[variable.name] = response
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return responses
|
|
170
|
-
}
|
|
171
|
-
|
|
172
24
|
export async function promptForOptions(
|
|
173
25
|
cliOptions: CliOptions,
|
|
174
26
|
{
|
|
@@ -176,277 +28,101 @@ export async function promptForOptions(
|
|
|
176
28
|
forcedMode,
|
|
177
29
|
}: {
|
|
178
30
|
forcedAddOns?: Array<string>
|
|
179
|
-
forcedMode?:
|
|
31
|
+
forcedMode?: Mode
|
|
180
32
|
},
|
|
181
|
-
): Promise<Required<Options
|
|
33
|
+
): Promise<Required<Options> | undefined> {
|
|
182
34
|
const options = {} as Required<Options>
|
|
183
35
|
|
|
184
|
-
|
|
185
|
-
options.framework = framework
|
|
186
|
-
// TODO: This is a bit of a hack to ensure that the framework is solid
|
|
187
|
-
if (options.framework.id === 'solid') {
|
|
188
|
-
options.typescript = true
|
|
189
|
-
options.tailwind = true
|
|
190
|
-
}
|
|
36
|
+
options.framework = getFrameworkById(cliOptions.framework || 'react-cra')!
|
|
191
37
|
|
|
192
|
-
|
|
193
|
-
options.typescript = true
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (!cliOptions.projectName) {
|
|
197
|
-
const value = await text({
|
|
198
|
-
message: 'What would you like to name your project?',
|
|
199
|
-
defaultValue: 'my-app',
|
|
200
|
-
validate(value) {
|
|
201
|
-
if (!value) {
|
|
202
|
-
return 'Please enter a name'
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
})
|
|
206
|
-
if (isCancel(value)) {
|
|
207
|
-
cancel('Operation cancelled.')
|
|
208
|
-
process.exit(0)
|
|
209
|
-
}
|
|
210
|
-
options.projectName = value
|
|
211
|
-
}
|
|
38
|
+
options.projectName = cliOptions.projectName || (await getProjectName())
|
|
212
39
|
|
|
213
40
|
// Router type selection
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
options: [
|
|
218
|
-
{
|
|
219
|
-
value: FILE_ROUTER,
|
|
220
|
-
label: 'File Router - File-based routing structure',
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
value: CODE_ROUTER,
|
|
224
|
-
label: 'Code Router - Traditional code-based routing',
|
|
225
|
-
},
|
|
226
|
-
],
|
|
227
|
-
initialValue: FILE_ROUTER,
|
|
228
|
-
})
|
|
229
|
-
if (isCancel(routerType)) {
|
|
230
|
-
cancel('Operation cancelled.')
|
|
231
|
-
process.exit(0)
|
|
232
|
-
}
|
|
233
|
-
options.mode = routerType as typeof CODE_ROUTER | typeof FILE_ROUTER
|
|
234
|
-
} else if (forcedMode) {
|
|
235
|
-
options.mode = forcedMode === 'file-router' ? FILE_ROUTER : CODE_ROUTER
|
|
236
|
-
options.typescript = options.mode === FILE_ROUTER
|
|
237
|
-
} else {
|
|
41
|
+
if (forcedMode) {
|
|
42
|
+
options.mode = forcedMode
|
|
43
|
+
} else if (cliOptions.template) {
|
|
238
44
|
options.mode =
|
|
239
45
|
cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
46
|
+
} else {
|
|
47
|
+
options.mode = await selectRouterType()
|
|
243
48
|
}
|
|
244
49
|
|
|
245
50
|
// TypeScript selection (if using Code Router)
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
initialValue: true,
|
|
251
|
-
})
|
|
252
|
-
if (isCancel(typescriptEnable)) {
|
|
253
|
-
cancel('Operation cancelled.')
|
|
254
|
-
process.exit(0)
|
|
255
|
-
}
|
|
256
|
-
options.typescript = typescriptEnable
|
|
257
|
-
} else {
|
|
258
|
-
options.typescript = true
|
|
259
|
-
}
|
|
51
|
+
options.typescript =
|
|
52
|
+
options.mode === FILE_ROUTER || options.framework.id === 'solid'
|
|
53
|
+
if (!options.typescript && options.mode === CODE_ROUTER) {
|
|
54
|
+
options.typescript = await selectTypescript()
|
|
260
55
|
}
|
|
261
56
|
|
|
262
57
|
// Tailwind selection
|
|
263
58
|
if (!cliOptions.tailwind && options.framework.id === 'react-cra') {
|
|
264
|
-
|
|
265
|
-
message: 'Would you like to use Tailwind CSS?',
|
|
266
|
-
initialValue: true,
|
|
267
|
-
})
|
|
268
|
-
if (isCancel(tailwind)) {
|
|
269
|
-
cancel('Operation cancelled.')
|
|
270
|
-
process.exit(0)
|
|
271
|
-
}
|
|
272
|
-
options.tailwind = tailwind
|
|
59
|
+
options.tailwind = await selectTailwind()
|
|
273
60
|
} else {
|
|
274
|
-
|
|
275
|
-
options.tailwind = options.framework.id === 'solid' || !!cliOptions.tailwind
|
|
61
|
+
options.tailwind = true
|
|
276
62
|
}
|
|
277
63
|
|
|
278
64
|
// Package manager selection
|
|
279
|
-
if (cliOptions.packageManager
|
|
280
|
-
const detectedPackageManager = getPackageManager()
|
|
281
|
-
if (!detectedPackageManager) {
|
|
282
|
-
const pm = await select({
|
|
283
|
-
message: 'Select package manager:',
|
|
284
|
-
options: SUPPORTED_PACKAGE_MANAGERS.map((pm) => ({
|
|
285
|
-
value: pm,
|
|
286
|
-
label: pm,
|
|
287
|
-
})),
|
|
288
|
-
initialValue: DEFAULT_PACKAGE_MANAGER,
|
|
289
|
-
})
|
|
290
|
-
if (isCancel(pm)) {
|
|
291
|
-
cancel('Operation cancelled.')
|
|
292
|
-
process.exit(0)
|
|
293
|
-
}
|
|
294
|
-
options.packageManager = pm
|
|
295
|
-
} else {
|
|
296
|
-
options.packageManager = detectedPackageManager
|
|
297
|
-
}
|
|
298
|
-
} else {
|
|
65
|
+
if (cliOptions.packageManager) {
|
|
299
66
|
options.packageManager = cliOptions.packageManager
|
|
67
|
+
} else {
|
|
68
|
+
const detectedPackageManager = await getPackageManager()
|
|
69
|
+
options.packageManager =
|
|
70
|
+
detectedPackageManager || (await selectPackageManager())
|
|
300
71
|
}
|
|
301
72
|
|
|
302
73
|
// Toolchain selection
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (addOn.type === 'toolchain') {
|
|
308
|
-
toolchains.add(addOn)
|
|
309
|
-
}
|
|
310
|
-
}
|
|
74
|
+
const toolchain = await selectToolchain(
|
|
75
|
+
options.framework,
|
|
76
|
+
cliOptions.toolchain,
|
|
77
|
+
)
|
|
311
78
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
label: tc.name,
|
|
322
|
-
})),
|
|
323
|
-
],
|
|
324
|
-
initialValue: undefined,
|
|
325
|
-
})
|
|
326
|
-
if (isCancel(tc)) {
|
|
327
|
-
cancel('Operation cancelled.')
|
|
328
|
-
process.exit(0)
|
|
329
|
-
}
|
|
330
|
-
toolchain = tc
|
|
331
|
-
} else {
|
|
332
|
-
for (const addOn of framework.getAddOns()) {
|
|
333
|
-
if (addOn.type === 'toolchain' && addOn.id === cliOptions.toolchain) {
|
|
334
|
-
toolchain = addOn
|
|
335
|
-
}
|
|
336
|
-
}
|
|
79
|
+
// Add-ons selection
|
|
80
|
+
const addOns: Set<string> = new Set()
|
|
81
|
+
|
|
82
|
+
if (toolchain) {
|
|
83
|
+
addOns.add(toolchain)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for (const addOn of forcedAddOns) {
|
|
87
|
+
addOns.add(addOn)
|
|
337
88
|
}
|
|
338
89
|
|
|
339
|
-
options.chosenAddOns = toolchain ? [toolchain] : []
|
|
340
90
|
if (Array.isArray(cliOptions.addOns)) {
|
|
341
|
-
|
|
91
|
+
for (const addOn of cliOptions.addOns) {
|
|
92
|
+
addOns.add(addOn)
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
for (const addOn of await selectAddOns(
|
|
342
96
|
options.framework,
|
|
343
97
|
options.mode,
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
)
|
|
348
|
-
|
|
349
|
-
} else if (cliOptions.addOns) {
|
|
350
|
-
// Select any add-ons
|
|
351
|
-
const allAddOns = await getAllAddOns(options.framework, options.mode)
|
|
352
|
-
const addOns = allAddOns.filter((addOn) => addOn.type === 'add-on')
|
|
353
|
-
let selectedAddOns: Array<string> = []
|
|
354
|
-
if (options.typescript && addOns.length > 0) {
|
|
355
|
-
const value = await multiselect({
|
|
356
|
-
message: 'What add-ons would you like for your project:',
|
|
357
|
-
options: addOns
|
|
358
|
-
.filter((addOn) => !forcedAddOns.includes(addOn.id))
|
|
359
|
-
.map((addOn) => ({
|
|
360
|
-
value: addOn.id,
|
|
361
|
-
label: addOn.name,
|
|
362
|
-
hint: addOn.description,
|
|
363
|
-
})),
|
|
364
|
-
required: false,
|
|
365
|
-
})
|
|
366
|
-
|
|
367
|
-
if (isCancel(value)) {
|
|
368
|
-
cancel('Operation cancelled.')
|
|
369
|
-
process.exit(0)
|
|
370
|
-
}
|
|
371
|
-
selectedAddOns = value
|
|
98
|
+
'add-on',
|
|
99
|
+
'What add-ons would you like for your project?',
|
|
100
|
+
forcedAddOns,
|
|
101
|
+
)) {
|
|
102
|
+
addOns.add(addOn)
|
|
372
103
|
}
|
|
373
104
|
|
|
374
|
-
|
|
375
|
-
let selectedExamples: Array<string> = []
|
|
376
|
-
const examples = allAddOns.filter((addOn) => addOn.type === 'example')
|
|
377
|
-
if (options.typescript && examples.length > 0) {
|
|
378
|
-
const value = await multiselect({
|
|
379
|
-
message: 'Would you like any examples?',
|
|
380
|
-
options: examples
|
|
381
|
-
.filter((addOn) => !forcedAddOns.includes(addOn.id))
|
|
382
|
-
.map((addOn) => ({
|
|
383
|
-
value: addOn.id,
|
|
384
|
-
label: addOn.name,
|
|
385
|
-
hint: addOn.description,
|
|
386
|
-
})),
|
|
387
|
-
required: false,
|
|
388
|
-
})
|
|
389
|
-
|
|
390
|
-
if (isCancel(value)) {
|
|
391
|
-
cancel('Operation cancelled.')
|
|
392
|
-
process.exit(0)
|
|
393
|
-
}
|
|
394
|
-
selectedExamples = value
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
if (
|
|
398
|
-
selectedAddOns.length > 0 ||
|
|
399
|
-
selectedExamples.length > 0 ||
|
|
400
|
-
forcedAddOns.length > 0 ||
|
|
401
|
-
toolchain
|
|
402
|
-
) {
|
|
403
|
-
options.chosenAddOns = await finalizeAddOns(
|
|
404
|
-
options.framework,
|
|
405
|
-
options.mode,
|
|
406
|
-
Array.from(
|
|
407
|
-
new Set([
|
|
408
|
-
...selectedAddOns,
|
|
409
|
-
...selectedExamples,
|
|
410
|
-
...forcedAddOns,
|
|
411
|
-
toolchain?.id,
|
|
412
|
-
]),
|
|
413
|
-
).filter(Boolean) as Array<string>,
|
|
414
|
-
)
|
|
415
|
-
options.tailwind = true
|
|
416
|
-
}
|
|
417
|
-
} else if (forcedAddOns.length > 0) {
|
|
418
|
-
options.chosenAddOns = await finalizeAddOns(
|
|
105
|
+
for (const addOn of await selectAddOns(
|
|
419
106
|
options.framework,
|
|
420
107
|
options.mode,
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
)
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
// Collect variables
|
|
428
|
-
const variables: Array<Variable> = []
|
|
429
|
-
for (const addOn of options.chosenAddOns) {
|
|
430
|
-
for (const variable of addOn.variables ?? []) {
|
|
431
|
-
variables.push(variable)
|
|
108
|
+
'example',
|
|
109
|
+
'Would you like any examples?',
|
|
110
|
+
forcedAddOns,
|
|
111
|
+
)) {
|
|
112
|
+
addOns.add(addOn)
|
|
432
113
|
}
|
|
433
114
|
}
|
|
434
|
-
options.variableValues = await collectVariables(variables)
|
|
435
115
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
cancel('Operation cancelled.')
|
|
444
|
-
process.exit(0)
|
|
445
|
-
}
|
|
446
|
-
options.git = git
|
|
447
|
-
} else {
|
|
448
|
-
options.git = !!cliOptions.git
|
|
116
|
+
options.chosenAddOns = Array.from(
|
|
117
|
+
await finalizeAddOns(options.framework, options.mode, Array.from(addOns)),
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if (options.chosenAddOns.length) {
|
|
121
|
+
options.tailwind = true
|
|
122
|
+
options.typescript = true
|
|
449
123
|
}
|
|
450
124
|
|
|
125
|
+
options.git = cliOptions.git || (await selectGit())
|
|
126
|
+
|
|
451
127
|
return options
|
|
452
128
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { PackageManager
|
|
1
|
+
import type { PackageManager } from '@tanstack/cta-engine'
|
|
2
|
+
|
|
3
|
+
export type TemplateOptions = 'typescript' | 'javascript' | 'file-router'
|
|
2
4
|
|
|
3
5
|
export interface CliOptions {
|
|
4
6
|
template?: TemplateOptions
|
|
@@ -14,4 +16,6 @@ export interface CliOptions {
|
|
|
14
16
|
mcpSse?: boolean
|
|
15
17
|
starter?: string
|
|
16
18
|
targetDir?: string
|
|
19
|
+
interactive?: boolean
|
|
20
|
+
ui?: boolean
|
|
17
21
|
}
|
package/src/ui-environment.ts
CHANGED
|
@@ -13,52 +13,60 @@ import { createDefaultEnvironment } from '@tanstack/cta-engine'
|
|
|
13
13
|
|
|
14
14
|
import type { Environment } from '@tanstack/cta-engine'
|
|
15
15
|
|
|
16
|
-
export function createUIEnvironment(
|
|
16
|
+
export function createUIEnvironment(
|
|
17
|
+
appName: string,
|
|
18
|
+
silent: boolean,
|
|
19
|
+
): Environment {
|
|
17
20
|
const defaultEnvironment = createDefaultEnvironment()
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
let newEnvironment = {
|
|
20
23
|
...defaultEnvironment,
|
|
21
|
-
|
|
22
|
-
intro(message)
|
|
23
|
-
},
|
|
24
|
-
outro: (message: string) => {
|
|
25
|
-
outro(message)
|
|
26
|
-
},
|
|
27
|
-
info: (title?: string, message?: string) => {
|
|
28
|
-
console.log('info', title, message)
|
|
29
|
-
log.info(
|
|
30
|
-
`${title ? chalk.red(title) : ''}${message ? chalk.green(message) : ''}`,
|
|
31
|
-
)
|
|
32
|
-
},
|
|
33
|
-
error: (title?: string, message?: string) => {
|
|
34
|
-
console.log('error', title, message)
|
|
35
|
-
log.error(`${title ? `${title}: ` : ''}${message}`)
|
|
36
|
-
},
|
|
37
|
-
warn: (title?: string, message?: string) => {
|
|
38
|
-
console.log('warn', title, message)
|
|
39
|
-
log.warn(`${title ? `${title}: ` : ''}${message}`)
|
|
40
|
-
},
|
|
41
|
-
confirm: async (message: string) => {
|
|
42
|
-
console.log('confirm', message)
|
|
43
|
-
const shouldContinue = await confirm({
|
|
44
|
-
message,
|
|
45
|
-
})
|
|
46
|
-
if (isCancel(shouldContinue)) {
|
|
47
|
-
cancel('Operation cancelled.')
|
|
48
|
-
process.exit(0)
|
|
49
|
-
}
|
|
50
|
-
return shouldContinue
|
|
51
|
-
},
|
|
52
|
-
spinner: () => {
|
|
53
|
-
const s = spinner()
|
|
54
|
-
return {
|
|
55
|
-
start: (message: string) => {
|
|
56
|
-
s.start(message)
|
|
57
|
-
},
|
|
58
|
-
stop: (message: string) => {
|
|
59
|
-
s.stop(message)
|
|
60
|
-
},
|
|
61
|
-
}
|
|
62
|
-
},
|
|
24
|
+
appName,
|
|
63
25
|
}
|
|
26
|
+
|
|
27
|
+
if (!silent) {
|
|
28
|
+
newEnvironment = {
|
|
29
|
+
...newEnvironment,
|
|
30
|
+
intro: (message: string) => {
|
|
31
|
+
intro(message)
|
|
32
|
+
},
|
|
33
|
+
outro: (message: string) => {
|
|
34
|
+
outro(message)
|
|
35
|
+
},
|
|
36
|
+
info: (title?: string, message?: string) => {
|
|
37
|
+
log.info(
|
|
38
|
+
`${title ? chalk.red(title) : ''}${message ? chalk.green(message) : ''}`,
|
|
39
|
+
)
|
|
40
|
+
},
|
|
41
|
+
error: (title?: string, message?: string) => {
|
|
42
|
+
log.error(`${title ? `${title}: ` : ''}${message}`)
|
|
43
|
+
},
|
|
44
|
+
warn: (title?: string, message?: string) => {
|
|
45
|
+
log.warn(`${title ? `${title}: ` : ''}${message}`)
|
|
46
|
+
},
|
|
47
|
+
confirm: async (message: string) => {
|
|
48
|
+
const shouldContinue = await confirm({
|
|
49
|
+
message,
|
|
50
|
+
})
|
|
51
|
+
if (isCancel(shouldContinue)) {
|
|
52
|
+
cancel('Operation cancelled.')
|
|
53
|
+
process.exit(0)
|
|
54
|
+
}
|
|
55
|
+
return shouldContinue
|
|
56
|
+
},
|
|
57
|
+
spinner: () => {
|
|
58
|
+
const s = spinner()
|
|
59
|
+
return {
|
|
60
|
+
start: (message: string) => {
|
|
61
|
+
s.start(message)
|
|
62
|
+
},
|
|
63
|
+
stop: (message: string) => {
|
|
64
|
+
s.stop(message)
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return newEnvironment
|
|
64
72
|
}
|