@tanstack/cli 0.69.5 → 0.70.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
@@ -111,6 +111,7 @@ function getCreateTelemetryProperties(projectName, options) {
111
111
  command_variant: getCreateCommandVariant(options),
112
112
  deployment: options.deployment ? sanitizeId(options.deployment) : undefined,
113
113
  examples: options.examples,
114
+ blank: !!options.blank,
114
115
  framework: options.framework ? sanitizeId(options.framework) : undefined,
115
116
  git: options.git,
116
117
  install: options.install !== false,
@@ -137,6 +138,7 @@ function getResolvedCreateTelemetryProperties(finalOptions, cliOptions) {
137
138
  add_on_ids: addOnIds,
138
139
  deployment: deployment ? sanitizeId(deployment.id) : undefined,
139
140
  examples: includeExamples,
141
+ blank: finalOptions.projectPreset === 'blank',
140
142
  framework: sanitizeId(finalOptions.framework.id),
141
143
  git: finalOptions.git,
142
144
  install: finalOptions.install !== false,
@@ -153,7 +155,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
153
155
  let currentTelemetry;
154
156
  const environment = createUIEnvironment(appName, false, () => currentTelemetry);
155
157
  const program = new Command();
156
- async function confirmTargetDirectorySafety(targetDir, forced) {
158
+ async function confirmTargetDirectorySafety(targetDir, forced, nonInteractive) {
157
159
  if (forced) {
158
160
  return;
159
161
  }
@@ -166,6 +168,9 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
166
168
  if (fs.readdirSync(targetDir).length === 0) {
167
169
  return;
168
170
  }
171
+ if (nonInteractive) {
172
+ throw new Error(`Target directory "${targetDir}" already exists and is not empty. Pass --force to continue non-interactively.`);
173
+ }
169
174
  const shouldContinue = await confirm({
170
175
  message: `Target directory "${targetDir}" already exists and is not empty. Continue anyway?`,
171
176
  initialValue: false,
@@ -181,6 +186,9 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
181
186
  lines.push(` Location: ${finalOptions.targetDir}`);
182
187
  lines.push(` Framework: ${finalOptions.framework.name}`);
183
188
  lines.push(` Mode: ${finalOptions.mode}`);
189
+ if (finalOptions.projectPreset === 'blank') {
190
+ lines.push(` Preset: blank`);
191
+ }
184
192
  lines.push(` Package manager: ${finalOptions.packageManager}`);
185
193
  if (finalOptions.starter) {
186
194
  lines.push(` Template: ${finalOptions.starter.name}`);
@@ -361,7 +369,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
361
369
  console.log(chalk.gray('├─') + ' ' + chalk.yellow('⟳') + ' installing packages...');
362
370
  }
363
371
  const silentEnvironment = createUIEnvironment(appName, true, () => currentTelemetry);
364
- await confirmTargetDirectorySafety(normalizedOpts.targetDir, options.force);
372
+ await confirmTargetDirectorySafety(normalizedOpts.targetDir, options.force, !!options.nonInteractive || !!options.yes);
365
373
  await createApp(silentEnvironment, normalizedOpts);
366
374
  console.log(chalk.gray('└─') + ' ' + chalk.green('✓') + ` app created`);
367
375
  // Now start the dev watch mode
@@ -449,6 +457,15 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
449
457
  .description(`${appName} CLI`)
450
458
  .version(VERSION, '-v, --version', 'output the current version');
451
459
  addHiddenAgentFlag(program);
460
+ function validateCreateCommandOptions(options) {
461
+ const legacyCreateFlags = validateLegacyCreateFlags(options);
462
+ if (legacyCreateFlags.error) {
463
+ throw new Error(legacyCreateFlags.error);
464
+ }
465
+ for (const warning of legacyCreateFlags.warnings) {
466
+ log.warn(warning);
467
+ }
468
+ }
452
469
  // Helper to create the create command action handler
453
470
  async function handleCreate(projectName, options) {
454
471
  try {
@@ -456,13 +473,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
456
473
  json: options.json,
457
474
  properties: getCreateTelemetryProperties(projectName, options),
458
475
  }, async (telemetry) => {
459
- const legacyCreateFlags = validateLegacyCreateFlags(options);
460
- if (legacyCreateFlags.error) {
461
- throw new Error(legacyCreateFlags.error);
462
- }
463
- for (const warning of legacyCreateFlags.warnings) {
464
- log.warn(warning);
465
- }
476
+ validateCreateCommandOptions(options);
466
477
  if (options.listAddOns) {
467
478
  const addOns = await getAllAddOns(getFrameworkByName(options.framework || defaultFramework || 'React'), defaultMode);
468
479
  const visibleAddOns = addOns.filter((a) => !forcedAddOns.includes(a.id));
@@ -666,7 +677,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
666
677
  if (cameFromPrompts) {
667
678
  await confirmCreateOptions(finalOptions);
668
679
  }
669
- await confirmTargetDirectorySafety(finalOptions.targetDir, options.force);
680
+ await confirmTargetDirectorySafety(finalOptions.targetDir, options.force, nonInteractive);
670
681
  await createApp(environment, finalOptions);
671
682
  });
672
683
  }
@@ -704,8 +715,9 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
704
715
  .option('--dev-watch <path>', 'Watch a framework directory for changes and auto-rebuild')
705
716
  .option('--run-dev', 'Run the app dev server alongside dev-watch', false)
706
717
  .option('--router-only', 'Use router-only compatibility mode (file-based routing without TanStack Start)')
707
- .option('--tailwind', 'Deprecated: compatibility flag; Tailwind is always enabled')
708
- .option('--no-tailwind', 'Deprecated: compatibility flag; Tailwind opt-out is ignored')
718
+ .option('--blank', 'Create a minimal one-route app without default starter UI, Tailwind, devtools, or tests')
719
+ .option('--tailwind', 'Deprecated: compatibility flag; standard scaffolds always enable Tailwind')
720
+ .option('--no-tailwind', 'Deprecated: compatibility flag; use --blank for a project without Tailwind')
709
721
  .option('--examples', 'include demo/example pages')
710
722
  .option('--no-examples', 'exclude demo/example pages');
711
723
  if (deployments.size > 0) {
@@ -772,6 +784,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
772
784
  run_dev: true,
773
785
  },
774
786
  }, async () => {
787
+ validateCreateCommandOptions(options);
775
788
  const frameworkName = options.framework || defaultFramework || 'React';
776
789
  const framework = getFrameworkByName(frameworkName);
777
790
  if (!framework) {
@@ -217,6 +217,25 @@ export async function listTemplateChoices(preferredFramework) {
217
217
  export function validateLegacyCreateFlags(cliOptions) {
218
218
  const warnings = [];
219
219
  const legacyTemplate = getLegacyTemplateValue(cliOptions.template);
220
+ if (cliOptions.blank &&
221
+ (cliOptions.starter || cliOptions.template || cliOptions.templateId)) {
222
+ return {
223
+ warnings,
224
+ error: '--blank cannot be combined with --starter, --template, or --template-id.',
225
+ };
226
+ }
227
+ if (cliOptions.blank && cliOptions.examples === true) {
228
+ return {
229
+ warnings,
230
+ error: '--blank cannot be combined with --examples.',
231
+ };
232
+ }
233
+ if (cliOptions.blank && cliOptions.tailwind === true) {
234
+ return {
235
+ warnings,
236
+ error: '--blank cannot be combined with --tailwind.',
237
+ };
238
+ }
220
239
  if (cliOptions.starter) {
221
240
  warnings.push('The --starter flag is deprecated; prefer --template instead. Backward compatibility remains for now.');
222
241
  }
@@ -239,10 +258,10 @@ export function validateLegacyCreateFlags(cliOptions) {
239
258
  warnings.push('Ignoring --template-id in router-only compatibility mode.');
240
259
  }
241
260
  if (cliOptions.tailwind === true) {
242
- warnings.push('The --tailwind flag is deprecated and ignored. Tailwind is always enabled in TanStack Start scaffolds.');
261
+ warnings.push('The --tailwind flag is deprecated and ignored. Tailwind is enabled in standard TanStack Start scaffolds.');
243
262
  }
244
- if (cliOptions.tailwind === false) {
245
- warnings.push('The --no-tailwind flag is deprecated and ignored. Tailwind opt-out is intentionally unsupported to keep add-on permutations maintainable; remove Tailwind after scaffolding if needed.');
263
+ if (cliOptions.tailwind === false && !cliOptions.blank) {
264
+ warnings.push('The --no-tailwind flag is deprecated and ignored for standard scaffolds. Use --blank to create a minimal project without Tailwind.');
246
265
  }
247
266
  if (!legacyTemplate) {
248
267
  return { warnings };
@@ -283,6 +302,7 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
283
302
  // Mode is always file-router (TanStack Start)
284
303
  let mode = 'file-router';
285
304
  let routerOnly = !!cliOptions.routerOnly;
305
+ const blank = cliOptions.blank === true;
286
306
  const legacyTemplate = getLegacyTemplateValue(cliOptions.template);
287
307
  if (!cliOptions.starter) {
288
308
  if (cliOptions.template && !legacyTemplate) {
@@ -300,12 +320,11 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
300
320
  cliOptions.starter = cliOptions.templateId;
301
321
  }
302
322
  const preferredFramework = (cliOptions.framework || 'react').toLowerCase();
303
- const starter = !routerOnly && cliOptions.starter
323
+ const starter = !routerOnly && !blank && cliOptions.starter
304
324
  ? await loadStarter(await resolveStarterSpecifier(cliOptions.starter, preferredFramework))
305
325
  : undefined;
306
- // TypeScript and Tailwind are always enabled with TanStack Start
326
+ // TypeScript is always enabled with TanStack Start.
307
327
  const typescript = true;
308
- const tailwind = true;
309
328
  if (starter) {
310
329
  cliOptions.framework = starter.framework;
311
330
  mode = starter.mode;
@@ -349,11 +368,12 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
349
368
  }
350
369
  return [];
351
370
  }
352
- const includeExamples = cliOptions.examples ?? !routerOnly;
371
+ const includeExamples = blank ? false : (cliOptions.examples ?? !routerOnly);
353
372
  const chosenAddOnsRaw = await selectAddOns();
354
373
  const chosenAddOns = includeExamples
355
374
  ? chosenAddOnsRaw
356
375
  : chosenAddOnsRaw.filter((addOn) => addOn.type !== 'example');
376
+ const tailwind = !blank || chosenAddOns.some((addOn) => addOn.tailwind === true);
357
377
  // Handle add-on configuration option
358
378
  let addOnOptionsFromCLI = {};
359
379
  if (cliOptions.addOnConfig) {
@@ -377,13 +397,14 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
377
397
  DEFAULT_PACKAGE_MANAGER,
378
398
  git: cliOptions.git ?? true,
379
399
  install: cliOptions.install,
380
- intent: cliOptions.intent ?? true,
400
+ intent: cliOptions.intent ?? !blank,
381
401
  chosenAddOns,
382
402
  addOnOptions: {
383
403
  ...populateAddOnOptionsDefaults(chosenAddOns),
384
404
  ...addOnOptionsFromCLI,
385
405
  },
386
406
  starter: starter,
407
+ projectPreset: blank ? 'blank' : 'default',
387
408
  };
388
409
  normalized.includeExamples =
389
410
  includeExamples;
package/dist/options.js CHANGED
@@ -5,6 +5,7 @@ import { listTemplateChoices, resolveStarterSpecifier, } 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 = {};
8
+ const blank = cliOptions.blank === true;
8
9
  if (cliOptions.framework) {
9
10
  options.framework = getFrameworkById(cliOptions.framework);
10
11
  }
@@ -47,7 +48,7 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
47
48
  cliOptions.starter = cliOptions.templateId;
48
49
  }
49
50
  }
50
- if (!routerOnly && !cliOptions.starter) {
51
+ if (!routerOnly && !blank && !cliOptions.starter) {
51
52
  const starterChoices = await listTemplateChoices(options.framework.id);
52
53
  const selectedTemplateId = await selectTemplate(starterChoices.map((choice) => ({
53
54
  id: choice.id,
@@ -58,7 +59,7 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
58
59
  cliOptions.starter = selectedTemplateId;
59
60
  }
60
61
  }
61
- const starter = !routerOnly && cliOptions.starter
62
+ const starter = !routerOnly && !blank && cliOptions.starter
62
63
  ? await loadStarter(await resolveStarterSpecifier(cliOptions.starter, options.framework.id))
63
64
  : undefined;
64
65
  if (starter) {
@@ -77,7 +78,9 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
77
78
  detectedPackageManager || (await selectPackageManager());
78
79
  }
79
80
  // Toolchain selection
80
- const toolchain = await selectToolchain(options.framework, cliOptions.toolchain);
81
+ const toolchain = blank && cliOptions.toolchain === undefined
82
+ ? undefined
83
+ : await selectToolchain(options.framework, cliOptions.toolchain);
81
84
  // Deployment selection
82
85
  let deployment;
83
86
  if (routerOnly) {
@@ -86,6 +89,9 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
86
89
  else if (cliOptions.deployment) {
87
90
  deployment = cliOptions.deployment;
88
91
  }
92
+ else if (blank) {
93
+ deployment = forcedDeployment;
94
+ }
89
95
  else if (showDeploymentOptions) {
90
96
  deployment = await selectDeployment(options.framework, cliOptions.deployment, forcedDeployment);
91
97
  }
@@ -95,7 +101,9 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
95
101
  // Add-ons selection
96
102
  const addOns = new Set();
97
103
  // Examples/demo pages are enabled by default
98
- const includeExamples = cliOptions.examples ?? (routerOnly ? false : await selectExamples());
104
+ const includeExamples = blank
105
+ ? false
106
+ : (cliOptions.examples ?? (routerOnly ? false : await selectExamples()));
99
107
  options.includeExamples =
100
108
  includeExamples;
101
109
  if (toolchain) {
@@ -120,7 +128,7 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
120
128
  addOns.add(addOn);
121
129
  }
122
130
  }
123
- else if (!routerOnly) {
131
+ else if (!routerOnly && !blank) {
124
132
  for (const addOn of await selectAddOns(options.framework, options.mode, 'add-on', 'What add-ons would you like for your project?', forcedAddOns)) {
125
133
  addOns.add(addOn);
126
134
  }
@@ -134,8 +142,9 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
134
142
  options.chosenAddOns = includeExamples
135
143
  ? chosenAddOns
136
144
  : chosenAddOns.filter((addOn) => addOn.type !== 'example');
137
- // Tailwind is always enabled
138
- options.tailwind = true;
145
+ options.tailwind =
146
+ !blank || options.chosenAddOns.some((addOn) => addOn.tailwind === true);
147
+ options.projectPreset = blank ? 'blank' : 'default';
139
148
  // Prompt for add-on options in interactive mode
140
149
  if (Array.isArray(cliOptions.addOns)) {
141
150
  // Non-interactive mode: use defaults
@@ -156,6 +165,7 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], fo
156
165
  envVarValues;
157
166
  options.git = cliOptions.git ?? (await selectGit());
158
167
  options.install = cliOptions.install ?? (await selectInstall());
168
+ options.intent = cliOptions.intent ?? !blank;
159
169
  if (starter) {
160
170
  options.starter = starter;
161
171
  }
@@ -0,0 +1,48 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ const partnerTierOrder = {
3
+ gold: 0,
4
+ silver: 1,
5
+ bronze: 2,
6
+ };
7
+ const cliSessionSeed = randomUUID();
8
+ function hashString(value) {
9
+ let hash = 2166136261;
10
+ for (let index = 0; index < value.length; index++) {
11
+ hash ^= value.charCodeAt(index);
12
+ hash = Math.imul(hash, 16777619);
13
+ }
14
+ return hash >>> 0;
15
+ }
16
+ function compareIdentity(left, right) {
17
+ const nameComparison = left.name.localeCompare(right.name);
18
+ return nameComparison || left.id.localeCompare(right.id);
19
+ }
20
+ function compareSeededPartnerOrder(left, right, seed) {
21
+ const leftWeight = left.partner.placementWeight ?? 1;
22
+ const rightWeight = right.partner.placementWeight ?? 1;
23
+ const leftRandom = (hashString(`${seed}:${left.partner.id}`) + 1) / 4294967297;
24
+ const rightRandom = (hashString(`${seed}:${right.partner.id}`) + 1) / 4294967297;
25
+ const seededComparison = -Math.log(leftRandom) / leftWeight + Math.log(rightRandom) / rightWeight;
26
+ return seededComparison || compareIdentity(left, right);
27
+ }
28
+ export function orderAddOnsForPartnerPlacement(addOns, surface, sessionSeed = cliSessionSeed) {
29
+ const rotationSeed = `${surface}:${sessionSeed}`;
30
+ const partners = addOns
31
+ .filter((addOn) => addOn.partner)
32
+ .sort((left, right) => {
33
+ const tierComparison = partnerTierOrder[left.partner.tier] -
34
+ partnerTierOrder[right.partner.tier];
35
+ if (tierComparison !== 0) {
36
+ return tierComparison;
37
+ }
38
+ if (surface === 'deployment' && left.partner.id === 'cloudflare') {
39
+ return -1;
40
+ }
41
+ if (surface === 'deployment' && right.partner.id === 'cloudflare') {
42
+ return 1;
43
+ }
44
+ return compareSeededPartnerOrder(left, right, rotationSeed);
45
+ });
46
+ const nonPartners = addOns.filter((addOn) => !addOn.partner);
47
+ return [...partners, ...nonPartners];
48
+ }
@@ -0,0 +1,2 @@
1
+ import type { AddOn } from '@tanstack/create';
2
+ export declare function orderAddOnsForPartnerPlacement(addOns: Array<AddOn>, surface: string, sessionSeed?: `${string}-${string}-${string}-${string}-${string}`): AddOn[];
@@ -23,6 +23,7 @@ export interface CliOptions {
23
23
  addOnConfig?: string;
24
24
  force?: boolean;
25
25
  routerOnly?: boolean;
26
+ blank?: boolean;
26
27
  template?: string;
27
28
  tailwind?: boolean;
28
29
  examples?: boolean;
@@ -1,6 +1,7 @@
1
1
  import { cancel, confirm, isCancel, multiselect, note, password, select, text, } from '@clack/prompts';
2
2
  import { DEFAULT_PACKAGE_MANAGER, SUPPORTED_PACKAGE_MANAGERS, getAllAddOns, } from '@tanstack/create';
3
3
  import { isCurrentDirectoryProjectNameInput, validateProjectName, } from './utils.js';
4
+ import { orderAddOnsForPartnerPlacement } from './partner-placement.js';
4
5
  export async function selectFramework(frameworks, defaultFrameworkId) {
5
6
  const initialValue = (defaultFrameworkId &&
6
7
  frameworks.find((f) => f.id.toLowerCase() === defaultFrameworkId.toLowerCase())?.id) ||
@@ -101,7 +102,7 @@ export async function selectAddOns(framework, mode, type, message, forcedAddOns
101
102
  return [];
102
103
  }
103
104
  if (allowMultiple) {
104
- const selectableAddOns = addOns.filter((addOn) => !forcedAddOns.includes(addOn.id));
105
+ const selectableAddOns = orderAddOnsForPartnerPlacement(addOns.filter((addOn) => !forcedAddOns.includes(addOn.id)), 'add-ons');
105
106
  if (selectableAddOns.length === 0) {
106
107
  return [];
107
108
  }
@@ -272,20 +273,19 @@ export async function promptForEnvVars(addOns) {
272
273
  export async function selectDeployment(framework, deployment, forcedDeployment) {
273
274
  const deployments = new Set();
274
275
  let initialValue = undefined;
275
- for (const addOn of framework
276
+ for (const addOn of orderAddOnsForPartnerPlacement(framework
276
277
  .getAddOns()
277
- .sort((a, b) => a.name.localeCompare(b.name))) {
278
- if (addOn.type === 'deployment') {
279
- deployments.add(addOn);
280
- if (deployment && addOn.id === deployment) {
281
- return deployment;
282
- }
283
- if (forcedDeployment && addOn.id === forcedDeployment) {
284
- initialValue = addOn.id;
285
- }
286
- else if (!initialValue && addOn.default) {
287
- initialValue = addOn.id;
288
- }
278
+ .filter((addOn) => addOn.type === 'deployment')
279
+ .sort((a, b) => a.name.localeCompare(b.name)), 'deployment')) {
280
+ deployments.add(addOn);
281
+ if (deployment && addOn.id === deployment) {
282
+ return deployment;
283
+ }
284
+ if (forcedDeployment && addOn.id === forcedDeployment) {
285
+ initialValue = addOn.id;
286
+ }
287
+ else if (!initialValue && addOn.default) {
288
+ initialValue = addOn.id;
289
289
  }
290
290
  }
291
291
  if (deployments.size === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/cli",
3
- "version": "0.69.5",
3
+ "version": "0.70.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.68.4"
47
+ "@tanstack/create": "0.69.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@playwright/test": "^1.58.2",
@@ -4,9 +4,10 @@ description: >
4
4
  Apply integrations to existing projects with tanstack add, including
5
5
  add-on id resolution, dependency chains, option prompts, and .cta.json
6
6
  project metadata preconditions.
7
- type: core
8
- library: tanstack-cli
9
- library_version: "0.62.1"
7
+ metadata:
8
+ type: core
9
+ library: tanstack-cli
10
+ library_version: "0.62.1"
10
11
  ---
11
12
 
12
13
  # Add Add-ons To Existing App
@@ -5,9 +5,10 @@ description: >
5
5
  tanstack ecosystem --json, tanstack create --list-add-ons --json, and
6
6
  tanstack create --addon-details --json. Covers exclusive categories,
7
7
  provider options, and router-only compatibility constraints.
8
- type: composition
9
- library: tanstack-cli
10
- library_version: "0.62.1"
8
+ metadata:
9
+ type: composition
10
+ library: tanstack-cli
11
+ library_version: "0.62.1"
11
12
  requires:
12
13
  - create-app-scaffold
13
14
  - query-docs-library-metadata
@@ -2,12 +2,13 @@
2
2
  name: create-app-scaffold
3
3
  description: >
4
4
  Scaffold a TanStack app with tanstack create using --framework, --template,
5
- --toolchain, --deployment, --add-ons, and --router-only. Covers flag
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
- type: core
9
- library: tanstack-cli
10
- library_version: "0.62.1"
8
+ metadata:
9
+ type: core
10
+ library: tanstack-cli
11
+ library_version: "0.70.0"
11
12
  ---
12
13
 
13
14
  # Create App Scaffold
@@ -37,6 +38,20 @@ npx @tanstack/cli create acme-solid \
37
38
  -y
38
39
  ```
39
40
 
41
+ ### Start from a minimal production-ready scaffold
42
+
43
+ Use `--blank` when the task does not need the default starter UI, examples,
44
+ Tailwind, devtools, test tooling, or local Intent setup. Explicit integrations
45
+ remain composable; add `--intent` when the generated project should contain
46
+ skill mappings for later coding-agent work.
47
+
48
+ ```bash
49
+ npx @tanstack/cli create acme-web \
50
+ --blank \
51
+ --deployment cloudflare \
52
+ -y
53
+ ```
54
+
40
55
  ### Use router-only mode for compatibility scaffolds only
41
56
 
42
57
  ```bash
@@ -75,7 +90,7 @@ In non-interactive runs, empty add-on selection can complete with defaults and s
75
90
 
76
91
  Source: https://github.com/TanStack/cli/issues/234
77
92
 
78
- ### HIGH Assume --no-tailwind is still supported
93
+ ### HIGH Avoid --no-tailwind; use --blank for a minimal scaffold
79
94
 
80
95
  Wrong:
81
96
  ```bash
@@ -84,12 +99,31 @@ npx @tanstack/cli create my-app --no-tailwind -y
84
99
 
85
100
  Correct:
86
101
  ```bash
87
- npx @tanstack/cli create my-app -y
102
+ npx @tanstack/cli create my-app --blank -y
103
+ ```
104
+
105
+ `--no-tailwind` remains a deprecated compatibility flag for standard
106
+ scaffolds. `--blank` is the supported preset for a minimal project without
107
+ Tailwind.
108
+
109
+ Source: packages/cli/src/command-line.ts:386
110
+
111
+ ### HIGH Combine --blank with a template, examples, or --tailwind
112
+
113
+ Wrong:
114
+ ```bash
115
+ npx @tanstack/cli create my-app --blank --template ecommerce -y
88
116
  ```
89
117
 
90
- `--no-tailwind` is deprecated and ignored, so output still includes Tailwind and diverges from expected stack constraints.
118
+ Correct:
119
+ ```bash
120
+ npx @tanstack/cli create my-app --blank --add-ons drizzle -y
121
+ ```
91
122
 
92
- Source: packages/cli/src/command-line.ts:369
123
+ Templates, examples, and `--tailwind` replace the output constraints that
124
+ `--blank` guarantees, so the CLI rejects those combinations. Deployment,
125
+ toolchain, and add-on selections remain supported; a selected styling add-on
126
+ can opt the generated app back into Tailwind when it requires it.
93
127
 
94
128
  ### CRITICAL Combine router-only with template/deployment/add-ons
95
129
 
@@ -1,6 +1,6 @@
1
1
  # Create Flag Compatibility Matrix
2
2
 
3
- Targets `@tanstack/cli` v0.62.1.
3
+ Targets `@tanstack/cli` v0.70.0.
4
4
 
5
5
  ## Compatibility
6
6
 
@@ -12,7 +12,8 @@ Targets `@tanstack/cli` v0.62.1.
12
12
  | `--deployment` | yes | no | Ignored in router-only mode. |
13
13
  | `--template` / `--starter` | yes | no | Ignored in router-only mode. |
14
14
  | `--template-id` | yes | no | Ignored in router-only mode. |
15
- | `--tailwind` / `--no-tailwind` | deprecated/ignored | deprecated/ignored | Tailwind is always enabled. |
15
+ | `--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
+ | `--tailwind` / `--no-tailwind` | deprecated/ignored | deprecated/ignored | Standard scaffolds enable Tailwind; use `--blank` for the supported minimal preset. |
16
17
 
17
18
  Source: `packages/cli/src/command-line.ts:337`
18
19
 
@@ -29,6 +30,9 @@ Source: `packages/cli/src/command-line.ts:337`
29
30
  # Full scaffold preset
30
31
  npx @tanstack/cli create app --framework react --add-ons tanstack-query --deployment netlify -y
31
32
 
33
+ # Minimal Start preset
34
+ npx @tanstack/cli create app --blank --framework react --deployment cloudflare -y
35
+
32
36
  # Router-only preset
33
37
  npx @tanstack/cli create app --router-only --framework react --toolchain biome -y
34
38
  ```
@@ -1,6 +1,6 @@
1
1
  # Framework Adapter Options
2
2
 
3
- Targets `@tanstack/cli` v0.61.0.
3
+ Targets `@tanstack/cli` v0.70.0.
4
4
 
5
5
  ## Supported values
6
6
 
@@ -4,9 +4,10 @@ description: >
4
4
  Build and iterate custom add-ons/templates with tanstack add-on init,
5
5
  add-on compile, add-on dev, and tanstack create --dev-watch, including sync
6
6
  loop preconditions, watch-path validation, and project metadata constraints.
7
- type: lifecycle
8
- library: tanstack-cli
9
- library_version: "0.62.1"
7
+ metadata:
8
+ type: lifecycle
9
+ library: tanstack-cli
10
+ library_version: "0.62.1"
10
11
  requires:
11
12
  - add-addons-existing-app
12
13
  ---
@@ -4,9 +4,10 @@ description: >
4
4
  Retrieve machine-readable context with tanstack libraries, tanstack doc,
5
5
  tanstack search-docs, tanstack create --list-add-ons --json, and
6
6
  --addon-details for agent-safe discovery and preflight validation.
7
- type: core
8
- library: tanstack-cli
9
- library_version: "0.62.1"
7
+ metadata:
8
+ type: core
9
+ library: tanstack-cli
10
+ library_version: "0.62.1"
10
11
  ---
11
12
 
12
13
  # Query Docs And Library Metadata