@tanstack/cta-cli 0.10.0-alpha.26 → 0.10.0-alpha.27

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 CHANGED
@@ -5,7 +5,7 @@ import chalk from 'chalk';
5
5
  import { SUPPORTED_PACKAGE_MANAGERS, addToApp, compileAddOn, compileStarter, createApp, createSerializedOptions, getAllAddOns, getFrameworkById, getFrameworkByName, getFrameworks, initAddOn, initStarter, } from '@tanstack/cta-engine';
6
6
  import { launchUI } from '@tanstack/cta-ui';
7
7
  import { runMCPServer } from './mcp.js';
8
- import { promptForOptions } from './options.js';
8
+ import { promptForAddOns, promptForCreateOptions } from './options.js';
9
9
  import { normalizeOptions } from './command-line.js';
10
10
  import { createUIEnvironment } from './ui-environment.js';
11
11
  import { convertTemplateToMode } from './utils.js';
@@ -51,8 +51,17 @@ export function cli({ name, appName, forcedMode, forcedAddOns = [], defaultTempl
51
51
  projectPath: process.cwd(),
52
52
  forcedRouterMode: forcedMode,
53
53
  forcedAddOns,
54
+ environmentFactory: () => createUIEnvironment(appName, false),
54
55
  });
55
56
  }
