-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathhashUtils.ts
89 lines (68 loc) Β· 2.44 KB
/
hashUtils.ts
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
import {PortablePath, xfs, npath, FakeFS, ppath} from '@yarnpkg/fslib';
import {createHash, BinaryLike} from 'crypto';
import fastGlob from 'fast-glob';
export function makeHash<T extends string = string>(...args: Array<BinaryLike | null>): T {
const hash = createHash(`sha512`);
let acc = ``;
for (const arg of args) {
if (typeof arg === `string`) {
acc += arg;
} else if (arg) {
if (acc) {
hash.update(acc);
acc = ``;
}
hash.update(arg);
}
}
if (acc)
hash.update(acc);
return hash.digest(`hex`) as T;
}
export async function checksumFile(path: PortablePath, {baseFs, algorithm}: {baseFs: FakeFS<PortablePath>, algorithm: string} = {baseFs: xfs, algorithm: `sha512`}) {
const fd = await baseFs.openPromise(path, `r`);
try {
const CHUNK_SIZE = 65536;
const chunk = Buffer.allocUnsafeSlow(CHUNK_SIZE);
const hash = createHash(algorithm);
let bytesRead = 0;
while ((bytesRead = await baseFs.readPromise(fd, chunk, 0, CHUNK_SIZE)) !== 0)
hash.update(bytesRead === CHUNK_SIZE ? chunk : chunk.slice(0, bytesRead));
return hash.digest(`hex`);
} finally {
await baseFs.closePromise(fd);
}
}
export async function checksumPattern(pattern: string, {cwd}: {cwd: PortablePath}) {
// Note: We use a two-pass glob instead of using globby with the expandDirectories
// option, because the native implementation is broken.
//
// Ref: https://github.com/sindresorhus/globby/issues/147
const dirListing = await fastGlob(pattern, {
cwd: npath.fromPortablePath(cwd),
onlyDirectories: true,
});
const dirPatterns = dirListing.map(entry => {
return `${entry}/**/*`;
});
const listing = await fastGlob([pattern, ...dirPatterns], {
cwd: npath.fromPortablePath(cwd),
onlyFiles: false,
});
// fast-glob returns results in arbitrary order
listing.sort();
const hashes = await Promise.all(listing.map(async entry => {
const parts: Array<Buffer> = [Buffer.from(entry)];
const p = ppath.join(cwd, npath.toPortablePath(entry));
const stat = await xfs.lstatPromise(p);
if (stat.isSymbolicLink())
parts.push(Buffer.from(await xfs.readlinkPromise(p)));
else if (stat.isFile())
parts.push(await xfs.readFilePromise(p));
return parts.join(`\u0000`);
}));
const hash = createHash(`sha512`);
for (const sub of hashes)
hash.update(sub);
return hash.digest(`hex`);
}