1+ import fs from 'fs-extra'
2+ import path from 'node:path'
13import { spawn } from 'node:child_process'
2- import * as path from 'node:path'
34import { red } from 'kolorist'
4- import * as fs from 'node:fs'
55import prompts from 'prompts'
66
77const cwd = process . cwd ( )
88const argTargetDir = process . argv . slice ( 2 ) . join ( ' ' )
99
10- const defaultTargetDir = 'electron-vite-app '
10+ const defaultTargetDir = 'electron-vite-project '
1111
1212async function init ( ) {
13- let template : prompts . Answers < 'projectName' | 'packageName' | 'repoName' >
13+ let template : prompts . Answers < 'projectName' | 'overwrite' | ' packageName' | 'repoName' >
1414
1515 let targetDir = argTargetDir ?? defaultTargetDir
1616
@@ -28,6 +28,25 @@ async function init() {
2828 targetDir = state ?. value . trim ( ) . replace ( / \/ + $ / g, '' ) ?? defaultTargetDir
2929 } ,
3030 } ,
31+ {
32+ type : ( ) =>
33+ ! fs . existsSync ( targetDir ) || isEmpty ( targetDir ) ? null : 'confirm' ,
34+ name : 'overwrite' ,
35+ message : ( ) =>
36+ ( targetDir === '.'
37+ ? 'Current directory'
38+ : `Target directory "${ targetDir } "` ) +
39+ ` is not empty. Remove existing files and continue?` ,
40+ } ,
41+ {
42+ type : ( _ , { overwrite } : { overwrite ?: boolean } ) => {
43+ if ( overwrite === false ) {
44+ throw new Error ( red ( '✖' ) + ' Operation cancelled' )
45+ }
46+ return null
47+ } ,
48+ name : 'overwriteChecker' ,
49+ } ,
3150 {
3251 type : ( ) => ( isValidPackageName ( getProjectName ( ) ) ? null : 'text' ) ,
3352 name : 'packageName' ,
@@ -66,22 +85,22 @@ async function init() {
6685 return
6786 }
6887
69- const { repoName, packageName } : { repoName : string , packageName : string } = template
88+ // user choice associated with prompts
89+ const { overwrite, repoName, packageName } = template
7090
71- const repo = `https://github.com/electron-vite/ ${ repoName } `
91+ const root = path . join ( cwd , targetDir )
7292
73- try {
74- if ( fs . existsSync ( targetDir ) && fs . statSync ( targetDir ) . isDirectory ( ) ) {
75- console . error ( `🚧 Directory '${ targetDir } ' already exists.` )
76- process . exit ( 1 )
77- }
93+ // https://github.com/vitejs/vite/pull/12390#issuecomment-1465457917
94+ if ( overwrite ) {
95+ emptyDir ( root )
96+ } else if ( ! fs . existsSync ( root ) ) {
97+ fs . mkdirSync ( root , { recursive : true } )
98+ }
7899
79- await gitClone ( { repo, targetDir , packageName } )
100+ const repo = `https://github.com/electron-vite/ ${ repoName } `
80101
81- fs . rmSync ( path . join ( cwd , targetDir , '.git' ) , {
82- recursive : true ,
83- force : true ,
84- } )
102+ try {
103+ await gitClone ( { repoName : repo , targetDir, packageName } )
85104 } catch ( err ) {
86105 if ( err instanceof Error ) {
87106 console . error ( err . message )
@@ -91,40 +110,74 @@ async function init() {
91110}
92111
93112function gitClone ( {
94- repo ,
113+ repoName ,
95114 targetDir,
96115 packageName,
97116 branch,
98117} : {
99- repo : string
118+ repoName : string
100119 targetDir : string
101120 packageName ?: string
102121 branch ?: string ,
103122} ) {
104123 return new Promise ( ( resolve , reject ) => {
105- const _branch = branch ? [ '-b' , branch ] : [ ]
106- packageName = packageName ?? targetDir
107- spawn ( 'git' , [ 'clone' , ..._branch , repo , targetDir , '--depth' , '1' ] , {
108- stdio : 'inherit'
109- } ) . on ( 'close' , ( code , signal ) => {
124+ const cloneTargetDir = `${ targetDir } /.temp`
125+
126+ spawn (
127+ 'git' ,
128+ [
129+ 'clone' , ...( branch ? [ '-b' , branch ] : [ ] ) ,
130+ repoName ,
131+ cloneTargetDir ,
132+ '--depth' ,
133+ '1' ,
134+ ] ,
135+ { stdio : 'inherit' } ,
136+ ) . on ( 'close' , ( code , signal ) => {
110137 if ( code ) {
111138 reject ( code )
112139 return
113140 }
114- // Modify the name of package.json
141+
142+ const fullTargetDir = path . join ( cwd , targetDir )
143+ const fullCloneTargetDir = path . join ( cwd , cloneTargetDir )
144+
145+ // extract files from the clone target dir
146+ fs . copySync ( fullCloneTargetDir , fullTargetDir )
147+ fs . rmSync ( fullCloneTargetDir , { recursive : true , force : true } )
148+
149+ // modify the name of package.json
115150 try {
116- const packageJSON = fs . readFileSync ( path . join ( cwd , targetDir , 'package.json' ) ) . toString ( )
151+ const packageJSON = fs . readFileSync ( path . join ( fullTargetDir , 'package.json' ) ) . toString ( )
117152 const packageInfo = JSON . parse ( packageJSON )
118- packageInfo . name = packageName
119- fs . writeFileSync ( path . join ( cwd , targetDir , 'package.json' ) , JSON . stringify ( packageInfo , null , 2 ) )
153+ packageInfo . name = packageName ?? targetDir
154+ fs . writeFileSync ( path . join ( fullTargetDir , 'package.json' ) , JSON . stringify ( packageInfo , null , 2 ) )
120155 } catch ( e ) {
121156 console . error ( e )
122157 }
158+
123159 resolve ( signal )
124160 } )
125161 } )
126162}
127163
164+ function isEmpty ( path : string ) {
165+ const files = fs . readdirSync ( path )
166+ return files . length === 0 || ( files . length === 1 && files [ 0 ] === '.git' )
167+ }
168+
169+ function emptyDir ( dir : string ) {
170+ if ( ! fs . existsSync ( dir ) ) {
171+ return
172+ }
173+ for ( const file of fs . readdirSync ( dir ) ) {
174+ if ( file === '.git' ) {
175+ continue
176+ }
177+ fs . rmSync ( path . resolve ( dir , file ) , { recursive : true , force : true } )
178+ }
179+ }
180+
128181function isValidPackageName ( projectName : string ) {
129182 return / ^ (?: @ [ a - z \d \- * ~ ] [ a - z \d \- * . _ ~ ] * \/ ) ? [ a - z \d \- ~ ] [ a - z \d \- . _ ~ ] * $ / . test ( projectName )
130183}
0 commit comments