Skip to content

Commit e5ef0ac

Browse files
authored
fix(windows): avoid EPERM rename error during platform project creation
EPERM: operation not permitted, rename 'C:\Users\....\DataDisk\projects\kang-cahya\nativescript\windows\nativescript-windows-pokedex\platforms\windows\__PROJECT_NAME__' -> 'C:\Users\....\DataDisk\projects\kang-cahya\nativescript\windows\nativescript-windows-pokedex\platforms\windows\nativescriptwindowspokedex'
1 parent c7f8432 commit e5ef0ac

1 file changed

Lines changed: 51 additions & 12 deletions

File tree

lib/services/windows-project-service.ts

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)