Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quick-humans-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/cli': patch
---

Fix blank project name submissions in interactive create prompts.
10 changes: 7 additions & 3 deletions packages/cli/src/ui-prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,16 @@ export async function selectInstall(): Promise<boolean> {
export async function getProjectName(): Promise<string> {
const value = await text({
message: 'Project name (leave empty to use current directory)',
// Clack prints `undefined` on blank submit when placeholder is omitted.
placeholder: '',
Comment on lines +70 to +71

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Does @clack/prompts text() function return undefined when the user submits a blank value and no placeholder is provided in version 0.10.0?

💡 Result:

In version 0.10.0 of @clack/prompts, the text function does not return undefined when a user submits a blank value without a placeholder; instead, it typically returns an empty string ('') [1]. Regarding the behavior of placeholders in version 0.10.0, the library's implementation at that time caused the placeholder value to be automatically used as the input value if the user submitted the prompt without typing anything [2][3][4]. This behavior was a known source of confusion and bugs, as it meant the placeholder was not merely a visual hint but also functioned as an implicit default value [4][5]. This behavior was subsequently addressed in later versions (starting after 0.10.1) by decoupling placeholders from the actual input value, ensuring that placeholders function solely as visual indicators [2][5]. For version 0.10.0, if no placeholder was provided and the user submitted a blank value, the result was an empty string [1].

Citations:


Clarify the rationale for placeholder: '' in @clack/prompts v0.10.0
In @clack/prompts v0.10.0, text() doesn’t return undefined on a blank submit (it resolves to ''); instead, placeholders were treated as an implicit default input value. Using placeholder: '' ensures a blank submission yields '' (so trimming/validation stays consistent). Update the inline comment to remove the “prints undefined” claim.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/ui-prompts.ts` around lines 70 - 71, Update the inline
comment next to the placeholder: '' setting to accurately describe why it's used
with `@clack/prompts` v0.10.0: explain that text() resolves to '' for a blank
submit and that placeholders are treated as an implicit default value, so
setting placeholder: '' ensures a blank submit yields an empty string (not an
implicit placeholder value); remove the incorrect claim that Clack “prints
`undefined`” and mention the behavior is specific to text() in v0.10.0.

validate(value) {
if (isCurrentDirectoryProjectNameInput(value)) {
const projectName = value ?? ''

if (isCurrentDirectoryProjectNameInput(projectName)) {
return
}

const { valid, error } = validateProjectName(value)
const { valid, error } = validateProjectName(projectName)
if (!valid) {
return error
}
Expand All @@ -84,7 +88,7 @@ export async function getProjectName(): Promise<string> {
process.exit(0)
}

return value.trim()
return (value ?? '').trim()
}

export async function selectPackageManager(): Promise<PackageManager> {
Expand Down
17 changes: 16 additions & 1 deletion packages/cli/tests/ui-prompts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,26 @@ describe('getProjectName', () => {
expect(textOptions.message).toBe(
'Project name (leave empty to use current directory)',
)
expect(textOptions.placeholder).toBeUndefined()
expect(textOptions.placeholder).toBe('')
expect(textOptions.validate?.('')).toBeUndefined()
expect(textOptions.validate?.('.')).toBeUndefined()
})

it('should handle undefined project name values as current directory creation', async () => {
const textSpy = vi
.spyOn(clack, 'text')
.mockImplementation(async () => undefined as unknown as string)
vi.spyOn(clack, 'isCancel').mockImplementation(() => false)

const projectName = await getProjectName()
const textOptions = textSpy.mock.calls[0]![0] as {
validate?: (value?: string) => string | undefined
}

expect(projectName).toBe('')
expect(textOptions.validate?.(undefined)).toBeUndefined()
})

it('should exit on cancel', async () => {
vi.spyOn(clack, 'text').mockImplementation(async () => 'Cancelled')
vi.spyOn(clack, 'isCancel').mockImplementation(() => true)
Expand Down
Loading