-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
63 lines (56 loc) · 2.22 KB
/
index.js
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
const crypto = require("crypto");
const fs = require("node:fs");
const fsp = fs.promises;
const DEFAULT_ALGORITHM = "md5";
async function safeStat(path) {
const stat = await fsp.stat(path);
return `${stat.mode}:${stat.uid}:${stat.gid}:${stat.size}:${stat.dev}:${stat.ino}:${stat.rdev}:${stat.blksize}:${stat.blocks}`;
}
// inspired by https://stackoverflow.com/a/44643479/2860309
function checksumFile(path, { algorithm = DEFAULT_ALGORITHM } = {}) {
return new Promise(async (resolve, reject) => {
const hash = crypto.createHash(algorithm);
const stream = fs.createReadStream(path);
stream.on("error", (err) => reject(err));
stream.on("data", (chunk) => hash.update(chunk));
stream.on("end", () => resolve(hash.digest("hex")));
});
}
function checksumDirectory(path, { algorithm = DEFAULT_ALGORITHM } = {}) {
return new Promise(async (resolve, reject) => {
const hash = crypto.createHash(algorithm);
hash.update(await safeStat(path));
fsp.readdir(path, { withFileTypes: true })
.then(async (files) => {
files.sort((a, b) => a.name.localeCompare(b.name));
for (let file of files) {
const filePath = `${path}/${file.name}`;
hash.update(file.name);
hash.update(await safeStat(filePath));
if (file.isDirectory()) {
hash.update(
await checksumDirectory(
path + "/" + file.name,
algorithm
)
);
} else if (file.isFile()) {
hash.update(
await checksumFile(
path + "/" + file.name,
algorithm
)
);
} else {
// file is not a file or directory, not evaluating
}
}
resolve(hash.digest("hex"));
})
.catch((err) => reject(err));
});
}
module.exports = {
checksumFile,
checksumDirectory,
};