@@ -201,14 +201,20 @@ export class WindowsProjectService
201201 _frameworkVersion : string ,
202202 projectData : IProjectData ,
203203 ) : Promise < void > {
204- this . $fs . ensureDirectoryExists (
205- this . getPlatformData ( projectData ) . projectRoot ,
206- ) ;
207- shell . cp (
208- "-R" ,
209- path . join ( frameworkDir , "*" ) ,
210- this . getPlatformData ( projectData ) . projectRoot ,
211- ) ;
204+ const projectRoot = this . getPlatformData ( projectData ) . projectRoot ;
205+ this . $fs . ensureDirectoryExists ( projectRoot ) ;
206+ const name = projectData . projectName ;
207+ const entries = fs . readdirSync ( frameworkDir , { withFileTypes : true } ) ;
208+ for ( const entry of entries ) {
209+ const srcPath = path . join ( frameworkDir , entry . name ) ;
210+ if ( entry . name === "__PROJECT_NAME__" ) {
211+ const destPath = path . join ( projectRoot , name ) ;
212+ shell . cp ( "-R" , srcPath , destPath ) ;
213+ } else {
214+ const destPath = path . join ( projectRoot , entry . name ) ;
215+ shell . cp ( "-R" , srcPath , destPath ) ;
216+ }
217+ }
212218 }
213219
214220 public async interpolateData ( projectData : IProjectData ) : Promise < void > {
@@ -218,21 +224,54 @@ export class WindowsProjectService
218224 const appId =
219225 projectData . projectIdentifiers ?. [ "windows" ] ?? projectData . projectId ;
220226
221- const templateDir = path . join ( projectRoot , placeholder ) ;
222227 const projectDir = path . join ( projectRoot , name ) ;
223228
224- if ( this . $fs . exists ( templateDir ) ) {
225- this . $fs . rename ( templateDir , projectDir ) ;
229+ const clearReadOnlyRecursive = ( dir : string ) : void => {
230+ try {
231+ const entries = fs . readdirSync ( dir , { withFileTypes : true } ) ;
232+ for ( const entry of entries ) {
233+ const fullPath = path . join ( dir , entry . name ) ;
234+ fs . chmodSync ( fullPath , 0o666 ) ;
235+ if ( entry . isDirectory ( ) ) {
236+ clearReadOnlyRecursive ( fullPath ) ;
237+ }
238+ }
239+ fs . chmodSync ( dir , 0o666 ) ;
240+ } catch ( e ) {
241+ // ignore
242+ }
243+ } ;
244+
245+ if ( process . platform === "win32" && this . $fs . exists ( projectDir ) ) {
246+ clearReadOnlyRecursive ( projectDir ) ;
226247 }
227248
249+ const renameWithRetry = async ( from : string , to : string ) : Promise < void > => {
250+ let retries = 30 ;
251+ while ( retries > 0 ) {
252+ try {
253+ fs . renameSync ( from , to ) ;
254+ return ;
255+ } catch ( err : any ) {
256+ if ( ( err . code === "EPERM" || err . code === "EBUSY" ) && retries > 1 ) {
257+ retries -- ;
258+ console . warn ( `Rename failed, retrying... (${ retries } retries left): ${ err . message } ` ) ;
259+ await new Promise ( ( resolve ) => setTimeout ( resolve , 300 ) ) ;
260+ } else {
261+ throw err ;
262+ }
263+ }
264+ }
265+ } ;
266+
228267 if ( ! this . $fs . exists ( projectDir ) ) {
229268 return ;
230269 }
231270
232271 const csprojPlaceholder = path . join ( projectDir , `${ placeholder } .csproj` ) ;
233272 const csprojDest = path . join ( projectDir , `${ name } .csproj` ) ;
234273 if ( this . $fs . exists ( csprojPlaceholder ) ) {
235- this . $fs . rename ( csprojPlaceholder , csprojDest ) ;
274+ await renameWithRetry ( csprojPlaceholder , csprojDest ) ;
236275 }
237276
238277 this . _replaceInFiles ( projectDir , placeholder , name ) ;
0 commit comments