@tanstack/cli 0.71.1 → 0.72.0

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
@@ -4,7 +4,7 @@ import { Command, InvalidArgumentError, Option } from 'commander';
4
4
  import { cancel, confirm, intro, isCancel, log } from '@clack/prompts';
5
5
  import chalk from 'chalk';
6
6
  import semver from 'semver';
7
- import { SUPPORTED_PACKAGE_MANAGERS, addToApp, compileAddOn, compileStarter, createApp, devAddOn, getAllAddOns, getFrameworkByName, getFrameworks, initAddOn, initStarter, isDemoFilePath, } from '@tanstack/create';
7
+ import { SUPPORTED_PACKAGE_MANAGERS, addToApp, compileAddOn, compileStarter, createApp, devAddOn, getAllAddOns, getBundlers, getFrameworkByName, getFrameworks, initAddOn, initStarter, isDemoFilePath, resolveBundler, } from '@tanstack/create';
8
8
  import { LIBRARY_GROUPS, fetchDocContent, fetchLibraries, fetchPartners, searchTanStackDocs, } from './discovery.js';
9
9
  import { getTelemetryStatus, setTelemetryEnabled, } from './telemetry-config.js';
10
10
  import { createTelemetryClient } from './telemetry.js';
@@ -97,7 +97,7 @@ function getCreateCommandVariant(options) {
97
97
  }
98
98
  return 'scaffold';
99
99
  }
