UNPKG

6.71 kBJavaScriptView Raw
1const fs = require('fs');
2const path = require('path');
3const fn = require('funclib');
4const { DirInfo, FileInfo, calcSizekb } = require('./base');
5
6/**
7 * Export a dir-parser promise
8 * @param target string
9 * @param options object
10 */
11module.exports = (target, options) => {
12 return new Promise((resolve, reject) => {
13 try {
14 resolve(dirParser(target, options));
15 } catch (err) {
16 reject(err);
17 }
18 });
19};
20
21/**
22 * Parse the target directory and generate it's structure tree.
23 * @param target string
24 * @param options object
25 */
26function dirParser(target, options = {}) {
27
28 if (!fs.statSync(target).isDirectory()) {
29 throw new Error('Target must be a directory!')
30 }
31
32 const isGetDirTree = fn.typeOf(options.dirTree, 'bol') ? options.dirTree : true;
33 const isFilesFirst = fn.get(options, 'filesFirst', 'bol');
34 const isNoNum = fn.get(options, 'noNum', 'bol');
35 const isGetFiles = fn.get(options, 'files', 'bol');
36 const isGetChildren = fn.get(options, 'children', 'bol');
37 const lineType = fn.get(options, 'lineType', 'str') || 'solid';
38
39 let excludes = fn.get(options, 'excludes', 'arr') || [];
40 let excPaths = fn.get(options, 'excPaths', 'arr') || [];
41 excludes = fn.drop(excludes.map(ex => fn.typeVal(ex, 'str')));
42 excPaths = fn.drop(excPaths.map(ex => fn.typeVal(ex, 'str') ? path.resolve(ex) : ''));
43
44 let patterns = fn.get(options, 'patterns', 'arr') || [];
45 patterns = fn.drop(patterns.map(ptn => {
46 if (fn.typeOf(ptn, 'ptn')) {
47 return ptn;
48 } else if (fn.typeVal(ptn, 'str')) {
49 return new RegExp(ptn.replace('.', '\\.').replace('*', '.*'));
50 } else {
51 return '';
52 }
53 }));
54
55 let dirTree = '';
56 const dirs = [];
57 const files = [];
58 const absTarget = path.resolve(target);
59 const tarName = path.basename(absTarget)
60 const tarInfo = new DirInfo(tarName, target);
61
62 if (isGetChildren) {
63 dirs.push({
64 'path': target,
65 'info': tarInfo
66 });
67 }
68
69 parseDir(target, tarInfo.children);
70
71 /**
72 * Parser The target directory
73 * @param dirPath string
74 * @param deep deep = 1
75 * @param prev prev = ''
76 */
77 function parseDir(dirPath, children, deep = 1, prev = '') {
78 const subDirs = [];
79 const subFiles = [];
80 let filesSize = 0;
81
82 // Classify directories and files of the dirPath
83 fs.readdirSync(dirPath).forEach(path_ => {
84 const iPath = path.join(dirPath, path_);
85 const iPath_ = iPath.replace(/\\/mg, '/');
86 const isExclude = excludes.includes(path_) || excPaths.some(ePath => iPath === ePath);
87 const isRejects = isExclude || patterns.some(ptn => !!iPath.match(ptn) || !!iPath_.match(ptn));
88 if (!isRejects) {
89 const stat = fs.statSync(iPath);
90 const memberInfo = {
91 'name': path_,
92 'path': iPath
93 };
94 if (stat.isDirectory()) {
95 subDirs.push(memberInfo);
96 } else if (stat.isFile()) {
97 subFiles.push(memberInfo);
98 }
99 }
100 });
101
102 if (isFilesFirst) {
103 filesHandler();
104 directoriesHandler();
105 } else {
106 directoriesHandler();
107 filesHandler();
108 }
109
110 /**
111 * handle directories
112 */
113 function directoriesHandler() {
114 let split = '';
115 let dirInfo = {};
116 subDirs.forEach((dir, i) => {
117 if (isGetChildren) {
118 dirInfo = new DirInfo(dir.name, dir.path);
119 dirs.push({
120 'path': dirInfo.path,
121 'info': dirInfo
122 });
123 children.push(dirInfo);
124 }
125 if (isGetDirTree) {
126 if (lineType === 'dashed') {
127 if (i < subDirs.length - 1 || (!isFilesFirst && subFiles.length > 0)) {
128 dirTree += `${prev} +-- ${dir.name}\r\n`;
129 split = ' ¦ ';
130 } else {
131 dirTree += `${prev} +-- ${dir.name}\r\n`;
132 split = ' ';
133 }
134 } else {
135 if (i < subDirs.length - 1 || (!isFilesFirst && subFiles.length > 0)) {
136 dirTree += `${prev} ├─ ${dir.name}\r\n`;
137 split = ' │';
138 } else {
139 dirTree += `${prev} └─ ${dir.name}\r\n`;
140 split = ' ';
141 }
142 }
143 }
144 const nextPath = path.join(dirPath, dir.name);
145 const nextMemb = isGetChildren ? dirInfo.children : children;
146 const nextDeep = deep + 1;
147 const nextSplit = prev + split;
148 parseDir(nextPath, nextMemb, nextDeep, nextSplit);
149 });
150 }
151
152 /**
153 * Handle files
154 */
155 function filesHandler() {
156 subFiles.forEach((file, i) => {
157 if (isGetChildren || isGetFiles) {
158 const fileInfo = new FileInfo(file.name, file.path);
159 if (isGetFiles) {
160 files.push(fileInfo);
161 }
162 if (isGetChildren) {
163 children.push(fileInfo);
164 }
165 filesSize += fileInfo.size;
166 }
167 if (isGetDirTree) {
168 if (lineType === 'dashed') {
169 if (i < subFiles.length - 1 || (isFilesFirst && subDirs.length > 0)) {
170 dirTree += `${prev} +-- ${file.name}\r\n`;
171 } else {
172 dirTree += `${prev} +-- ${file.name}\r\n`;
173 }
174 } else {
175 if (i < subFiles.length - 1 || (isFilesFirst && subDirs.length > 0)) {
176 dirTree += `${prev} ├─ ${file.name}\r\n`;
177 } else {
178 dirTree += `${prev} └─ ${file.name}\r\n`;
179 }
180 }
181 }
182 });
183 }
184
185 if (isGetChildren) {
186 dirs.forEach(dir => {
187 if (dirPath.includes(dir.path)) {
188 dir.info.dirNum += subDirs.length;
189 dir.info.fileNum += subFiles.length;
190 dir.info.size += filesSize;
191 dir.info.size_kb = calcSizekb(dir.info.size);
192 }
193 });
194 } else {
195 tarInfo.dirNum += subDirs.length;
196 tarInfo.fileNum += subFiles.length;
197 }
198 }
199
200 if (!isGetChildren) {
201 delete tarInfo.children;
202 if (!isGetFiles) {
203 delete tarInfo.size;
204 delete tarInfo.size_kb;
205 }
206 }
207 if (isGetFiles) {
208 tarInfo.files = files;
209 tarInfo.size = files.reduce((size, file) => size + file.size, tarInfo.size);
210 tarInfo.size_kb = calcSizekb(tarInfo.size);
211 }
212 if (isGetDirTree) {
213 if (isNoNum) {
214 dirTree = `${tarName}\r\n${dirTree}`;
215 } else {
216 dirTree = `${tarName} ( directories: ${tarInfo.dirNum}, Files: ${tarInfo.fileNum} )\r\n${dirTree}`;
217 }
218 tarInfo.dirTree = dirTree.replace(/\r\n$/, '');
219 }
220
221 return tarInfo;
222}
223