44import fs from 'node:fs'
55import path from 'node:path'
66import { fileURLToPath } from 'node:url'
7+ import spawn from 'cross-spawn'
78import minimist from 'minimist'
89import prompts from 'prompts'
910import {
1011 blue ,
1112 cyan ,
1213 green ,
14+ lightGreen ,
1315 lightRed ,
1416 magenta ,
1517 red ,
@@ -25,6 +27,7 @@ const cwd = process.cwd()
2527const FRAMEWORKS = [
2628 {
2729 name : 'vanilla' ,
30+ display : 'Vanilla' ,
2831 color : yellow ,
2932 variants : [
3033 {
@@ -41,6 +44,7 @@ const FRAMEWORKS = [
4144 } ,
4245 {
4346 name : 'vue' ,
47+ display : 'Vue' ,
4448 color : green ,
4549 variants : [
4650 {
@@ -52,11 +56,24 @@ const FRAMEWORKS = [
5256 name : 'vue-ts' ,
5357 display : 'TypeScript' ,
5458 color : blue
59+ } ,
60+ {
61+ name : 'custom-create-vue' ,
62+ display : 'Customize with create-vue' ,
63+ color : green ,
64+ customCommand : 'npm create vue@latest TARGET_DIR'
65+ } ,
66+ {
67+ name : 'custom-nuxt' ,
68+ display : 'Nuxt' ,
69+ color : lightGreen ,
70+ customCommand : 'npm exec nuxi init TARGET_DIR'
5571 }
5672 ]
5773 } ,
5874 {
5975 name : 'react' ,
76+ display : 'React' ,
6077 color : cyan ,
6178 variants : [
6279 {
@@ -73,6 +90,7 @@ const FRAMEWORKS = [
7390 } ,
7491 {
7592 name : 'preact' ,
93+ display : 'Preact' ,
7694 color : magenta ,
7795 variants : [
7896 {
@@ -89,6 +107,7 @@ const FRAMEWORKS = [
89107 } ,
90108 {
91109 name : 'lit' ,
110+ display : 'Lit' ,
92111 color : lightRed ,
93112 variants : [
94113 {
@@ -105,6 +124,7 @@ const FRAMEWORKS = [
105124 } ,
106125 {
107126 name : 'svelte' ,
127+ display : 'Svelte' ,
108128 color : red ,
109129 variants : [
110130 {
@@ -116,6 +136,12 @@ const FRAMEWORKS = [
116136 name : 'svelte-ts' ,
117137 display : 'TypeScript' ,
118138 color : blue
139+ } ,
140+ {
141+ name : 'custom-svelte-kit' ,
142+ display : 'SvelteKit' ,
143+ color : red ,
144+ customCommand : 'npm create svelte@latest TARGET_DIR'
119145 }
120146 ]
121147 }
@@ -191,7 +217,7 @@ async function init() {
191217 choices : FRAMEWORKS . map ( ( framework ) => {
192218 const frameworkColor = framework . color
193219 return {
194- title : frameworkColor ( framework . name ) ,
220+ title : frameworkColor ( framework . display || framework . name ) ,
195221 value : framework
196222 }
197223 } )
@@ -206,7 +232,7 @@ async function init() {
206232 framework . variants . map ( ( variant ) => {
207233 const variantColor = variant . color
208234 return {
209- title : variantColor ( variant . name ) ,
235+ title : variantColor ( variant . display || variant . name ) ,
210236 value : variant . name
211237 }
212238 } )
@@ -237,6 +263,46 @@ async function init() {
237263 // determine template
238264 template = variant || framework || template
239265
266+ const pkgInfo = pkgFromUserAgent ( process . env . npm_config_user_agent )
267+ const pkgManager = pkgInfo ? pkgInfo . name : 'npm'
268+ const isYarn1 = pkgManager === 'yarn' && pkgInfo ?. version . startsWith ( '1.' )
269+
270+ if ( template . startsWith ( 'custom-' ) ) {
271+ const getCustomCommand = ( name ) => {
272+ for ( const f of FRAMEWORKS ) {
273+ for ( const v of f . variants || [ ] ) {
274+ if ( v . name === name ) {
275+ return v . customCommand
276+ }
277+ }
278+ }
279+ }
280+ const customCommand = getCustomCommand ( template )
281+ const fullCustomCommand = customCommand
282+ . replace ( 'TARGET_DIR' , targetDir )
283+ . replace ( / ^ n p m c r e a t e / , `${ pkgManager } create` )
284+ // Only Yarn 1.x doesn't support `@version` in the `create` command
285+ . replace ( '@latest' , ( ) => ( isYarn1 ? '' : '@latest' ) )
286+ . replace ( / ^ n p m e x e c / , ( ) => {
287+ // Prefer `pnpm dlx` or `yarn dlx`
288+ if ( pkgManager === 'pnpm' ) {
289+ return 'pnpm dlx'
290+ }
291+ if ( pkgManager === 'yarn' && ! isYarn1 ) {
292+ return 'yarn dlx'
293+ }
294+ // Use `npm exec` in all other cases,
295+ // including Yarn 1.x and other custom npm clients.
296+ return 'npm exec'
297+ } )
298+
299+ const [ command , ...args ] = fullCustomCommand . split ( ' ' )
300+ const { status } = spawn . sync ( command , args , {
301+ stdio : 'inherit'
302+ } )
303+ process . exit ( status ?? 0 )
304+ }
305+
240306 console . log ( `\nScaffolding project in ${ root } ...` )
241307
242308 const templateDir = path . resolve (
@@ -269,9 +335,6 @@ async function init() {
269335
270336 write ( 'package.json' , JSON . stringify ( pkg , null , 2 ) )
271337
272- const pkgInfo = pkgFromUserAgent ( process . env . npm_config_user_agent )
273- const pkgManager = pkgInfo ? pkgInfo . name : 'npm'
274-
275338 console . log ( `\nDone. Now run:\n` )
276339 if ( root !== cwd ) {
277340 console . log ( ` cd ${ path . relative ( cwd , root ) } ` )
0 commit comments