@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
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { cancel, confirm, isCancel, multiselect, select, text, } from '@clack/prompts';
|
|
2
|
+
import { CODE_ROUTER, DEFAULT_PACKAGE_MANAGER, FILE_ROUTER, SUPPORTED_PACKAGE_MANAGERS, getAllAddOns, } from '@tanstack/cta-engine';
|
|
3
|
+
export async function getProjectName() {
|
|
4
|
+
const value = await text({
|
|
5
|
+
message: 'What would you like to name your project?',
|
|
6
|
+
defaultValue: 'my-app',
|
|
7
|
+
validate(value) {
|
|
8
|
+
if (!value) {
|
|
9
|
+
return 'Please enter a name';
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
if (isCancel(value)) {
|
|
14
|
+
cancel('Operation cancelled.');
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
export async function selectRouterType() {
|
|
20
|
+
const routerType = await select({
|
|
21
|
+
message: 'Select the router type:',
|
|
22
|
+
options: [
|
|
23
|
+
{
|
|
24
|
+
value: FILE_ROUTER,
|
|
25
|
+
label: 'File Router - File-based routing structure',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
value: CODE_ROUTER,
|
|
29
|
+
label: 'Code Router - Traditional code-based routing',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
initialValue: FILE_ROUTER,
|
|
33
|
+
});
|
|
34
|
+
if (isCancel(routerType)) {
|
|
35
|
+
cancel('Operation cancelled.');
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
return routerType;
|
|
39
|
+
}
|
|
40
|
+
export async function selectTypescript() {
|
|
41
|
+
const typescriptEnable = await confirm({
|
|
42
|
+
message: 'Would you like to use TypeScript?',
|
|
43
|
+
initialValue: true,
|
|
44
|
+
});
|
|
45
|
+
if (isCancel(typescriptEnable)) {
|
|
46
|
+
cancel('Operation cancelled.');
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
return typescriptEnable;
|
|
50
|
+
}
|
|
51
|
+
export async function selectTailwind() {
|
|
52
|
+
const tailwind = await confirm({
|
|
53
|
+
message: 'Would you like to use Tailwind CSS?',
|
|
54
|
+
initialValue: true,
|
|
55
|
+
});
|
|
56
|
+
if (isCancel(tailwind)) {
|
|
57
|
+
cancel('Operation cancelled.');
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
return tailwind;
|
|
61
|
+
}
|
|
62
|
+
export async function selectPackageManager() {
|
|
63
|
+
const packageManager = await select({
|
|
64
|
+
message: 'Select package manager:',
|
|
65
|
+
options: SUPPORTED_PACKAGE_MANAGERS.map((pm) => ({
|
|
66
|
+
value: pm,
|
|
67
|
+
label: pm,
|
|
68
|
+
})),
|
|
69
|
+
initialValue: DEFAULT_PACKAGE_MANAGER,
|
|
70
|
+
});
|
|
71
|
+
if (isCancel(packageManager)) {
|
|
72
|
+
cancel('Operation cancelled.');
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
return packageManager;
|
|
76
|
+
}
|
|
77
|
+
export async function selectAddOns(framework, mode, type, message, forcedAddOns = []) {
|
|
78
|
+
const allAddOns = await getAllAddOns(framework, mode);
|
|
79
|
+
const addOns = allAddOns.filter((addOn) => addOn.type === type);
|
|
80
|
+
if (addOns.length === 0) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
const value = await multiselect({
|
|
84
|
+
message,
|
|
85
|
+
options: addOns
|
|
86
|
+
.filter((addOn) => !forcedAddOns.includes(addOn.id))
|
|
87
|
+
.map((addOn) => ({
|
|
88
|
+
value: addOn.id,
|
|
89
|
+
label: addOn.name,
|
|
90
|
+
hint: addOn.description,
|
|
91
|
+
})),
|
|
92
|
+
required: false,
|
|
93
|
+
});
|
|
94
|
+
if (isCancel(value)) {
|
|
95
|
+
cancel('Operation cancelled.');
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
export async function selectGit() {
|
|
101
|
+
const git = await confirm({
|
|
102
|
+
message: 'Would you like to initialize a new git repository?',
|
|
103
|
+
initialValue: true,
|
|
104
|
+
});
|
|
105
|
+
if (isCancel(git)) {
|
|
106
|
+
cancel('Operation cancelled.');
|
|
107
|
+
process.exit(0);
|
|
108
|
+
}
|
|
109
|
+
return git;
|
|
110
|
+
}
|
|
111
|
+
export async function selectToolchain(framework, toolchain) {
|
|
112
|
+
const toolchains = new Set();
|
|
113
|
+
for (const addOn of framework.getAddOns()) {
|
|
114
|
+
if (addOn.type === 'toolchain') {
|
|
115
|
+
toolchains.add(addOn);
|
|
116
|
+
if (toolchain && addOn.id === toolchain) {
|
|
117
|
+
return toolchain;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const tc = await select({
|
|
122
|
+
message: 'Select toolchain',
|
|
123
|
+
options: [
|
|
124
|
+
{
|
|
125
|
+
value: undefined,
|
|
126
|
+
label: 'None',
|
|
127
|
+
},
|
|
128
|
+
...Array.from(toolchains).map((tc) => ({
|
|
129
|
+
value: tc.id,
|
|
130
|
+
label: tc.name,
|
|
131
|
+
})),
|
|
132
|
+
],
|
|
133
|
+
initialValue: undefined,
|
|
134
|
+
});
|
|
135
|
+
if (isCancel(tc)) {
|
|
136
|
+
cancel('Operation cancelled.');
|
|
137
|
+
process.exit(0);
|
|
138
|
+
}
|
|
139
|
+
return tc;
|
|
140
|
+
}
|
package/dist/utils.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/cta-cli",
|
|
3
|
-
"version": "0.10.0-alpha.
|
|
3
|
+
"version": "0.10.0-alpha.20",
|
|
4
4
|
"description": "Tanstack Application Builder CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -24,19 +24,23 @@
|
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@clack/prompts": "^0.10.0",
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.6.0",
|
|
27
28
|
"chalk": "^5.4.1",
|
|
28
29
|
"commander": "^13.1.0",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"@tanstack/cta-
|
|
32
|
-
"@tanstack/cta-
|
|
30
|
+
"express": "^4.21.2",
|
|
31
|
+
"zod": "^3.24.2",
|
|
32
|
+
"@tanstack/cta-engine": "0.10.0-alpha.20",
|
|
33
|
+
"@tanstack/cta-ui": "0.10.0-alpha.20"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@tanstack/config": "^0.16.2",
|
|
37
|
+
"@types/express": "^5.0.1",
|
|
36
38
|
"@types/node": "^22.13.4",
|
|
39
|
+
"@vitest/coverage-v8": "3.1.1",
|
|
37
40
|
"eslint": "^9.20.0",
|
|
38
41
|
"typescript": "^5.6.3",
|
|
39
|
-
"vitest": "^3.
|
|
42
|
+
"vitest": "^3.1.1",
|
|
43
|
+
"vitest-fetch-mock": "^0.4.5"
|
|
40
44
|
},
|
|
41
45
|
"scripts": {}
|
|
42
46
|
}
|
package/src/cli.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolve } from 'node:path'
|
|
1
2
|
import { Command, InvalidArgumentError } from 'commander'
|
|
2
3
|
import { intro, log } from '@clack/prompts'
|
|
3
4
|
import chalk from 'chalk'
|
|
@@ -5,43 +6,50 @@ import chalk from 'chalk'
|
|
|
5
6
|
import {
|
|
6
7
|
SUPPORTED_PACKAGE_MANAGERS,
|
|
7
8
|
addToApp,
|
|
9
|
+
compileAddOn,
|
|
10
|
+
compileStarter,
|
|
8
11
|
createApp,
|
|
12
|
+
createSerializedOptions,
|
|
9
13
|
getAllAddOns,
|
|
10
14
|
getFrameworkById,
|
|
11
15
|
getFrameworkByName,
|
|
12
16
|
getFrameworks,
|
|
17
|
+
initAddOn,
|
|
18
|
+
initStarter,
|
|
13
19
|
} from '@tanstack/cta-engine'
|
|
14
|
-
import { initAddOn } from '@tanstack/cta-custom-add-on'
|
|
15
|
-
|
|
16
|
-
import { runMCPServer } from '@tanstack/cta-mcp'
|
|
17
20
|
|
|
18
21
|
import { launchUI } from '@tanstack/cta-ui'
|
|
19
22
|
|
|
20
|
-
import {
|
|
23
|
+
import { runMCPServer } from './mcp.js'
|
|
24
|
+
|
|
25
|
+
import { promptForOptions } from './options.js'
|
|
26
|
+
import { normalizeOptions } from './command-line.js'
|
|
21
27
|
|
|
22
28
|
import { createUIEnvironment } from './ui-environment.js'
|
|
29
|
+
import { convertTemplateToMode } from './utils.js'
|
|
23
30
|
|
|
24
|
-
import type {
|
|
25
|
-
Mode,
|
|
26
|
-
PackageManager,
|
|
27
|
-
TemplateOptions,
|
|
28
|
-
} from '@tanstack/cta-engine'
|
|
31
|
+
import type { Mode, Options, PackageManager } from '@tanstack/cta-engine'
|
|
29
32
|
|
|
30
|
-
import type { CliOptions } from './types.js'
|
|
33
|
+
import type { CliOptions, TemplateOptions } from './types.js'
|
|
31
34
|
|
|
32
35
|
async function listAddOns(
|
|
33
36
|
options: CliOptions,
|
|
34
37
|
{
|
|
35
38
|
forcedMode,
|
|
36
|
-
forcedAddOns
|
|
39
|
+
forcedAddOns,
|
|
40
|
+
defaultTemplate,
|
|
37
41
|
}: {
|
|
38
|
-
forcedMode?:
|
|
39
|
-
forcedAddOns
|
|
42
|
+
forcedMode?: Mode
|
|
43
|
+
forcedAddOns: Array<string>
|
|
44
|
+
defaultTemplate?: TemplateOptions
|
|
40
45
|
},
|
|
41
46
|
) {
|
|
42
47
|
const addOns = await getAllAddOns(
|
|
43
48
|
getFrameworkById(options.framework || 'react-cra')!,
|
|
44
|
-
forcedMode ||
|
|
49
|
+
forcedMode ||
|
|
50
|
+
convertTemplateToMode(
|
|
51
|
+
options.template || defaultTemplate || 'javascript',
|
|
52
|
+
),
|
|
45
53
|
)
|
|
46
54
|
for (const addOn of addOns.filter((a) => !forcedAddOns.includes(a.id))) {
|
|
47
55
|
console.log(`${chalk.bold(addOn.id)}: ${addOn.description}`)
|
|
@@ -52,14 +60,16 @@ export function cli({
|
|
|
52
60
|
name,
|
|
53
61
|
appName,
|
|
54
62
|
forcedMode,
|
|
55
|
-
forcedAddOns,
|
|
63
|
+
forcedAddOns = [],
|
|
64
|
+
defaultTemplate = 'javascript',
|
|
56
65
|
}: {
|
|
57
66
|
name: string
|
|
58
67
|
appName: string
|
|
59
68
|
forcedMode?: Mode
|
|
60
69
|
forcedAddOns?: Array<string>
|
|
70
|
+
defaultTemplate?: TemplateOptions
|
|
61
71
|
}) {
|
|
62
|
-
const environment = createUIEnvironment()
|
|
72
|
+
const environment = createUIEnvironment(appName, false)
|
|
63
73
|
|
|
64
74
|
const program = new Command()
|
|
65
75
|
|
|
@@ -78,37 +88,61 @@ export function cli({
|
|
|
78
88
|
|
|
79
89
|
program
|
|
80
90
|
.command('add')
|
|
81
|
-
.argument(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
)
|
|
91
|
+
.argument(
|
|
92
|
+
'[add-on...]',
|
|
93
|
+
'Name of the add-ons (or add-ons separated by spaces or commas)',
|
|
94
|
+
)
|
|
95
|
+
.option('--forced', 'Force the add-on to be added', false)
|
|
96
|
+
.option('--ui', 'Add with the UI')
|
|
97
|
+
.action(async (addOns: Array<string>) => {
|
|
98
|
+
const parsedAddOns: Array<string> = []
|
|
99
|
+
for (const addOn of addOns) {
|
|
100
|
+
if (addOn.includes(',') || addOn.includes(' ')) {
|
|
101
|
+
parsedAddOns.push(
|
|
102
|
+
...addOn.split(/[\s,]+/).map((addon) => addon.trim()),
|
|
103
|
+
)
|
|
104
|
+
} else {
|
|
105
|
+
parsedAddOns.push(addOn.trim())
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (program.opts().ui) {
|
|
109
|
+
launchUI({
|
|
110
|
+
mode: 'add',
|
|
111
|
+
addOns: parsedAddOns,
|
|
112
|
+
})
|
|
113
|
+
} else {
|
|
114
|
+
await addToApp(environment, parsedAddOns, process.cwd(), {
|
|
115
|
+
forced: program.opts().forced,
|
|
116
|
+
})
|
|
117
|
+
}
|
|
90
118
|
})
|
|
91
119
|
|
|
92
120
|
const addOnCommand = program.command('add-on')
|
|
93
|
-
|
|
94
121
|
addOnCommand
|
|
95
|
-
.command('
|
|
96
|
-
.description('
|
|
122
|
+
.command('init')
|
|
123
|
+
.description('Initialize an add-on from the current project')
|
|
97
124
|
.action(async () => {
|
|
98
|
-
await initAddOn(
|
|
125
|
+
await initAddOn(environment)
|
|
99
126
|
})
|
|
100
127
|
addOnCommand
|
|
101
|
-
.command('
|
|
102
|
-
.description('
|
|
128
|
+
.command('compile')
|
|
129
|
+
.description('Update add-on from the current project')
|
|
103
130
|
.action(async () => {
|
|
104
|
-
|
|
131
|
+
await compileAddOn(environment)
|
|
105
132
|
})
|
|
106
133
|
|
|
107
|
-
program
|
|
108
|
-
|
|
109
|
-
.
|
|
134
|
+
const starterCommand = program.command('starter')
|
|
135
|
+
starterCommand
|
|
136
|
+
.command('init')
|
|
137
|
+
.description('Initialize a project starter from the current project')
|
|
138
|
+
.action(async () => {
|
|
139
|
+
await initStarter(environment)
|
|
140
|
+
})
|
|
141
|
+
starterCommand
|
|
142
|
+
.command('compile')
|
|
143
|
+
.description('Compile the starter JSON file for the current project')
|
|
110
144
|
.action(async () => {
|
|
111
|
-
await
|
|
145
|
+
await compileStarter(environment)
|
|
112
146
|
})
|
|
113
147
|
|
|
114
148
|
program.argument('[project-name]', 'name of the project')
|
|
@@ -179,6 +213,7 @@ export function cli({
|
|
|
179
213
|
return value
|
|
180
214
|
},
|
|
181
215
|
)
|
|
216
|
+
.option('--interactive', 'interactive mode', false)
|
|
182
217
|
.option('--tailwind', 'add Tailwind CSS', false)
|
|
183
218
|
.option<Array<string> | boolean>(
|
|
184
219
|
'--add-ons [...add-ons]',
|
|
@@ -199,16 +234,18 @@ export function cli({
|
|
|
199
234
|
)
|
|
200
235
|
.option('--mcp', 'run the MCP server', false)
|
|
201
236
|
.option('--mcp-sse', 'run the MCP server in SSE mode', false)
|
|
237
|
+
.option('--ui', 'Add with the UI')
|
|
202
238
|
|
|
203
239
|
program.action(async (projectName: string, options: CliOptions) => {
|
|
204
240
|
if (options.listAddOns) {
|
|
205
241
|
await listAddOns(options, {
|
|
206
|
-
forcedMode
|
|
242
|
+
forcedMode,
|
|
207
243
|
forcedAddOns,
|
|
244
|
+
defaultTemplate,
|
|
208
245
|
})
|
|
209
246
|
} else if (options.mcp || options.mcpSse) {
|
|
210
247
|
await runMCPServer(!!options.mcpSse, {
|
|
211
|
-
forcedMode
|
|
248
|
+
forcedMode,
|
|
212
249
|
forcedAddOns,
|
|
213
250
|
appName,
|
|
214
251
|
})
|
|
@@ -227,26 +264,54 @@ export function cli({
|
|
|
227
264
|
cliOptions.template = forcedMode as TemplateOptions
|
|
228
265
|
}
|
|
229
266
|
|
|
230
|
-
let finalOptions
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
267
|
+
let finalOptions: Options | undefined
|
|
268
|
+
if (cliOptions.interactive) {
|
|
269
|
+
cliOptions.addOns = true
|
|
270
|
+
} else {
|
|
271
|
+
finalOptions = await normalizeOptions(
|
|
272
|
+
cliOptions,
|
|
273
|
+
forcedMode,
|
|
274
|
+
forcedAddOns,
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (options.ui) {
|
|
279
|
+
const defaultOptions: Options = {
|
|
280
|
+
framework: getFrameworkById(cliOptions.framework || 'react-cra')!,
|
|
281
|
+
mode: 'file-router',
|
|
282
|
+
chosenAddOns: [],
|
|
283
|
+
packageManager: 'pnpm',
|
|
284
|
+
projectName: projectName || 'my-app',
|
|
285
|
+
targetDir: resolve(process.cwd(), projectName || 'my-app'),
|
|
286
|
+
typescript: true,
|
|
287
|
+
tailwind: true,
|
|
288
|
+
git: true,
|
|
289
|
+
}
|
|
290
|
+
launchUI({
|
|
291
|
+
mode: 'setup',
|
|
292
|
+
options: createSerializedOptions(finalOptions || defaultOptions),
|
|
293
|
+
})
|
|
294
|
+
return
|
|
295
|
+
}
|
|
296
|
+
|
|
235
297
|
if (finalOptions) {
|
|
236
298
|
intro(`Creating a new ${appName} app in ${projectName}...`)
|
|
237
299
|
} else {
|
|
238
300
|
intro(`Let's configure your ${appName} application`)
|
|
239
301
|
finalOptions = await promptForOptions(cliOptions, {
|
|
240
|
-
forcedMode
|
|
302
|
+
forcedMode,
|
|
241
303
|
forcedAddOns,
|
|
242
304
|
})
|
|
243
305
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
306
|
+
|
|
307
|
+
if (!finalOptions) {
|
|
308
|
+
throw new Error('No options were provided')
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
finalOptions.targetDir =
|
|
312
|
+
options.targetDir || resolve(process.cwd(), finalOptions.projectName)
|
|
313
|
+
|
|
314
|
+
await createApp(environment, finalOptions)
|
|
250
315
|
} catch (error) {
|
|
251
316
|
log.error(
|
|
252
317
|
error instanceof Error ? error.message : 'An unknown error occurred',
|
|
@@ -0,0 +1,111 @@
|
|
|
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
|
+
loadStarter,
|
|
11
|
+
} from '@tanstack/cta-engine'
|
|
12
|
+
|
|
13
|
+
import type { Mode, Options } 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
|
+
forcedMode || cliOptions.template === 'file-router'
|
|
39
|
+
? FILE_ROUTER
|
|
40
|
+
: CODE_ROUTER
|
|
41
|
+
|
|
42
|
+
const starter = cliOptions.starter
|
|
43
|
+
? await loadStarter(cliOptions.starter)
|
|
44
|
+
: undefined
|
|
45
|
+
|
|
46
|
+
if (starter) {
|
|
47
|
+
tailwind = starter.tailwind
|
|
48
|
+
typescript = starter.typescript
|
|
49
|
+
cliOptions.framework = starter.framework
|
|
50
|
+
mode = starter.mode as Mode
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const framework = getFrameworkById(cliOptions.framework || 'react-cra')!
|
|
54
|
+
|
|
55
|
+
async function selectAddOns() {
|
|
56
|
+
// Edge case for Windows Powershell
|
|
57
|
+
if (Array.isArray(cliOptions.addOns) && cliOptions.addOns.length === 1) {
|
|
58
|
+
const parseSeparatedArgs = cliOptions.addOns[0].split(' ')
|
|
59
|
+
if (parseSeparatedArgs.length > 1) {
|
|
60
|
+
cliOptions.addOns = parseSeparatedArgs
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (
|
|
65
|
+
Array.isArray(cliOptions.addOns) ||
|
|
66
|
+
starter?.dependsOn ||
|
|
67
|
+
forcedAddOns ||
|
|
68
|
+
cliOptions.toolchain
|
|
69
|
+
) {
|
|
70
|
+
const selectedAddOns = new Set<string>([
|
|
71
|
+
...(starter?.dependsOn || []),
|
|
72
|
+
...(forcedAddOns || []),
|
|
73
|
+
])
|
|
74
|
+
if (cliOptions.addOns && Array.isArray(cliOptions.addOns)) {
|
|
75
|
+
for (const a of cliOptions.addOns) {
|
|
76
|
+
selectedAddOns.add(a)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (cliOptions.toolchain) {
|
|
80
|
+
selectedAddOns.add(cliOptions.toolchain)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return await finalizeAddOns(framework, mode, Array.from(selectedAddOns))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return []
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const chosenAddOns = await selectAddOns()
|
|
90
|
+
|
|
91
|
+
if (chosenAddOns.length) {
|
|
92
|
+
tailwind = true
|
|
93
|
+
typescript = true
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
projectName: projectName,
|
|
98
|
+
targetDir: resolve(process.cwd(), projectName),
|
|
99
|
+
framework,
|
|
100
|
+
mode,
|
|
101
|
+
typescript,
|
|
102
|
+
tailwind,
|
|
103
|
+
packageManager:
|
|
104
|
+
cliOptions.packageManager ||
|
|
105
|
+
getPackageManager() ||
|
|
106
|
+
DEFAULT_PACKAGE_MANAGER,
|
|
107
|
+
git: !!cliOptions.git,
|
|
108
|
+
chosenAddOns,
|
|
109
|
+
starter: starter,
|
|
110
|
+
}
|
|
111
|
+
}
|