fs.js
5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// jshint ignore: start
import _fs from 'fs';
import rimraf from 'rimraf';
import md5file from 'md5-file';
import ncp from 'ncp';
import B from 'bluebird';
import mv from 'mv';
import which from 'which';
import glob from 'glob';
import crypto from 'crypto';
import klaw from 'klaw';
import sanitize from 'sanitize-filename';
import { pluralize } from './util';
import log from './logger';
import Timer from './timing';
const md5 = B.promisify(md5file);
let fs = {
async hasAccess (path) {
try {
await this.access(path, _fs.R_OK);
} catch (err) {
return false;
}
return true;
},
exists (path) { return this.hasAccess(path); },
rimraf: B.promisify(rimraf),
rimrafSync: rimraf.sync.bind(rimraf),
async mkdir (dirName) {
let _mkdir = B.promisify(_fs.mkdir);
try {
await _mkdir(dirName);
} catch (err) {
if (err && err.code !== 'EEXIST') {
throw err;
}
}
},
async copyFile (source, destination, ...otherArgs) {
if (!await this.hasAccess(source)) {
throw new Error(`The file at '${source}' does not exist or is not accessible`);
}
return await (B.promisify(ncp))(source, destination, ...otherArgs);
},
async md5 (filePath) {
return await md5(filePath);
},
mv: B.promisify(mv),
which: B.promisify(which),
glob: B.promisify(glob),
sanitizeName: sanitize,
async hash (filePath, algorithm = 'sha1') {
return await new B((resolve, reject) => {
const fileHash = crypto.createHash(algorithm);
const readStream = _fs.createReadStream(filePath);
readStream.on('error', (e) => reject(
new Error(`Cannot calculate ${algorithm} hash for '${filePath}'. Original error: ${e.message}`)));
readStream.on('data', (chunk) => fileHash.update(chunk));
readStream.on('end', () => resolve(fileHash.digest('hex')));
});
},
/** The callback function which will be called during the directory walking
* @name WalkDirCallback
* @function
* @param {string} itemPath The path of the file or folder
* @param {boolean} isDirectory Shows if it is a directory or a file
* @return {boolean} return true if you want to stop walking
*/
/**
* Walks a directory given according to the parameters given. The callback will be invoked with a path joined with the dir parameter
* @param {string} dir Directory path where we will start walking
* @param {boolean} recursive Set it to true if you want to continue walking sub directories
* @param {WalkDirCallback} callback The callback to be called when a new path is found
* @throws {Error} If the `dir` parameter contains a path to an invalid folder
* @return {?string} returns the found path or null if the item was not found
*/
async walkDir (dir, recursive, callback) { //eslint-disable-line promise/prefer-await-to-callbacks
let isValidRoot = false;
let errMsg = null;
try {
isValidRoot = (await fs.stat(dir)).isDirectory();
} catch (e) {
errMsg = e.message;
}
if (!isValidRoot) {
throw Error(`'${dir}' is not a valid root directory` + (errMsg ? `. Original error: ${errMsg}` : ''));
}
let walker;
let fileCount = 0;
let directoryCount = 0;
const timer = new Timer().start();
return await new B(function (resolve, reject) {
let lastFileProcessed = B.resolve();
walker = klaw(dir, {
depthLimit: recursive ? -1 : 0,
});
walker.on('data', function (item) {
walker.pause();
if (!item.stats.isDirectory()) {
fileCount++;
} else {
directoryCount++;
}
// eslint-disable-next-line promise/prefer-await-to-callbacks
lastFileProcessed = B.try(async () => await callback(item.path, item.stats.isDirectory()))
.then(function (done = false) {
if (done) {
resolve(item.path);
} else {
walker.resume();
}
})
.catch(reject);
})
.on('error', function (err, item) {
log.warn(`Got an error while walking '${item.path}': ${err.message}`);
// klaw cannot get back from an ENOENT error
if (err.code === 'ENOENT') {
log.warn('All files may not have been accessed');
reject(err);
}
})
.on('end', function () {
lastFileProcessed
.then(resolve)
.catch(function (err) {
log.warn(`Unexpected error: ${err.message}`);
reject(err);
});
});
}).finally(function () {
log.debug(`Traversed ${pluralize('directory', directoryCount, true)} ` +
`and ${pluralize('file', fileCount, true)} ` +
`in ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
if (walker) {
walker.destroy();
}
});
}
};
// add the supported `fs` functions
const simples = [
'open', 'close', 'access', 'readFile', 'writeFile', 'write', 'read',
'readlink', 'chmod', 'unlink', 'readdir', 'stat', 'rename', 'lstat',
];
for (const s of simples) {
fs[s] = B.promisify(_fs[s]);
}
const syncFunctions = [
'createReadStream',
'createWriteStream',
];
for (const s of syncFunctions) {
fs[s] = _fs[s];
}
// add the constants from `fs`
const constants = [
'F_OK', 'R_OK', 'W_OK', 'X_OK', 'constants',
];
for (const c of constants) {
fs[c] = _fs[c];
}
export { fs };
export default fs;