100
- function getCreateTelemetryProperties(projectName, options) {
100
+ export function getCreateTelemetryProperties(projectName, options) {
101
101
  const addOnIds = Array.isArray(options.addOns)
102
102
  ? sanitizeIdList(options.addOns)
103
103
  : undefined;
@@ -110,6 +110,7 @@ function getCreateTelemetryProperties(projectName, options) {
110
110
  : undefined,
111
111
  command_variant: getCreateCommandVariant(options),
112
112
  deployment: options.deployment ? sanitizeId(options.deployment) : undefined,
113
+ bundler: options.buildTool ? sanitizeId(options.buildTool) : undefined,
113
114
  examples: options.examples,
114
115
  blank: !!options.blank,
115
116
  framework: options.framework ? sanitizeId(options.framework) : undefined,
@@ -127,7 +128,7 @@ function getCreateTelemetryProperties(projectName, options) {
127
128
  yes: !!options.yes,
128
129
  };
129
130
  }
130
- function getResolvedCreateTelemetryProperties(finalOptions, cliOptions) {
131
+ export function getResolvedCreateTelemetryProperties(finalOptions, cliOptions) {
131
132
  const includeExamples = finalOptions.includeExamples !== false;
132
133
  const addOnIds = sanitizeIdList(finalOptions.chosenAddOns.map((addOn) => addOn.id));
133
134
  const deployment = finalOptions.chosenAddOns.find((addOn) => addOn.type === 'deployment');
@@ -137,6 +138,7 @@ function getResolvedCreateTelemetryProperties(finalOptions, cliOptions) {
137
138
  add_on_count: addOnIds.length,
138
139
  add_on_ids: addOnIds,
139
140
  deployment: deployment ? sanitizeId(deployment.id) : undefined,
141
+ bundler: sanitizeId(finalOptions.bundler ?? 'vite'),
140
142
  examples: includeExamples,
141
143
  blank: finalOptions.projectPreset === 'blank',
142
144
  framework: sanitizeId(finalOptions.framework.id),
@@ -185,6 +187,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
185
187
  lines.push(` Project: ${finalOptions.projectName}`);
186
188
  lines.push(` Location: ${finalOptions.targetDir}`);
187
189
  lines.push(` Framework: ${finalOptions.framework.name}`);
190
+ lines.push(` Build tool: ${resolveBundler(finalOptions.framework, finalOptions.bundler).name}`);
188
191
  lines.push(` Mode: ${finalOptions.mode}`);
189
192
  if (finalOptions.projectPreset === 'blank') {
190
193
  lines.push(` Preset: blank`);
@@ -401,6 +404,12 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
401
404
  }
402
405
  }
403
406
  }
407
+ const bundlers = new Set();
408
+ for (const framework of getFrameworks()) {
409
+ for (const bundler of getBundlers(framework)) {
410
+ bundlers.add(bundler.id);
411
+ }
412
+ }
404
413
  // Mode is always file-router (TanStack Start)
405
414
  const defaultMode = 'file-router';
406
415
  const categoryAliases = {
@@ -475,7 +484,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
475
484
  }, async (telemetry) => {
476
485
  validateCreateCommandOptions(options);
477
486
  if (options.listAddOns) {
478
- const addOns = await getAllAddOns(getFrameworkByName(options.framework || defaultFramework || 'React'), defaultMode);
487
+ const addOns = await getAllAddOns(getFrameworkByName(options.framework || defaultFramework || 'React'), defaultMode, options.buildTool);
479
488
  const visibleAddOns = addOns.filter((a) => !forcedAddOns.includes(a.id));
480
489
  telemetry.mergeProperties({
481
490
  result_count: visibleAddOns.length,
@@ -511,7 +520,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
511
520
  return;
512
521
  }
513
522
  if (options.addonDetails) {
514
- const addOns = await getAllAddOns(getFrameworkByName(options.framework || defaultFramework || 'React'), defaultMode);
523
+ const addOns = await getAllAddOns(getFrameworkByName(options.framework || defaultFramework || 'React'), defaultMode, options.buildTool);
515
524
  const addOn = addOns.find((a) => a.id === options.addonDetails) ??
516
525
  addOns.find((a) => a.id.toLowerCase() === options.addonDetails.toLowerCase());
517
526
  if (!addOn) {
@@ -702,6 +711,13 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
702
711
  });
703
712
  }
704
713
  cmd
714
+ .option(`--build-tool <${Array.from(bundlers).join('|')}>`, 'select the project build tool', (value) => {
715
+ const buildTool = Array.from(bundlers).find((item) => item.toLowerCase() === value.toLowerCase());
716
+ if (!buildTool) {
717
+ throw new InvalidArgumentError(`Invalid build tool: ${value}. The following are allowed: ${Array.from(bundlers).join(', ')}`);
718
+ }
719
+ return buildTool;
720
+ })
705
721
  .option('--starter [url-or-id]', 'DEPRECATED: use --template. Initializes from a template URL or built-in id', false)
706
722
  .option('--template-id <id>', 'initialize using a built-in template id')
707
723
  .option('--template [url-or-id]', 'initialize this project from a template URL or built-in template id')
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import fs from 'node:fs';
3
- import { DEFAULT_PACKAGE_MANAGER, finalizeAddOns, getFrameworkById, getPackageManager, getRawRegistry, loadStarter, populateAddOnOptionsDefaults, } from '@tanstack/create';
3
+ import { DEFAULT_PACKAGE_MANAGER, finalizeAddOns, getFrameworkById, getPackageManager, getRawRegistry, loadStarter, populateAddOnOptionsDefaults, resolveBundler, } from '@tanstack/create';
4
4
  import { resolveProjectLocation, validateProjectName, } from './utils.js';
5
5
  const SUPPORTED_LEGACY_TEMPLATES = new Set([
6
6
  'file-router',
@@ -217,6 +217,30 @@ export async function listTemplateChoices(preferredFramework) {
217
217
  export function validateLegacyCreateFlags(cliOptions) {
218
218
  const warnings = [];
219
219
  const legacyTemplate = getLegacyTemplateValue(cliOptions.template);
220
+ const isSupportedLegacyTemplate = legacyTemplate !== undefined &&
221
+ SUPPORTED_LEGACY_TEMPLATES.has(legacyTemplate);
222
+ if (cliOptions.buildTool?.toLowerCase() === 'rsbuild') {
223
+ if (cliOptions.starter ||
224
+ (cliOptions.template && !isSupportedLegacyTemplate) ||
225
+ cliOptions.templateId) {
226
+ return {
227
+ warnings,
228
+ error: 'Rsbuild does not currently support --starter, --template, or --template-id.',
229
+ };
230
+ }
231
+ if (cliOptions.deployment) {
232
+ return {
233
+ warnings,
234
+ error: 'Rsbuild does not currently support --deployment.',
235
+ };
236
+ }
237
+ if (cliOptions.addOns) {
238
+ return {
239
+ warnings,
240
+ error: 'Rsbuild currently supports toolchains only. Use --toolchain eslint or --toolchain biome.',
241
+ };
242
+ }
243
+ }
220
244
  if (cliOptions.blank &&
221
245
  (cliOptions.starter || cliOptions.template || cliOptions.templateId)) {
222
246
  return {
@@ -304,6 +328,8 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
304
328
  let routerOnly = !!cliOptions.routerOnly;
305
329
  const blank = cliOptions.blank === true;
306
330
  const legacyTemplate = getLegacyTemplateValue(cliOptions.template);
331
+ const isSupportedLegacyTemplate = legacyTemplate !== undefined &&
332
+ SUPPORTED_LEGACY_TEMPLATES.has(legacyTemplate);
307
333
  if (!cliOptions.starter) {
308
334
  if (cliOptions.template && !legacyTemplate) {
309
335
  cliOptions.starter = cliOptions.template;
@@ -320,6 +346,24 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
320
346
  cliOptions.starter = cliOptions.templateId;
321
347
  }
322
348
  const preferredFramework = (cliOptions.framework || 'react').toLowerCase();
349
+ const initialFramework = getFrameworkById(preferredFramework);
350
+ let framework = initialFramework;
351
+ const bundler = initialFramework
352
+ ? resolveBundler(initialFramework, cliOptions.buildTool).id
353
+ : (cliOptions.buildTool ?? 'vite');
354
+ if (bundler === 'rsbuild') {
355
+ if (cliOptions.starter ||
356
+ (cliOptions.template && !isSupportedLegacyTemplate) ||
357
+ cliOptions.templateId) {
358
+ throw new Error('Rsbuild does not currently support templates.');
359
+ }
360
+ if (cliOptions.deployment || opts?.forcedDeployment) {
361
+ throw new Error('Rsbuild does not currently support deployments.');
362
+ }
363
+ if (forcedAddOns?.length || cliOptions.addOns) {
364
+ throw new Error('Rsbuild currently supports toolchains only. Use --toolchain eslint or --toolchain biome.');
365
+ }
366
+ }
323
367
  const starter = !routerOnly && !blank && cliOptions.starter
324
368
  ? await loadStarter(await resolveStarterSpecifier(cliOptions.starter, preferredFramework))
325
369
  : undefined;
@@ -329,7 +373,11 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
329
373
  cliOptions.framework = starter.framework;
330
374
  mode = starter.mode;
331
375
  }
332
- const framework = getFrameworkById(cliOptions.framework || 'react');
376
+ const resolvedFramework = getFrameworkById(cliOptions.framework || 'react');
377
+ if (resolvedFramework) {
378
+ resolveBundler(resolvedFramework, bundler);
379
+ }
380
+ framework = resolvedFramework;
333
381
  async function selectAddOns() {
334
382
  // Edge case for Windows Powershell
335
383
  if (Array.isArray(cliOptions.addOns) && cliOptions.addOns.length === 1) {
@@ -364,7 +412,7 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
364
412
  if (!routerOnly && !cliOptions.deployment && opts?.forcedDeployment) {
365
413
  selectedAddOns.add(opts.forcedDeployment);
366
414
  }
367
- return await finalizeAddOns(framework, mode, Array.from(selectedAddOns));
415
+ return await finalizeAddOns(framework, mode, Array.from(selectedAddOns), bundler);
368
416
  }
369
417
  return [];
370
418
  }
@@ -390,6 +438,7 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
390
438
  targetDir,
391
439
  framework,
392
440
  mode,
441
+ bundler,
393
442
  typescript,
394
443
  tailwind,
395
444
  packageManager: cliOptions.packageManager ||
@@ -405,6 +454,7 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
405
454
  },
406
455
  starter: starter,
407
456
  projectPreset: blank ? 'blank' : 'default',
457
+ routerOnly,
408
458
  };
409
459
  normalized.includeExamples =
410
460
  includeExamples;
package/dist/dev-watch.js CHANGED
@@ -176,7 +176,7 @@ export class DevWatchManager {
176
176
  if (!registeredFramework) {
177
177
  throw new Error(`Failed to register framework: ${this.options.framework.id}`);
178
178
  }
179
- const updatedChosenAddons = await finalizeAddOns(registeredFramework, this.options.cliOptions.mode, chosenAddonIds);
179
+ const updatedChosenAddons = await finalizeAddOns(registeredFramework, this.options.cliOptions.mode, chosenAddonIds, this.options.cliOptions.bundler);
180
180
  // Check if package metadata was modified
181
181
  const packageMetadataChanged = Array.from(changes).some((filePath) => {
182
182
  const normalized = filePath.replace(/\\/g, '/');
@@ -327,6 +327,8 @@ export class DevWatchManager {
327
327
  addOns,
328
328
  basePackageJSON,
329
329
  optionalPackages,
330
+ bundlers: this.options.framework.bundlers,
331
+ defaultBundler: this.options.framework.defaultBundler,
330
332
  supportedModes: this.options.framework.supportedModes,
331
333
  };
332
334
  }
package/dist/options.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { intro } from '@clack/prompts';
2
- import { finalizeAddOns, getFrameworkById, getFrameworks, getPackageManager, loadStarter, populateAddOnOptionsDefaults, readConfigFile, } from '@tanstack/create';
3
- import { getProjectName, promptForAddOnOptions, promptForEnvVars, selectAddOns, selectDeployment, selectExamples, selectFramework, selectGit, selectInstall, selectPackageManager, selectTemplate, selectToolchain, } from './ui-prompts.js';
4
- import { listTemplateChoices, resolveStarterSpecifier, } from './command-line.js';
2
+ import { finalizeAddOns, getAllAddOns, getFrameworkById, getFrameworks, getPackageManager, loadStarter, populateAddOnOptionsDefaults, readConfigFile, resolveBundler, } from '@tanstack/create';
3
+ import { getProjectName, promptForAddOnOptions, promptForEnvVars, selectAddOns, selectBundler, selectDeployment, selectExamples, selectFramework, selectGit, selectInstall, selectPackageManager, selectTemplate, selectToolchain, } from './ui-prompts.js';
4
+ import { listTemplateChoices, resolveStarterSpecifier, validateLegacyCreateFlags, } from './command-line.js';
5
5
  import { resolveProjectLocation, validateProjectName, } from './utils.js';
6
6
  export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], forcedDeployment, showDeploymentOptions = true, defaultFrameworkId, }) {
7
7
  const options = {};
@@ -18,6 +18,25 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
18
18
  options.framework = await selectFramework(availableFrameworks, defaultFrameworkId);
19
19
  }
20
20
  }
21
+ options.bundler = await selectBundler(options.framework, cliOptions.buildTool);
22
+ if (options.bundler === 'rsbuild') {
23
+ const validation = validateLegacyCreateFlags({
24
+ ...cliOptions,
25
+ buildTool: options.bundler,
26
+ addOns: Array.isArray(cliOptions.addOns) && cliOptions.addOns.length > 0
27
+ ? cliOptions.addOns
28
+ : undefined,
29
+ });
30
+ if (validation.error) {
31
+ throw new Error(validation.error);
32
+ }
33
+ if (forcedAddOns.length > 0) {
34
+ throw new Error('Rsbuild does not currently support forced add-ons.');
35
+ }
36
+ if (forcedDeployment) {
37
+ throw new Error('Rsbuild does not currently support deployments.');
38
+ }
39
+ }
21
40
  const projectLocation = resolveProjectLocation({
22
41
  projectName: cliOptions.projectName ?? (await getProjectName()),
23
42
  targetDir: cliOptions.targetDir,
@@ -48,7 +67,7 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
48
67
  cliOptions.starter = cliOptions.templateId;
49
68
  }
50
69
  }
51
- if (!routerOnly && !blank && !cliOptions.starter) {
70
+ if (options.bundler !== 'rsbuild' && !routerOnly && !blank && !cliOptions.starter) {
52
71
  const starterChoices = await listTemplateChoices(options.framework.id);
53
72
  const selectedTemplateId = await selectTemplate(starterChoices.map((choice) => ({
54
73
  id: choice.id,
@@ -59,7 +78,7 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
59
78
  cliOptions.starter = selectedTemplateId;
60
79
  }
61
80
  }
62
- const starter = !routerOnly && !blank && cliOptions.starter
81
+ const starter = options.bundler !== 'rsbuild' && !routerOnly && !blank && cliOptions.starter
63
82
  ? await loadStarter(await resolveStarterSpecifier(cliOptions.starter, options.framework.id))
64
83
  : undefined;
65
84
  if (starter) {
@@ -78,12 +97,12 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
78
97
  detectedPackageManager || (await selectPackageManager());
79
98
  }
80
99
  // Toolchain selection
81
- const toolchain = blank && cliOptions.toolchain === undefined
100
+ const toolchain = blank && options.bundler !== 'rsbuild' && cliOptions.toolchain === undefined
82
101
  ? undefined
83
102
  : await selectToolchain(options.framework, cliOptions.toolchain);
84
103
  // Deployment selection
85
104
  let deployment;
86
- if (routerOnly) {
105
+ if (routerOnly || options.bundler === 'rsbuild') {
87
106
  deployment = undefined;
88
107
  }
89
108
  else if (cliOptions.deployment) {
@@ -112,7 +131,7 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
112
131
  if (deployment) {
113
132
  addOns.add(deployment);
114
133
  }
115
- if (!routerOnly) {
134
+ if (!routerOnly && options.bundler !== 'rsbuild') {
116
135
  for (const addOn of starter?.dependsOn || []) {
117
136
  addOns.add(addOn);
118
137
  }
@@ -120,7 +139,7 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
120
139
  addOns.add(addOn);
121
140
  }
122
141
  }
123
- if (!routerOnly && Array.isArray(cliOptions.addOns)) {
142
+ if (!routerOnly && options.bundler !== 'rsbuild' && Array.isArray(cliOptions.addOns)) {
124
143
  for (const addOn of cliOptions.addOns) {
125
144
  if (addOn.toLowerCase() === 'start') {
126
145
  continue;
@@ -128,7 +147,7 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
128
147
  addOns.add(addOn);
129
148
  }
130
149
  }
131
- else if (!routerOnly && !blank) {
150
+ else if (!routerOnly && options.bundler !== 'rsbuild' && !blank) {
132
151
  for (const addOn of await selectAddOns(options.framework, options.mode, 'add-on', 'What add-ons would you like for your project?', forcedAddOns)) {
133
152
  addOns.add(addOn);
134
153
  }
@@ -138,13 +157,14 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
138
157
  }
139
158
  }
140
159
  }
141
- const chosenAddOns = Array.from(await finalizeAddOns(options.framework, options.mode, Array.from(addOns)));
160
+ const chosenAddOns = Array.from(await finalizeAddOns(options.framework, options.mode, Array.from(addOns), options.bundler));
142
161
  options.chosenAddOns = includeExamples
143
162
  ? chosenAddOns
144
163
  : chosenAddOns.filter((addOn) => addOn.type !== 'example');
145
164
  options.tailwind =
146
165
  !blank || options.chosenAddOns.some((addOn) => addOn.tailwind === true);
147
166
  options.projectPreset = blank ? 'blank' : 'default';
167
+ options.routerOnly = routerOnly;
148
168
  // Prompt for add-on options in interactive mode
149
169
  if (Array.isArray(cliOptions.addOns)) {
150
170
  // Non-interactive mode: use defaults
@@ -184,6 +204,25 @@ export async function promptForAddOns() {
184
204
  }
185
205
  intro(`Adding new add-ons to '${config.projectName}'`);
186
206
  const addOns = new Set();
207
+ const bundler = resolveBundler(framework, config.bundler).id;
208
+ if (bundler === 'rsbuild') {
209
+ const toolchains = getAllAddOns(framework, config.mode, bundler).filter((addOn) => addOn.type === 'toolchain');
210
+ const configuredAddOnIds = new Set(config.chosenAddOns.map((addOn) => addOn.toLowerCase()));
211
+ const configuredExclusiveGroups = new Set(toolchains
212
+ .filter((addOn) => configuredAddOnIds.has(addOn.id.toLowerCase()))
213
+ .flatMap((addOn) => addOn.exclusive ?? []));
214
+ const unavailableToolchains = toolchains
215
+ .filter((addOn) => configuredAddOnIds.has(addOn.id.toLowerCase()) ||
216
+ addOn.exclusive?.some((exclusive) => configuredExclusiveGroups.has(exclusive)))
217
+ .map((addOn) => addOn.id);
218
+ if (unavailableToolchains.length === toolchains.length) {
219
+ return [];
220
+ }
221
+ for (const addOn of await selectAddOns(framework, config.mode, 'toolchain', 'Which toolchain would you like to add?', unavailableToolchains, false, bundler)) {
222
+ addOns.add(addOn);
223
+ }
224
+ return Array.from(addOns);
225
+ }
187
226
  for (const addOn of await selectAddOns(framework, config.mode, 'add-on', 'What add-ons would you like for your project?', config.chosenAddOns)) {
188
227
  addOns.add(addOn);
189
228
  }
@@ -1,4 +1,127 @@
1
- import type { FrameworkDefinition } from '@tanstack/create';
1
+ import type { CliOptions } from './types.js';
2
+ import type { FrameworkDefinition, Options } from '@tanstack/create';
3
+ export declare function getCreateTelemetryProperties(projectName: string, options: CliOptions): {
4
+ add_on_count: number | undefined;
5
+ add_on_ids: string[] | undefined;
6
+ addon_details_id: string | undefined;
7
+ command_variant: string;
8
+ deployment: string | undefined;
9
+ bundler: string | undefined;
10
+ examples: boolean | undefined;
11
+ blank: boolean;
12
+ framework: string | undefined;
13
+ git: boolean | undefined;
14
+ install: boolean;
15
+ intent: boolean;
16
+ interactive: boolean;
17
+ json: boolean;
18
+ non_interactive: boolean;
19
+ package_manager: "npm" | "yarn" | "pnpm" | "bun" | "deno" | undefined;
20
+ project_name_provided: boolean;
21
+ router_only: boolean;
22
+ target_dir_flag: boolean;
23
+ toolchain: string | undefined;
24
+ yes: boolean;
25
+ starter_kind?: undefined;
26
+ starter_id?: undefined;
27
+ } | {
28
+ add_on_count: number | undefined;
29
+ add_on_ids: string[] | undefined;
30
+ addon_details_id: string | undefined;
31
+ command_variant: string;
32
+ deployment: string | undefined;
33
+ bundler: string | undefined;
34
+ examples: boolean | undefined;
35
+ blank: boolean;
36
+ framework: string | undefined;
37
+ git: boolean | undefined;
38
+ install: boolean;
39
+ intent: boolean;
40
+ interactive: boolean;
41
+ json: boolean;
42
+ non_interactive: boolean;
43
+ package_manager: "npm" | "yarn" | "pnpm" | "bun" | "deno" | undefined;
44
+ project_name_provided: boolean;
45
+ router_only: boolean;
46
+ target_dir_flag: boolean;
47
+ toolchain: string | undefined;
48
+ yes: boolean;
49
+ starter_kind: string;
50
+ starter_id?: undefined;
51
+ } | {
52
+ add_on_count: number | undefined;
53
+ add_on_ids: string[] | undefined;
54
+ addon_details_id: string | undefined;
55
+ command_variant: string;
56
+ deployment: string | undefined;
57
+ bundler: string | undefined;
58
+ examples: boolean | undefined;
59
+ blank: boolean;
60
+ framework: string | undefined;
61
+ git: boolean | undefined;
62
+ install: boolean;
63
+ intent: boolean;
64
+ interactive: boolean;
65
+ json: boolean;
66
+ non_interactive: boolean;
67
+ package_manager: "npm" | "yarn" | "pnpm" | "bun" | "deno" | undefined;
68
+ project_name_provided: boolean;
69
+ router_only: boolean;
70
+ target_dir_flag: boolean;
71
+ toolchain: string | undefined;
72
+ yes: boolean;
73
+ starter_id: string | undefined;
74
+ starter_kind: string;
75
+ };
76
+ export declare function getResolvedCreateTelemetryProperties(finalOptions: Options, cliOptions: CliOptions): {
77
+ add_on_count: number;
78
+ add_on_ids: string[];
79
+ deployment: string | undefined;
80
+ bundler: string | undefined;
81
+ examples: boolean;
82
+ blank: boolean;
83
+ framework: string | undefined;
84
+ git: boolean;
85
+ install: boolean;
86
+ intent: boolean;
87
+ package_manager: "npm" | "yarn" | "pnpm" | "bun" | "deno";
88
+ router_only: boolean;
89
+ toolchain: string | undefined;
90
+ starter_kind?: undefined;
91
+ starter_id?: undefined;
92
+ } | {
93
+ add_on_count: number;
94
+ add_on_ids: string[];
95
+ deployment: string | undefined;
96
+ bundler: string | undefined;
97
+ examples: boolean;
98
+ blank: boolean;
99
+ framework: string | undefined;
100
+ git: boolean;
101
+ install: boolean;
102
+ intent: boolean;
103
+ package_manager: "npm" | "yarn" | "pnpm" | "bun" | "deno";
104
+ router_only: boolean;
105
+ toolchain: string | undefined;
106
+ starter_kind: string;
107
+ starter_id?: undefined;
108
+ } | {
109
+ add_on_count: number;
110
+ add_on_ids: string[];
111
+ deployment: string | undefined;
112
+ bundler: string | undefined;
113
+ examples: boolean;
114
+ blank: boolean;
115
+ framework: string | undefined;
116
+ git: boolean;
117
+ install: boolean;
118
+ intent: boolean;
119
+ package_manager: "npm" | "yarn" | "pnpm" | "bun" | "deno";
120
+ router_only: boolean;
121
+ toolchain: string | undefined;
122
+ starter_id: string | undefined;
123
+ starter_kind: string;
124
+ };
2
125
  export declare function cli({ name, appName, forcedAddOns, forcedDeployment, defaultFramework, frameworkDefinitionInitializers, showDeploymentOptions, legacyAutoCreate, defaultRouterOnly, }: {
3
126
  name: string;
4
127
  appName: string;
@@ -1,6 +1,7 @@
1
1
  import type { PackageManager } from '@tanstack/create';
2
2
  export interface CliOptions {
3
3
  framework?: string;
4
+ buildTool?: string;
4
5
  packageManager?: PackageManager;
5
6
  toolchain?: string | false;
6
7
  deployment?: string;
@@ -1,5 +1,6 @@
1
1
  import type { AddOn, PackageManager } from '@tanstack/create';
2
2
  import type { Framework } from '@tanstack/create/dist/types/types.js';
3
+ export declare function selectBundler(framework: Framework, bundlerId?: string): Promise<string>;
3
4
  export declare function selectFramework(frameworks: Array<Framework>, defaultFrameworkId?: string): Promise<Framework>;
4
5
  export declare function selectInstall(): Promise<boolean>;
5
6
  export declare function getProjectName(): Promise<string>;
@@ -9,7 +10,7 @@ export declare function selectTemplate(templates: Array<{
9
10
  name: string;
10
11
  description?: string;
11
12
  }>): Promise<string | undefined>;
12
- export declare function selectAddOns(framework: Framework, mode: string, type: string, message: string, forcedAddOns?: Array<string>, allowMultiple?: boolean): Promise<Array<string>>;
13
+ export declare function selectAddOns(framework: Framework, mode: string, type: string, message: string, forcedAddOns?: Array<string>, allowMultiple?: boolean, bundlerId?: string): Promise<Array<string>>;
13
14
  export declare function selectGit(): Promise<boolean>;
14
15
  export declare function selectExamples(): Promise<boolean>;
15
16
  export declare function selectToolchain(framework: Framework, toolchain?: string | false): Promise<string | undefined>;
@@ -1,7 +1,30 @@
1
1
  import { cancel, confirm, isCancel, multiselect, note, password, select, text, } from '@clack/prompts';
2
- import { DEFAULT_PACKAGE_MANAGER, SUPPORTED_PACKAGE_MANAGERS, getAllAddOns, } from '@tanstack/create';
2
+ import { DEFAULT_PACKAGE_MANAGER, SUPPORTED_PACKAGE_MANAGERS, getAllAddOns, getBundlers, getDefaultBundler, resolveBundler, } from '@tanstack/create';
3
3
  import { isCurrentDirectoryProjectNameInput, validateProjectName, } from './utils.js';
4
4
  import { orderAddOnsForPartnerPlacement } from './partner-placement.js';
5
+ export async function selectBundler(framework, bundlerId) {
6
+ if (bundlerId) {
7
+ return resolveBundler(framework, bundlerId).id;
8
+ }
9
+ const bundlers = getBundlers(framework);
10
+ if (bundlers.length === 1) {
11
+ return bundlers[0].id;
12
+ }
13
+ const selected = await select({
14
+ message: 'Select build tool:',
15
+ options: bundlers.map((bundler) => ({
16
+ value: bundler.id,
17
+ label: bundler.name,
18
+ hint: bundler.description,
19
+ })),
20
+ initialValue: getDefaultBundler(framework).id,
21
+ });
22
+ if (isCancel(selected)) {
23
+ cancel('Operation cancelled.');
24
+ process.exit(0);
25
+ }
26
+ return resolveBundler(framework, selected).id;
27
+ }
5
28
  export async function selectFramework(frameworks, defaultFrameworkId) {
6
29
  const initialValue = (defaultFrameworkId &&
7
30
  frameworks.find((f) => f.id.toLowerCase() === defaultFrameworkId.toLowerCase())?.id) ||
@@ -95,8 +118,8 @@ export async function selectTemplate(templates) {
95
118
  }
96
119
  return selected;
97
120
  }
98
- export async function selectAddOns(framework, mode, type, message, forcedAddOns = [], allowMultiple = true) {
99
- const allAddOns = await getAllAddOns(framework, mode);
121
+ export async function selectAddOns(framework, mode, type, message, forcedAddOns = [], allowMultiple = true, bundlerId) {
122
+ const allAddOns = await getAllAddOns(framework, mode, bundlerId);
100
123
  const addOns = allAddOns.filter((addOn) => addOn.type === type);
101
124
  if (addOns.length === 0) {
102
125
  return [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/cli",
3
- "version": "0.71.1",
3
+ "version": "0.72.0",
4
4
  "description": "TanStack CLI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -44,7 +44,7 @@
44
44
  "tempy": "^3.1.0",
45
45
  "validate-npm-package-name": "^7.0.0",
46
46
  "zod": "^3.24.2",
47
- "@tanstack/create": "0.70.1"
47
+ "@tanstack/create": "0.71.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@playwright/test": "^1.58.2",
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: create-app-scaffold
3
3
  description: >
4
- Scaffold a TanStack app with tanstack create using --framework, --template,
4
+ Scaffold a TanStack app with tanstack create using --framework, --build-tool, --template,
5
5
  --toolchain, --deployment, --add-ons, --blank, and --router-only. Covers flag
6
6
  compatibility, non-interactive defaults, and intent-preserving command
7
7
  construction.
8
8
  metadata:
9
9
  type: core
10
10
  library: tanstack-cli
11
- library_version: "0.70.0"
11
+ library_version: "0.71.0"
12
12
  ---
13
13
 
14
14
  # Create App Scaffold
@@ -28,6 +28,19 @@ npx @tanstack/cli create acme-web \
28
28
 
29
29
  ## Core Patterns
30
30
 
31
+ ### Choose Vite or Rsbuild explicitly
32
+
33
+ Vite is the default. Use Rsbuild for React or Solid when the requested project
34
+ fits its initial compatibility surface.
35
+
36
+ ```bash
37
+ npx @tanstack/cli create acme-web \
38
+ --framework react \
39
+ --build-tool rsbuild \
40
+ --toolchain biome \
41
+ -y
42
+ ```
43
+
31
44
  ### Build a deterministic non-interactive scaffold
32
45
 
33
46
  ```bash
@@ -162,5 +175,6 @@ See also: query-docs-library-metadata/SKILL.md § Common Mistakes
162
175
 
163
176
  - [Create flag compatibility matrix](references/create-flag-compatibility-matrix.md)
164
177
  - [Framework adapter options](references/framework-adapters.md)
178
+ - [Build tool options](references/bundlers.md)
165
179
  - [Deployment provider options](references/deployment-providers.md)
166
180
  - [Toolchain options](references/toolchains.md)
@@ -0,0 +1,27 @@
1
+ # Build Tool Options
2
+
3
+ Targets `@tanstack/cli` v0.71.0.
4
+
5
+ ## Supported values
6
+
7
+ - `vite` (default)
8
+ - `rsbuild`
9
+
10
+ ## Usage
11
+
12
+ ```bash
13
+ npx @tanstack/cli create app --framework react --build-tool vite -y
14
+ npx @tanstack/cli create app --framework solid --build-tool rsbuild -y
15
+ ```
16
+
17
+ Rsbuild supports full TanStack Start and router-only projects, including the
18
+ standard and `--blank` presets, built-in example pages, Tailwind, ESLint, and
19
+ Biome. It does not currently support templates, deployment adapters, or
20
+ catalog business/example add-ons. `tanstack add` is limited to ESLint/Biome in
21
+ Rsbuild projects.
22
+
23
+ Use the build-tool-aware catalog view before constructing an Rsbuild command:
24
+
25
+ ```bash
26
+ npx @tanstack/cli create --list-add-ons --build-tool rsbuild --json
27
+ ```
@@ -1,12 +1,13 @@
1
1
  # Create Flag Compatibility Matrix
2
2
 
3
- Targets `@tanstack/cli` v0.70.0.
3
+ Targets `@tanstack/cli` v0.71.0.
4
4
 
5
5
  ## Compatibility
6
6
 
7
7
  | Flag | Works with normal create | Works with `--router-only` | Notes |
8
8
  |---|---|---|---|
9
9
  | `--framework` | yes | yes | Framework is still honored in both modes. |
10
+ | `--build-tool` | yes | yes | `vite` is the default; `rsbuild` supports React and Solid. |
10
11
  | `--toolchain` | yes | yes | Toolchain selection remains available. |
11
12
  | `--add-ons` | yes | no | Ignored in router-only mode. |
12
13
  | `--deployment` | yes | no | Ignored in router-only mode. |
@@ -15,14 +16,18 @@ Targets `@tanstack/cli` v0.70.0.
15
16
  | `--blank` | yes | yes | Omits starter UI, examples, Tailwind, devtools, and test tooling unless an explicit integration requires them. Cannot be combined with a template, `--examples`, or `--tailwind`. |
16
17
  | `--tailwind` / `--no-tailwind` | deprecated/ignored | deprecated/ignored | Standard scaffolds enable Tailwind; use `--blank` for the supported minimal preset. |
17
18
 
19
+ With `--build-tool rsbuild`, only toolchain catalog entries are compatible.
20
+ Templates, deployments, and business/example add-ons are rejected. Built-in
21
+ demo pages controlled by `--examples` remain available.
22
+
18
23
  Source: `packages/cli/src/command-line.ts:337`
19
24
 
20
25
  ## Recommended command construction order
21
26
 
22
- 1. Choose mode (`--router-only` or full scaffold).
23
- 2. If full scaffold, resolve add-ons and deployment first.
24
- 3. Add framework and toolchain.
25
- 4. Pass explicit add-on ids and use `-y` only after flags are final.
27
+ 1. Choose framework and build tool.
28
+ 2. Choose mode (`--router-only` or full scaffold).
29
+ 3. If using Vite full scaffold, resolve add-ons, template, and deployment.
30
+ 4. Add the toolchain and use `-y` only after flags are final.
26
31
 
27
32
  ## Safe presets
28
33
 
@@ -35,4 +40,7 @@ npx @tanstack/cli create app --blank --framework react --deployment cloudflare -
35
40
 
36
41
  # Router-only preset
37
42
  npx @tanstack/cli create app --router-only --framework react --toolchain biome -y
43
+
44
+ # Rsbuild preset
45
+ npx @tanstack/cli create app --framework solid --build-tool rsbuild --toolchain biome -y
38
46
  ```
@@ -1,6 +1,6 @@
1
1
  # Framework Adapter Options
2
2
 
3
- Targets `@tanstack/cli` v0.70.0.
3
+ Targets `@tanstack/cli` v0.71.0.
4
4
 
5
5
  ## Supported values
6
6