@tanstack/cli 0.63.1 → 0.64.1

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
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import { resolve } from 'node:path';
3
- import { Command, InvalidArgumentError } from 'commander';
3
+ 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';
@@ -37,6 +37,19 @@ function sanitizeIdList(values) {
37
37
  .map((value) => sanitizeId(value))
38
38
  .filter((value) => Boolean(value))));
39
39
  }
40
+ const AGENT_FLAG = '--agent';
41
+ function addHiddenAgentFlag(cmd) {
42
+ if (cmd.options.some((option) => option.long === AGENT_FLAG)) {
43
+ return cmd;
44
+ }
45
+ cmd.addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp());
46
+ return cmd;
47
+ }
48
+ function getInvocationTelemetryProperties() {
49
+ return {
50
+ invoked_by_agent: process.argv.includes(AGENT_FLAG),
51
+ };
52
+ }
40
53
  function getStarterTelemetryProperties(value) {
41
54
  if (!value) {
42
55
  return {};
@@ -134,7 +147,7 @@ function getResolvedCreateTelemetryProperties(finalOptions, cliOptions) {
134
147
  function formatErrorMessage(error) {
135
148
  return error instanceof Error ? error.message : 'An unknown error occurred';
136
149
  }
137
- export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaultFramework, frameworkDefinitionInitializers, showDeploymentOptions = false, legacyAutoCreate = false, defaultRouterOnly = false, }) {
150
+ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaultFramework, frameworkDefinitionInitializers, showDeploymentOptions = true, legacyAutoCreate = false, defaultRouterOnly = false, }) {
138
151
  let currentTelemetry;
139
152
  const environment = createUIEnvironment(appName, false, () => currentTelemetry);
140
153
  const program = new Command();
@@ -275,6 +288,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
275
288
  const startedAt = Date.now();
276
289
  currentTelemetry = telemetry;
277
290
  telemetry.captureCommandStarted(command, {
291
+ ...getInvocationTelemetryProperties(),
278
292
  ...opts.properties,
279
293
  cli_version: VERSION,
280
294
  });
@@ -295,6 +309,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
295
309
  .name(name)
296
310
  .description(`${appName} CLI`)
297
311
  .version(VERSION, '-v, --version', 'output the current version');
312
+ addHiddenAgentFlag(program);
298
313
  // Helper to create the create command action handler
299
314
  async function handleCreate(projectName, options) {
300
315
  try {
@@ -444,38 +459,49 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
444
459
  cliOptions.template.toLowerCase() !== 'file-router') {
445
460
  cliOptions.routerOnly = true;
446
461
  }
447
- cliOptions.framework = getFrameworkByName(options.framework || defaultFramework || 'React').id;
462
+ if (options.framework) {
463
+ cliOptions.framework = getFrameworkByName(options.framework).id;
464
+ }
465
+ else if (defaultFramework) {
466
+ cliOptions.framework = getFrameworkByName(defaultFramework).id;
467
+ }
448
468
  const nonInteractive = !!cliOptions.nonInteractive || !!cliOptions.yes;
449
469
  if (cliOptions.interactive && nonInteractive) {
450
470
  throw new Error('Cannot combine --interactive with --non-interactive/--yes.');
451
471
  }
452
- const addOnsFlagPassed = process.argv.includes('--add-ons');
472
+ const hasInteractiveTerminal = !!process.stdin.isTTY && !!process.stdout.isTTY && !process.env.CI;
453
473
  const wantsInteractiveMode = !nonInteractive &&
454
- (cliOptions.interactive ||
455
- (cliOptions.addOns === true && addOnsFlagPassed));
474
+ (cliOptions.interactive || hasInteractiveTerminal);
456
475
  let finalOptions;
457
476
  if (wantsInteractiveMode) {
458
- cliOptions.addOns = true;
477
+ if (cliOptions.addOns === undefined) {
478
+ cliOptions.addOns = true;
479
+ }
459
480
  }
460
481
  else {
482
+ if (!cliOptions.framework) {
483
+ cliOptions.framework = getFrameworkByName(defaultFramework || 'React').id;
484
+ }
461
485
  finalOptions = await normalizeOptions(cliOptions, forcedAddOns, { forcedDeployment });
462
486
  }
463
- if (nonInteractive) {
464
- if (cliOptions.addOns === true) {
465
- throw new Error('When using --non-interactive/--yes, pass explicit add-ons via --add-ons <ids>.');
466
- }
487
+ if (!wantsInteractiveMode && cliOptions.addOns === true) {
488
+ throw new Error('When running non-interactively, pass explicit add-ons via --add-ons <ids>.');
467
489
  }
468
490
  if (finalOptions) {
469
491
  intro(`Creating a new ${appName} app in ${projectName}...`);
470
492
  }
471
493
  else {
472
- if (nonInteractive) {
494
+ if (!wantsInteractiveMode) {
473
495
  throw new Error('Project name is required in non-interactive mode. Pass [project-name] or --target-dir.');
474
496
  }
475
497
  intro(`Let's configure your ${appName} application`);
476
498
  finalOptions = await promptForCreateOptions(cliOptions, {
477
499
  forcedAddOns,
500
+ forcedDeployment,
478
501
  showDeploymentOptions,
502
+ defaultFrameworkId: defaultFramework
503
+ ? getFrameworkByName(defaultFramework)?.id
504
+ : undefined,
479
505
  });
480
506
  }
481
507
  if (!finalOptions) {
@@ -507,6 +533,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
507
533
  }
508
534
  // Helper to configure create command options
509
535
  function configureCreateCommand(cmd) {
536
+ addHiddenAgentFlag(cmd);
510
537
  cmd.argument('[project-name]', 'name of the project');
511
538
  if (!defaultFramework) {
512
539
  cmd.option('--framework <type>', `project framework (${availableFrameworks.join(', ')})`, (value) => {
@@ -517,7 +544,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
517
544
  throw new InvalidArgumentError(`Invalid framework: ${value}. Only the following are allowed: ${availableFrameworks.join(', ')}`);
518
545
  }
519
546
  return value;
520
- }, defaultFramework || 'React');
547
+ });
521
548
  }
522
549
  cmd
523
550
  .option('--starter [url-or-id]', 'DEPRECATED: use --template. Initializes from a template URL or built-in id', false)
@@ -624,6 +651,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
624
651
  program
625
652
  .command('libraries')
626
653
  .description('List TanStack libraries')
654
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
627
655
  .option('--group <group>', `filter by group (${LIBRARY_GROUPS.join(', ')})`)
628
656
  .option('--json', 'output JSON for automation', false)
629
657
  .action(async (options) => {
@@ -681,6 +709,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
681
709
  program
682
710
  .command('doc')
683
711
  .description('Fetch a TanStack documentation page')
712
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
684
713
  .argument('<library>', 'library ID (eg. query, router, table)')
685
714
  .argument('<path>', 'documentation path (eg. framework/react/overview)')
686
715
  .option('--docs-version <version>', 'docs version (default: latest)', 'latest')
@@ -757,6 +786,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
757
786
  program
758
787
  .command('search-docs')
759
788
  .description('Search TanStack documentation')
789
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
760
790
  .argument('<query>', 'search query')
761
791
  .option('--library <id>', 'filter to specific library')
762
792
  .option('--framework <name>', 'filter to specific framework')
@@ -804,6 +834,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
804
834
  program
805
835
  .command('ecosystem')
806
836
  .description('List TanStack ecosystem partners')
837
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
807
838
  .option('--category <category>', 'filter by category')
808
839
  .option('--library <id>', 'filter by TanStack library')
809
840
  .option('--json', 'output JSON for automation', false)
@@ -873,6 +904,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
873
904
  program
874
905
  .command('pin-versions')
875
906
  .description('Pin versions of the TanStack libraries')
907
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
876
908
  .action(async () => {
877
909
  try {
878
910
  await runWithTelemetry('pin-versions', {}, async (telemetry) => {
@@ -943,6 +975,7 @@ Remove your node_modules directory and package lock file and re-install.`);
943
975
  telemetryCommand
944
976
  .command('status')
945
977
  .description('Show anonymous telemetry status')
978
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
946
979
  .option('--json', 'output JSON for automation', false)
947
980
  .action(async (options) => {
948
981
  const status = await getTelemetryStatus({ createIfMissing: true });
@@ -966,6 +999,7 @@ Remove your node_modules directory and package lock file and re-install.`);
966
999
  telemetryCommand
967
1000
  .command('enable')
968
1001
  .description('Enable anonymous telemetry')
1002
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
969
1003
  .action(async () => {
970
1004
  await setTelemetryEnabled(true);
971
1005
  console.log('Anonymous telemetry enabled');
@@ -973,6 +1007,7 @@ Remove your node_modules directory and package lock file and re-install.`);
973
1007
  telemetryCommand
974
1008
  .command('disable')
975
1009
  .description('Disable anonymous telemetry')
1010
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
976
1011
  .action(async () => {
977
1012
  await setTelemetryEnabled(false);
978
1013
  console.log('Anonymous telemetry disabled');
@@ -980,6 +1015,7 @@ Remove your node_modules directory and package lock file and re-install.`);
980
1015
  // === ADD SUBCOMMAND ===
981
1016
  program
982
1017
  .command('add')
1018
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
983
1019
  .argument('[add-on...]', 'Name of the add-ons (or add-ons separated by spaces or commas)')
984
1020
  .option('--forced', 'Force the add-on to be added', false)
985
1021
  .action(async (addOns, options) => {
@@ -1032,6 +1068,7 @@ Remove your node_modules directory and package lock file and re-install.`);
1032
1068
  addOnCommand
1033
1069
  .command('init')
1034
1070
  .description('Initialize an add-on from the current project')
1071
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
1035
1072
  .action(async () => {
1036
1073
  try {
1037
1074
  await runWithTelemetry('add-on:init', {}, async () => {
@@ -1046,6 +1083,7 @@ Remove your node_modules directory and package lock file and re-install.`);
1046
1083
  addOnCommand
1047
1084
  .command('compile')
1048
1085
  .description('Update add-on from the current project')
1086
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
1049
1087
  .action(async () => {
1050
1088
  try {
1051
1089
  await runWithTelemetry('add-on:compile', {}, async () => {
@@ -1060,6 +1098,7 @@ Remove your node_modules directory and package lock file and re-install.`);
1060
1098
  addOnCommand
1061
1099
  .command('dev')
1062
1100
  .description('Watch project files and continuously refresh .add-on and add-on.json')
1101
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
1063
1102
  .action(async () => {
1064
1103
  try {
1065
1104
  await runWithTelemetry('add-on:dev', {}, async () => {
@@ -1076,6 +1115,7 @@ Remove your node_modules directory and package lock file and re-install.`);
1076
1115
  templateCommand
1077
1116
  .command('init')
1078
1117
  .description('Initialize a project template from the current project')
1118
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
1079
1119
  .action(async () => {
1080
1120
  try {
1081
1121
  await runWithTelemetry('template:init', {}, async () => {
@@ -1090,6 +1130,7 @@ Remove your node_modules directory and package lock file and re-install.`);
1090
1130
  templateCommand
1091
1131
  .command('compile')
1092
1132
  .description('Compile the template JSON file for the current project')
1133
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
1093
1134
  .action(async () => {
1094
1135
  try {
1095
1136
  await runWithTelemetry('template:compile', {}, async () => {
@@ -1106,6 +1147,7 @@ Remove your node_modules directory and package lock file and re-install.`);
1106
1147
  starterCommand
1107
1148
  .command('init')
1108
1149
  .description('Deprecated alias: initialize a project template')
1150
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
1109
1151
  .action(async () => {
1110
1152
  try {
1111
1153
  await runWithTelemetry('starter:init', {}, async () => {
@@ -1120,6 +1162,7 @@ Remove your node_modules directory and package lock file and re-install.`);
1120
1162
  starterCommand
1121
1163
  .command('compile')
1122
1164
  .description('Deprecated alias: compile the template JSON file')
1165
+ .addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
1123
1166
  .action(async () => {
1124
1167
  try {
1125
1168
  await runWithTelemetry('starter:compile', {}, async () => {
package/dist/options.js CHANGED
@@ -1,11 +1,22 @@
1
1
  import { intro } from '@clack/prompts';
2
- import { finalizeAddOns, getFrameworkById, getPackageManager, loadStarter, populateAddOnOptionsDefaults, readConfigFile, } from '@tanstack/create';
3
- import { getProjectName, promptForAddOnOptions, promptForEnvVars, selectAddOns, selectDeployment, selectExamples, selectGit, selectPackageManager, selectTemplate, selectToolchain, } from './ui-prompts.js';
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
4
  import { listTemplateChoices, resolveStarterSpecifier, } from './command-line.js';
5
5
  import { getCurrentDirectoryName, sanitizePackageName, validateProjectName, } from './utils.js';
6
- export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], showDeploymentOptions = false, }) {
6
+ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], forcedDeployment, showDeploymentOptions = true, defaultFrameworkId, }) {
7
7
  const options = {};
8
- options.framework = getFrameworkById(cliOptions.framework || 'react');
8
+ if (cliOptions.framework) {
9
+ options.framework = getFrameworkById(cliOptions.framework);
10
+ }
11
+ else {
12
+ const availableFrameworks = getFrameworks();
13
+ if (defaultFrameworkId || availableFrameworks.length <= 1) {
14
+ options.framework = getFrameworkById(defaultFrameworkId || 'react');
15
+ }
16
+ else {
17
+ options.framework = await selectFramework(availableFrameworks, defaultFrameworkId);
18
+ }
19
+ }
9
20
  // Validate project name
10
21
  if (cliOptions.projectName) {
11
22
  // Handle "." as project name - use sanitized current directory name
@@ -71,11 +82,19 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], sh
71
82
  // Toolchain selection
72
83
  const toolchain = await selectToolchain(options.framework, cliOptions.toolchain);
73
84
  // Deployment selection
74
- const deployment = showDeploymentOptions
75
- ? routerOnly
76
- ? undefined
77
- : await selectDeployment(options.framework, cliOptions.deployment)
78
- : undefined;
85
+ let deployment;
86
+ if (routerOnly) {
87
+ deployment = undefined;
88
+ }
89
+ else if (cliOptions.deployment) {
90
+ deployment = cliOptions.deployment;
91
+ }
92
+ else if (showDeploymentOptions) {
93
+ deployment = await selectDeployment(options.framework, cliOptions.deployment, forcedDeployment);
94
+ }
95
+ else {
96
+ deployment = forcedDeployment;
97
+ }
79
98
  // Add-ons selection
80
99
  const addOns = new Set();
81
100
  // Examples/demo pages are enabled by default
@@ -139,9 +158,7 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], sh
139
158
  options.envVarValues =
140
159
  envVarValues;
141
160
  options.git = cliOptions.git ?? (await selectGit());
142
- if (cliOptions.install === false) {
143
- options.install = false;
144
- }
161
+ options.install = cliOptions.install ?? (await selectInstall());
145
162
  if (starter) {
146
163
  options.starter = starter;
147
164
  }
package/dist/telemetry.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import { version as nodeVersion } from 'node:process';
2
2
  import { getTelemetryStatus, markTelemetryNoticeSeen, TELEMETRY_NOTICE_VERSION, } from './telemetry-config.js';
3
- const POSTHOG_API_HOST = 'https://us.i.posthog.com';
4
- const POSTHOG_CAPTURE_ENDPOINT = `${POSTHOG_API_HOST}/capture/`;
5
- const POSTHOG_PROJECT_TOKEN = 'phc_xJ2VBahJBzy3BShLhuGpw7EyoSuQtgwXXvhE9BYtHuKQ';
3
+ const TELEMETRY_TRANSPORT_ENDPOINT = 'https://www.google-analytics.com/g/collect';
4
+ const TELEMETRY_PROPERTY_ID = 'G-JMT1Z50SPS';
6
5
  const TELEMETRY_NOTICE = 'TanStack CLI sends anonymous usage telemetry by default. It never sends project names, paths, raw search text, template URLs, add-on config values, or raw error messages. Disable it with `tanstack telemetry disable` or `TANSTACK_CLI_TELEMETRY_DISABLED=1`.';
7
6
  const TELEMETRY_TIMEOUT_MS = 1200;
7
+ const TELEMETRY_VALUE_MAX_LENGTH = 500;
8
+ const TELEMETRY_NUMERIC_PREFIX = 'epn.';
9
+ const TELEMETRY_STRING_PREFIX = 'ep.';
8
10
  let telemetryStatusPromise;
9
11
  function getNodeMajorVersion() {
10
12
  return Number.parseInt(nodeVersion.replace(/^v/, '').split('.')[0] || '0', 10);
@@ -26,6 +28,57 @@ function cleanProperties(value) {
26
28
  }
27
29
  return value;
28
30
  }
31
+ function truncateValue(value) {
32
+ return value.length > TELEMETRY_VALUE_MAX_LENGTH
33
+ ? `${value.slice(0, TELEMETRY_VALUE_MAX_LENGTH - 1)}…`
34
+ : value;
35
+ }
36
+ function normalizeParamKey(key) {
37
+ const normalized = key.replace(/[^a-zA-Z0-9_]/g, '_').replace(/^_+/, '');
38
+ const prefixed = /^[a-zA-Z]/.test(normalized)
39
+ ? normalized
40
+ : `p_${normalized || 'value'}`;
41
+ return prefixed.slice(0, 40);
42
+ }
43
+ function normalizeParamValue(value) {
44
+ if (value === undefined || value === null) {
45
+ return undefined;
46
+ }
47
+ if (typeof value === 'boolean') {
48
+ return value ? 1 : 0;
49
+ }
50
+ if (typeof value === 'number') {
51
+ return Number.isFinite(value) ? value : undefined;
52
+ }
53
+ if (typeof value === 'string') {
54
+ return truncateValue(value);
55
+ }
56
+ const cleaned = cleanProperties(value);
57
+ if (cleaned === undefined) {
58
+ return undefined;
59
+ }
60
+ return truncateValue(JSON.stringify(cleaned));
61
+ }
62
+ function createTelemetryRequestBody(event, distinctId, properties) {
63
+ const params = new URLSearchParams({
64
+ cid: distinctId,
65
+ en: event,
66
+ tid: TELEMETRY_PROPERTY_ID,
67
+ v: '2',
68
+ });
69
+ for (const [key, value] of Object.entries(properties)) {
70
+ const normalizedValue = normalizeParamValue(value);
71
+ if (normalizedValue === undefined) {
72
+ continue;
73
+ }
74
+ const normalizedKey = normalizeParamKey(key);
75
+ const paramName = typeof normalizedValue === 'number'
76
+ ? `${TELEMETRY_NUMERIC_PREFIX}${normalizedKey}`
77
+ : `${TELEMETRY_STRING_PREFIX}${normalizedKey}`;
78
+ params.append(paramName, String(normalizedValue));
79
+ }
80
+ return params.toString();
81
+ }
29
82
  function getErrorCode(error) {
30
83
  if (!error || typeof error !== 'object') {
31
84
  return 'unknown_error';
@@ -63,17 +116,12 @@ async function postEvent(event, distinctId, properties) {
63
116
  controller.abort();
64
117
  }, TELEMETRY_TIMEOUT_MS);
65
118
  try {
66
- await fetch(POSTHOG_CAPTURE_ENDPOINT, {
119
+ await fetch(TELEMETRY_TRANSPORT_ENDPOINT, {
67
120
  method: 'POST',
68
121
  headers: {
69
- 'Content-Type': 'application/json',
122
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
70
123
  },
71
- body: JSON.stringify({
72
- api_key: POSTHOG_PROJECT_TOKEN,
73
- distinct_id: distinctId,
74
- event,
75
- properties,
76
- }),
124
+ body: createTelemetryRequestBody(event, distinctId, properties),
77
125
  signal: controller.signal,
78
126
  });
79
127
  }
@@ -171,7 +219,7 @@ export class TelemetryClient {
171
219
  }
172
220
  baseProperties() {
173
221
  return {
174
- $lib: 'tanstack-cli',
222
+ client_lib: 'tanstack-cli',
175
223
  disabled_by: this.disabledBy,
176
224
  node_major: getNodeMajorVersion(),
177
225
  os_arch: process.arch,
@@ -1,7 +1,9 @@
1
1
  import type { Options } from '@tanstack/create';
2
2
  import type { CliOptions } from './types.js';
3
- export declare function promptForCreateOptions(cliOptions: CliOptions, { forcedAddOns, showDeploymentOptions, }: {
3
+ export declare function promptForCreateOptions(cliOptions: CliOptions, { forcedAddOns, forcedDeployment, showDeploymentOptions, defaultFrameworkId, }: {
4
4
  forcedAddOns?: Array<string>;
5
+ forcedDeployment?: string;
5
6
  showDeploymentOptions?: boolean;
7
+ defaultFrameworkId?: string;
6
8
  }): Promise<Required<Options> | undefined>;
7
9
  export declare function promptForAddOns(): Promise<Array<string>>;
@@ -1,5 +1,7 @@
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 selectFramework(frameworks: Array<Framework>, defaultFrameworkId?: string): Promise<Framework>;
4
+ export declare function selectInstall(): Promise<boolean>;
3
5
  export declare function getProjectName(): Promise<string>;
4
6
  export declare function selectPackageManager(): Promise<PackageManager>;
5
7
  export declare function selectTemplate(templates: Array<{
@@ -13,4 +15,4 @@ export declare function selectExamples(): Promise<boolean>;
13
15
  export declare function selectToolchain(framework: Framework, toolchain?: string | false): Promise<string | undefined>;
14
16
  export declare function promptForAddOnOptions(addOnIds: Array<string>, framework: Framework): Promise<Record<string, Record<string, any>>>;
15
17
  export declare function promptForEnvVars(addOns: Array<AddOn>): Promise<Record<string, string>>;
16
- export declare function selectDeployment(framework: Framework, deployment?: string): Promise<string | undefined>;
18
+ export declare function selectDeployment(framework: Framework, deployment?: string, forcedDeployment?: string): Promise<string | undefined>;
@@ -1,6 +1,36 @@
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 { validateProjectName } from './utils.js';
4
+ export async function selectFramework(frameworks, defaultFrameworkId) {
5
+ const initialValue = (defaultFrameworkId &&
6
+ frameworks.find((f) => f.id.toLowerCase() === defaultFrameworkId.toLowerCase())?.id) ||
7
+ frameworks[0].id;
8
+ const selected = await select({
9
+ message: 'Select framework:',
10
+ options: frameworks.map((f) => ({ value: f.id, label: f.name })),
11
+ initialValue,
12
+ });
13
+ if (isCancel(selected)) {
14
+ cancel('Operation cancelled.');
15
+ process.exit(0);
16
+ }
17
+ const framework = frameworks.find((f) => f.id === selected);
18
+ if (!framework) {
19
+ throw new Error(`Unknown framework: ${selected}`);
20
+ }
21
+ return framework;
22
+ }
23
+ export async function selectInstall() {
24
+ const install = await confirm({
25
+ message: 'Would you like to install dependencies now?',
26
+ initialValue: true,
27
+ });
28
+ if (isCancel(install)) {
29
+ cancel('Operation cancelled.');
30
+ process.exit(0);
31
+ }
32
+ return install;
33
+ }
4
34
  export async function getProjectName() {
5
35
  const value = await text({
6
36
  message: 'What would you like to name your project?',
@@ -249,7 +279,7 @@ export async function promptForEnvVars(addOns) {
249
279
  }
250
280
  return result;
251
281
  }
252
- export async function selectDeployment(framework, deployment) {
282
+ export async function selectDeployment(framework, deployment, forcedDeployment) {
253
283
  const deployments = new Set();
254
284
  let initialValue = undefined;
255
285
  for (const addOn of framework
@@ -260,20 +290,27 @@ export async function selectDeployment(framework, deployment) {
260
290
  if (deployment && addOn.id === deployment) {
261
291
  return deployment;
262
292
  }
263
- if (addOn.default) {
293
+ if (forcedDeployment && addOn.id === forcedDeployment) {
294
+ initialValue = addOn.id;
295
+ }
296
+ else if (!initialValue && addOn.default) {
264
297
  initialValue = addOn.id;
265
298
  }
266
299
  }
267
300
  }
301
+ if (deployments.size === 0) {
302
+ return undefined;
303
+ }
268
304
  const dp = await select({
269
- message: 'Select deployment adapter',
305
+ message: 'Select deployment adapter:',
270
306
  options: [
307
+ { value: undefined, label: 'None' },
271
308
  ...Array.from(deployments).map((d) => ({
272
309
  value: d.id,
273
310
  label: d.name,
274
311
  })),
275
312
  ],
276
- initialValue: initialValue,
313
+ initialValue,
277
314
  });
278
315
  if (isCancel(dp)) {
279
316
  cancel('Operation cancelled.');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/cli",
3
- "version": "0.63.1",
3
+ "version": "0.64.1",
4
4
  "description": "TanStack CLI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -41,7 +41,7 @@
41
41
  "tempy": "^3.1.0",
42
42
  "validate-npm-package-name": "^7.0.0",
43
43
  "zod": "^3.24.2",
44
- "@tanstack/create": "0.63.3"
44
+ "@tanstack/create": "0.63.5"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@playwright/test": "^1.58.2",