@tanstack/cli 0.61.1 → 0.62.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/package.json +11 -5
- package/skills/CHANGELOG.md +18 -0
- package/skills/add-addons-existing-app/SKILL.md +113 -0
- package/skills/choose-ecosystem-integrations/SKILL.md +140 -0
- package/skills/choose-ecosystem-integrations/references/authentication-providers.md +19 -0
- package/skills/choose-ecosystem-integrations/references/data-layer-providers.md +20 -0
- package/skills/choose-ecosystem-integrations/references/deployment-targets.md +19 -0
- package/skills/create-app-scaffold/SKILL.md +132 -0
- package/skills/create-app-scaffold/references/create-flag-compatibility-matrix.md +34 -0
- package/skills/create-app-scaffold/references/deployment-providers.md +19 -0
- package/skills/create-app-scaffold/references/framework-adapters.md +17 -0
- package/skills/create-app-scaffold/references/toolchains.md +17 -0
- package/skills/maintain-custom-addons-dev-watch/SKILL.md +118 -0
- package/skills/query-docs-library-metadata/SKILL.md +85 -0
- package/skills/query-docs-library-metadata/references/discovery-command-output-schemas.md +70 -0
- package/CHANGELOG.md +0 -815
- package/playwright-report/index.html +0 -85
- package/playwright.config.ts +0 -21
- package/src/bin.ts +0 -15
- package/src/cli.ts +0 -1099
- package/src/command-line.ts +0 -612
- package/src/dev-watch.ts +0 -564
- package/src/discovery.ts +0 -209
- package/src/file-syncer.ts +0 -263
- package/src/index.ts +0 -21
- package/src/options.ts +0 -280
- package/src/types.ts +0 -27
- package/src/ui-environment.ts +0 -74
- package/src/ui-prompts.ts +0 -387
- package/src/utils.ts +0 -30
- package/test-results/.last-run.json +0 -4
- package/tests/command-line.test.ts +0 -703
- package/tests/index.test.ts +0 -9
- package/tests/options.test.ts +0 -281
- package/tests/setupVitest.ts +0 -6
- package/tests/ui-environment.test.ts +0 -97
- package/tests/ui-prompts.test.ts +0 -233
- package/tests-e2e/addons-smoke.spec.ts +0 -31
- package/tests-e2e/create-smoke.spec.ts +0 -39
- package/tests-e2e/helpers.ts +0 -526
- package/tests-e2e/matrix-opportunistic.spec.ts +0 -142
- package/tests-e2e/router-only-smoke.spec.ts +0 -54
- package/tests-e2e/solid-smoke.spec.ts +0 -26
- package/tests-e2e/templates-smoke.spec.ts +0 -52
- package/tsconfig.json +0 -17
- package/vitest.config.js +0 -8
package/src/ui-prompts.ts
DELETED
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
cancel,
|
|
3
|
-
confirm,
|
|
4
|
-
isCancel,
|
|
5
|
-
multiselect,
|
|
6
|
-
note,
|
|
7
|
-
password,
|
|
8
|
-
select,
|
|
9
|
-
text,
|
|
10
|
-
} from '@clack/prompts'
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
DEFAULT_PACKAGE_MANAGER,
|
|
14
|
-
SUPPORTED_PACKAGE_MANAGERS,
|
|
15
|
-
getAllAddOns,
|
|
16
|
-
} from '@tanstack/create'
|
|
17
|
-
|
|
18
|
-
import { validateProjectName } from './utils.js'
|
|
19
|
-
import type { AddOn, PackageManager } from '@tanstack/create'
|
|
20
|
-
|
|
21
|
-
import type { Framework } from '@tanstack/create/dist/types/types.js'
|
|
22
|
-
|
|
23
|
-
export async function getProjectName(): Promise<string> {
|
|
24
|
-
const value = await text({
|
|
25
|
-
message: 'What would you like to name your project?',
|
|
26
|
-
defaultValue: 'my-app',
|
|
27
|
-
validate(value) {
|
|
28
|
-
if (!value) {
|
|
29
|
-
return 'Please enter a name'
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const { valid, error } = validateProjectName(value)
|
|
33
|
-
if (!valid) {
|
|
34
|
-
return error
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
if (isCancel(value)) {
|
|
40
|
-
cancel('Operation cancelled.')
|
|
41
|
-
process.exit(0)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return value
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export async function selectPackageManager(): Promise<PackageManager> {
|
|
48
|
-
const packageManager = await select({
|
|
49
|
-
message: 'Select package manager:',
|
|
50
|
-
options: SUPPORTED_PACKAGE_MANAGERS.map((pm) => ({
|
|
51
|
-
value: pm,
|
|
52
|
-
label: pm,
|
|
53
|
-
})),
|
|
54
|
-
initialValue: DEFAULT_PACKAGE_MANAGER,
|
|
55
|
-
})
|
|
56
|
-
if (isCancel(packageManager)) {
|
|
57
|
-
cancel('Operation cancelled.')
|
|
58
|
-
process.exit(0)
|
|
59
|
-
}
|
|
60
|
-
return packageManager
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export async function selectTemplate(
|
|
64
|
-
templates: Array<{ id: string; name: string; description?: string }>,
|
|
65
|
-
): Promise<string | undefined> {
|
|
66
|
-
if (templates.length === 0) {
|
|
67
|
-
return undefined
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const selected = await select({
|
|
71
|
-
message: 'Would you like to start from a template?',
|
|
72
|
-
options: [
|
|
73
|
-
{
|
|
74
|
-
value: undefined,
|
|
75
|
-
label: 'None (base starter)',
|
|
76
|
-
hint: 'Two-page baseline (Home + About)',
|
|
77
|
-
},
|
|
78
|
-
...templates.map((template) => ({
|
|
79
|
-
value: template.id,
|
|
80
|
-
label: template.name,
|
|
81
|
-
hint: template.description,
|
|
82
|
-
})),
|
|
83
|
-
],
|
|
84
|
-
initialValue: undefined,
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
if (isCancel(selected)) {
|
|
88
|
-
cancel('Operation cancelled.')
|
|
89
|
-
process.exit(0)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return selected
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Track if we've shown the multiselect help text
|
|
96
|
-
let hasShownMultiselectHelp = false
|
|
97
|
-
|
|
98
|
-
export async function selectAddOns(
|
|
99
|
-
framework: Framework,
|
|
100
|
-
mode: string,
|
|
101
|
-
type: string,
|
|
102
|
-
message: string,
|
|
103
|
-
forcedAddOns: Array<string> = [],
|
|
104
|
-
allowMultiple: boolean = true,
|
|
105
|
-
): Promise<Array<string>> {
|
|
106
|
-
const allAddOns = await getAllAddOns(framework, mode)
|
|
107
|
-
const addOns = allAddOns.filter((addOn) => addOn.type === type)
|
|
108
|
-
if (addOns.length === 0) {
|
|
109
|
-
return []
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Show help text only once
|
|
113
|
-
if (!hasShownMultiselectHelp) {
|
|
114
|
-
note(
|
|
115
|
-
'Use ↑/↓ to navigate • Space to select/deselect • Enter to confirm',
|
|
116
|
-
'Keyboard Shortcuts',
|
|
117
|
-
)
|
|
118
|
-
hasShownMultiselectHelp = true
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (allowMultiple) {
|
|
122
|
-
const selectableAddOns = addOns.filter(
|
|
123
|
-
(addOn) => !forcedAddOns.includes(addOn.id),
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
if (selectableAddOns.length === 0) {
|
|
127
|
-
return []
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const value = await multiselect({
|
|
131
|
-
message,
|
|
132
|
-
options: selectableAddOns.map((addOn) => ({
|
|
133
|
-
value: addOn.id,
|
|
134
|
-
label: addOn.name,
|
|
135
|
-
hint: addOn.description,
|
|
136
|
-
})),
|
|
137
|
-
maxItems: selectableAddOns.length,
|
|
138
|
-
required: false,
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
if (isCancel(value)) {
|
|
142
|
-
cancel('Operation cancelled.')
|
|
143
|
-
process.exit(0)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return value
|
|
147
|
-
} else {
|
|
148
|
-
const value = await select({
|
|
149
|
-
message,
|
|
150
|
-
options: [
|
|
151
|
-
{
|
|
152
|
-
value: 'none',
|
|
153
|
-
label: 'None',
|
|
154
|
-
},
|
|
155
|
-
...addOns
|
|
156
|
-
.filter((addOn) => !forcedAddOns.includes(addOn.id))
|
|
157
|
-
.map((addOn) => ({
|
|
158
|
-
value: addOn.id,
|
|
159
|
-
label: addOn.name,
|
|
160
|
-
hint: addOn.description,
|
|
161
|
-
})),
|
|
162
|
-
],
|
|
163
|
-
initialValue: 'none',
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
if (isCancel(value)) {
|
|
167
|
-
cancel('Operation cancelled.')
|
|
168
|
-
process.exit(0)
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return value === 'none' ? [] : [value]
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export async function selectGit(): Promise<boolean> {
|
|
176
|
-
const git = await confirm({
|
|
177
|
-
message: 'Would you like to initialize a new git repository?',
|
|
178
|
-
initialValue: true,
|
|
179
|
-
})
|
|
180
|
-
if (isCancel(git)) {
|
|
181
|
-
cancel('Operation cancelled.')
|
|
182
|
-
process.exit(0)
|
|
183
|
-
}
|
|
184
|
-
return git
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export async function selectExamples(): Promise<boolean> {
|
|
188
|
-
const includeExamples = await confirm({
|
|
189
|
-
message: 'Would you like to include demo/example pages?',
|
|
190
|
-
initialValue: true,
|
|
191
|
-
})
|
|
192
|
-
if (isCancel(includeExamples)) {
|
|
193
|
-
cancel('Operation cancelled.')
|
|
194
|
-
process.exit(0)
|
|
195
|
-
}
|
|
196
|
-
return includeExamples
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export async function selectToolchain(
|
|
200
|
-
framework: Framework,
|
|
201
|
-
toolchain?: string | false,
|
|
202
|
-
): Promise<string | undefined> {
|
|
203
|
-
if (toolchain === false) {
|
|
204
|
-
return undefined
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const toolchains = new Set<AddOn>()
|
|
208
|
-
for (const addOn of framework.getAddOns()) {
|
|
209
|
-
if (addOn.type === 'toolchain') {
|
|
210
|
-
toolchains.add(addOn)
|
|
211
|
-
if (toolchain && addOn.id === toolchain) {
|
|
212
|
-
return toolchain
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const tc = await select({
|
|
218
|
-
message: 'Select toolchain',
|
|
219
|
-
options: [
|
|
220
|
-
{
|
|
221
|
-
value: undefined,
|
|
222
|
-
label: 'None',
|
|
223
|
-
},
|
|
224
|
-
...Array.from(toolchains).map((tc) => ({
|
|
225
|
-
value: tc.id,
|
|
226
|
-
label: tc.name,
|
|
227
|
-
})),
|
|
228
|
-
],
|
|
229
|
-
initialValue: undefined,
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
if (isCancel(tc)) {
|
|
233
|
-
cancel('Operation cancelled.')
|
|
234
|
-
process.exit(0)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return tc
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export async function promptForAddOnOptions(
|
|
241
|
-
addOnIds: Array<string>,
|
|
242
|
-
framework: Framework,
|
|
243
|
-
): Promise<Record<string, Record<string, any>>> {
|
|
244
|
-
const addOnOptions: Record<string, Record<string, any>> = {}
|
|
245
|
-
|
|
246
|
-
for (const addOnId of addOnIds) {
|
|
247
|
-
const addOn = framework.getAddOns().find((a) => a.id === addOnId)
|
|
248
|
-
if (!addOn || !addOn.options) continue
|
|
249
|
-
|
|
250
|
-
addOnOptions[addOnId] = {}
|
|
251
|
-
|
|
252
|
-
for (const [optionName, option] of Object.entries(addOn.options)) {
|
|
253
|
-
if (option && typeof option === 'object' && 'type' in option) {
|
|
254
|
-
if (option.type === 'select') {
|
|
255
|
-
const selectOption = option as {
|
|
256
|
-
type: 'select'
|
|
257
|
-
label: string
|
|
258
|
-
description?: string
|
|
259
|
-
default: string
|
|
260
|
-
options: Array<{ value: string; label: string }>
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const value = await select({
|
|
264
|
-
message: `${addOn.name}: ${selectOption.label}`,
|
|
265
|
-
options: selectOption.options.map((opt) => ({
|
|
266
|
-
value: opt.value,
|
|
267
|
-
label: opt.label,
|
|
268
|
-
})),
|
|
269
|
-
initialValue: selectOption.default,
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
if (isCancel(value)) {
|
|
273
|
-
cancel('Operation cancelled.')
|
|
274
|
-
process.exit(0)
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
addOnOptions[addOnId][optionName] = value
|
|
278
|
-
}
|
|
279
|
-
// Future option types can be added here
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return addOnOptions
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export async function promptForEnvVars(
|
|
288
|
-
addOns: Array<AddOn>,
|
|
289
|
-
): Promise<Record<string, string>> {
|
|
290
|
-
const envVars = new Map<
|
|
291
|
-
string,
|
|
292
|
-
{
|
|
293
|
-
name: string
|
|
294
|
-
description?: string
|
|
295
|
-
required?: boolean
|
|
296
|
-
default?: string
|
|
297
|
-
secret?: boolean
|
|
298
|
-
}
|
|
299
|
-
>()
|
|
300
|
-
|
|
301
|
-
for (const addOn of addOns as Array<any>) {
|
|
302
|
-
for (const envVar of addOn.envVars || []) {
|
|
303
|
-
if (!envVars.has(envVar.name)) {
|
|
304
|
-
envVars.set(envVar.name, envVar)
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const result: Record<string, string> = {}
|
|
310
|
-
|
|
311
|
-
for (const envVar of envVars.values()) {
|
|
312
|
-
const label = envVar.description
|
|
313
|
-
? `${envVar.name} (${envVar.description})`
|
|
314
|
-
: envVar.name
|
|
315
|
-
|
|
316
|
-
const value = envVar.secret
|
|
317
|
-
? await password({
|
|
318
|
-
message: `Enter ${label}`,
|
|
319
|
-
validate: envVar.required
|
|
320
|
-
? (v) =>
|
|
321
|
-
v && v.trim().length > 0
|
|
322
|
-
? undefined
|
|
323
|
-
: `${envVar.name} is required`
|
|
324
|
-
: undefined,
|
|
325
|
-
})
|
|
326
|
-
: await text({
|
|
327
|
-
message: `Enter ${label}`,
|
|
328
|
-
defaultValue: envVar.default,
|
|
329
|
-
validate: envVar.required
|
|
330
|
-
? (v) =>
|
|
331
|
-
v && v.trim().length > 0
|
|
332
|
-
? undefined
|
|
333
|
-
: `${envVar.name} is required`
|
|
334
|
-
: undefined,
|
|
335
|
-
})
|
|
336
|
-
|
|
337
|
-
if (isCancel(value)) {
|
|
338
|
-
cancel('Operation cancelled.')
|
|
339
|
-
process.exit(0)
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
if (value && value.trim()) {
|
|
343
|
-
result[envVar.name] = value.trim()
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
return result
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
export async function selectDeployment(
|
|
351
|
-
framework: Framework,
|
|
352
|
-
deployment?: string,
|
|
353
|
-
): Promise<string | undefined> {
|
|
354
|
-
const deployments = new Set<AddOn>()
|
|
355
|
-
let initialValue: string | undefined = undefined
|
|
356
|
-
for (const addOn of framework
|
|
357
|
-
.getAddOns()
|
|
358
|
-
.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
359
|
-
if (addOn.type === 'deployment') {
|
|
360
|
-
deployments.add(addOn)
|
|
361
|
-
if (deployment && addOn.id === deployment) {
|
|
362
|
-
return deployment
|
|
363
|
-
}
|
|
364
|
-
if (addOn.default) {
|
|
365
|
-
initialValue = addOn.id
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const dp = await select({
|
|
371
|
-
message: 'Select deployment adapter',
|
|
372
|
-
options: [
|
|
373
|
-
...Array.from(deployments).map((d) => ({
|
|
374
|
-
value: d.id,
|
|
375
|
-
label: d.name,
|
|
376
|
-
})),
|
|
377
|
-
],
|
|
378
|
-
initialValue: initialValue,
|
|
379
|
-
})
|
|
380
|
-
|
|
381
|
-
if (isCancel(dp)) {
|
|
382
|
-
cancel('Operation cancelled.')
|
|
383
|
-
process.exit(0)
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
return dp
|
|
387
|
-
}
|
package/src/utils.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { basename } from 'node:path'
|
|
2
|
-
import validatePackageName from 'validate-npm-package-name'
|
|
3
|
-
|
|
4
|
-
export function sanitizePackageName(name: string): string {
|
|
5
|
-
return name
|
|
6
|
-
.toLowerCase()
|
|
7
|
-
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
|
8
|
-
.replace(/_/g, '-') // Replace underscores with hyphens
|
|
9
|
-
.replace(/[^a-z0-9-]/g, '') // Remove invalid characters
|
|
10
|
-
.replace(/^[^a-z]+/, '') // Ensure it starts with a letter
|
|
11
|
-
.replace(/-+/g, '-') // Collapse multiple hyphens
|
|
12
|
-
.replace(/-$/, '') // Remove trailing hyphen
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function getCurrentDirectoryName(): string {
|
|
16
|
-
return basename(process.cwd())
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function validateProjectName(name: string) {
|
|
20
|
-
const { validForNewPackages, validForOldPackages, errors, warnings } =
|
|
21
|
-
validatePackageName(name)
|
|
22
|
-
const error = errors?.[0] || warnings?.[0]
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
valid: validForNewPackages && validForOldPackages,
|
|
26
|
-
error:
|
|
27
|
-
error?.replace(/name/g, 'Project name') ||
|
|
28
|
-
'Project name does not meet npm package naming requirements',
|
|
29
|
-
}
|
|
30
|
-
}
|