@tanstack/cli 0.48.7 → 0.58.5
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/CHANGELOG.md +539 -0
- package/dist/bin.js +0 -1
- package/dist/cli.js +23 -60
- package/dist/command-line.js +35 -36
- package/dist/mcp.js +60 -0
- package/dist/options.js +8 -46
- package/dist/types/cli.d.ts +1 -5
- package/dist/types/command-line.d.ts +5 -1
- package/dist/types/mcp.d.ts +0 -1
- package/dist/types/options.d.ts +1 -2
- package/dist/types/types.d.ts +1 -3
- package/dist/types/ui-prompts.d.ts +0 -3
- package/dist/types/utils.d.ts +0 -2
- package/dist/ui-prompts.js +0 -43
- package/dist/utils.js +0 -6
- package/package.json +3 -3
- package/src/bin.ts +0 -1
- package/src/cli.ts +35 -79
- package/src/command-line.ts +51 -46
- package/src/mcp.ts +74 -1
- package/src/options.ts +6 -50
- package/src/types.ts +1 -4
- package/src/ui-prompts.ts +0 -50
- package/src/utils.ts +0 -8
- package/tests/command-line.test.ts +44 -40
- package/tests/options.test.ts +11 -99
- package/tests/ui-prompts.test.ts +0 -38
package/src/cli.ts
CHANGED
|
@@ -24,13 +24,16 @@ import { launchUI } from '@tanstack/create-ui'
|
|
|
24
24
|
import { runMCPServer } from './mcp.js'
|
|
25
25
|
|
|
26
26
|
import { promptForAddOns, promptForCreateOptions } from './options.js'
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
normalizeOptions,
|
|
29
|
+
validateDevWatchOptions,
|
|
30
|
+
validateLegacyCreateFlags,
|
|
31
|
+
} from './command-line.js'
|
|
28
32
|
|
|
29
33
|
import { createUIEnvironment } from './ui-environment.js'
|
|
30
|
-
import { convertTemplateToMode } from './utils.js'
|
|
31
34
|
import { DevWatchManager } from './dev-watch.js'
|
|
32
35
|
|
|
33
|
-
import type { CliOptions
|
|
36
|
+
import type { CliOptions } from './types.js'
|
|
34
37
|
import type {
|
|
35
38
|
FrameworkDefinition,
|
|
36
39
|
Options,
|
|
@@ -45,12 +48,9 @@ const VERSION = packageJson.version
|
|
|
45
48
|
export function cli({
|
|
46
49
|
name,
|
|
47
50
|
appName,
|
|
48
|
-
forcedMode,
|
|
49
51
|
forcedAddOns = [],
|
|
50
|
-
defaultTemplate = 'javascript',
|
|
51
52
|
forcedDeployment,
|
|
52
53
|
defaultFramework,
|
|
53
|
-
craCompatible = false,
|
|
54
54
|
webBase,
|
|
55
55
|
frameworkDefinitionInitializers,
|
|
56
56
|
showDeploymentOptions = false,
|
|
@@ -58,12 +58,9 @@ export function cli({
|
|
|
58
58
|
}: {
|
|
59
59
|
name: string
|
|
60
60
|
appName: string
|
|
61
|
-
forcedMode?: string
|
|
62
61
|
forcedAddOns?: Array<string>
|
|
63
62
|
forcedDeployment?: string
|
|
64
|
-
defaultTemplate?: TemplateOptions
|
|
65
63
|
defaultFramework?: string
|
|
66
|
-
craCompatible?: boolean
|
|
67
64
|
webBase?: string
|
|
68
65
|
frameworkDefinitionInitializers?: Array<() => FrameworkDefinition>
|
|
69
66
|
showDeploymentOptions?: boolean
|
|
@@ -93,23 +90,8 @@ export function cli({
|
|
|
93
90
|
}
|
|
94
91
|
}
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
for (const framework of getFrameworks()) {
|
|
99
|
-
for (const mode of Object.keys(framework.supportedModes)) {
|
|
100
|
-
supportedModes.add(mode)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (defaultMode && !supportedModes.has(defaultMode)) {
|
|
104
|
-
throw new InvalidArgumentError(
|
|
105
|
-
`Invalid mode: ${defaultMode}. The following are allowed: ${Array.from(
|
|
106
|
-
supportedModes,
|
|
107
|
-
).join(', ')}`,
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
if (supportedModes.size < 2) {
|
|
111
|
-
defaultMode = Array.from(supportedModes)[0]
|
|
112
|
-
}
|
|
93
|
+
// Mode is always file-router (TanStack Start)
|
|
94
|
+
const defaultMode = 'file-router'
|
|
113
95
|
|
|
114
96
|
program
|
|
115
97
|
.name(name)
|
|
@@ -118,11 +100,20 @@ export function cli({
|
|
|
118
100
|
|
|
119
101
|
// Helper to create the create command action handler
|
|
120
102
|
async function handleCreate(projectName: string, options: CliOptions) {
|
|
103
|
+
const legacyCreateFlags = validateLegacyCreateFlags(options)
|
|
104
|
+
if (legacyCreateFlags.error) {
|
|
105
|
+
log.error(legacyCreateFlags.error)
|
|
106
|
+
process.exit(1)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const warning of legacyCreateFlags.warnings) {
|
|
110
|
+
log.warn(warning)
|
|
111
|
+
}
|
|
112
|
+
|
|
121
113
|
if (options.listAddOns) {
|
|
122
114
|
const addOns = await getAllAddOns(
|
|
123
115
|
getFrameworkByName(options.framework || defaultFramework || 'React')!,
|
|
124
|
-
defaultMode
|
|
125
|
-
convertTemplateToMode(options.template || defaultTemplate),
|
|
116
|
+
defaultMode,
|
|
126
117
|
)
|
|
127
118
|
let hasConfigurableAddOns = false
|
|
128
119
|
for (const addOn of addOns.filter((a) => !forcedAddOns.includes(a.id))) {
|
|
@@ -143,10 +134,14 @@ export function cli({
|
|
|
143
134
|
if (options.addonDetails) {
|
|
144
135
|
const addOns = await getAllAddOns(
|
|
145
136
|
getFrameworkByName(options.framework || defaultFramework || 'React')!,
|
|
146
|
-
defaultMode
|
|
147
|
-
convertTemplateToMode(options.template || defaultTemplate),
|
|
137
|
+
defaultMode,
|
|
148
138
|
)
|
|
149
|
-
const addOn =
|
|
139
|
+
const addOn =
|
|
140
|
+
addOns.find((a) => a.id === options.addonDetails) ??
|
|
141
|
+
addOns.find(
|
|
142
|
+
(a) =>
|
|
143
|
+
a.id.toLowerCase() === options.addonDetails!.toLowerCase(),
|
|
144
|
+
)
|
|
150
145
|
if (!addOn) {
|
|
151
146
|
console.error(`Add-on '${options.addonDetails}' not found`)
|
|
152
147
|
process.exit(1)
|
|
@@ -239,7 +234,6 @@ export function cli({
|
|
|
239
234
|
projectName,
|
|
240
235
|
framework: framework.id,
|
|
241
236
|
},
|
|
242
|
-
defaultMode,
|
|
243
237
|
forcedAddOns,
|
|
244
238
|
)
|
|
245
239
|
|
|
@@ -285,33 +279,12 @@ export function cli({
|
|
|
285
279
|
options.framework || defaultFramework || 'React',
|
|
286
280
|
)!.id
|
|
287
281
|
|
|
288
|
-
if (defaultMode) {
|
|
289
|
-
cliOptions.template = defaultMode as TemplateOptions
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Default to Start unless --router-only is specified
|
|
293
|
-
// Skip this if forcedAddOns already includes 'start' (e.g., from cli-aliases)
|
|
294
|
-
if (!options.routerOnly && !forcedAddOns.includes('start')) {
|
|
295
|
-
if (Array.isArray(cliOptions.addOns)) {
|
|
296
|
-
if (!cliOptions.addOns.includes('start')) {
|
|
297
|
-
cliOptions.addOns = [...cliOptions.addOns, 'start']
|
|
298
|
-
}
|
|
299
|
-
} else if (cliOptions.addOns !== true) {
|
|
300
|
-
cliOptions.addOns = ['start']
|
|
301
|
-
}
|
|
302
|
-
// Also set template to file-router for Start
|
|
303
|
-
if (!cliOptions.template) {
|
|
304
|
-
cliOptions.template = 'file-router'
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
282
|
let finalOptions: Options | undefined
|
|
309
283
|
if (cliOptions.interactive) {
|
|
310
284
|
cliOptions.addOns = true
|
|
311
285
|
} else {
|
|
312
286
|
finalOptions = await normalizeOptions(
|
|
313
287
|
cliOptions,
|
|
314
|
-
defaultMode,
|
|
315
288
|
forcedAddOns,
|
|
316
289
|
{ forcedDeployment },
|
|
317
290
|
)
|
|
@@ -320,7 +293,6 @@ export function cli({
|
|
|
320
293
|
if (options.ui) {
|
|
321
294
|
const optionsFromCLI = await normalizeOptions(
|
|
322
295
|
cliOptions,
|
|
323
|
-
defaultMode,
|
|
324
296
|
forcedAddOns,
|
|
325
297
|
{ disableNameCheck: true, forcedDeployment },
|
|
326
298
|
)
|
|
@@ -346,7 +318,6 @@ export function cli({
|
|
|
346
318
|
} else {
|
|
347
319
|
intro(`Let's configure your ${appName} application`)
|
|
348
320
|
finalOptions = await promptForCreateOptions(cliOptions, {
|
|
349
|
-
forcedMode: defaultMode,
|
|
350
321
|
forcedAddOns,
|
|
351
322
|
showDeploymentOptions,
|
|
352
323
|
})
|
|
@@ -384,25 +355,6 @@ export function cli({
|
|
|
384
355
|
function configureCreateCommand(cmd: Command) {
|
|
385
356
|
cmd.argument('[project-name]', 'name of the project')
|
|
386
357
|
|
|
387
|
-
if (!defaultMode && craCompatible) {
|
|
388
|
-
cmd.option<'typescript' | 'javascript' | 'file-router'>(
|
|
389
|
-
'--template <type>',
|
|
390
|
-
'project template (typescript, javascript, file-router)',
|
|
391
|
-
(value) => {
|
|
392
|
-
if (
|
|
393
|
-
value !== 'typescript' &&
|
|
394
|
-
value !== 'javascript' &&
|
|
395
|
-
value !== 'file-router'
|
|
396
|
-
) {
|
|
397
|
-
throw new InvalidArgumentError(
|
|
398
|
-
`Invalid template: ${value}. Only the following are allowed: typescript, javascript, file-router`,
|
|
399
|
-
)
|
|
400
|
-
}
|
|
401
|
-
return value
|
|
402
|
-
},
|
|
403
|
-
)
|
|
404
|
-
}
|
|
405
|
-
|
|
406
358
|
if (!defaultFramework) {
|
|
407
359
|
cmd.option<string>(
|
|
408
360
|
'--framework <type>',
|
|
@@ -448,6 +400,14 @@ export function cli({
|
|
|
448
400
|
'--dev-watch <path>',
|
|
449
401
|
'Watch a framework directory for changes and auto-rebuild',
|
|
450
402
|
)
|
|
403
|
+
.option(
|
|
404
|
+
'--router-only',
|
|
405
|
+
'Deprecated: compatibility flag from create-tsrouter-app',
|
|
406
|
+
)
|
|
407
|
+
.option(
|
|
408
|
+
'--template <type>',
|
|
409
|
+
'Deprecated: compatibility flag from create-tsrouter-app',
|
|
410
|
+
)
|
|
451
411
|
|
|
452
412
|
if (deployments.size > 0) {
|
|
453
413
|
cmd.option<string>(
|
|
@@ -486,10 +446,7 @@ export function cli({
|
|
|
486
446
|
}
|
|
487
447
|
|
|
488
448
|
cmd
|
|
489
|
-
.option('--router-only', 'create a Router-only SPA without TanStack Start (SSR)', false)
|
|
490
449
|
.option('--interactive', 'interactive mode', false)
|
|
491
|
-
.option('--tailwind', 'add Tailwind CSS')
|
|
492
|
-
.option('--no-tailwind', 'skip Tailwind CSS')
|
|
493
450
|
.option<Array<string> | boolean>(
|
|
494
451
|
'--add-ons [...add-ons]',
|
|
495
452
|
'pick from a list of available add-ons (comma separated list)',
|
|
@@ -526,7 +483,7 @@ export function cli({
|
|
|
526
483
|
}
|
|
527
484
|
|
|
528
485
|
// === CREATE SUBCOMMAND ===
|
|
529
|
-
//
|
|
486
|
+
// Creates a TanStack Start app (file-router mode).
|
|
530
487
|
const createCommand = program
|
|
531
488
|
.command('create')
|
|
532
489
|
.description(`Create a new TanStack Start application`)
|
|
@@ -541,7 +498,6 @@ export function cli({
|
|
|
541
498
|
.option('--sse', 'Run in SSE mode instead of stdio', false)
|
|
542
499
|
.action(async (options: { sse: boolean }) => {
|
|
543
500
|
await runMCPServer(options.sse, {
|
|
544
|
-
forcedMode: defaultMode,
|
|
545
501
|
forcedAddOns,
|
|
546
502
|
appName,
|
|
547
503
|
})
|
package/src/command-line.ts
CHANGED
|
@@ -19,9 +19,54 @@ import type { Options } from '@tanstack/create'
|
|
|
19
19
|
|
|
20
20
|
import type { CliOptions } from './types.js'
|
|
21
21
|
|
|
22
|
+
const SUPPORTED_LEGACY_TEMPLATES = new Set([
|
|
23
|
+
'file-router',
|
|
24
|
+
'typescript',
|
|
25
|
+
'tsx',
|
|
26
|
+
])
|
|
27
|
+
|
|
28
|
+
export function validateLegacyCreateFlags(cliOptions: CliOptions): {
|
|
29
|
+
warnings: Array<string>
|
|
30
|
+
error?: string
|
|
31
|
+
} {
|
|
32
|
+
const warnings: Array<string> = []
|
|
33
|
+
|
|
34
|
+
if (cliOptions.routerOnly) {
|
|
35
|
+
warnings.push(
|
|
36
|
+
'The --router-only flag is deprecated and ignored. `tanstack create` already creates router-based apps.',
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!cliOptions.template) {
|
|
41
|
+
return { warnings }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const template = cliOptions.template.toLowerCase().trim()
|
|
45
|
+
|
|
46
|
+
if (template === 'javascript' || template === 'js' || template === 'jsx') {
|
|
47
|
+
return {
|
|
48
|
+
warnings,
|
|
49
|
+
error:
|
|
50
|
+
'JavaScript/JSX templates are not supported. TanStack Start file-router templates are TypeScript-only.',
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!SUPPORTED_LEGACY_TEMPLATES.has(template)) {
|
|
55
|
+
return {
|
|
56
|
+
warnings,
|
|
57
|
+
error: `Invalid --template value: ${cliOptions.template}. Supported values are: file-router, typescript, tsx.`,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
warnings.push(
|
|
62
|
+
'The --template flag is deprecated. TypeScript/TSX is the default and only supported template.',
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return { warnings }
|
|
66
|
+
}
|
|
67
|
+
|
|
22
68
|
export async function normalizeOptions(
|
|
23
69
|
cliOptions: CliOptions,
|
|
24
|
-
forcedMode?: string,
|
|
25
70
|
forcedAddOns?: Array<string>,
|
|
26
71
|
opts?: {
|
|
27
72
|
disableNameCheck?: boolean
|
|
@@ -51,42 +96,24 @@ export async function normalizeOptions(
|
|
|
51
96
|
}
|
|
52
97
|
}
|
|
53
98
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
let mode: string =
|
|
57
|
-
forcedMode ||
|
|
58
|
-
(cliOptions.template === 'file-router' ? 'file-router' : 'code-router')
|
|
99
|
+
// Mode is always file-router (TanStack Start)
|
|
100
|
+
let mode = 'file-router'
|
|
59
101
|
|
|
60
102
|
const starter = cliOptions.starter
|
|
61
103
|
? await loadStarter(cliOptions.starter)
|
|
62
104
|
: undefined
|
|
63
105
|
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
cliOptions.template === 'file-router' ||
|
|
68
|
-
cliOptions.framework === 'solid'
|
|
106
|
+
// TypeScript and Tailwind are always enabled with TanStack Start
|
|
107
|
+
const typescript = true
|
|
108
|
+
const tailwind = true
|
|
69
109
|
|
|
70
110
|
if (starter) {
|
|
71
|
-
tailwind = starter.tailwind
|
|
72
|
-
typescript = starter.typescript
|
|
73
111
|
cliOptions.framework = starter.framework
|
|
74
112
|
mode = starter.mode
|
|
75
113
|
}
|
|
76
114
|
|
|
77
115
|
const framework = getFrameworkById(cliOptions.framework || 'react-cra')!
|
|
78
116
|
|
|
79
|
-
if (
|
|
80
|
-
forcedMode &&
|
|
81
|
-
framework.supportedModes?.[forcedMode]?.forceTypescript !== undefined
|
|
82
|
-
) {
|
|
83
|
-
typescript = true
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (cliOptions.framework === 'solid') {
|
|
87
|
-
tailwind = true
|
|
88
|
-
}
|
|
89
|
-
|
|
90
117
|
async function selectAddOns() {
|
|
91
118
|
// Edge case for Windows Powershell
|
|
92
119
|
if (Array.isArray(cliOptions.addOns) && cliOptions.addOns.length === 1) {
|
|
@@ -131,28 +158,6 @@ export async function normalizeOptions(
|
|
|
131
158
|
|
|
132
159
|
const chosenAddOns = await selectAddOns()
|
|
133
160
|
|
|
134
|
-
if (chosenAddOns.length) {
|
|
135
|
-
typescript = true
|
|
136
|
-
|
|
137
|
-
// Check if any add-on explicitly requires tailwind
|
|
138
|
-
const addOnsRequireTailwind = chosenAddOns.some(
|
|
139
|
-
(addOn) => addOn.tailwind === true,
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
// Only set tailwind to true if:
|
|
143
|
-
// 1. An add-on explicitly requires it, OR
|
|
144
|
-
// 2. User explicitly set it via CLI
|
|
145
|
-
if (addOnsRequireTailwind) {
|
|
146
|
-
tailwind = true
|
|
147
|
-
} else if (cliOptions.tailwind === true) {
|
|
148
|
-
tailwind = true
|
|
149
|
-
} else if (cliOptions.tailwind === false) {
|
|
150
|
-
tailwind = false
|
|
151
|
-
}
|
|
152
|
-
// If cliOptions.tailwind is undefined and no add-ons require it,
|
|
153
|
-
// leave tailwind as is (will be prompted in interactive mode)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
161
|
// Handle add-on configuration option
|
|
157
162
|
let addOnOptionsFromCLI = {}
|
|
158
163
|
if (cliOptions.addOnConfig) {
|
package/src/mcp.ts
CHANGED
|
@@ -55,6 +55,11 @@ function createServer({
|
|
|
55
55
|
id: addOn.id,
|
|
56
56
|
name: addOn.name,
|
|
57
57
|
description: addOn.description,
|
|
58
|
+
type: addOn.type,
|
|
59
|
+
category: addOn.category,
|
|
60
|
+
link: addOn.link,
|
|
61
|
+
warning: addOn.warning,
|
|
62
|
+
exclusive: addOn.exclusive,
|
|
58
63
|
options: addOn.options,
|
|
59
64
|
dependsOn: addOn.dependsOn,
|
|
60
65
|
})),
|
|
@@ -65,6 +70,75 @@ function createServer({
|
|
|
65
70
|
},
|
|
66
71
|
)
|
|
67
72
|
|
|
73
|
+
server.tool(
|
|
74
|
+
'getAddOnDetails',
|
|
75
|
+
'Get detailed information about a specific add-on including implementation patterns, routes, dependencies, and documentation',
|
|
76
|
+
{
|
|
77
|
+
framework: z
|
|
78
|
+
.string()
|
|
79
|
+
.describe(
|
|
80
|
+
`The framework to use. Available frameworks: ${frameworkNames.join(', ')}`,
|
|
81
|
+
),
|
|
82
|
+
addOnId: z
|
|
83
|
+
.string()
|
|
84
|
+
.describe('The ID of the add-on to get details for'),
|
|
85
|
+
},
|
|
86
|
+
async ({ framework: frameworkName, addOnId }) => {
|
|
87
|
+
const framework = getFrameworkByName(frameworkName)!
|
|
88
|
+
const allAddOns = framework.getAddOns()
|
|
89
|
+
const addOn =
|
|
90
|
+
allAddOns.find((a) => a.id === addOnId) ??
|
|
91
|
+
allAddOns.find(
|
|
92
|
+
(a) => a.id.toLowerCase() === addOnId.toLowerCase(),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if (!addOn) {
|
|
96
|
+
return {
|
|
97
|
+
content: [
|
|
98
|
+
{
|
|
99
|
+
type: 'text',
|
|
100
|
+
text: JSON.stringify({ error: `Add-on '${addOnId}' not found` }),
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Get file list for context
|
|
107
|
+
const files = await addOn.getFiles()
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: 'text',
|
|
113
|
+
text: JSON.stringify({
|
|
114
|
+
id: addOn.id,
|
|
115
|
+
name: addOn.name,
|
|
116
|
+
description: addOn.description,
|
|
117
|
+
type: addOn.type,
|
|
118
|
+
category: addOn.category,
|
|
119
|
+
phase: addOn.phase,
|
|
120
|
+
modes: addOn.modes,
|
|
121
|
+
link: addOn.link,
|
|
122
|
+
warning: addOn.warning,
|
|
123
|
+
exclusive: addOn.exclusive,
|
|
124
|
+
dependsOn: addOn.dependsOn,
|
|
125
|
+
options: addOn.options,
|
|
126
|
+
routes: addOn.routes,
|
|
127
|
+
packageAdditions: addOn.packageAdditions,
|
|
128
|
+
shadcnComponents: addOn.shadcnComponents,
|
|
129
|
+
integrations: addOn.integrations,
|
|
130
|
+
readme: addOn.readme,
|
|
131
|
+
files,
|
|
132
|
+
author: addOn.author,
|
|
133
|
+
version: addOn.version,
|
|
134
|
+
license: addOn.license,
|
|
135
|
+
}),
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
)
|
|
141
|
+
|
|
68
142
|
server.tool(
|
|
69
143
|
'createTanStackApplication',
|
|
70
144
|
'Create a new TanStack application',
|
|
@@ -156,7 +230,6 @@ export async function runMCPServer(
|
|
|
156
230
|
appName,
|
|
157
231
|
name,
|
|
158
232
|
}: {
|
|
159
|
-
forcedMode?: string
|
|
160
233
|
forcedAddOns?: Array<string>
|
|
161
234
|
appName?: string
|
|
162
235
|
name?: string
|
package/src/options.ts
CHANGED
|
@@ -16,10 +16,7 @@ import {
|
|
|
16
16
|
selectDeployment,
|
|
17
17
|
selectGit,
|
|
18
18
|
selectPackageManager,
|
|
19
|
-
selectRouterType,
|
|
20
|
-
selectTailwind,
|
|
21
19
|
selectToolchain,
|
|
22
|
-
selectTypescript,
|
|
23
20
|
} from './ui-prompts.js'
|
|
24
21
|
|
|
25
22
|
import {
|
|
@@ -35,11 +32,9 @@ export async function promptForCreateOptions(
|
|
|
35
32
|
cliOptions: CliOptions,
|
|
36
33
|
{
|
|
37
34
|
forcedAddOns = [],
|
|
38
|
-
forcedMode,
|
|
39
35
|
showDeploymentOptions = false,
|
|
40
36
|
}: {
|
|
41
37
|
forcedAddOns?: Array<string>
|
|
42
|
-
forcedMode?: string
|
|
43
38
|
showDeploymentOptions?: boolean
|
|
44
39
|
},
|
|
45
40
|
): Promise<Required<Options> | undefined> {
|
|
@@ -81,29 +76,11 @@ export async function promptForCreateOptions(
|
|
|
81
76
|
}
|
|
82
77
|
}
|
|
83
78
|
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
options.mode = forcedMode
|
|
87
|
-
} else if (cliOptions.template) {
|
|
88
|
-
options.mode =
|
|
89
|
-
cliOptions.template === 'file-router' ? 'file-router' : 'code-router'
|
|
90
|
-
} else {
|
|
91
|
-
options.mode = await selectRouterType()
|
|
92
|
-
}
|
|
79
|
+
// Mode is always file-router (TanStack Start)
|
|
80
|
+
options.mode = 'file-router'
|
|
93
81
|
|
|
94
|
-
// TypeScript
|
|
95
|
-
|
|
96
|
-
options.typescript =
|
|
97
|
-
options.mode === 'file-router' || options.framework.id === 'solid'
|
|
98
|
-
if (
|
|
99
|
-
forcedMode &&
|
|
100
|
-
options.framework.supportedModes[forcedMode].forceTypescript
|
|
101
|
-
) {
|
|
102
|
-
options.typescript = true
|
|
103
|
-
}
|
|
104
|
-
if (!options.typescript && options.mode === 'code-router') {
|
|
105
|
-
options.typescript = await selectTypescript()
|
|
106
|
-
}
|
|
82
|
+
// TypeScript is always enabled with file-router
|
|
83
|
+
options.typescript = true
|
|
107
84
|
|
|
108
85
|
// Package manager selection
|
|
109
86
|
if (cliOptions.packageManager) {
|
|
@@ -170,29 +147,8 @@ export async function promptForCreateOptions(
|
|
|
170
147
|
await finalizeAddOns(options.framework, options.mode, Array.from(addOns)),
|
|
171
148
|
)
|
|
172
149
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Tailwind selection
|
|
178
|
-
// Only treat add-ons as requiring tailwind if they explicitly have "tailwind": true
|
|
179
|
-
const addOnsRequireTailwind = options.chosenAddOns.some(
|
|
180
|
-
(addOn) => addOn.tailwind === true,
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
if (addOnsRequireTailwind) {
|
|
184
|
-
// If any add-on explicitly requires tailwind, enable it automatically
|
|
185
|
-
options.tailwind = true
|
|
186
|
-
} else if (cliOptions.tailwind !== undefined) {
|
|
187
|
-
// User explicitly provided a CLI flag, respect it
|
|
188
|
-
options.tailwind = !!cliOptions.tailwind
|
|
189
|
-
} else if (options.framework.id === 'react-cra') {
|
|
190
|
-
// Only show prompt for react-cra when no CLI flag and no add-ons require it
|
|
191
|
-
options.tailwind = await selectTailwind()
|
|
192
|
-
} else {
|
|
193
|
-
// For other frameworks (like solid), default to true
|
|
194
|
-
options.tailwind = true
|
|
195
|
-
}
|
|
150
|
+
// Tailwind is always enabled
|
|
151
|
+
options.tailwind = true
|
|
196
152
|
|
|
197
153
|
// Prompt for add-on options in interactive mode
|
|
198
154
|
if (Array.isArray(cliOptions.addOns)) {
|
package/src/types.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import type { PackageManager } from '@tanstack/create'
|
|
2
2
|
|
|
3
|
-
export type TemplateOptions = 'typescript' | 'javascript' | 'file-router'
|
|
4
|
-
|
|
5
3
|
export interface CliOptions {
|
|
6
|
-
template?: TemplateOptions
|
|
7
4
|
framework?: string
|
|
8
|
-
tailwind?: boolean
|
|
9
5
|
packageManager?: PackageManager
|
|
10
6
|
toolchain?: string | false
|
|
11
7
|
deployment?: string
|
|
@@ -25,4 +21,5 @@ export interface CliOptions {
|
|
|
25
21
|
addOnConfig?: string
|
|
26
22
|
force?: boolean
|
|
27
23
|
routerOnly?: boolean
|
|
24
|
+
template?: string
|
|
28
25
|
}
|
package/src/ui-prompts.ts
CHANGED
|
@@ -43,56 +43,6 @@ export async function getProjectName(): Promise<string> {
|
|
|
43
43
|
return value
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
export async function selectRouterType(): Promise<string> {
|
|
47
|
-
const routerType = await select({
|
|
48
|
-
message: 'Select the router type:',
|
|
49
|
-
options: [
|
|
50
|
-
{
|
|
51
|
-
value: 'file-router',
|
|
52
|
-
label: 'File Router - File-based routing structure',
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
value: 'code-router',
|
|
56
|
-
label: 'Code Router - Traditional code-based routing',
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
initialValue: 'file-router',
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
if (isCancel(routerType)) {
|
|
63
|
-
cancel('Operation cancelled.')
|
|
64
|
-
process.exit(0)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return routerType
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export async function selectTypescript(): Promise<boolean> {
|
|
71
|
-
const typescriptEnable = await confirm({
|
|
72
|
-
message: 'Would you like to use TypeScript?',
|
|
73
|
-
initialValue: true,
|
|
74
|
-
})
|
|
75
|
-
if (isCancel(typescriptEnable)) {
|
|
76
|
-
cancel('Operation cancelled.')
|
|
77
|
-
process.exit(0)
|
|
78
|
-
}
|
|
79
|
-
return typescriptEnable
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export async function selectTailwind(): Promise<boolean> {
|
|
83
|
-
const tailwind = await confirm({
|
|
84
|
-
message: 'Would you like to use Tailwind CSS?',
|
|
85
|
-
initialValue: true,
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
if (isCancel(tailwind)) {
|
|
89
|
-
cancel('Operation cancelled.')
|
|
90
|
-
process.exit(0)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return tailwind
|
|
94
|
-
}
|
|
95
|
-
|
|
96
46
|
export async function selectPackageManager(): Promise<PackageManager> {
|
|
97
47
|
const packageManager = await select({
|
|
98
48
|
message: 'Select package manager:',
|
package/src/utils.ts
CHANGED
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import { basename } from 'node:path'
|
|
2
2
|
import validatePackageName from 'validate-npm-package-name'
|
|
3
|
-
import type { TemplateOptions } from './types.js'
|
|
4
|
-
|
|
5
|
-
export function convertTemplateToMode(template: TemplateOptions): string {
|
|
6
|
-
if (template === 'typescript' || template === 'javascript') {
|
|
7
|
-
return 'code-router'
|
|
8
|
-
}
|
|
9
|
-
return 'file-router'
|
|
10
|
-
}
|
|
11
3
|
|
|
12
4
|
export function sanitizePackageName(name: string): string {
|
|
13
5
|
return name
|