@tanstack/cli 0.59.8 → 0.60.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/CHANGELOG.md +18 -0
- package/dist/bin.js +5 -0
- package/dist/cli.js +118 -93
- package/dist/command-line.js +143 -8
- package/dist/dev-watch.js +117 -16
- package/dist/file-syncer.js +30 -1
- package/dist/index.js +15 -1
- package/dist/options.js +5 -2
- package/dist/types/cli.d.ts +1 -2
- package/dist/types/dev-watch.d.ts +6 -0
- package/dist/types/file-syncer.d.ts +8 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/types.d.ts +2 -1
- package/package.json +8 -3
- package/playwright-report/index.html +85 -0
- package/playwright.config.ts +21 -0
- package/src/bin.ts +8 -0
- package/src/cli.ts +150 -119
- package/src/command-line.ts +193 -7
- package/src/dev-watch.ts +163 -29
- package/src/file-syncer.ts +59 -1
- package/src/index.ts +21 -1
- package/src/options.ts +8 -2
- package/src/types.ts +2 -1
- package/test-results/.last-run.json +4 -0
- package/tests/command-line.test.ts +203 -15
- package/tests/options.test.ts +2 -2
- package/tests-e2e/addons-smoke.spec.ts +31 -0
- package/tests-e2e/create-smoke.spec.ts +39 -0
- package/tests-e2e/helpers.ts +526 -0
- package/tests-e2e/matrix-opportunistic.spec.ts +142 -0
- package/tests-e2e/router-only-smoke.spec.ts +68 -0
- package/tests-e2e/solid-smoke.spec.ts +25 -0
- package/tests-e2e/templates-smoke.spec.ts +52 -0
- package/vitest.config.js +1 -0
package/src/dev-watch.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
+
import { spawn } from 'node:child_process'
|
|
3
4
|
|
|
4
5
|
import chokidar from 'chokidar'
|
|
5
6
|
import chalk from 'chalk'
|
|
6
7
|
import { temporaryDirectory } from 'tempy'
|
|
7
8
|
import {
|
|
8
9
|
createApp,
|
|
10
|
+
finalizeAddOns,
|
|
9
11
|
getFrameworkById,
|
|
10
12
|
registerFramework,
|
|
13
|
+
scanAddOnDirectories,
|
|
14
|
+
scanProjectDirectory,
|
|
11
15
|
} from '@tanstack/create'
|
|
12
16
|
import { FileSyncer } from './file-syncer.js'
|
|
13
17
|
import { createUIEnvironment } from './ui-environment.js'
|
|
@@ -25,6 +29,7 @@ export interface DevWatchOptions {
|
|
|
25
29
|
framework: Framework
|
|
26
30
|
cliOptions: Options
|
|
27
31
|
packageManager: string
|
|
32
|
+
runDevCommand?: boolean
|
|
28
33
|
environment: Environment
|
|
29
34
|
frameworkDefinitionInitializers?: Array<() => FrameworkDefinition>
|
|
30
35
|
}
|
|
@@ -82,6 +87,8 @@ export class DevWatchManager {
|
|
|
82
87
|
private tempDir: string | null = null
|
|
83
88
|
private isBuilding = false
|
|
84
89
|
private buildCount = 0
|
|
90
|
+
private appDevProcess: ReturnType<typeof spawn> | null = null
|
|
91
|
+
private lastSyncedSourceFiles: Set<string> | null = null
|
|
85
92
|
|
|
86
93
|
constructor(private options: DevWatchOptions) {
|
|
87
94
|
this.syncer = new FileSyncer()
|
|
@@ -110,6 +117,9 @@ export class DevWatchManager {
|
|
|
110
117
|
console.log(chalk.bold('dev-watch'))
|
|
111
118
|
this.log.tree('', `watching: ${chalk.cyan(this.options.watchPath)}`)
|
|
112
119
|
this.log.tree('', `target: ${chalk.cyan(this.options.targetDir)}`)
|
|
120
|
+
if (this.options.runDevCommand) {
|
|
121
|
+
this.log.tree('', `app dev server: ${chalk.cyan('enabled')}`)
|
|
122
|
+
}
|
|
113
123
|
this.log.tree('', 'ready', true)
|
|
114
124
|
|
|
115
125
|
// Setup signal handlers
|
|
@@ -118,6 +128,10 @@ export class DevWatchManager {
|
|
|
118
128
|
|
|
119
129
|
// Start watching
|
|
120
130
|
this.startWatcher()
|
|
131
|
+
|
|
132
|
+
if (this.options.runDevCommand) {
|
|
133
|
+
this.startAppDevServer()
|
|
134
|
+
}
|
|
121
135
|
}
|
|
122
136
|
|
|
123
137
|
async stop(): Promise<void> {
|
|
@@ -196,33 +210,30 @@ export class DevWatchManager {
|
|
|
196
210
|
this.log.section(`build #${buildId}`)
|
|
197
211
|
const startTime = Date.now()
|
|
198
212
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
'There must be framework initalizers passed to frameworkDefinitionInitializers to use --dev-watch',
|
|
202
|
-
)
|
|
203
|
-
}
|
|
213
|
+
let refreshedFramework: FrameworkDefinition | null | undefined =
|
|
214
|
+
this.createFrameworkDefinitionFromWatchPath()
|
|
204
215
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
(
|
|
208
|
-
|
|
216
|
+
if (!refreshedFramework && this.options.frameworkDefinitionInitializers) {
|
|
217
|
+
const refreshedFrameworks =
|
|
218
|
+
this.options.frameworkDefinitionInitializers.map(
|
|
219
|
+
(frameworkInitalizer) => frameworkInitalizer(),
|
|
220
|
+
)
|
|
209
221
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
222
|
+
refreshedFramework = refreshedFrameworks.find(
|
|
223
|
+
(f) => f.id === this.options.framework.id,
|
|
224
|
+
)
|
|
225
|
+
}
|
|
213
226
|
|
|
214
227
|
if (!refreshedFramework) {
|
|
215
|
-
throw new Error(
|
|
228
|
+
throw new Error(
|
|
229
|
+
'Could not refresh framework from watch path or framework initializers',
|
|
230
|
+
)
|
|
216
231
|
}
|
|
217
232
|
|
|
218
233
|
// Update the chosen addons to use the latest code
|
|
219
234
|
const chosenAddonIds = this.options.cliOptions.chosenAddOns.map(
|
|
220
235
|
(m) => m.id,
|
|
221
236
|
)
|
|
222
|
-
const updatedChosenAddons = refreshedFramework.addOns.filter((f) =>
|
|
223
|
-
chosenAddonIds.includes(f.id),
|
|
224
|
-
)
|
|
225
|
-
|
|
226
237
|
// Create temp directory for this build using tempy
|
|
227
238
|
this.tempDir = temporaryDirectory()
|
|
228
239
|
|
|
@@ -242,22 +253,29 @@ export class DevWatchManager {
|
|
|
242
253
|
)
|
|
243
254
|
}
|
|
244
255
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
256
|
+
const updatedChosenAddons = await finalizeAddOns(
|
|
257
|
+
registeredFramework,
|
|
258
|
+
this.options.cliOptions.mode,
|
|
259
|
+
chosenAddonIds,
|
|
248
260
|
)
|
|
249
261
|
|
|
262
|
+
// Check if package metadata was modified
|
|
263
|
+
const packageMetadataChanged = Array.from(changes).some((filePath) => {
|
|
264
|
+
const normalized = filePath.replace(/\\/g, '/')
|
|
265
|
+
return /(^|\/)package\.json(\.ejs)?$/.test(normalized)
|
|
266
|
+
})
|
|
267
|
+
|
|
250
268
|
const updatedOptions: Options = {
|
|
251
269
|
...this.options.cliOptions,
|
|
252
270
|
chosenAddOns: updatedChosenAddons,
|
|
253
271
|
framework: registeredFramework,
|
|
254
272
|
targetDir: this.tempDir,
|
|
255
273
|
git: false,
|
|
256
|
-
install:
|
|
274
|
+
install: packageMetadataChanged,
|
|
257
275
|
}
|
|
258
276
|
|
|
259
277
|
// Show package installation indicator if needed
|
|
260
|
-
if (
|
|
278
|
+
if (packageMetadataChanged) {
|
|
261
279
|
this.log.tree(' ', `${chalk.yellow('⟳')} installing packages...`)
|
|
262
280
|
}
|
|
263
281
|
|
|
@@ -269,10 +287,11 @@ export class DevWatchManager {
|
|
|
269
287
|
await createApp(silentEnvironment, updatedOptions)
|
|
270
288
|
|
|
271
289
|
// Sync files to target directory
|
|
272
|
-
const syncResult = await this.syncer.sync(
|
|
273
|
-
this.
|
|
274
|
-
this.
|
|
275
|
-
)
|
|
290
|
+
const syncResult = await this.syncer.sync(this.tempDir, this.options.targetDir, {
|
|
291
|
+
deleteRemoved: this.lastSyncedSourceFiles !== null,
|
|
292
|
+
previousSourceFiles: this.lastSyncedSourceFiles ?? undefined,
|
|
293
|
+
})
|
|
294
|
+
this.lastSyncedSourceFiles = new Set(syncResult.sourceFiles)
|
|
276
295
|
|
|
277
296
|
// Clean up temp directory after sync is complete
|
|
278
297
|
try {
|
|
@@ -288,7 +307,7 @@ export class DevWatchManager {
|
|
|
288
307
|
// Build tree-style summary
|
|
289
308
|
this.log.tree(' ', `duration: ${chalk.cyan(elapsed + 'ms')}`)
|
|
290
309
|
|
|
291
|
-
if (
|
|
310
|
+
if (packageMetadataChanged) {
|
|
292
311
|
this.log.tree(' ', `packages: ${chalk.green('✓ installed')}`)
|
|
293
312
|
}
|
|
294
313
|
|
|
@@ -296,19 +315,29 @@ export class DevWatchManager {
|
|
|
296
315
|
const noMoreTreeItems =
|
|
297
316
|
syncResult.updated.length === 0 &&
|
|
298
317
|
syncResult.created.length === 0 &&
|
|
318
|
+
syncResult.deleted.length === 0 &&
|
|
299
319
|
syncResult.errors.length === 0
|
|
300
320
|
|
|
301
321
|
if (syncResult.updated.length > 0) {
|
|
302
322
|
this.log.tree(
|
|
303
323
|
' ',
|
|
304
324
|
`updated: ${chalk.green(syncResult.updated.length + ' file' + (syncResult.updated.length > 1 ? 's' : ''))}`,
|
|
305
|
-
syncResult.created.length === 0 &&
|
|
325
|
+
syncResult.created.length === 0 &&
|
|
326
|
+
syncResult.deleted.length === 0 &&
|
|
327
|
+
syncResult.errors.length === 0,
|
|
306
328
|
)
|
|
307
329
|
}
|
|
308
330
|
if (syncResult.created.length > 0) {
|
|
309
331
|
this.log.tree(
|
|
310
332
|
' ',
|
|
311
333
|
`created: ${chalk.green(syncResult.created.length + ' file' + (syncResult.created.length > 1 ? 's' : ''))}`,
|
|
334
|
+
syncResult.deleted.length === 0 && syncResult.errors.length === 0,
|
|
335
|
+
)
|
|
336
|
+
}
|
|
337
|
+
if (syncResult.deleted.length > 0) {
|
|
338
|
+
this.log.tree(
|
|
339
|
+
' ',
|
|
340
|
+
`deleted: ${chalk.green(syncResult.deleted.length + ' file' + (syncResult.deleted.length > 1 ? 's' : ''))}`,
|
|
312
341
|
syncResult.errors.length === 0,
|
|
313
342
|
)
|
|
314
343
|
}
|
|
@@ -368,11 +397,19 @@ export class DevWatchManager {
|
|
|
368
397
|
// Show created files
|
|
369
398
|
if (syncResult.created.length > 0) {
|
|
370
399
|
syncResult.created.forEach((file, index) => {
|
|
371
|
-
const isLast =
|
|
400
|
+
const isLast =
|
|
401
|
+
index === syncResult.created.length - 1 && syncResult.deleted.length === 0
|
|
372
402
|
this.log.treeItem(' ', `${chalk.green('+')} ${file}`, isLast)
|
|
373
403
|
})
|
|
374
404
|
}
|
|
375
405
|
|
|
406
|
+
if (syncResult.deleted.length > 0) {
|
|
407
|
+
syncResult.deleted.forEach((file, index) => {
|
|
408
|
+
const isLast = index === syncResult.deleted.length - 1
|
|
409
|
+
this.log.treeItem(' ', `${chalk.red('-')} ${file}`, isLast)
|
|
410
|
+
})
|
|
411
|
+
}
|
|
412
|
+
|
|
376
413
|
// Always show errors
|
|
377
414
|
if (syncResult.errors.length > 0) {
|
|
378
415
|
console.log() // Add spacing
|
|
@@ -393,6 +430,46 @@ export class DevWatchManager {
|
|
|
393
430
|
}
|
|
394
431
|
}
|
|
395
432
|
|
|
433
|
+
private createFrameworkDefinitionFromWatchPath(): FrameworkDefinition | null {
|
|
434
|
+
const frameworkRoot = this.options.watchPath
|
|
435
|
+
const projectDirectory = path.join(frameworkRoot, 'project')
|
|
436
|
+
const baseDirectory = path.join(projectDirectory, 'base')
|
|
437
|
+
|
|
438
|
+
if (!fs.existsSync(projectDirectory) || !fs.existsSync(baseDirectory)) {
|
|
439
|
+
return null
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const addOnDirectoryCandidates = [
|
|
443
|
+
path.join(frameworkRoot, 'add-ons'),
|
|
444
|
+
path.join(frameworkRoot, 'toolchains'),
|
|
445
|
+
path.join(frameworkRoot, 'examples'),
|
|
446
|
+
path.join(frameworkRoot, 'hosts'),
|
|
447
|
+
]
|
|
448
|
+
|
|
449
|
+
const addOnDirectories = addOnDirectoryCandidates.filter((dir) =>
|
|
450
|
+
fs.existsSync(dir),
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
const addOns =
|
|
454
|
+
addOnDirectories.length > 0 ? scanAddOnDirectories(addOnDirectories) : []
|
|
455
|
+
const { files, basePackageJSON, optionalPackages } = scanProjectDirectory(
|
|
456
|
+
projectDirectory,
|
|
457
|
+
baseDirectory,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
return {
|
|
461
|
+
id: this.options.framework.id,
|
|
462
|
+
name: this.options.framework.name,
|
|
463
|
+
description: this.options.framework.description,
|
|
464
|
+
version: this.options.framework.version,
|
|
465
|
+
base: files,
|
|
466
|
+
addOns,
|
|
467
|
+
basePackageJSON,
|
|
468
|
+
optionalPackages,
|
|
469
|
+
supportedModes: this.options.framework.supportedModes,
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
396
473
|
private cleanup(): void {
|
|
397
474
|
console.log()
|
|
398
475
|
console.log('Cleaning up...')
|
|
@@ -408,9 +485,66 @@ export class DevWatchManager {
|
|
|
408
485
|
}
|
|
409
486
|
}
|
|
410
487
|
|
|
488
|
+
if (this.appDevProcess && !this.appDevProcess.killed) {
|
|
489
|
+
this.appDevProcess.kill('SIGTERM')
|
|
490
|
+
this.appDevProcess = null
|
|
491
|
+
}
|
|
492
|
+
|
|
411
493
|
process.exit(0)
|
|
412
494
|
}
|
|
413
495
|
|
|
496
|
+
private startAppDevServer(): void {
|
|
497
|
+
if (this.appDevProcess) {
|
|
498
|
+
return
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const { command, args } = this.getDevCommandForPackageManager(
|
|
502
|
+
this.options.packageManager,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
this.log.section('app dev server')
|
|
506
|
+
this.log.tree(' ', `starting: ${chalk.cyan([command, ...args].join(' '))}`)
|
|
507
|
+
|
|
508
|
+
this.appDevProcess = spawn(command, args, {
|
|
509
|
+
cwd: this.options.targetDir,
|
|
510
|
+
stdio: 'inherit',
|
|
511
|
+
shell: process.platform === 'win32',
|
|
512
|
+
env: process.env,
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
this.appDevProcess.on('exit', (code, signal) => {
|
|
516
|
+
if (signal) {
|
|
517
|
+
this.log.warning(`app dev server exited via signal ${signal}`)
|
|
518
|
+
} else if (code && code !== 0) {
|
|
519
|
+
this.log.warning(`app dev server exited with code ${code}`)
|
|
520
|
+
}
|
|
521
|
+
this.appDevProcess = null
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
this.appDevProcess.on('error', (error) => {
|
|
525
|
+
this.log.error(`Failed to start app dev server: ${error.message}`)
|
|
526
|
+
this.appDevProcess = null
|
|
527
|
+
})
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
private getDevCommandForPackageManager(packageManager: string): {
|
|
531
|
+
command: string
|
|
532
|
+
args: Array<string>
|
|
533
|
+
} {
|
|
534
|
+
switch (packageManager) {
|
|
535
|
+
case 'npm':
|
|
536
|
+
return { command: 'npm', args: ['run', 'dev'] }
|
|
537
|
+
case 'yarn':
|
|
538
|
+
return { command: 'yarn', args: ['dev'] }
|
|
539
|
+
case 'bun':
|
|
540
|
+
return { command: 'bun', args: ['run', 'dev'] }
|
|
541
|
+
case 'deno':
|
|
542
|
+
return { command: 'deno', args: ['task', 'dev'] }
|
|
543
|
+
default:
|
|
544
|
+
return { command: 'pnpm', args: ['dev'] }
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
414
548
|
private log = {
|
|
415
549
|
tree: (prefix: string, msg: string, isLast = false) => {
|
|
416
550
|
const connector = isLast ? '└─' : '├─'
|
package/src/file-syncer.ts
CHANGED
|
@@ -12,15 +12,28 @@ export interface SyncResult {
|
|
|
12
12
|
updated: Array<FileUpdate>
|
|
13
13
|
skipped: Array<string>
|
|
14
14
|
created: Array<string>
|
|
15
|
+
deleted: Array<string>
|
|
16
|
+
sourceFiles: Array<string>
|
|
15
17
|
errors: Array<string>
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
export interface SyncOptions {
|
|
21
|
+
deleteRemoved?: boolean
|
|
22
|
+
previousSourceFiles?: Set<string>
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
export class FileSyncer {
|
|
19
|
-
async sync(
|
|
26
|
+
async sync(
|
|
27
|
+
sourceDir: string,
|
|
28
|
+
targetDir: string,
|
|
29
|
+
options?: SyncOptions,
|
|
30
|
+
): Promise<SyncResult> {
|
|
20
31
|
const result: SyncResult = {
|
|
21
32
|
updated: [],
|
|
22
33
|
skipped: [],
|
|
23
34
|
created: [],
|
|
35
|
+
deleted: [],
|
|
36
|
+
sourceFiles: [],
|
|
24
37
|
errors: [],
|
|
25
38
|
}
|
|
26
39
|
|
|
@@ -35,6 +48,16 @@ export class FileSyncer {
|
|
|
35
48
|
// Walk through source directory and sync files
|
|
36
49
|
await this.syncDirectory(sourceDir, targetDir, sourceDir, result)
|
|
37
50
|
|
|
51
|
+
if (options?.deleteRemoved && options.previousSourceFiles) {
|
|
52
|
+
const currentSourceFileSet = new Set(result.sourceFiles)
|
|
53
|
+
await this.deleteRemovedFiles(
|
|
54
|
+
targetDir,
|
|
55
|
+
options.previousSourceFiles,
|
|
56
|
+
currentSourceFileSet,
|
|
57
|
+
result,
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
38
61
|
return result
|
|
39
62
|
}
|
|
40
63
|
|
|
@@ -72,6 +95,8 @@ export class FileSyncer {
|
|
|
72
95
|
continue
|
|
73
96
|
}
|
|
74
97
|
|
|
98
|
+
result.sourceFiles.push(relativePath)
|
|
99
|
+
|
|
75
100
|
try {
|
|
76
101
|
const shouldUpdate = await this.shouldUpdateFile(
|
|
77
102
|
sourcePath,
|
|
@@ -202,4 +227,37 @@ export class FileSyncer {
|
|
|
202
227
|
const ext = path.extname(name).toLowerCase()
|
|
203
228
|
return skipExtensions.includes(ext)
|
|
204
229
|
}
|
|
230
|
+
|
|
231
|
+
private async deleteRemovedFiles(
|
|
232
|
+
targetDir: string,
|
|
233
|
+
previousSourceFiles: Set<string>,
|
|
234
|
+
currentSourceFiles: Set<string>,
|
|
235
|
+
result: SyncResult,
|
|
236
|
+
): Promise<void> {
|
|
237
|
+
for (const relativePath of previousSourceFiles) {
|
|
238
|
+
if (currentSourceFiles.has(relativePath)) {
|
|
239
|
+
continue
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const targetPath = path.join(targetDir, relativePath)
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
if (!fs.existsSync(targetPath)) {
|
|
246
|
+
continue
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const stats = await fs.promises.stat(targetPath)
|
|
250
|
+
if (!stats.isFile()) {
|
|
251
|
+
continue
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
await fs.promises.unlink(targetPath)
|
|
255
|
+
result.deleted.push(relativePath)
|
|
256
|
+
} catch (error) {
|
|
257
|
+
result.errors.push(
|
|
258
|
+
`${relativePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
259
|
+
)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
205
263
|
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
import { pathToFileURL } from 'node:url'
|
|
2
|
+
import {
|
|
3
|
+
createReactFrameworkDefinition,
|
|
4
|
+
createSolidFrameworkDefinition,
|
|
5
|
+
} from '@tanstack/create'
|
|
6
|
+
|
|
7
|
+
import { cli } from './cli.js'
|
|
8
|
+
|
|
9
|
+
export { cli }
|
|
10
|
+
|
|
11
|
+
const entryPath = process.argv[1]
|
|
12
|
+
if (entryPath && import.meta.url === pathToFileURL(entryPath).href) {
|
|
13
|
+
cli({
|
|
14
|
+
name: 'tanstack',
|
|
15
|
+
appName: 'TanStack',
|
|
16
|
+
frameworkDefinitionInitializers: [
|
|
17
|
+
createReactFrameworkDefinition,
|
|
18
|
+
createSolidFrameworkDefinition,
|
|
19
|
+
],
|
|
20
|
+
})
|
|
21
|
+
}
|
package/src/options.ts
CHANGED
|
@@ -41,7 +41,7 @@ export async function promptForCreateOptions(
|
|
|
41
41
|
): Promise<Required<Options> | undefined> {
|
|
42
42
|
const options = {} as Required<Options>
|
|
43
43
|
|
|
44
|
-
options.framework = getFrameworkById(cliOptions.framework || 'react
|
|
44
|
+
options.framework = getFrameworkById(cliOptions.framework || 'react')!
|
|
45
45
|
|
|
46
46
|
// Validate project name
|
|
47
47
|
if (cliOptions.projectName) {
|
|
@@ -63,8 +63,14 @@ export async function promptForCreateOptions(
|
|
|
63
63
|
// Mode is always file-router (TanStack Start)
|
|
64
64
|
options.mode = 'file-router'
|
|
65
65
|
const template = cliOptions.template?.toLowerCase().trim()
|
|
66
|
+
const isLegacyTemplate =
|
|
67
|
+
template &&
|
|
68
|
+
['file-router', 'typescript', 'tsx', 'javascript', 'js', 'jsx'].includes(
|
|
69
|
+
template,
|
|
70
|
+
)
|
|
66
71
|
const routerOnly =
|
|
67
|
-
!!cliOptions.routerOnly ||
|
|
72
|
+
!!cliOptions.routerOnly ||
|
|
73
|
+
(isLegacyTemplate ? template !== 'file-router' : false)
|
|
68
74
|
|
|
69
75
|
// TypeScript is always enabled with file-router
|
|
70
76
|
options.typescript = true
|
package/src/types.ts
CHANGED
|
@@ -13,10 +13,11 @@ export interface CliOptions {
|
|
|
13
13
|
mcp?: boolean
|
|
14
14
|
mcpSse?: boolean
|
|
15
15
|
starter?: string
|
|
16
|
+
templateId?: string
|
|
16
17
|
targetDir?: string
|
|
17
18
|
interactive?: boolean
|
|
18
|
-
ui?: boolean
|
|
19
19
|
devWatch?: string
|
|
20
|
+
runDev?: boolean
|
|
20
21
|
install?: boolean
|
|
21
22
|
addOnConfig?: string
|
|
22
23
|
force?: boolean
|