@tanstack/cli 0.64.6 → 0.66.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 +211 -2
- package/dist/command-line.js +1 -0
- package/dist/dev-watch.js +1 -0
- package/dist/types/types.d.ts +1 -0
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { resolve } from 'node:path';
|
|
2
|
+
import { relative, resolve } from 'node:path';
|
|
3
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';
|
|
7
|
-
import { SUPPORTED_PACKAGE_MANAGERS, addToApp, compileAddOn, compileStarter, createApp, devAddOn, getAllAddOns, getFrameworkByName, getFrameworks, initAddOn, initStarter, } from '@tanstack/create';
|
|
7
|
+
import { SUPPORTED_PACKAGE_MANAGERS, addToApp, compileAddOn, compileStarter, createApp, devAddOn, getAllAddOns, getFrameworkByName, getFrameworks, initAddOn, initStarter, isDemoFilePath, } 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';
|
|
@@ -114,6 +114,7 @@ function getCreateTelemetryProperties(projectName, options) {
|
|
|
114
114
|
framework: options.framework ? sanitizeId(options.framework) : undefined,
|
|
115
115
|
git: options.git,
|
|
116
116
|
install: options.install !== false,
|
|
117
|
+
intent: options.intent !== false,
|
|
117
118
|
interactive: !!options.interactive,
|
|
118
119
|
json: !!options.json,
|
|
119
120
|
non_interactive: !!options.nonInteractive || !!options.yes,
|
|
@@ -139,6 +140,7 @@ function getResolvedCreateTelemetryProperties(finalOptions, cliOptions) {
|
|
|
139
140
|
framework: sanitizeId(finalOptions.framework.id),
|
|
140
141
|
git: finalOptions.git,
|
|
141
142
|
install: finalOptions.install !== false,
|
|
143
|
+
intent: finalOptions.intent,
|
|
142
144
|
package_manager: finalOptions.packageManager,
|
|
143
145
|
router_only: !!cliOptions.routerOnly,
|
|
144
146
|
toolchain: toolchain ? sanitizeId(toolchain.id) : undefined,
|
|
@@ -173,6 +175,144 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
|
|
|
173
175
|
process.exit(0);
|
|
174
176
|
}
|
|
175
177
|
}
|
|
178
|
+
async function confirmCreateOptions(finalOptions) {
|
|
179
|
+
const lines = [];
|
|
180
|
+
lines.push(` Project: ${finalOptions.projectName}`);
|
|
181
|
+
lines.push(` Location: ${finalOptions.targetDir}`);
|
|
182
|
+
lines.push(` Framework: ${finalOptions.framework.name}`);
|
|
183
|
+
lines.push(` Mode: ${finalOptions.mode}`);
|
|
184
|
+
lines.push(` Package manager: ${finalOptions.packageManager}`);
|
|
185
|
+
if (finalOptions.starter) {
|
|
186
|
+
lines.push(` Template: ${finalOptions.starter.name}`);
|
|
187
|
+
}
|
|
188
|
+
const auth = [];
|
|
189
|
+
const database = [];
|
|
190
|
+
const orm = [];
|
|
191
|
+
const deploy = [];
|
|
192
|
+
const otherAddOns = [];
|
|
193
|
+
for (const addOn of finalOptions.chosenAddOns) {
|
|
194
|
+
switch (addOn.category) {
|
|
195
|
+
case 'auth':
|
|
196
|
+
auth.push(addOn.name);
|
|
197
|
+
break;
|
|
198
|
+
case 'database':
|
|
199
|
+
database.push(addOn.name);
|
|
200
|
+
break;
|
|
201
|
+
case 'orm':
|
|
202
|
+
orm.push(addOn.name);
|
|
203
|
+
break;
|
|
204
|
+
case 'deploy':
|
|
205
|
+
deploy.push(addOn.name);
|
|
206
|
+
break;
|
|
207
|
+
default:
|
|
208
|
+
otherAddOns.push(addOn.name);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (auth.length +
|
|
212
|
+
database.length +
|
|
213
|
+
orm.length +
|
|
214
|
+
deploy.length +
|
|
215
|
+
otherAddOns.length >
|
|
216
|
+
0) {
|
|
217
|
+
lines.push('');
|
|
218
|
+
}
|
|
219
|
+
if (auth.length > 0) {
|
|
220
|
+
lines.push(` Auth: ${auth.join(', ')}`);
|
|
221
|
+
}
|
|
222
|
+
if (database.length > 0) {
|
|
223
|
+
lines.push(` Database: ${database.join(', ')}`);
|
|
224
|
+
}
|
|
225
|
+
if (orm.length > 0) {
|
|
226
|
+
lines.push(` ORM: ${orm.join(', ')}`);
|
|
227
|
+
}
|
|
228
|
+
if (deploy.length > 0) {
|
|
229
|
+
lines.push(` Deploy: ${deploy.join(', ')}`);
|
|
230
|
+
}
|
|
231
|
+
if (otherAddOns.length > 0) {
|
|
232
|
+
lines.push(` Other add-ons: ${otherAddOns.join(', ')}`);
|
|
233
|
+
}
|
|
234
|
+
lines.push('');
|
|
235
|
+
lines.push(` Initialize git: ${finalOptions.git ? 'yes' : 'no'}`);
|
|
236
|
+
lines.push(` Install deps: ${finalOptions.install === false ? 'no' : 'yes'}`);
|
|
237
|
+
lines.push(` Agent skills: ${finalOptions.intent ? 'yes' : 'no'}`);
|
|
238
|
+
log.info(`About to create:\n\n${lines.join('\n')}`);
|
|
239
|
+
const conflicts = findExclusiveConflicts(finalOptions.chosenAddOns);
|
|
240
|
+
if (conflicts.length > 0) {
|
|
241
|
+
log.warn(`Conflicting selections detected:\n${conflicts
|
|
242
|
+
.map((c) => ` • ${c.category}: ${c.names.join(', ')}`)
|
|
243
|
+
.join('\n')}`);
|
|
244
|
+
}
|
|
245
|
+
const shouldContinue = await confirm({
|
|
246
|
+
message: 'Continue with these settings?',
|
|
247
|
+
initialValue: true,
|
|
248
|
+
});
|
|
249
|
+
if (isCancel(shouldContinue) || !shouldContinue) {
|
|
250
|
+
cancel('Operation cancelled.');
|
|
251
|
+
process.exit(0);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const CLEAN_DEMOS_SKIP_DIRS = new Set([
|
|
255
|
+
'node_modules',
|
|
256
|
+
'.git',
|
|
257
|
+
'dist',
|
|
258
|
+
'.output',
|
|
259
|
+
'.tanstack',
|
|
260
|
+
'.nitro',
|
|
261
|
+
'.wrangler',
|
|
262
|
+
]);
|
|
263
|
+
function findDemoFiles(root) {
|
|
264
|
+
const results = [];
|
|
265
|
+
function walk(dir) {
|
|
266
|
+
let entries;
|
|
267
|
+
try {
|
|
268
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
for (const entry of entries) {
|
|
274
|
+
const full = resolve(dir, entry.name);
|
|
275
|
+
if (entry.isDirectory()) {
|
|
276
|
+
if (CLEAN_DEMOS_SKIP_DIRS.has(entry.name))
|
|
277
|
+
continue;
|
|
278
|
+
walk(full);
|
|
279
|
+
}
|
|
280
|
+
else if (entry.isFile() && isDemoFilePath(full)) {
|
|
281
|
+
results.push(full);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
walk(root);
|
|
286
|
+
return results.sort();
|
|
287
|
+
}
|
|
288
|
+
function pruneEmptyDemoDirs(root) {
|
|
289
|
+
const candidates = ['src/routes/demo', 'src/routes/example'];
|
|
290
|
+
for (const rel of candidates) {
|
|
291
|
+
const dir = resolve(root, rel);
|
|
292
|
+
if (!fs.existsSync(dir))
|
|
293
|
+
continue;
|
|
294
|
+
try {
|
|
295
|
+
if (fs.readdirSync(dir).length === 0) {
|
|
296
|
+
fs.rmdirSync(dir);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
// ignore
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function findExclusiveConflicts(addOns) {
|
|
305
|
+
const buckets = {};
|
|
306
|
+
for (const addOn of addOns) {
|
|
307
|
+
for (const exclusive of addOn.exclusive || []) {
|
|
308
|
+
buckets[exclusive] ?? (buckets[exclusive] = []);
|
|
309
|
+
buckets[exclusive].push(addOn.name);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return Object.entries(buckets)
|
|
313
|
+
.filter(([_, names]) => names.length > 1)
|
|
314
|
+
.map(([category, names]) => ({ category, names }));
|
|
315
|
+
}
|
|
176
316
|
const availableFrameworks = getFrameworks().map((f) => f.name);
|
|
177
317
|
function resolveBuiltInDevWatchPath(frameworkId) {
|
|
178
318
|
const candidates = [
|
|
@@ -487,6 +627,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
|
|
|
487
627
|
if (!wantsInteractiveMode && cliOptions.addOns === true) {
|
|
488
628
|
throw new Error('When running non-interactively, pass explicit add-ons via --add-ons <ids>.');
|
|
489
629
|
}
|
|
630
|
+
let cameFromPrompts = false;
|
|
490
631
|
if (finalOptions) {
|
|
491
632
|
intro(`Creating a new ${appName} app in ${projectName}...`);
|
|
492
633
|
}
|
|
@@ -503,6 +644,7 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
|
|
|
503
644
|
? getFrameworkByName(defaultFramework)?.id
|
|
504
645
|
: undefined,
|
|
505
646
|
});
|
|
647
|
+
cameFromPrompts = true;
|
|
506
648
|
}
|
|
507
649
|
if (!finalOptions) {
|
|
508
650
|
throw new Error('No options were provided');
|
|
@@ -522,6 +664,9 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
|
|
|
522
664
|
else {
|
|
523
665
|
finalOptions.targetDir = resolve(process.cwd(), finalOptions.projectName);
|
|
524
666
|
}
|
|
667
|
+
if (cameFromPrompts) {
|
|
668
|
+
await confirmCreateOptions(finalOptions);
|
|
669
|
+
}
|
|
525
670
|
await confirmTargetDirectorySafety(finalOptions.targetDir, options.force);
|
|
526
671
|
await createApp(environment, finalOptions);
|
|
527
672
|
});
|
|
@@ -598,6 +743,8 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
|
|
|
598
743
|
.option('--json', 'output JSON for automation', false)
|
|
599
744
|
.option('--git', 'create a git repository')
|
|
600
745
|
.option('--no-git', 'do not create a git repository')
|
|
746
|
+
.option('--intent', 'set up TanStack Intent skill mappings for coding agents')
|
|
747
|
+
.option('--no-intent', 'skip TanStack Intent setup')
|
|
601
748
|
.option('--target-dir <path>', 'the target directory for the application root')
|
|
602
749
|
.option('--add-on-config <config>', 'JSON string with add-on configuration options')
|
|
603
750
|
.option('-f, --force', 'force project creation even if the target directory is not empty', false);
|
|
@@ -971,6 +1118,64 @@ Remove your node_modules directory and package lock file and re-install.`);
|
|
|
971
1118
|
process.exit(1);
|
|
972
1119
|
}
|
|
973
1120
|
});
|
|
1121
|
+
// === CLEAN-DEMOS SUBCOMMAND ===
|
|
1122
|
+
program
|
|
1123
|
+
.command('clean-demos')
|
|
1124
|
+
.description('Remove demo/example files from a scaffolded TanStack project')
|
|
1125
|
+
.argument('[target-dir]', 'project directory (default: current directory)', '.')
|
|
1126
|
+
.addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
|
|
1127
|
+
.option('-y, --yes', 'skip confirmation prompt', false)
|
|
1128
|
+
.option('--dry-run', 'list files without deleting', false)
|
|
1129
|
+
.action(async (targetDir, cmdOptions) => {
|
|
1130
|
+
try {
|
|
1131
|
+
await runWithTelemetry('clean-demos', {
|
|
1132
|
+
properties: {
|
|
1133
|
+
yes: cmdOptions.yes,
|
|
1134
|
+
dry_run: cmdOptions.dryRun,
|
|
1135
|
+
},
|
|
1136
|
+
}, async (telemetry) => {
|
|
1137
|
+
const root = resolve(targetDir);
|
|
1138
|
+
if (!fs.existsSync(root)) {
|
|
1139
|
+
throw new Error(`Directory not found: ${root}`);
|
|
1140
|
+
}
|
|
1141
|
+
if (!fs.existsSync(resolve(root, '.cta.json'))) {
|
|
1142
|
+
log.warn(`No .cta.json in ${root} — this may not be a TanStack scaffold. Continuing anyway.`);
|
|
1143
|
+
}
|
|
1144
|
+
const demoFiles = findDemoFiles(root);
|
|
1145
|
+
telemetry.mergeProperties({ result_count: demoFiles.length });
|
|
1146
|
+
if (demoFiles.length === 0) {
|
|
1147
|
+
log.info('No demo or example files found.');
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
log.info(`Found ${demoFiles.length} demo/example file(s):\n${demoFiles
|
|
1151
|
+
.map((f) => ` • ${relative(root, f)}`)
|
|
1152
|
+
.join('\n')}`);
|
|
1153
|
+
if (cmdOptions.dryRun) {
|
|
1154
|
+
log.info('(dry run — nothing deleted)');
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
if (!cmdOptions.yes) {
|
|
1158
|
+
const ok = await confirm({
|
|
1159
|
+
message: 'Delete these files?',
|
|
1160
|
+
initialValue: false,
|
|
1161
|
+
});
|
|
1162
|
+
if (isCancel(ok) || !ok) {
|
|
1163
|
+
cancel('Operation cancelled.');
|
|
1164
|
+
process.exit(0);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
for (const file of demoFiles) {
|
|
1168
|
+
fs.rmSync(file, { force: true });
|
|
1169
|
+
}
|
|
1170
|
+
pruneEmptyDemoDirs(root);
|
|
1171
|
+
log.info(`Deleted ${demoFiles.length} file(s). Run your dev server to regenerate routeTree.gen.ts.`);
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
catch (error) {
|
|
1175
|
+
log.error(formatErrorMessage(error));
|
|
1176
|
+
process.exit(1);
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
974
1179
|
const telemetryCommand = program.command('telemetry');
|
|
975
1180
|
telemetryCommand
|
|
976
1181
|
.command('status')
|
|
@@ -1018,6 +1223,8 @@ Remove your node_modules directory and package lock file and re-install.`);
|
|
|
1018
1223
|
.addOption(new Option(AGENT_FLAG, 'internal: invocation originated from an agent').hideHelp())
|
|
1019
1224
|
.argument('[add-on...]', 'Name of the add-ons (or add-ons separated by spaces or commas)')
|
|
1020
1225
|
.option('--forced', 'Force the add-on to be added', false)
|
|
1226
|
+
.option('--intent', 'set up TanStack Intent skill mappings for coding agents')
|
|
1227
|
+
.option('--no-intent', 'skip TanStack Intent setup')
|
|
1021
1228
|
.action(async (addOns, options) => {
|
|
1022
1229
|
try {
|
|
1023
1230
|
await runWithTelemetry('add', {
|
|
@@ -1044,6 +1251,7 @@ Remove your node_modules directory and package lock file and re-install.`);
|
|
|
1044
1251
|
if (selectedAddOns.length) {
|
|
1045
1252
|
await addToApp(environment, selectedAddOns, resolve(process.cwd()), {
|
|
1046
1253
|
forced: options.forced,
|
|
1254
|
+
intent: options.intent,
|
|
1047
1255
|
});
|
|
1048
1256
|
}
|
|
1049
1257
|
return;
|
|
@@ -1055,6 +1263,7 @@ Remove your node_modules directory and package lock file and re-install.`);
|
|
|
1055
1263
|
});
|
|
1056
1264
|
await addToApp(environment, parsedAddOns, resolve(process.cwd()), {
|
|
1057
1265
|
forced: options.forced,
|
|
1266
|
+
intent: options.intent,
|
|
1058
1267
|
});
|
|
1059
1268
|
});
|
|
1060
1269
|
}
|
package/dist/command-line.js
CHANGED
|
@@ -381,6 +381,7 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
|
|
|
381
381
|
DEFAULT_PACKAGE_MANAGER,
|
|
382
382
|
git: cliOptions.git ?? true,
|
|
383
383
|
install: cliOptions.install,
|
|
384
|
+
intent: cliOptions.intent ?? true,
|
|
384
385
|
chosenAddOns,
|
|
385
386
|
addOnOptions: {
|
|
386
387
|
...populateAddOnOptionsDefaults(chosenAddOns),
|
package/dist/dev-watch.js
CHANGED
package/dist/types/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.66.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.
|
|
47
|
+
"@tanstack/create": "0.65.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@playwright/test": "^1.58.2",
|