@tanstack/cta-cli 0.10.0-alpha.19 → 0.10.0-alpha.21
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 +82 -33
- package/dist/command-line.js +75 -0
- package/dist/mcp.js +170 -0
- package/dist/options.js +40 -331
- 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 +2 -3
- package/dist/types/types.d.ts +3 -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 +102 -46
- package/src/command-line.ts +111 -0
- package/src/mcp.ts +247 -0
- package/src/options.ts +69 -395
- package/src/types.ts +4 -1
- package/src/ui-environment.ts +54 -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,279 +28,101 @@ export async function promptForOptions(
|
|
|
176
28
|
forcedMode,
|
|
177
29
|
}: {
|
|
178
30
|
forcedAddOns?: Array<string>
|
|
179
|
-
forcedMode?:
|
|
31
|
+
forcedMode?: Mode
|
|
180
32
|
},
|
|
181
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
|
-
}
|
|
191
|
-
|
|
192
|
-
if (cliOptions.addOns) {
|
|
193
|
-
options.typescript = true
|
|
194
|
-
}
|
|
36
|
+
options.framework = getFrameworkById(cliOptions.framework || 'react-cra')!
|
|
195
37
|
|
|
196
|
-
|
|
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
|
-
} else {
|
|
212
|
-
options.projectName = cliOptions.projectName
|
|
213
|
-
}
|
|
38
|
+
options.projectName = cliOptions.projectName || (await getProjectName())
|
|
214
39
|
|
|
215
40
|
// Router type selection
|
|
216
|
-
if (
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
options: [
|
|
220
|
-
{
|
|
221
|
-
value: FILE_ROUTER,
|
|
222
|
-
label: 'File Router - File-based routing structure',
|
|
223
|
-
},
|
|
224
|
-
{
|
|
225
|
-
value: CODE_ROUTER,
|
|
226
|
-
label: 'Code Router - Traditional code-based routing',
|
|
227
|
-
},
|
|
228
|
-
],
|
|
229
|
-
initialValue: FILE_ROUTER,
|
|
230
|
-
})
|
|
231
|
-
if (isCancel(routerType)) {
|
|
232
|
-
cancel('Operation cancelled.')
|
|
233
|
-
process.exit(0)
|
|
234
|
-
}
|
|
235
|
-
options.mode = routerType as typeof CODE_ROUTER | typeof FILE_ROUTER
|
|
236
|
-
} else if (forcedMode) {
|
|
237
|
-
options.mode = forcedMode === 'file-router' ? FILE_ROUTER : CODE_ROUTER
|
|
238
|
-
options.typescript = options.mode === FILE_ROUTER
|
|
239
|
-
} else {
|
|
41
|
+
if (forcedMode) {
|
|
42
|
+
options.mode = forcedMode
|
|
43
|
+
} else if (cliOptions.template) {
|
|
240
44
|
options.mode =
|
|
241
45
|
cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
46
|
+
} else {
|
|
47
|
+
options.mode = await selectRouterType()
|
|
245
48
|
}
|
|
246
49
|
|
|
247
50
|
// TypeScript selection (if using Code Router)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
initialValue: true,
|
|
253
|
-
})
|
|
254
|
-
if (isCancel(typescriptEnable)) {
|
|
255
|
-
cancel('Operation cancelled.')
|
|
256
|
-
process.exit(0)
|
|
257
|
-
}
|
|
258
|
-
options.typescript = typescriptEnable
|
|
259
|
-
} else {
|
|
260
|
-
options.typescript = true
|
|
261
|
-
}
|
|
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()
|
|
262
55
|
}
|
|
263
56
|
|
|
264
57
|
// Tailwind selection
|
|
265
58
|
if (!cliOptions.tailwind && options.framework.id === 'react-cra') {
|
|
266
|
-
|
|
267
|
-
message: 'Would you like to use Tailwind CSS?',
|
|
268
|
-
initialValue: true,
|
|
269
|
-
})
|
|
270
|
-
if (isCancel(tailwind)) {
|
|
271
|
-
cancel('Operation cancelled.')
|
|
272
|
-
process.exit(0)
|
|
273
|
-
}
|
|
274
|
-
options.tailwind = tailwind
|
|
59
|
+
options.tailwind = await selectTailwind()
|
|
275
60
|
} else {
|
|
276
|
-
|
|
277
|
-
options.tailwind = options.framework.id === 'solid' || !!cliOptions.tailwind
|
|
61
|
+
options.tailwind = true
|
|
278
62
|
}
|
|
279
63
|
|
|
280
64
|
// Package manager selection
|
|
281
|
-
if (cliOptions.packageManager
|
|
282
|
-
const detectedPackageManager = getPackageManager()
|
|
283
|
-
if (!detectedPackageManager) {
|
|
284
|
-
const pm = await select({
|
|
285
|
-
message: 'Select package manager:',
|
|
286
|
-
options: SUPPORTED_PACKAGE_MANAGERS.map((pm) => ({
|
|
287
|
-
value: pm,
|
|
288
|
-
label: pm,
|
|
289
|
-
})),
|
|
290
|
-
initialValue: DEFAULT_PACKAGE_MANAGER,
|
|
291
|
-
})
|
|
292
|
-
if (isCancel(pm)) {
|
|
293
|
-
cancel('Operation cancelled.')
|
|
294
|
-
process.exit(0)
|
|
295
|
-
}
|
|
296
|
-
options.packageManager = pm
|
|
297
|
-
} else {
|
|
298
|
-
options.packageManager = detectedPackageManager
|
|
299
|
-
}
|
|
300
|
-
} else {
|
|
65
|
+
if (cliOptions.packageManager) {
|
|
301
66
|
options.packageManager = cliOptions.packageManager
|
|
67
|
+
} else {
|
|
68
|
+
const detectedPackageManager = await getPackageManager()
|
|
69
|
+
options.packageManager =
|
|
70
|
+
detectedPackageManager || (await selectPackageManager())
|
|
302
71
|
}
|
|
303
72
|
|
|
304
73
|
// Toolchain selection
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (addOn.type === 'toolchain') {
|
|
310
|
-
toolchains.add(addOn)
|
|
311
|
-
}
|
|
312
|
-
}
|
|
74
|
+
const toolchain = await selectToolchain(
|
|
75
|
+
options.framework,
|
|
76
|
+
cliOptions.toolchain,
|
|
77
|
+
)
|
|
313
78
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
label: tc.name,
|
|
324
|
-
})),
|
|
325
|
-
],
|
|
326
|
-
initialValue: undefined,
|
|
327
|
-
})
|
|
328
|
-
if (isCancel(tc)) {
|
|
329
|
-
cancel('Operation cancelled.')
|
|
330
|
-
process.exit(0)
|
|
331
|
-
}
|
|
332
|
-
toolchain = tc
|
|
333
|
-
} else {
|
|
334
|
-
for (const addOn of framework.getAddOns()) {
|
|
335
|
-
if (addOn.type === 'toolchain' && addOn.id === cliOptions.toolchain) {
|
|
336
|
-
toolchain = addOn
|
|
337
|
-
}
|
|
338
|
-
}
|
|
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)
|
|
339
88
|
}
|
|
340
89
|
|
|
341
|
-
options.chosenAddOns = toolchain ? [toolchain] : []
|
|
342
90
|
if (Array.isArray(cliOptions.addOns)) {
|
|
343
|
-
|
|
91
|
+
for (const addOn of cliOptions.addOns) {
|
|
92
|
+
addOns.add(addOn)
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
for (const addOn of await selectAddOns(
|
|
344
96
|
options.framework,
|
|
345
97
|
options.mode,
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
)
|
|
350
|
-
|
|
351
|
-
} else if (cliOptions.addOns) {
|
|
352
|
-
// Select any add-ons
|
|
353
|
-
const allAddOns = await getAllAddOns(options.framework, options.mode)
|
|
354
|
-
const addOns = allAddOns.filter((addOn) => addOn.type === 'add-on')
|
|
355
|
-
let selectedAddOns: Array<string> = []
|
|
356
|
-
if (options.typescript && addOns.length > 0) {
|
|
357
|
-
const value = await multiselect({
|
|
358
|
-
message: 'What add-ons would you like for your project:',
|
|
359
|
-
options: addOns
|
|
360
|
-
.filter((addOn) => !forcedAddOns.includes(addOn.id))
|
|
361
|
-
.map((addOn) => ({
|
|
362
|
-
value: addOn.id,
|
|
363
|
-
label: addOn.name,
|
|
364
|
-
hint: addOn.description,
|
|
365
|
-
})),
|
|
366
|
-
required: false,
|
|
367
|
-
})
|
|
368
|
-
|
|
369
|
-
if (isCancel(value)) {
|
|
370
|
-
cancel('Operation cancelled.')
|
|
371
|
-
process.exit(0)
|
|
372
|
-
}
|
|
373
|
-
selectedAddOns = value
|
|
98
|
+
'add-on',
|
|
99
|
+
'What add-ons would you like for your project?',
|
|
100
|
+
forcedAddOns,
|
|
101
|
+
)) {
|
|
102
|
+
addOns.add(addOn)
|
|
374
103
|
}
|
|
375
104
|
|
|
376
|
-
|
|
377
|
-
let selectedExamples: Array<string> = []
|
|
378
|
-
const examples = allAddOns.filter((addOn) => addOn.type === 'example')
|
|
379
|
-
if (options.typescript && examples.length > 0) {
|
|
380
|
-
const value = await multiselect({
|
|
381
|
-
message: 'Would you like any examples?',
|
|
382
|
-
options: examples
|
|
383
|
-
.filter((addOn) => !forcedAddOns.includes(addOn.id))
|
|
384
|
-
.map((addOn) => ({
|
|
385
|
-
value: addOn.id,
|
|
386
|
-
label: addOn.name,
|
|
387
|
-
hint: addOn.description,
|
|
388
|
-
})),
|
|
389
|
-
required: false,
|
|
390
|
-
})
|
|
391
|
-
|
|
392
|
-
if (isCancel(value)) {
|
|
393
|
-
cancel('Operation cancelled.')
|
|
394
|
-
process.exit(0)
|
|
395
|
-
}
|
|
396
|
-
selectedExamples = value
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (
|
|
400
|
-
selectedAddOns.length > 0 ||
|
|
401
|
-
selectedExamples.length > 0 ||
|
|
402
|
-
forcedAddOns.length > 0 ||
|
|
403
|
-
toolchain
|
|
404
|
-
) {
|
|
405
|
-
options.chosenAddOns = await finalizeAddOns(
|
|
406
|
-
options.framework,
|
|
407
|
-
options.mode,
|
|
408
|
-
Array.from(
|
|
409
|
-
new Set([
|
|
410
|
-
...selectedAddOns,
|
|
411
|
-
...selectedExamples,
|
|
412
|
-
...forcedAddOns,
|
|
413
|
-
toolchain?.id,
|
|
414
|
-
]),
|
|
415
|
-
).filter(Boolean) as Array<string>,
|
|
416
|
-
)
|
|
417
|
-
options.tailwind = true
|
|
418
|
-
}
|
|
419
|
-
} else if (forcedAddOns.length > 0) {
|
|
420
|
-
options.chosenAddOns = await finalizeAddOns(
|
|
105
|
+
for (const addOn of await selectAddOns(
|
|
421
106
|
options.framework,
|
|
422
107
|
options.mode,
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
)
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
// Collect variables
|
|
430
|
-
const variables: Array<Variable> = []
|
|
431
|
-
for (const addOn of options.chosenAddOns) {
|
|
432
|
-
for (const variable of addOn.variables ?? []) {
|
|
433
|
-
variables.push(variable)
|
|
108
|
+
'example',
|
|
109
|
+
'Would you like any examples?',
|
|
110
|
+
forcedAddOns,
|
|
111
|
+
)) {
|
|
112
|
+
addOns.add(addOn)
|
|
434
113
|
}
|
|
435
114
|
}
|
|
436
|
-
options.variableValues = await collectVariables(variables)
|
|
437
115
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
cancel('Operation cancelled.')
|
|
446
|
-
process.exit(0)
|
|
447
|
-
}
|
|
448
|
-
options.git = git
|
|
449
|
-
} else {
|
|
450
|
-
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
|
|
451
123
|
}
|
|
452
124
|
|
|
125
|
+
options.git = cliOptions.git || (await selectGit())
|
|
126
|
+
|
|
453
127
|
return options
|
|
454
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
|
|
@@ -15,4 +17,5 @@ export interface CliOptions {
|
|
|
15
17
|
starter?: string
|
|
16
18
|
targetDir?: string
|
|
17
19
|
interactive?: boolean
|
|
20
|
+
ui?: boolean
|
|
18
21
|
}
|
package/src/ui-environment.ts
CHANGED
|
@@ -13,52 +13,62 @@ 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 ? '\n' + chalk.green(message) : ''}`,
|
|
39
|
+
)
|
|
40
|
+
},
|
|
41
|
+
error: (title?: string, message?: string) => {
|
|
42
|
+
log.error(
|
|
43
|
+
`${title ? `${title}: ` : ''}${message ? '\n' + message : ''}`,
|
|
44
|
+
)
|
|
45
|
+
},
|
|
46
|
+
warn: (title?: string, message?: string) => {
|
|
47
|
+
log.warn(`${title ? `${title}: ` : ''}${message ? '\n' + message : ''}`)
|
|
48
|
+
},
|
|
49
|
+
confirm: async (message: string) => {
|
|
50
|
+
const shouldContinue = await confirm({
|
|
51
|
+
message,
|
|
52
|
+
})
|
|
53
|
+
if (isCancel(shouldContinue)) {
|
|
54
|
+
cancel('Operation cancelled.')
|
|
55
|
+
process.exit(0)
|
|
56
|
+
}
|
|
57
|
+
return shouldContinue
|
|
58
|
+
},
|
|
59
|
+
spinner: () => {
|
|
60
|
+
const s = spinner()
|
|
61
|
+
return {
|
|
62
|
+
start: (message: string) => {
|
|
63
|
+
s.start(message)
|
|
64
|
+
},
|
|
65
|
+
stop: (message: string) => {
|
|
66
|
+
s.stop(message)
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return newEnvironment
|
|
64
74
|
}
|