1 | import { pFilter, pMap, _since } from '@naturalcycles/js-lib'
|
2 | import * as fs from 'fs-extra'
|
3 | import * as globby from 'globby'
|
4 | import { dimGrey, yellow } from '../colors'
|
5 |
|
6 | export interface DelOptions {
|
7 | |
8 |
|
9 |
|
10 | patterns: string[]
|
11 |
|
12 | |
13 |
|
14 |
|
15 | concurrency?: number
|
16 |
|
17 | verbose?: boolean
|
18 |
|
19 | silent?: boolean
|
20 |
|
21 | debug?: boolean
|
22 |
|
23 | dry?: boolean
|
24 | }
|
25 |
|
26 | export type DelSingleOption = string
|
27 |
|
28 | const DEF_OPT: DelOptions = {
|
29 | patterns: [],
|
30 | concurrency: Number.POSITIVE_INFINITY,
|
31 | }
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | export async function del(_opt: DelOptions | DelSingleOption): Promise<void> {
|
39 | const started = Date.now()
|
40 |
|
41 |
|
42 | if (typeof _opt === 'string') {
|
43 | _opt = {
|
44 | patterns: [_opt],
|
45 | }
|
46 | }
|
47 |
|
48 | const opt = {
|
49 | ...DEF_OPT,
|
50 | ..._opt,
|
51 | concurrency: _opt.concurrency || DEF_OPT.concurrency,
|
52 | }
|
53 | const { patterns, concurrency, verbose, silent, debug, dry } = opt
|
54 |
|
55 | if (debug) {
|
56 | console.log(opt)
|
57 | }
|
58 |
|
59 |
|
60 |
|
61 | const filenames = await globby(patterns, {
|
62 | dot: true,
|
63 | expandDirectories: true,
|
64 | onlyFiles: true,
|
65 | })
|
66 |
|
67 | if (verbose || debug || dry) {
|
68 | console.log(`Will delete ${yellow(filenames.length)} files:`, filenames)
|
69 | }
|
70 |
|
71 | if (dry) return
|
72 |
|
73 | await pMap(filenames, filepath => fs.remove(filepath), { concurrency })
|
74 |
|
75 |
|
76 | let dirnames = await globby(patterns, {
|
77 | dot: true,
|
78 | expandDirectories: true,
|
79 | onlyDirectories: true,
|
80 | })
|
81 |
|
82 |
|
83 | dirnames = dirnames.concat(
|
84 | await pFilter(patterns, async pattern => {
|
85 | return (await fs.pathExists(pattern)) && (await fs.lstat(pattern)).isDirectory()
|
86 | }),
|
87 | )
|
88 |
|
89 | const dirnamesSorted = dirnames.sort().reverse()
|
90 |
|
91 |
|
92 |
|
93 | const deletedDirs = await pFilter(
|
94 | dirnamesSorted,
|
95 | async dirpath => {
|
96 | if (await isEmptyDir(dirpath)) {
|
97 |
|
98 | await fs.remove(dirpath)
|
99 | return true
|
100 | }
|
101 | return false
|
102 | },
|
103 | { concurrency: 1 },
|
104 | )
|
105 |
|
106 | if (verbose || debug) console.log({ deletedDirs })
|
107 |
|
108 | if (!silent) {
|
109 | console.log(
|
110 | `del deleted ${yellow(filenames.length)} files and ${yellow(
|
111 | deletedDirs.length,
|
112 | )} dirs ${dimGrey(_since(started))}`,
|
113 | )
|
114 | }
|
115 | }
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 | async function isEmptyDir(dir: string): Promise<boolean> {
|
123 | return (await fs.readdir(dir)).length === 0
|
124 | }
|