@@ -10,6 +10,7 @@ import { defineCommand, runMain } from "citty";
1010import { consola } from "consola" ;
1111import { colors } from "consola/utils" ;
1212import { downloadTemplate } from "giget" ;
13+ import { hasTTY , isAgent } from "std-env" ;
1314import { installDependencies , packageManagers , runScriptCommand } from "nypm" ;
1415
1516// Based on: https://github.com/unjs/giget/blob/main/src/cli.ts
@@ -109,8 +110,22 @@ const mainCommand = defineCommand({
109110 type : "boolean" ,
110111 description : "Show verbose debugging info" ,
111112 } ,
113+ help : {
114+ type : "boolean" ,
115+ alias : "h" ,
116+ description : "Show usage information" ,
117+ } ,
112118 } ,
113119 run : async ( { args } ) => {
120+ // Show usage and exit
121+ const hasArgs = args . dir || args . template ;
122+ if ( args . help || ( isAgent && ! hasArgs ) ) {
123+ console . log ( getUsage ( ) ) ;
124+ process . exit ( args . help ? 0 : 1 ) ;
125+ }
126+
127+ const interactive = hasTTY && ! isAgent ;
128+
114129 process . stderr . write ( BANNER ) ;
115130
116131 if ( args . verbose ) {
@@ -119,14 +134,18 @@ const mainCommand = defineCommand({
119134
120135 // Prompt the user where to create the Nitro app
121136 if ( ! args . dir ) {
122- ( args as any ) /* readonly */ . dir = await consola
123- . prompt ( `Where would you like to create your ${ NAME } app?` , {
124- placeholder : `./${ DEFAULT_DIR } ` ,
125- type : "text" ,
126- default : DEFAULT_DIR ,
127- cancel : "reject" ,
128- } )
129- . catch ( ( ) => process . exit ( 1 ) ) ;
137+ if ( ! interactive ) {
138+ ( args as any ) . dir = DEFAULT_DIR ;
139+ } else {
140+ ( args as any ) /* readonly */ . dir = await consola
141+ . prompt ( `Where would you like to create your ${ NAME } app?` , {
142+ placeholder : `./${ DEFAULT_DIR } ` ,
143+ type : "text" ,
144+ default : DEFAULT_DIR ,
145+ cancel : "reject" ,
146+ } )
147+ . catch ( ( ) => process . exit ( 1 ) ) ;
148+ }
130149 }
131150
132151 const cwd = resolve ( args . cwd ) ;
@@ -138,6 +157,12 @@ const mainCommand = defineCommand({
138157 // Prompt the user if the template download directory already exists
139158 // when no `--force` flag is provided
140159 let shouldForce = Boolean ( args . force ) ;
160+ if ( existsSync ( templateDownloadPath ) && ! shouldForce && ! interactive ) {
161+ consola . error (
162+ `Directory ${ colors . cyan ( relative ( process . cwd ( ) , templateDownloadPath ) ) } already exists. Use --force to override.` ,
163+ ) ;
164+ process . exit ( 1 ) ;
165+ }
141166 while ( existsSync ( templateDownloadPath ) && ! shouldForce ) {
142167 const selectedAction = await consola . prompt (
143168 `The directory ${ colors . cyan ( relative ( process . cwd ( ) , templateDownloadPath ) ) } already exists. What would you like to do?` ,
@@ -176,16 +201,20 @@ const mainCommand = defineCommand({
176201
177202 // Prompt the user which template to use
178203 if ( ! args . template ) {
179- ( args as any ) /* readonly */ . template = await consola
180- . prompt ( `What template would you like to use?` , {
181- type : "select" ,
182- options : TEMPLATES . map ( ( t ) => ( {
183- value : t . name ,
184- label : t . description ,
185- } ) ) ,
186- cancel : "reject" ,
187- } )
188- . catch ( ( ) => process . exit ( 1 ) ) ;
204+ if ( ! interactive ) {
205+ ( args as any ) . template = TEMPLATES [ 0 ] ! . name ;
206+ } else {
207+ ( args as any ) /* readonly */ . template = await consola
208+ . prompt ( `What template would you like to use?` , {
209+ type : "select" ,
210+ options : TEMPLATES . map ( ( t ) => ( {
211+ value : t . name ,
212+ label : t . description ,
213+ } ) ) ,
214+ cancel : "reject" ,
215+ } )
216+ . catch ( ( ) => process . exit ( 1 ) ) ;
217+ }
189218 }
190219
191220 // Download the template
@@ -210,26 +239,28 @@ const mainCommand = defineCommand({
210239 const packageManagerArg = args . packageManager as PackageManagerName ;
211240 const selectedPackageManager = pmNames . includes ( packageManagerArg )
212241 ? packageManagerArg
213- : await consola . prompt ( "Which package manager would you like to use?" , {
214- type : "select" ,
215- initial : currentPackageManager ,
216- cancel : "undefined" ,
217- options : [
218- {
219- label : "(none)" ,
220- value : "" as PackageManagerName ,
221- hint : "Skip install dependencies step" ,
222- } ,
223- ...pmNames . map (
224- ( pm ) =>
225- ( {
226- label : pm ,
227- value : pm ,
228- hint : currentPackageManager === pm ? "current" : undefined ,
229- } ) satisfies SelectPromptOptions [ "options" ] [ number ] ,
230- ) ,
231- ] ,
232- } ) ;
242+ : ! interactive
243+ ? ( currentPackageManager || "npm" )
244+ : await consola . prompt ( "Which package manager would you like to use?" , {
245+ type : "select" ,
246+ initial : currentPackageManager ,
247+ cancel : "undefined" ,
248+ options : [
249+ {
250+ label : "(none)" ,
251+ value : "" as PackageManagerName ,
252+ hint : "Skip install dependencies step" ,
253+ } ,
254+ ...pmNames . map (
255+ ( pm ) =>
256+ ( {
257+ label : pm ,
258+ value : pm ,
259+ hint : currentPackageManager === pm ? "current" : undefined ,
260+ } ) satisfies SelectPromptOptions [ "options" ] [ number ] ,
261+ ) ,
262+ ] ,
263+ } ) ;
233264
234265 // Install project dependencies
235266 // or skip installation based on the '--no-install' flag
@@ -258,12 +289,16 @@ const mainCommand = defineCommand({
258289 }
259290
260291 if ( args . gitInit === undefined ) {
261- ( args as any ) /* readonly */ . gitInit = await consola
262- . prompt ( "Initialize git repository?" , {
263- type : "confirm" ,
264- cancel : "undefined" ,
265- } )
266- . then ( Boolean ) ;
292+ if ( ! interactive ) {
293+ ( args as any ) . gitInit = false ;
294+ } else {
295+ ( args as any ) /* readonly */ . gitInit = await consola
296+ . prompt ( "Initialize git repository?" , {
297+ type : "confirm" ,
298+ cancel : "undefined" ,
299+ } )
300+ . then ( Boolean ) ;
301+ }
267302 }
268303 if ( args . gitInit ) {
269304 try {
@@ -297,6 +332,26 @@ runMain(mainCommand);
297332
298333// ---- Internal utils ----
299334
335+ function getUsage ( ) {
336+ const bin = globalThis . __pkg_name__ || "create-nitro-app" ;
337+ const templates = TEMPLATES . map ( ( t ) => t . name ) . join ( ", " ) ;
338+ return `Usage: ${ bin } <dir> [options]
339+
340+ Options:
341+ --template, -t <name> Template name (${ templates } )
342+ --packageManager, -p <name> Package manager (${ pmNames . join ( ", " ) } )
343+ --force Overwrite existing directory
344+ --forceClean Remove existing directory before cloning
345+ --no-install Skip dependency installation
346+ --gitInit Initialize git repository
347+ --offline Do not attempt to download, use cache
348+ --preferOffline Use cache if exists, otherwise download
349+ --help, -h Show this help message
350+
351+ Example:
352+ ${ bin } my-app --template vite --packageManager npm --gitInit` ;
353+ }
354+
300355function detectCurrentPackageManager ( ) {
301356 const userAgent = process . env . npm_config_user_agent ;
302357 if ( ! userAgent ) {
0 commit comments