Skip to content

Commit f44ca9b

Browse files
committed
feat: create-start-app creates start applications only
1 parent 00d414a commit f44ca9b

File tree

8 files changed

+136
-73
lines changed

8 files changed

+136
-73
lines changed

cli/create-start-app/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
#!/usr/bin/env node
22
import { cli } from '@tanstack/cta-engine'
33

4-
cli()
4+
cli({
5+
name: 'create-start-app',
6+
forcedMode: 'file-router',
7+
forcedAddOns: ['start'],
8+
})

cli/create-tsrouter-app/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#!/usr/bin/env node
22
import { cli } from '@tanstack/cta-engine'
33

4-
cli()
4+
cli({
5+
name: 'create-tsrouter-app',
6+
})

packages/cta-engine/src/add.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { readConfigFile, writeConfigFile } from './config-file.js'
2525

2626
import type { PersistedOptions } from './config-file.js'
2727

28-
import type { Options } from './types.js'
28+
import type { Framework, Options } from './types.js'
2929

3030
function isDirectory(path: string) {
3131
return statSync(path).isDirectory()
@@ -43,10 +43,11 @@ async function createOptions(
4343
return {
4444
...json,
4545
tailwind: true,
46-
chosenAddOns: await finalizeAddOns(json.framework!, json.mode!, [
47-
...json.existingAddOns,
48-
...addOns,
49-
]),
46+
chosenAddOns: await finalizeAddOns(
47+
json.framework as Framework,
48+
json.mode as string,
49+
[...json.existingAddOns, ...addOns],
50+
),
5051
} as Required<Options>
5152
}
5253

@@ -56,6 +57,7 @@ async function runCreateApp(options: Required<Options>) {
5657
silent: true,
5758
environment,
5859
cwd: process.cwd(),
60+
name: 'create-tsrouter-app',
5961
})
6062
return output
6163
}

packages/cta-engine/src/cli.ts

Lines changed: 66 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,15 @@ import type { PackageManager } from './package-manager.js'
1818
import type { ToolChain } from './toolchain.js'
1919
import type { CliOptions, Framework } from './types.js'
2020

