2.2 KB79 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import fs from 'node:fs';
9import path from 'node:path';
10
11/**
12 * fs.promises.rm() was introduced in Node v14.14.0, so to in order to run in
13 * Node v10, we must provide our own implementation.
14 *
15 * This functions like `rm -rf <file>`.
16 */
17export default async function rmtree(file: string): Promise<void> {
18 let stat;
19 try {
20 stat = await fs.promises.lstat(file);
21 } catch (error) {
22 if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
23 // If the file does not exist, nothing to do!
24 return;
25 } else {
26 throw error;
27 }
28 }
29
30 if (stat.isDirectory()) {
31 const stack = [file];
32 while (stack.length !== 0) {
33 // eslint-disable-next-line no-await-in-loop
34 await rmtreeIterative(stack);
35 }
36 } else {
37 await fs.promises.unlink(file);
38 }
39}
40
41/**
42 * fs.promises.rm() was introduced in Node v14.14.0, so to in order to run in
43 * Node v10, we must provide our own implementation.
44 *
45 * @param stack a list of folders to remove recursively. Folders at the end of
46 * the array will be removed before preceding folders.
47 */
48async function rmtreeIterative(stack: Array<string>): Promise<void> {
49 // This is effectively a "peek" on the stack.
50 const folder = stack[stack.length - 1];
51 if (folder == null) {
52 throw new Error(`invariant violation: empty stack`);
53 }
54
55 // We rely on the caller to ensure `folder` is a path to a directory rather
56 // than stat each argument that was passed in.
57 const files = await fs.promises.readdir(folder);
58
59 const stackLength = stack.length;
60 await Promise.all(
61 files.map(async (file: string) => {
62 const fullPath = path.join(folder, file);
63 const stat = await fs.promises.lstat(fullPath);
64 if (stat.isDirectory()) {
65 stack.push(fullPath);
66 } else {
67 await fs.promises.unlink(fullPath);
68 }
69 }),
70 );
71
72 // If nothing was pushed onto the stack, then we can assume this folder is
73 // now empty and rmdir() will succeed.
74 if (stack.length === stackLength) {
75 await fs.promises.rmdir(folder);
76 stack.pop();
77 }
78}
79