@tanstack/cta-cli 0.46.2 → 0.48.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 +27 -0
- package/dist/cli.js +67 -7
- package/dist/command-line.js +57 -1
- package/dist/dev-watch.js +290 -0
- package/dist/file-syncer.js +148 -0
- package/dist/options.js +39 -10
- package/dist/types/cli.d.ts +3 -1
- package/dist/types/command-line.d.ts +4 -0
- package/dist/types/dev-watch.d.ts +27 -0
- package/dist/types/file-syncer.d.ts +18 -0
- package/dist/types/types.d.ts +4 -1
- package/dist/types/ui-prompts.d.ts +1 -1
- package/dist/ui-prompts.js +3 -0
- package/package.json +8 -3
- package/src/cli.ts +104 -17
- package/src/command-line.ts +69 -1
- package/src/dev-watch.ts +430 -0
- package/src/file-syncer.ts +205 -0
- package/src/options.ts +45 -10
- package/src/types.ts +4 -1
- package/src/ui-prompts.ts +5 -2
- package/tests/command-line.test.ts +6 -2
- package/tests/options.test.ts +5 -0
package/src/options.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import { cancel, confirm, intro, isCancel } from '@clack/prompts'
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
finalizeAddOns,
|
|
@@ -12,8 +13,8 @@ import {
|
|
|
12
13
|
getProjectName,
|
|
13
14
|
promptForAddOnOptions,
|
|
14
15
|
selectAddOns,
|
|
15
|
-
selectGit,
|
|
16
16
|
selectDeployment,
|
|
17
|
+
selectGit,
|
|
17
18
|
selectPackageManager,
|
|
18
19
|
selectRouterType,
|
|
19
20
|
selectTailwind,
|
|
@@ -46,6 +47,7 @@ export async function promptForCreateOptions(
|
|
|
46
47
|
|
|
47
48
|
options.framework = getFrameworkById(cliOptions.framework || 'react-cra')!
|
|
48
49
|
|
|
50
|
+
// Validate project name
|
|
49
51
|
if (cliOptions.projectName) {
|
|
50
52
|
// Handle "." as project name - use sanitized current directory name
|
|
51
53
|
if (cliOptions.projectName === '.') {
|
|
@@ -62,6 +64,23 @@ export async function promptForCreateOptions(
|
|
|
62
64
|
options.projectName = await getProjectName()
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
// Check if target directory is empty
|
|
68
|
+
if (
|
|
69
|
+
!cliOptions.force &&
|
|
70
|
+
fs.existsSync(options.projectName) &&
|
|
71
|
+
fs.readdirSync(options.projectName).length > 0
|
|
72
|
+
) {
|
|
73
|
+
const shouldContinue = await confirm({
|
|
74
|
+
message: `Target directory ${options.projectName} is not empty. Do you want to continue?`,
|
|
75
|
+
initialValue: true,
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
if (isCancel(shouldContinue) || !shouldContinue) {
|
|
79
|
+
cancel('Operation cancelled.')
|
|
80
|
+
process.exit(0)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
65
84
|
// Router type selection
|
|
66
85
|
if (forcedMode) {
|
|
67
86
|
options.mode = forcedMode
|
|
@@ -86,13 +105,6 @@ export async function promptForCreateOptions(
|
|
|
86
105
|
options.typescript = await selectTypescript()
|
|
87
106
|
}
|
|
88
107
|
|
|
89
|
-
// Tailwind selection
|
|
90
|
-
if (!cliOptions.tailwind && options.framework.id === 'react-cra') {
|
|
91
|
-
options.tailwind = await selectTailwind()
|
|
92
|
-
} else {
|
|
93
|
-
options.tailwind = true
|
|
94
|
-
}
|
|
95
|
-
|
|
96
108
|
// Package manager selection
|
|
97
109
|
if (cliOptions.packageManager) {
|
|
98
110
|
options.packageManager = cliOptions.packageManager
|
|
@@ -159,10 +171,29 @@ export async function promptForCreateOptions(
|
|
|
159
171
|
)
|
|
160
172
|
|
|
161
173
|
if (options.chosenAddOns.length) {
|
|
162
|
-
options.tailwind = true
|
|
163
174
|
options.typescript = true
|
|
164
175
|
}
|
|
165
176
|
|
|
177
|
+
// Tailwind selection
|
|
178
|
+
// Only treat add-ons as requiring tailwind if they explicitly have "tailwind": true
|
|
179
|
+
const addOnsRequireTailwind = options.chosenAddOns.some(
|
|
180
|
+
(addOn) => addOn.tailwind === true,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if (addOnsRequireTailwind) {
|
|
184
|
+
// If any add-on explicitly requires tailwind, enable it automatically
|
|
185
|
+
options.tailwind = true
|
|
186
|
+
} else if (cliOptions.tailwind !== undefined) {
|
|
187
|
+
// User explicitly provided a CLI flag, respect it
|
|
188
|
+
options.tailwind = !!cliOptions.tailwind
|
|
189
|
+
} else if (options.framework.id === 'react-cra') {
|
|
190
|
+
// Only show prompt for react-cra when no CLI flag and no add-ons require it
|
|
191
|
+
options.tailwind = await selectTailwind()
|
|
192
|
+
} else {
|
|
193
|
+
// For other frameworks (like solid), default to true
|
|
194
|
+
options.tailwind = true
|
|
195
|
+
}
|
|
196
|
+
|
|
166
197
|
// Prompt for add-on options in interactive mode
|
|
167
198
|
if (Array.isArray(cliOptions.addOns)) {
|
|
168
199
|
// Non-interactive mode: use defaults
|
|
@@ -177,7 +208,11 @@ export async function promptForCreateOptions(
|
|
|
177
208
|
// Merge user options with defaults
|
|
178
209
|
options.addOnOptions = { ...defaultOptions, ...userOptions }
|
|
179
210
|
}
|
|
211
|
+
|
|
180
212
|
options.git = cliOptions.git || (await selectGit())
|
|
213
|
+
if (cliOptions.install === false) {
|
|
214
|
+
options.install = false
|
|
215
|
+
}
|
|
181
216
|
|
|
182
217
|
return options
|
|
183
218
|
}
|
package/src/types.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface CliOptions {
|
|
|
7
7
|
framework?: string
|
|
8
8
|
tailwind?: boolean
|
|
9
9
|
packageManager?: PackageManager
|
|
10
|
-
toolchain?: string
|
|
10
|
+
toolchain?: string | false
|
|
11
11
|
deployment?: string
|
|
12
12
|
projectName?: string
|
|
13
13
|
git?: boolean
|
|
@@ -20,5 +20,8 @@ export interface CliOptions {
|
|
|
20
20
|
targetDir?: string
|
|
21
21
|
interactive?: boolean
|
|
22
22
|
ui?: boolean
|
|
23
|
+
devWatch?: string
|
|
24
|
+
install?: boolean
|
|
23
25
|
addOnConfig?: string
|
|
26
|
+
force?: boolean
|
|
24
27
|
}
|
package/src/ui-prompts.ts
CHANGED
|
@@ -18,7 +18,6 @@ import { validateProjectName } from './utils.js'
|
|
|
18
18
|
import type { AddOn, PackageManager } from '@tanstack/cta-engine'
|
|
19
19
|
|
|
20
20
|
import type { Framework } from '@tanstack/cta-engine/dist/types/types.js'
|
|
21
|
-
import { InitialData } from '../../cta-ui/src/types'
|
|
22
21
|
|
|
23
22
|
export async function getProjectName(): Promise<string> {
|
|
24
23
|
const value = await text({
|
|
@@ -197,8 +196,12 @@ export async function selectGit(): Promise<boolean> {
|
|
|
197
196
|
|
|
198
197
|
export async function selectToolchain(
|
|
199
198
|
framework: Framework,
|
|
200
|
-
toolchain?: string,
|
|
199
|
+
toolchain?: string | false,
|
|
201
200
|
): Promise<string | undefined> {
|
|
201
|
+
if (toolchain === false) {
|
|
202
|
+
return undefined
|
|
203
|
+
}
|
|
204
|
+
|
|
202
205
|
const toolchains = new Set<AddOn>()
|
|
203
206
|
for (const addOn of framework.getAddOns()) {
|
|
204
207
|
if (addOn.type === 'toolchain') {
|
|
@@ -236,7 +236,9 @@ describe('normalizeOptions', () => {
|
|
|
236
236
|
)
|
|
237
237
|
expect(options?.chosenAddOns.map((a) => a.id).includes('foo')).toBe(true)
|
|
238
238
|
expect(options?.chosenAddOns.map((a) => a.id).includes('baz')).toBe(true)
|
|
239
|
-
|
|
239
|
+
// Tailwind is not automatically set to true unless an add-on explicitly requires it
|
|
240
|
+
// Since mock add-ons don't have tailwind: true, tailwind should be false
|
|
241
|
+
expect(options?.tailwind).toBe(false)
|
|
240
242
|
expect(options?.typescript).toBe(true)
|
|
241
243
|
})
|
|
242
244
|
|
|
@@ -263,7 +265,9 @@ describe('normalizeOptions', () => {
|
|
|
263
265
|
toolchain: 'biome',
|
|
264
266
|
})
|
|
265
267
|
expect(options?.chosenAddOns.map((a) => a.id).includes('biome')).toBe(true)
|
|
266
|
-
|
|
268
|
+
// Tailwind is not automatically set to true unless an add-on explicitly requires it
|
|
269
|
+
// Since mock add-ons don't have tailwind: true, tailwind should be false
|
|
270
|
+
expect(options?.tailwind).toBe(false)
|
|
267
271
|
expect(options?.typescript).toBe(true)
|
|
268
272
|
})
|
|
269
273
|
|
package/tests/options.test.ts
CHANGED
|
@@ -257,6 +257,7 @@ describe('promptForCreateOptions', () => {
|
|
|
257
257
|
expect(options?.chosenAddOns.map((a) => a.id).sort()).toEqual([
|
|
258
258
|
'react-query',
|
|
259
259
|
])
|
|
260
|
+
// Tailwind should be prompted (and mock returns true) since no add-on explicitly requires it
|
|
260
261
|
expect(options?.tailwind).toBe(true)
|
|
261
262
|
expect(options?.typescript).toBe(true)
|
|
262
263
|
})
|
|
@@ -273,6 +274,9 @@ describe('promptForCreateOptions', () => {
|
|
|
273
274
|
'biome',
|
|
274
275
|
'react-query',
|
|
275
276
|
])
|
|
277
|
+
// In non-interactive mode with add-ons, tailwind defaults to false unless explicitly required
|
|
278
|
+
// But since we're in interactive mode (addOns is an array but we still prompt), tailwind is prompted
|
|
279
|
+
// The mock returns true, so tailwind should be true
|
|
276
280
|
expect(options?.tailwind).toBe(true)
|
|
277
281
|
expect(options?.typescript).toBe(true)
|
|
278
282
|
})
|
|
@@ -293,6 +297,7 @@ describe('promptForCreateOptions', () => {
|
|
|
293
297
|
'biome',
|
|
294
298
|
'react-query',
|
|
295
299
|
])
|
|
300
|
+
// Tailwind should be prompted (and mock returns true) since no add-on explicitly requires it
|
|
296
301
|
expect(options?.tailwind).toBe(true)
|
|
297
302
|
expect(options?.typescript).toBe(true)
|
|
298
303
|
})
|