fix(cli): validate project name using npm package name rules (#9161)

* fix(cli): #9160 updated   CLI name validation

* chore: minor refactor and error message

---------

Co-authored-by: shadcn <m@shadcn.com>
This commit is contained in:
Md Kawsar Islam Yeasin
2026-01-06 15:28:31 +06:00
committed by GitHub
parent c2fd847d65
commit f2583391ea
4 changed files with 38 additions and 8 deletions

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
validate app name on create

View File

@@ -82,6 +82,7 @@
"@babel/preset-typescript": "^7.27.1",
"@dotenvx/dotenvx": "^1.48.4",
"@modelcontextprotocol/sdk": "^1.17.2",
"@types/validate-npm-package-name": "^4.0.2",
"browserslist": "^4.26.2",
"commander": "^14.0.0",
"cosmiconfig": "^9.0.0",
@@ -105,6 +106,7 @@
"stringify-object": "^5.0.0",
"ts-morph": "^26.0.0",
"tsconfig-paths": "^4.2.0",
"validate-npm-package-name": "^7.0.1",
"zod": "^3.24.1",
"zod-to-json-schema": "^3.24.6"
},

View File

@@ -14,6 +14,7 @@ import { Command } from "commander"
import dedent from "dedent"
import open from "open"
import prompts from "prompts"
import validateProjectName from "validate-npm-package-name"
import { initOptionsSchema, runInit } from "./init"
@@ -88,10 +89,15 @@ export const create = new Command()
message: "What is your project named?",
initial: opts.template ? `${opts.template}-app` : "my-app",
format: (value: string) => value.trim(),
validate: (value: string) =>
value.length > 128
? `Name should be less than 128 characters.`
: true,
validate: (name) => {
const validation = validateProjectName(
path.basename(path.resolve(name))
)
if (validation.validForNewPackages) {
return true
}
return "Invalid project name. Name should be lowercase, URL-friendly, and not start with a period or underscore."
},
})
if (!enteredName) {

25
pnpm-lock.yaml generated
View File

@@ -458,6 +458,9 @@ importers:
'@modelcontextprotocol/sdk':
specifier: ^1.17.2
version: 1.17.2
'@types/validate-npm-package-name':
specifier: ^4.0.2
version: 4.0.2
browserslist:
specifier: ^4.26.2
version: 4.26.2
@@ -527,6 +530,9 @@ importers:
tsconfig-paths:
specifier: ^4.2.0
version: 4.2.0
validate-npm-package-name:
specifier: ^7.0.1
version: 7.0.1
zod:
specifier: ^3.24.1
version: 3.25.76
@@ -3400,6 +3406,9 @@ packages:
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
'@types/validate-npm-package-name@4.0.2':
resolution: {integrity: sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==}
'@types/yauzl@2.10.3':
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
@@ -8061,6 +8070,10 @@ packages:
validate-npm-package-name@3.0.0:
resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==}
validate-npm-package-name@7.0.1:
resolution: {integrity: sha512-BM0Upcemlce8/9+HE+/VpWqn3u3mYh6Om/FEC8yPMnEHwf710fW5Q6fhjT1SQyRlZD1G9CJbgfH+rWgAcIvjlQ==}
engines: {node: ^20.17.0 || >=22.9.0}
vary@1.1.2:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
@@ -11287,6 +11300,8 @@ snapshots:
'@types/unist@3.0.3': {}
'@types/validate-npm-package-name@4.0.2': {}
'@types/yauzl@2.10.3':
dependencies:
'@types/node': 20.19.10
@@ -12789,7 +12804,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.1(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1):
eslint-module-utils@2.12.1(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1):
dependencies:
debug: 3.2.7
optionalDependencies:
@@ -12800,7 +12815,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1)):
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1)):
dependencies:
debug: 3.2.7
optionalDependencies:
@@ -12822,7 +12837,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1)
eslint-module-utils: 2.12.1(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1)
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -12851,7 +12866,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.33.0(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1))
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -16878,6 +16893,8 @@ snapshots:
dependencies:
builtins: 1.0.3
validate-npm-package-name@7.0.1: {}
vary@1.1.2: {}
vaul@1.1.2(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):