Skip to content

Commit a79c5bf

Browse files
committed
chore: CLI tests
1 parent 78f0442 commit a79c5bf

13 files changed

+1137
-409
lines changed

packages/cta-cli/package.json

+9-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
"scripts": {
99
"build": "tsc",
1010
"dev": "tsc --watch",
11-
"test:lint": "eslint ./src"
11+
"test:lint": "eslint ./src",
12+
"test": "vitest run",
13+
"test:watch": "vitest",
14+
"test:coverage": "vitest run --coverage"
1215
},
1316
"repository": {
1417
"type": "git",
@@ -29,19 +32,21 @@
2932
"license": "MIT",
3033
"packageManager": "pnpm@9.15.5",
3134
"dependencies": {
32-
"@tanstack/cta-engine": "workspace:*",
35+
"@clack/prompts": "^0.10.0",
3336
"@tanstack/cta-custom-add-on": "workspace:*",
37+
"@tanstack/cta-engine": "workspace:*",
3438
"@tanstack/cta-mcp": "workspace:*",
3539
"@tanstack/cta-ui": "workspace:*",
36-
"@clack/prompts": "^0.10.0",
3740
"chalk": "^5.4.1",
3841
"commander": "^13.1.0"
3942
},
4043
"devDependencies": {
4144
"@tanstack/config": "^0.16.2",
4245
"@types/node": "^22.13.4",
46+
"@vitest/coverage-v8": "3.1.1",
4347
"eslint": "^9.20.0",
4448
"typescript": "^5.6.3",
45-
"vitest": "^3.0.8"
49+
"vitest": "^3.1.1",
50+
"vitest-fetch-mock": "^0.4.5"
4651
}
4752
}

packages/cta-cli/src/cli.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import { runMCPServer } from '@tanstack/cta-mcp'
1818

1919
import { launchUI } from '@tanstack/cta-ui'
2020

21-
import { normalizeOptions, promptForOptions } from './options.js'
21+
import { promptForOptions } from './options.js'
22+
import { normalizeOptions } from './command-line.js'
2223

2324
import { createUIEnvironment } from './ui-environment.js'
2425

@@ -246,7 +247,7 @@ export function cli({
246247
} else {
247248
intro(`Let's configure your ${appName} application`)
248249
finalOptions = await promptForOptions(cliOptions, {
249-
forcedMode: forcedMode as TemplateOptions,
250+
forcedMode: forcedMode as Mode,
250251
forcedAddOns,
251252
})
252253
}
@@ -256,9 +257,9 @@ export function cli({
256257
}
257258

258259
finalOptions.targetDir =
259-
options.targetDir || resolve(process.cwd(), finalOptions!.projectName)
260+
options.targetDir || resolve(process.cwd(), finalOptions.projectName)
260261

261-
await createApp(environment, finalOptions!)
262+
await createApp(environment, finalOptions)
262263
} catch (error) {
263264
log.error(
264265
error instanceof Error ? error.message : 'An unknown error occurred',

packages/cta-cli/src/command-line.ts

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { resolve } from 'node:path'
2+
3+
import {
4+
CODE_ROUTER,
5+
DEFAULT_PACKAGE_MANAGER,
6+
FILE_ROUTER,
7+
finalizeAddOns,
8+
getFrameworkById,
9+
getPackageManager,
10+
loadRemoteAddOn,
11+
} from '@tanstack/cta-engine'
12+
13+
import type { Mode, Options, Starter } from '@tanstack/cta-engine'
14+
15+
import type { CliOptions } from './types.js'
16+
17+
export async function normalizeOptions(
18+
cliOptions: CliOptions,
19+
forcedMode?: Mode,
20+
forcedAddOns?: Array<string>,
21+
): Promise<Options | undefined> {
22+
const projectName = (cliOptions.projectName ?? '').trim()
23+
if (!projectName) {
24+
return undefined
25+
}
26+
27+
let typescript =
28+
cliOptions.template === 'typescript' ||
29+
cliOptions.template === 'file-router' ||
30+
cliOptions.framework === 'solid'
31+
32+
let tailwind = !!cliOptions.tailwind
33+
if (cliOptions.framework === 'solid') {
34+
tailwind = true
35+
}
36+
37+
let mode: typeof FILE_ROUTER | typeof CODE_ROUTER =
38+
cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER
39+
40+
const starter = cliOptions.starter
41+
? ((await loadRemoteAddOn(cliOptions.starter)) as Starter)
42+
: undefined
43+
44+
if (starter) {
45+
tailwind = starter.tailwind
46+
typescript = starter.typescript
47+
cliOptions.framework = starter.framework
48+
mode = starter.mode
49+
}
50+
51+
const framework = getFrameworkById(cliOptions.framework || 'react-cra')!
52+
53+
async function selectAddOns() {
54+
// Edge case for Windows Powershell
55+
if (Array.isArray(cliOptions.addOns) && cliOptions.addOns.length === 1) {
56+
const parseSeparatedArgs = cliOptions.addOns[0].split(' ')
57+
if (parseSeparatedArgs.length > 1) {
58+
cliOptions.addOns = parseSeparatedArgs
59+
}
60+
}
61+
62+
if (
63+
Array.isArray(cliOptions.addOns) ||
64+
starter?.dependsOn ||
65+
forcedAddOns ||
66+
cliOptions.toolchain
67+
) {
68+
const selectedAddOns = new Set<string>([
69+
...(starter?.dependsOn || []),
70+
...(forcedAddOns || []),
71+
])
72+
if (cliOptions.addOns && Array.isArray(cliOptions.addOns)) {
73+
for (const a of cliOptions.addOns) {
74+
selectedAddOns.add(a)
75+
}
76+
}
77+
if (cliOptions.toolchain) {
78+
selectedAddOns.add(cliOptions.toolchain)
79+
}
80+
81+
return await finalizeAddOns(
82+
framework,
83+
forcedMode || cliOptions.template === 'file-router'
84+
? FILE_ROUTER
85+
: CODE_ROUTER,
86+
Array.from(selectedAddOns),
87+
)
88+
}
89+
90+
return []
91+
}
92+
93+
const chosenAddOns = await selectAddOns()
94+
95+
if (chosenAddOns.length) {
96+
tailwind = true
97+
typescript = true
98+
}
99+
100+
return {
101+
projectName: projectName,
102+
targetDir: resolve(process.cwd(), projectName),
103+
framework,
104+
mode,
105+
typescript,
106+
tailwind,
107+
packageManager:
108+
cliOptions.packageManager ||
109+
getPackageManager() ||
110+
DEFAULT_PACKAGE_MANAGER,
111+
git: !!cliOptions.git,
112+
chosenAddOns,
113+
starter,
114+
variableValues: {},
115+
}
116+
}

0 commit comments

Comments
 (0)