Skip to content

Commit cf23983

Browse files
egvr2002jherr
andauthored
feat: Add project name validation (#226)
* feat: add project name validation when creating a new project * refactor: remove redundant fallback in validateProjectName * fix: resolve ESLint warnings & improve error message in validateProjectName * fix: pnpm-lock update --------- Co-authored-by: Jack Herrington <jherr@pobox.com>
1 parent e847ad4 commit cf23983

File tree

6 files changed

+59
-1
lines changed

6 files changed

+59
-1
lines changed

packages/cta-cli/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,15 @@
4040
"commander": "^13.1.0",
4141
"express": "^4.21.2",
4242
"semver": "^7.7.2",
43+
"validate-npm-package-name": "^7.0.0",
4344
"zod": "^3.24.2"
4445
},
4546
"devDependencies": {
4647
"@tanstack/config": "^0.16.2",
4748
"@types/express": "^5.0.1",
4849
"@types/node": "^22.13.4",
4950
"@types/semver": "^7.7.0",
51+
"@types/validate-npm-package-name": "^4.0.2",
5052
"@vitest/coverage-v8": "3.1.1",
5153
"eslint": "^9.20.0",
5254
"typescript": "^5.6.3",

packages/cta-cli/src/command-line.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
populateAddOnOptionsDefaults,
1010
} from '@tanstack/cta-engine'
1111

12+
import { validateProjectName } from './utils.js'
1213
import type { Options } from '@tanstack/cta-engine'
1314

1415
import type { CliOptions } from './types.js'
@@ -27,6 +28,14 @@ export async function normalizeOptions(
2728
return undefined
2829
}
2930

31+
if (projectName) {
32+
const { valid, error } = validateProjectName(projectName)
33+
if (!valid) {
34+
console.error(error)
35+
process.exit(1)
36+
}
37+
}
38+
3039
let tailwind = !!cliOptions.tailwind
3140

3241
let mode: string =

packages/cta-cli/src/options.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
selectTypescript,
2222
} from './ui-prompts.js'
2323

24+
import { validateProjectName } from './utils.js'
2425
import type { Options } from '@tanstack/cta-engine'
2526

2627
import type { CliOptions } from './types.js'
@@ -41,7 +42,16 @@ export async function promptForCreateOptions(
4142

4243
options.framework = getFrameworkById(cliOptions.framework || 'react-cra')!
4344

44-
options.projectName = cliOptions.projectName || (await getProjectName())
45+
if (cliOptions.projectName) {
46+
const { valid, error } = validateProjectName(cliOptions.projectName)
47+
if (!valid) {
48+
console.error(error)
49+
process.exit(1)
50+
}
51+
options.projectName = cliOptions.projectName
52+
} else {
53+
options.projectName = await getProjectName()
54+
}
4555

4656
// Router type selection
4757
if (forcedMode) {

packages/cta-cli/src/ui-prompts.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
getAllAddOns,
1515
} from '@tanstack/cta-engine'
1616

17+
import { validateProjectName } from './utils.js'
1718
import type { AddOn, PackageManager } from '@tanstack/cta-engine'
1819

1920
import type { Framework } from '@tanstack/cta-engine/dist/types/types.js'
@@ -27,6 +28,11 @@ export async function getProjectName(): Promise<string> {
2728
if (!value) {
2829
return 'Please enter a name'
2930
}
31+
32+
const { valid, error } = validateProjectName(value)
33+
if (!valid) {
34+
return error
35+
}
3036
},
3137
})
3238

packages/cta-cli/src/utils.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import validatePackageName from 'validate-npm-package-name'
12
import type { TemplateOptions } from './types.js'
23

34
export function convertTemplateToMode(template: TemplateOptions): string {
@@ -6,3 +7,16 @@ export function convertTemplateToMode(template: TemplateOptions): string {
67
}
78
return 'file-router'
89
}
10+
11+
export function validateProjectName(name: string) {
12+
const { validForNewPackages, validForOldPackages, errors, warnings } =
13+
validatePackageName(name)
14+
const error = errors?.[0] || warnings?.[0]
15+
16+
return {
17+
valid: validForNewPackages && validForOldPackages,
18+
error:
19+
error?.replace(/name/g, 'Project name') ||
20+
'Project name does not meet npm package naming requirements',
21+
}
22+
}

pnpm-lock.yaml

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)