bin.mjs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. #!/usr/bin/env node
  2. import { readFile } from 'fs/promises';
  3. import { rimraf } from './index.js';
  4. const pj = fileURLToPath(new URL('../package.json', import.meta.url));
  5. const pjDist = fileURLToPath(new URL('../../package.json', import.meta.url));
  6. const { version } = JSON.parse(await readFile(pjDist, 'utf8').catch(() => readFile(pj, 'utf8')));
  7. const runHelpForUsage = () => console.error('run `rimraf --help` for usage information');
  8. export const help = `rimraf version ${version}
  9. Usage: rimraf <path> [<path> ...]
  10. Deletes all files and folders at "path", recursively.
  11. Options:
  12. -- Treat all subsequent arguments as paths
  13. -h --help Display this usage info
  14. --preserve-root Do not remove '/' recursively (default)
  15. --no-preserve-root Do not treat '/' specially
  16. -G --no-glob Treat arguments as literal paths, not globs (default)
  17. -g --glob Treat arguments as glob patterns
  18. -v --verbose Be verbose when deleting files, showing them as
  19. they are removed. Not compatible with --impl=native
  20. -V --no-verbose Be silent when deleting files, showing nothing as
  21. they are removed (default)
  22. -i --interactive Ask for confirmation before deleting anything
  23. Not compatible with --impl=native
  24. -I --no-interactive Do not ask for confirmation before deleting
  25. --impl=<type> Specify the implementation to use:
  26. rimraf: choose the best option (default)
  27. native: the built-in implementation in Node.js
  28. manual: the platform-specific JS implementation
  29. posix: the Posix JS implementation
  30. windows: the Windows JS implementation (falls back to
  31. move-remove on ENOTEMPTY)
  32. move-remove: a slow reliable Windows fallback
  33. Implementation-specific options:
  34. --tmp=<path> Temp file folder for 'move-remove' implementation
  35. --max-retries=<n> maxRetries for 'native' and 'windows' implementations
  36. --retry-delay=<n> retryDelay for 'native' implementation, default 100
  37. --backoff=<n> Exponential backoff factor for retries (default: 1.2)
  38. `;
  39. import { parse, relative, resolve } from 'path';
  40. const cwd = process.cwd();
  41. import { createInterface } from 'readline';
  42. import { fileURLToPath } from 'url';
  43. const prompt = async (rl, q) => new Promise(res => rl.question(q, res));
  44. const interactiveRimraf = async (impl, paths, opt) => {
  45. const existingFilter = opt.filter || (() => true);
  46. let allRemaining = false;
  47. let noneRemaining = false;
  48. const queue = [];
  49. let processing = false;
  50. const processQueue = async () => {
  51. if (processing)
  52. return;
  53. processing = true;
  54. let next;
  55. while ((next = queue.shift())) {
  56. await next();
  57. }
  58. processing = false;
  59. };
  60. const oneAtATime = (fn) => async (s, e) => {
  61. const p = new Promise(res => {
  62. queue.push(async () => {
  63. const result = await fn(s, e);
  64. res(result);
  65. return result;
  66. });
  67. });
  68. processQueue();
  69. return p;
  70. };
  71. const rl = createInterface({
  72. input: process.stdin,
  73. output: process.stdout,
  74. });
  75. opt.filter = oneAtATime(async (path, ent) => {
  76. if (noneRemaining) {
  77. return false;
  78. }
  79. while (!allRemaining) {
  80. const a = (await prompt(rl, `rm? ${relative(cwd, path)}\n[(Yes)/No/All/Quit] > `)).trim();
  81. if (/^n/i.test(a)) {
  82. return false;
  83. }
  84. else if (/^a/i.test(a)) {
  85. allRemaining = true;
  86. break;
  87. }
  88. else if (/^q/i.test(a)) {
  89. noneRemaining = true;
  90. return false;
  91. }
  92. else if (a === '' || /^y/i.test(a)) {
  93. break;
  94. }
  95. else {
  96. continue;
  97. }
  98. }
  99. return existingFilter(path, ent);
  100. });
  101. await impl(paths, opt);
  102. rl.close();
  103. };
  104. const main = async (...args) => {
  105. const verboseFilter = (s) => {
  106. console.log(relative(cwd, s));
  107. return true;
  108. };
  109. if (process.env.__RIMRAF_TESTING_BIN_FAIL__ === '1') {
  110. throw new Error('simulated rimraf failure');
  111. }
  112. const opt = {};
  113. const paths = [];
  114. let dashdash = false;
  115. let impl = rimraf;
  116. let interactive = false;
  117. for (const arg of args) {
  118. if (dashdash) {
  119. paths.push(arg);
  120. continue;
  121. }
  122. if (arg === '--') {
  123. dashdash = true;
  124. continue;
  125. }
  126. else if (arg === '-rf' || arg === '-fr') {
  127. // this never did anything, but people put it there I guess
  128. continue;
  129. }
  130. else if (arg === '-h' || arg === '--help') {
  131. console.log(help);
  132. return 0;
  133. }
  134. else if (arg === '--interactive' || arg === '-i') {
  135. interactive = true;
  136. continue;
  137. }
  138. else if (arg === '--no-interactive' || arg === '-I') {
  139. interactive = false;
  140. continue;
  141. }
  142. else if (arg === '--verbose' || arg === '-v') {
  143. opt.filter = verboseFilter;
  144. continue;
  145. }
  146. else if (arg === '--no-verbose' || arg === '-V') {
  147. opt.filter = undefined;
  148. continue;
  149. }
  150. else if (arg === '-g' || arg === '--glob') {
  151. opt.glob = true;
  152. continue;
  153. }
  154. else if (arg === '-G' || arg === '--no-glob') {
  155. opt.glob = false;
  156. continue;
  157. }
  158. else if (arg === '--preserve-root') {
  159. opt.preserveRoot = true;
  160. continue;
  161. }
  162. else if (arg === '--no-preserve-root') {
  163. opt.preserveRoot = false;
  164. continue;
  165. }
  166. else if (/^--tmp=/.test(arg)) {
  167. const val = arg.substring('--tmp='.length);
  168. opt.tmp = val;
  169. continue;
  170. }
  171. else if (/^--max-retries=/.test(arg)) {
  172. const val = +arg.substring('--max-retries='.length);
  173. opt.maxRetries = val;
  174. continue;
  175. }
  176. else if (/^--retry-delay=/.test(arg)) {
  177. const val = +arg.substring('--retry-delay='.length);
  178. opt.retryDelay = val;
  179. continue;
  180. }
  181. else if (/^--backoff=/.test(arg)) {
  182. const val = +arg.substring('--backoff='.length);
  183. opt.backoff = val;
  184. continue;
  185. }
  186. else if (/^--impl=/.test(arg)) {
  187. const val = arg.substring('--impl='.length);
  188. switch (val) {
  189. case 'rimraf':
  190. impl = rimraf;
  191. continue;
  192. case 'native':
  193. case 'manual':
  194. case 'posix':
  195. case 'windows':
  196. impl = rimraf[val];
  197. continue;
  198. case 'move-remove':
  199. impl = rimraf.moveRemove;
  200. continue;
  201. default:
  202. console.error(`unknown implementation: ${val}`);
  203. runHelpForUsage();
  204. return 1;
  205. }
  206. }
  207. else if (/^-/.test(arg)) {
  208. console.error(`unknown option: ${arg}`);
  209. runHelpForUsage();
  210. return 1;
  211. }
  212. else {
  213. paths.push(arg);
  214. }
  215. }
  216. if (opt.preserveRoot !== false) {
  217. for (const path of paths.map(p => resolve(p))) {
  218. if (path === parse(path).root) {
  219. console.error(`rimraf: it is dangerous to operate recursively on '/'`);
  220. console.error('use --no-preserve-root to override this failsafe');
  221. return 1;
  222. }
  223. }
  224. }
  225. if (!paths.length) {
  226. console.error('rimraf: must provide a path to remove');
  227. runHelpForUsage();
  228. return 1;
  229. }
  230. if (impl === rimraf.native && (interactive || opt.filter)) {
  231. console.error('native implementation does not support -v or -i');
  232. runHelpForUsage();
  233. return 1;
  234. }
  235. if (interactive) {
  236. await interactiveRimraf(impl, paths, opt);
  237. }
  238. else {
  239. await impl(paths, opt);
  240. }
  241. return 0;
  242. };
  243. main.help = help;
  244. export default main;
  245. if (process.env.__TESTING_RIMRAF_BIN__ !== '1') {
  246. const args = process.argv.slice(2);
  247. main(...args).then(code => process.exit(code), er => {
  248. console.error(er);
  249. process.exit(1);
  250. });
  251. }
  252. //# sourceMappingURL=bin.mjs.map