Skip to content

Commit

Permalink
fix(@angular-devkit/core): support Node.js v16 with NodeJsSyncHost/…
Browse files Browse the repository at this point in the history
…`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.
  • Loading branch information
clydin authored and filipesilva committed Oct 6, 2021
1 parent f0e9b31 commit a54e5e0
Showing 1 changed file with 36 additions and 16 deletions.
52 changes: 36 additions & 16 deletions packages/angular_devkit/core/node/host.ts
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {
import fs, {
PathLike,
Stats,
constants,
Expand All @@ -16,7 +16,6 @@ import {
readFileSync,
readdirSync,
renameSync,
rmdirSync,
statSync,
unlinkSync,
writeFileSync,
Expand Down Expand Up @@ -103,21 +102,24 @@ export class NodeJsAsyncHost implements virtualFs.Host<Stats> {
return this.isDirectory(path).pipe(
mergeMap(async (isDirectory) => {
if (isDirectory) {
const recursiveDelete = async (dirPath: string) => {
for (const fragment of await fsPromises.readdir(dirPath)) {
const sysPath = pathJoin(dirPath, fragment);
const stats = await fsPromises.stat(sysPath);

if (stats.isDirectory()) {
await recursiveDelete(sysPath);
await fsPromises.rmdir(sysPath);
} else {
await fsPromises.unlink(sysPath);
}
}
// The below should be removed and replaced with just `rm` when support for Node.Js 12 is removed.
const { rm, rmdir } = fsPromises as typeof fsPromises & {
rm?: (
path: fs.PathLike,
options?: {
force?: boolean;
maxRetries?: number;
recursive?: boolean;
retryDelay?: number;
},
) => Promise<void>;
};

await recursiveDelete(getSystemPath(path));
if (rm) {
await rm(getSystemPath(path), { force: true, recursive: true, maxRetries: 3 });
} else {
await rmdir(getSystemPath(path), { recursive: true, maxRetries: 3 });
}
} else {
await fsPromises.unlink(getSystemPath(path));
}
Expand Down Expand Up @@ -221,7 +223,25 @@ export class NodeJsSyncHost implements virtualFs.Host<Stats> {
if (isDir) {
const dirPaths = readdirSync(getSystemPath(path));
const rmDirComplete = new Observable<void>((obs) => {
rmdirSync(getSystemPath(path));
// The below should be removed and replaced with just `rmSync` when support for Node.Js 12 is removed.
const { rmSync, rmdirSync } = fs as typeof fs & {
rmSync?: (
path: fs.PathLike,
options?: {
force?: boolean;
maxRetries?: number;
recursive?: boolean;
retryDelay?: number;
},
) => void;
};

if (rmSync) {
rmSync(getSystemPath(path), { force: true, recursive: true, maxRetries: 3 });
} else {
rmdirSync(getSystemPath(path), { recursive: true, maxRetries: 3 });
}

obs.complete();
});

Expand Down

0 comments on commit a54e5e0

Please sign in to comment.