electron-rebuild
Version:
Electron supporting package to rebuild native node modules against the currently installed electron
97 lines (87 loc) • 2.94 kB
text/typescript
import * as crypto from 'crypto';
import * as fs from 'fs-extra';
import * as path from 'path';
import * as zlib from 'zlib';
class Snap {
constructor(public hash: string, public data: Buffer) {}
}
interface Snapshot {
[key: string]: Snap | Snapshot;
}
const takeSnapshot = async (dir: string, relativeTo = dir) => {
const snap: Snapshot = {};
await Promise.all((await fs.readdir(dir)).map(async (child) => {
if (child === 'node_modules') return;
const childPath = path.resolve(dir, child);
const relative = path.relative(relativeTo, childPath);
if ((await fs.stat(childPath)).isDirectory()) {
snap[relative] = await takeSnapshot(childPath, relativeTo);
} else {
const data = await fs.readFile(childPath);
snap[relative] = new Snap(
crypto.createHash('SHA256').update(data).digest('hex'),
data,
);
}
}));
return snap;
};
const writeSnapshot = async (diff: Snapshot, dir: string) => {
for (const key in diff) {
if (diff[key] instanceof Snap) {
await fs.mkdirp(path.dirname(path.resolve(dir, key)));
await fs.writeFile(path.resolve(dir, key), (diff[key] as Snap).data);
} else {
await fs.mkdirp(path.resolve(dir, key));
await writeSnapshot(diff[key] as Snapshot, dir);
}
}
};
const serialize = (snap: Snapshot) => {
const jsonReady: any = {};
for (const key in snap) {
if (snap[key] instanceof Snap) {
const s = snap[key] as Snap;
jsonReady[key] = {
__isSnap: true,
hash: s.hash,
data: s.data.toString('base64')
};
} else {
jsonReady[key] = serialize(snap[key] as Snapshot);
}
}
return jsonReady;
};
const unserialize = (jsonReady: any) => {
const snap: Snapshot = {};
for (const key in jsonReady) {
if (jsonReady[key].__isSnap) {
snap[key] = new Snap(
jsonReady[key].hash,
Buffer.from(jsonReady[key].data, 'base64')
);
} else {
snap[key] = unserialize(jsonReady[key]);
}
}
return snap;
};
export const cacheModuleState = async (dir: string, cachePath: string, key: string) => {
const snap = await takeSnapshot(dir);
const moduleBuffer = Buffer.from(JSON.stringify(serialize(snap)));
const zipped = await new Promise(resolve => zlib.gzip(moduleBuffer, (_, result) => resolve(result)));
await fs.mkdirp(cachePath);
await fs.writeFile(path.resolve(cachePath, key), zipped);
};
export const lookupModuleState = async (cachePath: string, key: string) => {
if (await fs.pathExists(path.resolve(cachePath, key))) {
return async function applyDiff(dir: string) {
const zipped = await fs.readFile(path.resolve(cachePath, key));
const unzipped: Buffer = await new Promise(resolve => { zlib.gunzip(zipped, (_, result) => resolve(result)); });
const diff = unserialize(JSON.parse(unzipped.toString()));
await writeSnapshot(diff, dir);
};
}
return false;
};