UNPKG

3.79 kBJavaScriptView Raw
1"use strict";
2
3const pathUtil = require("path");
4const fs = require("./utils/fs");
5const validate = require("./utils/validate");
6const list = require("./list");
7
8const validateInput = (methodName, path) => {
9 const methodSignature = `${methodName}([path])`;
10 validate.argument(methodSignature, "path", path, ["string", "undefined"]);
11};
12
13// ---------------------------------------------------------
14// Sync
15// ---------------------------------------------------------
16
17const removeSync = path => {
18 try {
19 // Assume the path is a file and just try to remove it.
20 fs.unlinkSync(path);
21 } catch (err) {
22 if (
23 err.code === "EPERM" ||
24 err.code === "EISDIR" ||
25 err.code === "ENOTEMPTY"
26 ) {
27 // Must delete everything inside the directory first.
28 list.sync(path).forEach(filename => {
29 removeSync(pathUtil.join(path, filename));
30 });
31 // Everything inside directory has been removed,
32 // it's safe now do go for the directory itself.
33 fs.rmdirSync(path);
34 } else if (err.code === "ENOENT") {
35 // File already doesn't exist. We're done.
36 } else {
37 // Something unexpected happened. Rethrow original error.
38 throw err;
39 }
40 }
41};
42
43// ---------------------------------------------------------
44// Async
45// ---------------------------------------------------------
46
47const removeAsyncInternal = (path, retryCount) => {
48 return new Promise((resolve, reject) => {
49 const retryInAWhileOrFail = err => {
50 if (retryCount === 3) {
51 // Too many retries already. Fail.
52 reject(err);
53 } else {
54 // Try the same action after some pause.
55 setTimeout(() => {
56 removeAsyncInternal(path, retryCount + 1).then(resolve, reject);
57 }, 100);
58 }
59 };
60
61 const removeEverythingInsideDirectory = () => {
62 return list.async(path).then(filenamesInsideDir => {
63 const promises = filenamesInsideDir.map(filename => {
64 return removeAsyncInternal(pathUtil.join(path, filename), 0);
65 });
66 return Promise.all(promises);
67 });
68 };
69
70 // Assume the path is a file and just try to remove it.
71 fs.unlink(path)
72 .then(resolve)
73 .catch(err => {
74 if (err.code === "EBUSY") {
75 retryInAWhileOrFail(err);
76 } else if (
77 err.code === "EPERM" ||
78 err.code === "EISDIR" ||
79 err.code === "ENOTEMPTY"
80 ) {
81 // File deletion attempt failed. Probably it's not a file, it's a directory.
82 // So try to proceed with that assumption.
83 removeEverythingInsideDirectory()
84 .then(() => {
85 // Now go for the directory.
86 return fs.rmdir(path);
87 })
88 .then(resolve)
89 .catch(err2 => {
90 if (
91 err2.code === "EBUSY" ||
92 err2.code === "EPERM" ||
93 err2.code === "ENOTEMPTY"
94 ) {
95 // Failed again. This might be due to other processes reading
96 // something inside the directory. Let's take a nap and retry.
97 retryInAWhileOrFail(err2);
98 } else {
99 reject(err2);
100 }
101 });
102 } else if (err.code === "ENOENT") {
103 // File already doesn't exist. We're done.
104 resolve();
105 } else {
106 // Something unexpected happened. Rethrow original error.
107 reject(err);
108 }
109 });
110 });
111};
112
113const removeAsync = path => {
114 return removeAsyncInternal(path, 0);
115};
116
117// ---------------------------------------------------------
118// API
119// ---------------------------------------------------------
120
121exports.validateInput = validateInput;
122exports.sync = removeSync;
123exports.async = removeAsync;