57
+ else if (parsedAddOns.length < 1) {
58
+ const addOns = await promptForAddOns();
59
+ if (addOns.length) {
60
+ await addToApp(environment, addOns, process.cwd(), {
61
+ forced: program.opts().forced,
62
+ });
63
+ }
64
+ }
56
65
  else {
57
66
  await addToApp(environment, parsedAddOns, process.cwd(), {
58
67
  forced: program.opts().forced,
@@ -164,22 +173,17 @@ export function cli({ name, appName, forcedMode, forcedAddOns = [], defaultTempl
164
173
  finalOptions = await normalizeOptions(cliOptions, forcedMode, forcedAddOns);
165
174
  }
166
175
  if (options.ui) {
167
- const defaultOptions = {
168
- framework: getFrameworkById(cliOptions.framework || 'react-cra'),
169
- mode: 'file-router',
170
- chosenAddOns: [],
171
- packageManager: 'pnpm',
172
- projectName: projectName || 'my-app',
173
- targetDir: resolve(process.cwd(), projectName || 'my-app'),
174
- typescript: true,
175
- tailwind: true,
176
- git: true,
177
- };
176
+ const optionsFromCLI = await normalizeOptions(cliOptions, forcedMode, forcedAddOns, { disableNameCheck: true });
178
177
  launchUI({
179
178
  mode: 'setup',
180
- options: createSerializedOptions(finalOptions || defaultOptions),
179
+ options: {
180
+ ...createSerializedOptions(optionsFromCLI),
181
+ projectName: 'my-app',
182
+ targetDir: resolve(process.cwd(), 'my-app'),
183
+ },
181
184
  forcedRouterMode: forcedMode,
182
185
  forcedAddOns,
186
+ environmentFactory: () => createUIEnvironment(appName, false),
183
187
  });
184
188
  return;
185
189
  }
@@ -188,7 +192,7 @@ export function cli({ name, appName, forcedMode, forcedAddOns = [], defaultTempl
188
192
  }
189
193
  else {
190
194
  intro(`Let's configure your ${appName} application`);
191
- finalOptions = await promptForOptions(cliOptions, {
195
+ finalOptions = await promptForCreateOptions(cliOptions, {
192
196
  forcedMode,
193
197
  forcedAddOns,
194
198
  });
@@ -1,8 +1,8 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { CODE_ROUTER, DEFAULT_PACKAGE_MANAGER, FILE_ROUTER, finalizeAddOns, getFrameworkById, getPackageManager, loadStarter, } from '@tanstack/cta-engine';
3
- export async function normalizeOptions(cliOptions, forcedMode, forcedAddOns) {
3
+ export async function normalizeOptions(cliOptions, forcedMode, forcedAddOns, opts) {
4
4
  const projectName = (cliOptions.projectName ?? '').trim();
5
- if (!projectName) {
5
+ if (!projectName && !opts?.disableNameCheck) {
6
6
  return undefined;
7
7
  }
8
8
  let typescript = cliOptions.template === 'typescript' ||
package/dist/options.js CHANGED
@@ -1,6 +1,7 @@
1
- import { CODE_ROUTER, FILE_ROUTER, finalizeAddOns, getFrameworkById, getPackageManager, } from '@tanstack/cta-engine';
1
+ import { CODE_ROUTER, FILE_ROUTER, finalizeAddOns, getFrameworkById, getPackageManager, readConfigFile, } from '@tanstack/cta-engine';
2
2
  import { getProjectName, selectAddOns, selectGit, selectPackageManager, selectRouterType, selectTailwind, selectToolchain, selectTypescript, } from './ui-prompts.js';
3
- export async function promptForOptions(cliOptions, { forcedAddOns = [], forcedMode, }) {
3
+ import { intro } from '@clack/prompts';
4
+ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], forcedMode, }) {
4
5
  const options = {};
5
6
  options.framework = getFrameworkById(cliOptions.framework || 'react-cra');
6
7
  options.projectName = cliOptions.projectName || (await getProjectName());
@@ -68,3 +69,24 @@ export async function promptForOptions(cliOptions, { forcedAddOns = [], forcedMo
68
69
  options.git = cliOptions.git || (await selectGit());
69
70
  return options;
70
71
  }
72
+ export async function promptForAddOns() {
73
+ const config = await readConfigFile(process.cwd());
74
+ if (!config) {
75
+ console.error('No config file found');
76
+ process.exit(1);
77
+ }
78
+ const framework = getFrameworkById(config.framework);
79
+ if (!framework) {
80
+ console.error(`Unknown framework: ${config.framework}`);
81
+ process.exit(1);
82
+ }
83
+ intro(`Adding new add-ons to '${config.projectName}'`);
84
+ const addOns = new Set();
85
+ for (const addOn of await selectAddOns(framework, config.mode, 'add-on', 'What add-ons would you like for your project?', config.chosenAddOns)) {
86
+ addOns.add(addOn);
87
+ }
88
+ for (const addOn of await selectAddOns(framework, config.mode, 'example', 'Would you like any examples?', config.chosenAddOns)) {
89
+ addOns.add(addOn);
90
+ }
91
+ return Array.from(addOns);
92
+ }
@@ -1,3 +1,5 @@
1
1
  import type { Mode, Options } from '@tanstack/cta-engine';
2
2
  import type { CliOptions } from './types.js';
3
- export declare function normalizeOptions(cliOptions: CliOptions, forcedMode?: Mode, forcedAddOns?: Array<string>): Promise<Options | undefined>;
3
+ export declare function normalizeOptions(cliOptions: CliOptions, forcedMode?: Mode, forcedAddOns?: Array<string>, opts?: {
4
+ disableNameCheck?: boolean;
5
+ }): Promise<Options | undefined>;
@@ -1,6 +1,7 @@
1
1
  import type { Mode, Options } from '@tanstack/cta-engine';
2
2
  import type { CliOptions } from './types.js';
3
- export declare function promptForOptions(cliOptions: CliOptions, { forcedAddOns, forcedMode, }: {
3
+ export declare function promptForCreateOptions(cliOptions: CliOptions, { forcedAddOns, forcedMode, }: {
4
4
  forcedAddOns?: Array<string>;
5
5
  forcedMode?: Mode;
6
6
  }): Promise<Required<Options> | undefined>;
7
+ export declare function promptForAddOns(): Promise<Array<string>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/cta-cli",
3
- "version": "0.10.0-alpha.26",
3
+ "version": "0.10.0-alpha.27",
4
4
  "description": "Tanstack Application Builder CLI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -29,8 +29,8 @@
29
29
  "commander": "^13.1.0",
30
30
  "express": "^4.21.2",
31
31
  "zod": "^3.24.2",
32
- "@tanstack/cta-ui": "0.10.0-alpha.26",
33
- "@tanstack/cta-engine": "0.10.0-alpha.26"
32
+ "@tanstack/cta-engine": "0.10.0-alpha.27",
33
+ "@tanstack/cta-ui": "0.10.0-alpha.27"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@tanstack/config": "^0.16.2",
package/src/cli.ts CHANGED
@@ -22,7 +22,7 @@ import { launchUI } from '@tanstack/cta-ui'
22
22
 
23
23
  import { runMCPServer } from './mcp.js'
24
24
 
25
- import { promptForOptions } from './options.js'
25
+ import { promptForAddOns, promptForCreateOptions } from './options.js'
26
26
  import { normalizeOptions } from './command-line.js'
27
27
 
28
28
  import { createUIEnvironment } from './ui-environment.js'
@@ -112,7 +112,15 @@ export function cli({
112
112
  projectPath: process.cwd(),
113
113
  forcedRouterMode: forcedMode,
114
114
  forcedAddOns,
115
+ environmentFactory: () => createUIEnvironment(appName, false),
115
116
  })
117
+ } else if (parsedAddOns.length < 1) {
118
+ const addOns = await promptForAddOns()
119
+ if (addOns.length) {
120
+ await addToApp(environment, addOns, process.cwd(), {
121
+ forced: program.opts().forced,
122
+ })
123
+ }
116
124
  } else {
117
125
  await addToApp(environment, parsedAddOns, process.cwd(), {
118
126
  forced: program.opts().forced,
@@ -279,22 +287,22 @@ export function cli({
279
287
  }
280
288
 
281
289
  if (options.ui) {
282
- const defaultOptions: Options = {
283
- framework: getFrameworkById(cliOptions.framework || 'react-cra')!,
284
- mode: 'file-router',
285
- chosenAddOns: [],
286
- packageManager: 'pnpm',
287
- projectName: projectName || 'my-app',
288
- targetDir: resolve(process.cwd(), projectName || 'my-app'),
289
- typescript: true,
290
- tailwind: true,
291
- git: true,
292
- }
290
+ const optionsFromCLI = await normalizeOptions(
291
+ cliOptions,
292
+ forcedMode,
293
+ forcedAddOns,
294
+ { disableNameCheck: true },
295
+ )
293
296
  launchUI({
294
297
  mode: 'setup',
295
- options: createSerializedOptions(finalOptions || defaultOptions),
298
+ options: {
299
+ ...createSerializedOptions(optionsFromCLI!),
300
+ projectName: 'my-app',
301
+ targetDir: resolve(process.cwd(), 'my-app'),
302
+ },
296
303
  forcedRouterMode: forcedMode,
297
304
  forcedAddOns,
305
+ environmentFactory: () => createUIEnvironment(appName, false),
298
306
  })
299
307
  return
300
308
  }
@@ -303,7 +311,7 @@ export function cli({
303
311
  intro(`Creating a new ${appName} app in ${projectName}...`)
304
312
  } else {
305
313
  intro(`Let's configure your ${appName} application`)
306
- finalOptions = await promptForOptions(cliOptions, {
314
+ finalOptions = await promptForCreateOptions(cliOptions, {
307
315
  forcedMode,
308
316
  forcedAddOns,
309
317
  })
@@ -18,9 +18,12 @@ export async function normalizeOptions(
18
18
  cliOptions: CliOptions,
19
19
  forcedMode?: Mode,
20
20
  forcedAddOns?: Array<string>,
21
+ opts?: {
22
+ disableNameCheck?: boolean
23
+ },
21
24
  ): Promise<Options | undefined> {
22
25
  const projectName = (cliOptions.projectName ?? '').trim()
23
- if (!projectName) {
26
+ if (!projectName && !opts?.disableNameCheck) {
24
27
  return undefined
25
28
  }
26
29
 
package/src/options.ts CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  finalizeAddOns,
5
5
  getFrameworkById,
6
6
  getPackageManager,
7
+ readConfigFile,
7
8
  } from '@tanstack/cta-engine'
8
9
 
9
10
  import {
@@ -20,8 +21,9 @@ import {
20
21
  import type { Mode, Options } from '@tanstack/cta-engine'
21
22
 
22
23
  import type { CliOptions } from './types.js'
24
+ import { intro } from '@clack/prompts'
23
25
 
24
- export async function promptForOptions(
26
+ export async function promptForCreateOptions(
25
27
  cliOptions: CliOptions,
26
28
  {
27
29
  forcedAddOns = [],
@@ -126,3 +128,45 @@ export async function promptForOptions(
126
128
 
127
129
  return options
128
130
  }
131
+
132
+ export async function promptForAddOns(): Promise<Array<string>> {
133
+ const config = await readConfigFile(process.cwd())
134
+
135
+ if (!config) {
136
+ console.error('No config file found')
137
+ process.exit(1)
138
+ }
139
+
140
+ const framework = getFrameworkById(config.framework)
141
+
142
+ if (!framework) {
143
+ console.error(`Unknown framework: ${config.framework}`)
144
+ process.exit(1)
145
+ }
146
+
147
+ intro(`Adding new add-ons to '${config.projectName}'`)
148
+
149
+ const addOns: Set<string> = new Set()
150
+
151
+ for (const addOn of await selectAddOns(
152
+ framework,
153
+ config.mode!,
154
+ 'add-on',
155
+ 'What add-ons would you like for your project?',
156
+ config.chosenAddOns,
157
+ )) {
158
+ addOns.add(addOn)
159
+ }
160
+
161
+ for (const addOn of await selectAddOns(
162
+ framework,
163
+ config.mode!,
164
+ 'example',
165
+ 'Would you like any examples?',
166
+ config.chosenAddOns,
167
+ )) {
168
+ addOns.add(addOn)
169
+ }
170
+
171
+ return Array.from(addOns)
172
+ }
@@ -1,6 +1,6 @@
1
1
  import { beforeEach, describe, it, expect, vi } from 'vitest'
2
2
 
3
- import { promptForOptions } from '../src/options'
3
+ import { promptForCreateOptions } from '../src/options'
4
4
  import {
5
5
  __testClearFrameworks,
6
6
  __testRegisterFramework,
@@ -67,13 +67,13 @@ function setBasicSpies() {
67
67
  vi.spyOn(prompts, 'selectAddOns').mockImplementation(async () => [])
68
68
  }
69
69
 
70
- describe('promptForOptions', () => {
70
+ describe('promptForCreateOptions', () => {
71
71
  //// Project name
72
72
 
73
73
  it('prompt for a project name', async () => {
74
74
  setBasicSpies()
75
75
 
76
- const options = await promptForOptions(baseCliOptions, {})
76
+ const options = await promptForCreateOptions(baseCliOptions, {})
77
77
 
78
78
  expect(options?.projectName).toBe('hello')
79
79
  })
@@ -81,7 +81,7 @@ describe('promptForOptions', () => {
81
81
  it('accept incoming project name', async () => {
82
82
  setBasicSpies()
83
83
 
84
- const options = await promptForOptions(
84
+ const options = await promptForCreateOptions(
85
85
  { ...baseCliOptions, projectName: 'override' },
86
86
  {},
87
87
  )
@@ -94,7 +94,7 @@ describe('promptForOptions', () => {
94
94
  it('forceMode should override template', async () => {
95
95
  setBasicSpies()
96
96
 
97
- const options = await promptForOptions(
97
+ const options = await promptForCreateOptions(
98
98
  { ...baseCliOptions, template: 'javascript' },
99
99
  { forcedMode: 'file-router' },
100
100
  )
@@ -110,7 +110,7 @@ describe('promptForOptions', () => {
110
110
  async () => 'code-router',
111
111
  )
112
112
 
113
- const options = await promptForOptions(
113
+ const options = await promptForCreateOptions(
114
114
  { ...baseCliOptions, template: 'javascript' },
115
115
  {},
116
116
  )
@@ -125,7 +125,7 @@ describe('promptForOptions', () => {
125
125
  async () => 'code-router',
126
126
  )
127
127
 
128
- const options = await promptForOptions(
128
+ const options = await promptForCreateOptions(
129
129
  { ...baseCliOptions, template: 'file-router' },
130
130
  {},
131
131
  )
@@ -140,7 +140,7 @@ describe('promptForOptions', () => {
140
140
  async () => 'code-router',
141
141
  )
142
142
 
143
- const options = await promptForOptions(
143
+ const options = await promptForCreateOptions(
144
144
  { ...baseCliOptions, tailwind: false, framework: undefined },
145
145
  {},
146
146
  )
@@ -153,7 +153,7 @@ describe('promptForOptions', () => {
153
153
  it('prompt for tailwind when unspecified in react-cra', async () => {
154
154
  setBasicSpies()
155
155
  vi.spyOn(prompts, 'selectTailwind').mockImplementation(async () => false)
156
- const options = await promptForOptions(
156
+ const options = await promptForCreateOptions(
157
157
  { ...baseCliOptions, tailwind: undefined },
158
158
  {},
159
159
  )
@@ -164,7 +164,7 @@ describe('promptForOptions', () => {
164
164
  it('prompt for tailwind when unspecified in react-cra - true', async () => {
165
165
  setBasicSpies()
166
166
  vi.spyOn(prompts, 'selectTailwind').mockImplementation(async () => true)
167
- const options = await promptForOptions(
167
+ const options = await promptForCreateOptions(
168
168
  { ...baseCliOptions, tailwind: undefined },
169
169
  {},
170
170
  )
@@ -174,7 +174,7 @@ describe('promptForOptions', () => {
174
174
 
175
175
  it('set tailwind when solid', async () => {
176
176
  setBasicSpies()
177
- const options = await promptForOptions(
177
+ const options = await promptForCreateOptions(
178
178
  { ...baseCliOptions, tailwind: undefined, framework: 'solid' },
179
179
  {},
180
180
  )
@@ -187,7 +187,7 @@ describe('promptForOptions', () => {
187
187
  it('uses the package manager from the cli options', async () => {
188
188
  setBasicSpies()
189
189
 
190
- const options = await promptForOptions(
190
+ const options = await promptForCreateOptions(
191
191
  { ...baseCliOptions, packageManager: 'bun' },
192
192
  {},
193
193
  )
@@ -200,7 +200,7 @@ describe('promptForOptions', () => {
200
200
 
201
201
  process.env.npm_config_userconfig = 'blarg'
202
202
 
203
- const options = await promptForOptions(
203
+ const options = await promptForCreateOptions(
204
204
  { ...baseCliOptions, packageManager: undefined },
205
205
  {},
206
206
  )
@@ -212,7 +212,7 @@ describe('promptForOptions', () => {
212
212
  it('should be clean when no add-ons are selected', async () => {
213
213
  setBasicSpies()
214
214
 
215
- const options = await promptForOptions({ ...baseCliOptions }, {})
215
+ const options = await promptForCreateOptions({ ...baseCliOptions }, {})
216
216
 
217
217
  expect(options?.chosenAddOns).toEqual([])
218
218
  })
@@ -222,7 +222,7 @@ describe('promptForOptions', () => {
222
222
 
223
223
  vi.spyOn(prompts, 'selectToolchain').mockImplementation(async () => 'biome')
224
224
 
225
- const options = await promptForOptions(
225
+ const options = await promptForCreateOptions(
226
226
  { ...baseCliOptions, toolchain: 'biome' },
227
227
  {},
228
228
  )
@@ -237,7 +237,7 @@ describe('promptForOptions', () => {
237
237
  async () => undefined,
238
238
  )
239
239
 
240
- const options = await promptForOptions(
240
+ const options = await promptForCreateOptions(
241
241
  { ...baseCliOptions },
242
242
  { forcedAddOns: ['react-query'] },
243
243
  )
@@ -252,7 +252,7 @@ describe('promptForOptions', () => {
252
252
  it('should handle add-ons from the CLI', async () => {
253
253
  setBasicSpies()
254
254
 
255
- const options = await promptForOptions(
255
+ const options = await promptForCreateOptions(
256
256
  { ...baseCliOptions, addOns: ['biome', 'react-query'] },
257
257
  {},
258
258
  )
@@ -272,7 +272,7 @@ describe('promptForOptions', () => {
272
272
  Promise.resolve(['biome', 'react-query']),
273
273
  )
274
274
 
275
- const options = await promptForOptions(
275
+ const options = await promptForCreateOptions(
276
276
  { ...baseCliOptions, addOns: undefined },
277
277
  {},
278
278
  )