@tanstack/cta-cli 0.10.0-alpha.23 → 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';
@@ -48,10 +48,20 @@ export function cli({ name, appName, forcedMode, forcedAddOns = [], defaultTempl
48
48
  launchUI({
49
49
  mode: 'add',
50
50
  addOns: parsedAddOns,
51
- forcedMode,
51
+ projectPath: process.cwd(),
52
+ forcedRouterMode: forcedMode,
52
53
  forcedAddOns,
54
+ environmentFactory: () => createUIEnvironment(appName, false),
53
55
  });
54
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
+ }
55
65
  else {
56
66
  await addToApp(environment, parsedAddOns, process.cwd(), {
57
67
  forced: program.opts().forced,
@@ -163,22 +173,17 @@ export function cli({ name, appName, forcedMode, forcedAddOns = [], defaultTempl
163
173
  finalOptions = await normalizeOptions(cliOptions, forcedMode, forcedAddOns);
164
174
  }
165
175
  if (options.ui) {
166
- const defaultOptions = {
167
- framework: getFrameworkById(cliOptions.framework || 'react-cra'),
168
- mode: 'file-router',
169
- chosenAddOns: [],
170
- packageManager: 'pnpm',
171
- projectName: projectName || 'my-app',
172
- targetDir: resolve(process.cwd(), projectName || 'my-app'),
173
- typescript: true,
174
- tailwind: true,
175
- git: true,
176
- };
176
+ const optionsFromCLI = await normalizeOptions(cliOptions, forcedMode, forcedAddOns, { disableNameCheck: true });
177
177
  launchUI({
178
178
  mode: 'setup',
179
- options: createSerializedOptions(finalOptions || defaultOptions),
180
- forcedMode,
179
+ options: {
180
+ ...createSerializedOptions(optionsFromCLI),
181
+ projectName: 'my-app',
182
+ targetDir: resolve(process.cwd(), 'my-app'),
183
+ },
184
+ forcedRouterMode: forcedMode,
181
185
  forcedAddOns,
186
+ environmentFactory: () => createUIEnvironment(appName, false),
182
187
  });
183
188
  return;
184
189
  }
@@ -187,7 +192,7 @@ export function cli({ name, appName, forcedMode, forcedAddOns = [], defaultTempl
187
192
  }
188
193
  else {
189
194
  intro(`Let's configure your ${appName} application`);
190
- finalOptions = await promptForOptions(cliOptions, {
195
+ finalOptions = await promptForCreateOptions(cliOptions, {
191
196
  forcedMode,
192
197
  forcedAddOns,
193
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.23",
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.23",
33
- "@tanstack/cta-engine": "0.10.0-alpha.21"
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'
@@ -109,9 +109,18 @@ export function cli({
109
109
  launchUI({
110
110
  mode: 'add',
111
111
  addOns: parsedAddOns,
112
- forcedMode,
112
+ projectPath: process.cwd(),
113
+ forcedRouterMode: forcedMode,
113
114
  forcedAddOns,
115
+ environmentFactory: () => createUIEnvironment(appName, false),
114
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
+ }
115
124
  } else {
116
125
  await addToApp(environment, parsedAddOns, process.cwd(), {
117
126
  forced: program.opts().forced,
@@ -278,22 +287,22 @@ export function cli({
278
287
  }
279
288
 
280
289
  if (options.ui) {
281
- const defaultOptions: Options = {
282
- framework: getFrameworkById(cliOptions.framework || 'react-cra')!,
283
- mode: 'file-router',
284
- chosenAddOns: [],
285
- packageManager: 'pnpm',
286
- projectName: projectName || 'my-app',
287
- targetDir: resolve(process.cwd(), projectName || 'my-app'),
288
- typescript: true,
289
- tailwind: true,
290
- git: true,
291
- }
290
+ const optionsFromCLI = await normalizeOptions(
291
+ cliOptions,
292
+ forcedMode,
293
+ forcedAddOns,
294
+ { disableNameCheck: true },
295
+ )
292
296
  launchUI({
293
297
  mode: 'setup',
294
- options: createSerializedOptions(finalOptions || defaultOptions),
295
- forcedMode,
298
+ options: {
299
+ ...createSerializedOptions(optionsFromCLI!),
300
+ projectName: 'my-app',
301
+ targetDir: resolve(process.cwd(), 'my-app'),
302
+ },
303
+ forcedRouterMode: forcedMode,
296
304
  forcedAddOns,
305
+ environmentFactory: () => createUIEnvironment(appName, false),
297
306
  })
298
307
  return
299
308
  }
@@ -302,7 +311,7 @@ export function cli({
302
311
  intro(`Creating a new ${appName} app in ${projectName}...`)
303
312
  } else {
304
313
  intro(`Let's configure your ${appName} application`)
305
- finalOptions = await promptForOptions(cliOptions, {
314
+ finalOptions = await promptForCreateOptions(cliOptions, {
306
315
  forcedMode,
307
316
  forcedAddOns,
308
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
  )