Files
ObsidianPad/node_modules/@electron/rebuild/lib/cache.js
T

148 lines
5.4 KiB
JavaScript
Raw Normal View History

2026-05-31 18:44:04 +08:00
import crypto from 'node:crypto';
import debug from 'debug';
import fs from 'graceful-fs';
import path from 'node:path';
import zlib from 'node:zlib';
import { promisifiedGracefulFs } from './promisifiedGracefulFs.js';
const d = debug('electron-rebuild');
// Update this number if you change the caching logic to ensure no bad cache hits
const ELECTRON_REBUILD_CACHE_ID = 1;
class Snap {
hash;
data;
constructor(hash, data) {
this.hash = hash;
this.data = data;
}
}
const takeSnapshot = async (dir, relativeTo = dir) => {
const snap = {};
await Promise.all((await promisifiedGracefulFs.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.promises.stat(childPath)).isDirectory()) {
snap[relative] = await takeSnapshot(childPath, relativeTo);
}
else {
const data = await promisifiedGracefulFs.readFile(childPath);
snap[relative] = new Snap(crypto.createHash('SHA256').update(data).digest('hex'), data);
}
}));
return snap;
};
const writeSnapshot = async (diff, dir) => {
for (const key in diff) {
if (diff[key] instanceof Snap) {
await fs.promises.mkdir(path.dirname(path.resolve(dir, key)), { recursive: true });
await promisifiedGracefulFs.writeFile(path.resolve(dir, key), diff[key].data);
}
else {
await fs.promises.mkdir(path.resolve(dir, key), { recursive: true });
await writeSnapshot(diff[key], dir);
}
}
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const serialize = (snap) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const jsonReady = {};
for (const key in snap) {
if (snap[key] instanceof Snap) {
const s = snap[key];
jsonReady[key] = {
__isSnap: true,
hash: s.hash,
data: s.data.toString('base64')
};
}
else {
jsonReady[key] = serialize(snap[key]);
}
}
return jsonReady;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const unserialize = (jsonReady) => {
const snap = {};
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, cachePath, key) => {
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.promises.mkdir(cachePath, { recursive: true });
await promisifiedGracefulFs.writeFile(path.resolve(cachePath, key), zipped);
};
export const lookupModuleState = async (cachePath, key) => {
if (fs.existsSync(path.resolve(cachePath, key))) {
return async function applyDiff(dir) {
const zipped = await promisifiedGracefulFs.readFile(path.resolve(cachePath, key));
const unzipped = await new Promise(resolve => { zlib.gunzip(zipped, (_, result) => resolve(result)); });
const diff = unserialize(JSON.parse(unzipped.toString()));
await writeSnapshot(diff, dir);
};
}
return false;
};
function dHashTree(tree, hash) {
for (const key of Object.keys(tree).sort()) {
hash.update(key);
if (typeof tree[key] === 'string') {
hash.update(tree[key]);
}
else {
dHashTree(tree[key], hash);
}
}
}
async function hashDirectory(dir, relativeTo) {
relativeTo ??= dir;
d('hashing dir', dir);
const dirTree = {};
await Promise.all((await promisifiedGracefulFs.readdir(dir)).map(async (child) => {
d('found child', child, 'in dir', dir);
// Ignore output directories
if (dir === relativeTo && (child === 'build' || child === 'bin'))
return;
// Don't hash nested node_modules
if (child === 'node_modules')
return;
const childPath = path.resolve(dir, child);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const relative = path.relative(relativeTo, childPath);
if ((await fs.promises.stat(childPath)).isDirectory()) {
dirTree[relative] = await hashDirectory(childPath, relativeTo);
}
else {
dirTree[relative] = crypto.createHash('SHA256').update(await promisifiedGracefulFs.readFile(childPath)).digest('hex');
}
}));
return dirTree;
}
export async function generateCacheKey(opts) {
const tree = await hashDirectory(opts.modulePath);
const hasher = crypto.createHash('SHA256')
.update(`${ELECTRON_REBUILD_CACHE_ID}`)
.update(path.basename(opts.modulePath))
.update(opts.ABI)
.update(opts.arch)
.update(opts.platform)
.update(opts.debug ? 'debug' : 'not debug')
.update(opts.headerURL)
.update(opts.electronVersion);
dHashTree(tree, hasher);
const hash = hasher.digest('hex');
d('calculated hash of', opts.modulePath, 'to be', hash);
return hash;
}
//# sourceMappingURL=cache.js.map