Skip to content

Commit a54e5e0

Browse files
clydinfilipesilva
authored andcommitted
fix(@angular-devkit/core): support Node.js v16 with NodeJsSyncHost/NodeJsAsyncHost delete operation
The `NodeJsSyncHost`/`NodeJsAsyncHost` classes' `delete` method uses `fs.rmdirSync`/`fs.rmdir` to delete directories. However, Node.js v16's `fs.rmdirSync`/`fs.rmdir` will now throw an `ENOENT` error if the path does not exist. `fs.rmSync`/`fs.rm` is now the preferred option when using Node.js v16 but since this function is not available on Node.js v12 both are tried with `fs.rmSync`/`fs.rm` given preference. Once Node.js 12 support is dropped, the `delete` method could potentially be refactored to avoid the `isDirectory` check and only use `fs.rmSync`/`fs.rm` which supports both files and directories.
1 parent f0e9b31 commit a54e5e0

File tree

1 file changed

+36
-16
lines changed
  • packages/angular_devkit/core/node

1 file changed

+36
-16
lines changed

packages/angular_devkit/core/node/host.ts

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {
9+
import fs, {
1010
PathLike,
1111
Stats,
1212
constants,
@@ -16,7 +16,6 @@ import {
1616
readFileSync,
1717
readdirSync,
1818
renameSync,
19-
rmdirSync,
2019
statSync,
2120
unlinkSync,
2221
writeFileSync,
@@ -103,21 +102,24 @@ export class NodeJsAsyncHost implements virtualFs.Host<Stats> {
103102
return this.isDirectory(path).pipe(
104103
mergeMap(async (isDirectory) => {
105104
if (isDirectory) {
106-
const recursiveDelete = async (dirPath: string) => {
107-
for (const fragment of await fsPromises.readdir(dirPath)) {
108-
const sysPath = pathJoin(dirPath, fragment);
109-
const stats = await fsPromises.stat(sysPath);
110-
111-
if (stats.isDirectory()) {
112-
await recursiveDelete(sysPath);
113-
await fsPromises.rmdir(sysPath);
114-
} else {
115-
await fsPromises.unlink(sysPath);
116-
}
117-
}
105+
// The below should be removed and replaced with just `rm` when support for Node.Js 12 is removed.
106+
const { rm, rmdir } = fsPromises as typeof fsPromises & {
107+
rm?: (
108+
path: fs.PathLike,
109+
options?: {
110+
force?: boolean;
111+
maxRetries?: number;
112+
recursive?: boolean;
113+
retryDelay?: number;
114+
},
115+
) => Promise<void>;
118116
};
119117

120-
await recursiveDelete(getSystemPath(path));
118+
if (rm) {
119+
await rm(getSystemPath(path), { force: true, recursive: true, maxRetries: 3 });
120+
} else {
121+
await rmdir(getSystemPath(path), { recursive: true, maxRetries: 3 });
122+
}
121123
} else {
122124
await fsPromises.unlink(getSystemPath(path));
123125
}
@@ -221,7 +223,25 @@ export class NodeJsSyncHost implements virtualFs.Host<Stats> {
221223
if (isDir) {
222224
const dirPaths = readdirSync(getSystemPath(path));
223225
const rmDirComplete = new Observable<void>((obs) => {
224-
rmdirSync(getSystemPath(path));
226+
// The below should be removed and replaced with just `rmSync` when support for Node.Js 12 is removed.
227+
const { rmSync, rmdirSync } = fs as typeof fs & {
228+
rmSync?: (
229+
path: fs.PathLike,
230+
options?: {
231+
force?: boolean;
232+
maxRetries?: number;
233+
recursive?: boolean;
234+
retryDelay?: number;
235+
},
236+
) => void;
237+
};
238+
239+
if (rmSync) {
240+
rmSync(getSystemPath(path), { force: true, recursive: true, maxRetries: 3 });
241+
} else {
242+
rmdirSync(getSystemPath(path), { recursive: true, maxRetries: 3 });
243+
}
244+
225245
obs.complete();
226246
});
227247

0 commit comments

Comments
 (0)