21-
export function cli() {
21+
export function cli({
22+
name,
23+
forcedMode,
24+
forcedAddOns,
25+
}: {
26+
name: string
27+
forcedMode?: 'typescript' | 'javascript' | 'file-router'
28+
forcedAddOns?: Array<string>
29+
}) {
2230
const program = new Command()
2331

2432
program
@@ -46,7 +54,7 @@ export function cli() {
4654
// await initAddOn('overlay')
4755
// })
4856

49-
program // 104 22
57+
program
5058
.argument('[project-name]', 'name of the project')
5159
.option('--no-git', 'do not create a git repository')
5260
.option('--target-dir <path>', 'the directory to create the project in')
@@ -65,22 +73,6 @@ export function cli() {
6573
},
6674
DEFAULT_FRAMEWORK,
6775
)
68-
.option<'typescript' | 'javascript' | 'file-router'>(
69-
'--template <type>',
70-
'project template (typescript, javascript, file-router)',
71-
(value) => {
72-
if (
73-
value !== 'typescript' &&
74-
value !== 'javascript' &&
75-
value !== 'file-router'
76-
) {
77-
throw new InvalidArgumentError(
78-
`Invalid template: ${value}. Only the following are allowed: typescript, javascript, file-router`,
79-
)
80-
}
81-
return value
82-
},
83-
)
8476
.option<PackageManager>(
8577
`--package-manager <${SUPPORTED_PACKAGE_MANAGERS.join('|')}>`,
8678
`Explicitly tell the CLI to use this package manager`,
@@ -122,42 +114,68 @@ export function cli() {
122114
},
123115
)
124116
.option('--list-add-ons', 'list all available add-ons', false)
125-
.option('--overlay [url]', 'add an overlay from a URL', false)
117+
// .option('--overlay [url]', 'add an overlay from a URL', false)
126118
.option('--mcp', 'run the MCP server', false)
127119
.option('--mcp-sse', 'run the MCP server in SSE mode', false)
128-
.action(async (projectName: string, options: CliOptions) => {
129-
if (options.listAddOns) {
130-
await listAddOns(options)
131-
} else if (options.mcp || options.mcpSse) {
132-
await runServer(!!options.mcpSse)
133-
} else {
134-
try {
135-
const cliOptions = {
136-
projectName,
137-
...options,
138-
} as CliOptions
139120

140-
let finalOptions = await normalizeOptions(cliOptions)
141-
if (finalOptions) {
142-
intro(`Creating a new TanStack app in ${projectName}...`)
143-
} else {
144-
intro("Let's configure your TanStack application")
145-
finalOptions = await promptForOptions(cliOptions)
146-
}
147-
await createApp(finalOptions, {
148-
environment: createDefaultEnvironment(),
149-
cwd: options.targetDir || undefined,
150-
})
151-
} catch (error) {
152-
log.error(
153-
error instanceof Error
154-
? error.message
155-
: 'An unknown error occurred',
121+
if (!forcedMode) {
122+
program.option<'typescript' | 'javascript' | 'file-router'>(
123+
'--template <type>',
124+
'project template (typescript, javascript, file-router)',
125+
(value) => {
126+
if (
127+
value !== 'typescript' &&
128+
value !== 'javascript' &&
129+
value !== 'file-router'
130+
) {
131+
throw new InvalidArgumentError(
132+
`Invalid template: ${value}. Only the following are allowed: typescript, javascript, file-router`,
156133
)
157-
process.exit(1)
158134
}
135+
return value
136+
},
137+
)
138+
}
139+
140+
program.action(async (projectName: string, options: CliOptions) => {
141+
if (options.listAddOns) {
142+
await listAddOns(options)
143+
} else if (options.mcp || options.mcpSse) {
144+
await runServer(!!options.mcpSse)
145+
} else {
146+
try {
147+
const cliOptions = {
148+
projectName,
149+
...options,
150+
} as CliOptions
151+
152+
if (forcedMode) {
153+
cliOptions.template = forcedMode
154+
}
155+
156+
let finalOptions = await normalizeOptions(cliOptions, forcedAddOns)
157+
if (finalOptions) {
158+
intro(`Creating a new TanStack app in ${projectName}...`)
159+
} else {
160+
intro("Let's configure your TanStack application")
161+
finalOptions = await promptForOptions(cliOptions, {
162+
forcedMode,
163+
forcedAddOns,
164+
})
165+
}
166+
await createApp(finalOptions, {
167+
environment: createDefaultEnvironment(),
168+
cwd: options.targetDir || undefined,
169+
name,
170+
})
171+
} catch (error) {
172+
log.error(
173+
error instanceof Error ? error.message : 'An unknown error occurred',
174+
)
175+
process.exit(1)
159176
}
160-
})
177+
}
178+
})
161179

162180
program.parse()
163181
}

packages/cta-engine/src/create-app.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,12 @@ export async function createApp(
304304
silent = false,
305305
environment,
306306
cwd,
307+
name = 'create-tsrouter-app',
307308
}: {
308309
silent?: boolean
309310
environment: Environment
310311
cwd?: string
312+
name: string
311313
},
312314
) {
313315
environment.startRun()

packages/cta-engine/src/custom-add-on.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { createApp } from './create-app.js'
88
import { readConfigFile } from './config-file.js'
99
import { finalizeAddOns } from './add-ons.js'
1010

11-
import type { Options } from './types.js'
11+
import type { Framework, Options } from './types.js'
1212
import type { PersistedOptions } from './config-file.js'
1313

1414
type AddOnMode = 'add-on' | 'overlay'
@@ -106,9 +106,11 @@ async function createOptions(
106106
): Promise<Required<Options>> {
107107
return {
108108
...json,
109-
chosenAddOns: await finalizeAddOns(json.framework!, json.mode!, [
110-
...json.existingAddOns,
111-
]),
109+
chosenAddOns: await finalizeAddOns(
110+
json.framework as Framework,
111+
json.mode as string,
112+
[...json.existingAddOns],
113+
),
112114
} as Required<Options>
113115
}
114116

@@ -118,6 +120,7 @@ async function runCreateApp(options: Required<Options>) {
118120
silent: true,
119121
environment,
120122
cwd: process.cwd(),
123+
name: 'create-tsrouter-app',
121124
})
122125
return output
123126
}

packages/cta-engine/src/mcp.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ server.tool(
114114
{
115115
silent: true,
116116
environment: createDefaultEnvironment(),
117+
name: 'create-tsrouter-app',
117118
},
118119
)
119120
return {
@@ -209,6 +210,7 @@ server.tool(
209210
{
210211
silent: true,
211212
environment: createDefaultEnvironment(),
213+
name: 'create-tsrouter-app',
212214
},
213215
)
214216
return {

packages/cta-engine/src/options.ts

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import type { AddOn, CliOptions, Options, Overlay, Variable } from './types.js'
2121
// If all CLI options are provided, use them directly
2222
export async function normalizeOptions(
2323
cliOptions: CliOptions,
24+
forcedAddOns?: Array<string>,
2425
): Promise<Options | undefined> {
2526
// in some cases, if you use windows/powershell, the argument for addons
2627
// if sepparated by comma is not really passed as an array, but as a string
@@ -30,6 +31,9 @@ export async function normalizeOptions(
3031
if (parseSeparatedArgs.length > 1) {
3132
cliOptions.addOns = parseSeparatedArgs
3233
}
34+
if (forcedAddOns) {
35+
cliOptions.addOns = [...cliOptions.addOns, ...forcedAddOns]
36+
}
3337
}
3438
if (cliOptions.projectName) {
3539
let typescript =
@@ -135,6 +139,13 @@ async function collectVariables(
135139

136140
export async function promptForOptions(
137141
cliOptions: CliOptions,
142+
{
143+
forcedAddOns = [],
144+
forcedMode,
145+
}: {
146+
forcedAddOns?: Array<string>
147+
forcedMode?: 'typescript' | 'javascript' | 'file-router'
148+
},
138149
): Promise<Required<Options>> {
139150
const options = {} as Required<Options>
140151

@@ -166,7 +177,7 @@ export async function promptForOptions(
166177
}
167178

168179
// Router type selection
169-
if (!cliOptions.template) {
180+
if (!cliOptions.template && !forcedMode) {
170181
const routerType = await select({
171182
message: 'Select the router type:',
172183
options: [
@@ -186,6 +197,9 @@ export async function promptForOptions(
186197
process.exit(0)
187198
}
188199
options.mode = routerType as typeof CODE_ROUTER | typeof FILE_ROUTER
200+
} else if (forcedMode) {
201+
options.mode = forcedMode === 'file-router' ? FILE_ROUTER : CODE_ROUTER
202+
options.typescript = options.mode === FILE_ROUTER
189203
} else {
190204
options.mode =
191205
cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER
@@ -274,7 +288,7 @@ export async function promptForOptions(
274288
options.chosenAddOns = await finalizeAddOns(
275289
options.framework,
276290
options.mode,
277-
cliOptions.addOns,
291+
Array.from(new Set([...cliOptions.addOns, ...forcedAddOns])),
278292
)
279293
options.tailwind = true
280294
} else if (cliOptions.addOns) {
@@ -285,11 +299,13 @@ export async function promptForOptions(
285299
if (options.typescript && addOns.length > 0) {
286300
const value = await multiselect({
287301
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-
})),
302+
options: addOns
303+
.filter((addOn) => !forcedAddOns.includes(addOn.id))
304+
.map((addOn) => ({
305+
value: addOn.id,
306+
label: addOn.name,
307+
hint: addOn.description,
308+
})),
293309
required: false,
294310
})
295311

@@ -306,11 +322,13 @@ export async function promptForOptions(
306322
if (options.typescript && examples.length > 0) {
307323
const value = await multiselect({
308324
message: 'Would you like any examples?',
309-
options: examples.map((addOn) => ({
310-
value: addOn.id,
311-
label: addOn.name,
312-
hint: addOn.description,
313-
})),
325+
options: examples
326+
.filter((addOn) => !forcedAddOns.includes(addOn.id))
327+
.map((addOn) => ({
328+
value: addOn.id,
329+
label: addOn.name,
330+
hint: addOn.description,
331+
})),
314332
required: false,
315333
})
316334

@@ -321,14 +339,26 @@ export async function promptForOptions(
321339
selectedExamples = value
322340
}
323341

324-
if (selectedAddOns.length > 0 || selectedExamples.length > 0) {
342+
if (
343+
selectedAddOns.length > 0 ||
344+
selectedExamples.length > 0 ||
345+
forcedAddOns.length > 0
346+
) {
325347
options.chosenAddOns = await finalizeAddOns(
326348
options.framework,
327349
options.mode,
328-
[...selectedAddOns, ...selectedExamples],
350+
Array.from(
351+
new Set([...selectedAddOns, ...selectedExamples, ...forcedAddOns]),
352+
),
329353
)
330354
options.tailwind = true
331355
}
356+
} else if (forcedAddOns.length > 0) {
357+
options.chosenAddOns = await finalizeAddOns(
358+
options.framework,
359+
options.mode,
360+
forcedAddOns,
361+
)
332362
}
333363

334364
// Collect variables

0 commit comments

Comments
 (0)