pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
76 lines (75 loc) • 2.36 kB
JavaScript
/**
* Rule Cache for pi-lens
*
* Provides disk-based caching for parsed tree-sitter rules with
* automatic invalidation based on rule file modification times.
*/
import * as crypto from "node:crypto";
import * as fs from "node:fs";
import * as path from "node:path";
import { getProjectDataDir } from "../file-utils.js";
const CACHE_VERSION = "v3";
export class RuleCache {
cacheFile;
cacheDir;
constructor(language, rootDir = process.cwd()) {
this.cacheDir = path.join(getProjectDataDir(rootDir), "cache");
this.cacheFile = path.join(this.cacheDir, `${language}-rules-${CACHE_VERSION}.json`);
}
ensureCacheDir() {
if (!fs.existsSync(this.cacheDir)) {
fs.mkdirSync(this.cacheDir, { recursive: true });
}
}
computeRuleHash(ruleFiles) {
const hash = crypto.createHash("sha256");
for (const file of ruleFiles.sort((a, b) => a.localeCompare(b))) {
if (fs.existsSync(file)) {
const stat = fs.statSync(file);
hash.update(`${file}:${stat.mtimeMs}:${stat.size}`);
}
}
return hash.digest("hex").slice(0, 16);
}
get(ruleFiles) {
try {
this.ensureCacheDir();
if (!fs.existsSync(this.cacheFile))
return null;
const cached = JSON.parse(fs.readFileSync(this.cacheFile, "utf-8"));
const currentHash = this.computeRuleHash(ruleFiles);
if (cached.version !== CACHE_VERSION || cached.ruleHash !== currentHash) {
return null; // Cache invalid
}
return cached;
}
catch {
return null;
}
}
set(ruleFiles, queries) {
try {
this.ensureCacheDir();
const entry = {
version: CACHE_VERSION,
timestamp: Date.now(),
ruleHash: this.computeRuleHash(ruleFiles),
queries,
};
fs.writeFileSync(this.cacheFile, JSON.stringify(entry, null, 2));
}
catch {
// Cache write failure is non-fatal
}
}
clear() {
try {
if (fs.existsSync(this.cacheFile)) {
fs.unlinkSync(this.cacheFile);
}
}
catch {
// Ignore
}
}
}