@fimbul/wotan
Version:
Pluggable TypeScript and JavaScript linter
137 lines • 5.39 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CachedFileSystem = exports.FileKind = void 0;
const tslib_1 = require("tslib");
const inversify_1 = require("inversify");
const ymir_1 = require("@fimbul/ymir");
const bind_decorator_1 = require("bind-decorator");
const utils_1 = require("../utils");
const path = require("path");
var FileKind;
(function (FileKind) {
FileKind[FileKind["NonExistent"] = 0] = "NonExistent";
FileKind[FileKind["File"] = 1] = "File";
FileKind[FileKind["Directory"] = 2] = "Directory";
FileKind[FileKind["Other"] = 3] = "Other";
})(FileKind = exports.FileKind || (exports.FileKind = {}));
let CachedFileSystem = class CachedFileSystem {
constructor(fs, cache) {
this.fs = fs;
this.realpath = this.fs.realpath === undefined ? undefined : (file) => {
return utils_1.resolveCachedResult(this.realpathCache, this.fs.normalizePath(file), () => this.fs.realpath(file));
};
this.fileKindCache = cache.create();
this.realpathCache = cache.create();
this.direntCache = cache.create();
}
isFile(file) {
return utils_1.resolveCachedResult(this.fileKindCache, this.fs.normalizePath(file), this.doGetKind) === 1 /* File */;
}
isDirectory(dir) {
return utils_1.resolveCachedResult(this.fileKindCache, this.fs.normalizePath(dir), this.doGetKind) === 2 /* Directory */;
}
getKind(file) {
return utils_1.resolveCachedResult(this.fileKindCache, this.fs.normalizePath(file), this.doGetKind);
}
doGetKind(file) {
try {
return statsToKind(this.fs.stat(file));
}
catch {
return 0 /* NonExistent */;
}
}
readDirectory(dir) {
dir = this.fs.normalizePath(dir);
let cachedResult = this.direntCache.get(dir);
if (cachedResult !== undefined)
return cachedResult.map((entry) => ({ kind: this.fileKindCache.get(this.fs.normalizePath(path.join(dir, entry))), name: entry }));
cachedResult = [];
const result = [];
for (const entry of this.fs.readDirectory(dir)) {
if (typeof entry === 'string') {
cachedResult.push(entry);
result.push({ kind: this.getKind(path.join(dir, entry)), name: entry });
}
else {
cachedResult.push(entry.name);
const filePath = path.join(dir, entry.name);
let kind;
if (entry.isSymbolicLink()) {
kind = this.getKind(filePath);
}
else {
kind = statsToKind(entry);
this.fileKindCache.set(this.fs.normalizePath(filePath), kind);
}
result.push({ kind, name: entry.name });
}
}
this.direntCache.set(dir, cachedResult);
return result;
}
readFile(file) {
return this.fs.readFile(this.fs.normalizePath(file));
}
writeFile(file, content) {
file = this.fs.normalizePath(file);
this.fs.writeFile(file, content);
this.updateCache(file, 1 /* File */);
}
remove(file) {
file = this.fs.normalizePath(file);
this.fs.deleteFile(file);
this.updateCache(file, 0 /* NonExistent */);
}
createDirectory(dir) {
dir = this.fs.normalizePath(dir);
if (this.fileKindCache.get(dir) === 2 /* Directory */)
return;
return this.doCreateDirectory(dir);
}
doCreateDirectory(dir) {
try {
this.fs.createDirectory(dir);
}
catch (e) {
try {
const stat = this.fs.stat(dir);
if (!stat.isDirectory())
throw e;
}
catch {
const parent = this.fs.normalizePath(path.dirname(dir));
if (parent === dir)
throw e;
this.doCreateDirectory(parent);
this.fs.createDirectory(dir);
}
}
this.updateCache(dir, 2 /* Directory */);
}
updateCache(file, kind) {
// this currently doesn't handle directory removal as there is no API for that
if (this.fileKindCache.get(file) === kind)
return;
this.fileKindCache.set(file, kind);
if (kind === 0 /* NonExistent */)
this.realpathCache.delete(file); // only invalidate realpath cache on file removal
// invalidate direntCache unconditionally as the new file's name might differ in case from the one we have here
this.direntCache.delete(this.fs.normalizePath(path.dirname(file)));
}
};
tslib_1.__decorate([
bind_decorator_1.default,
tslib_1.__metadata("design:type", Function),
tslib_1.__metadata("design:paramtypes", [String]),
tslib_1.__metadata("design:returntype", Number)
], CachedFileSystem.prototype, "doGetKind", null);
CachedFileSystem = tslib_1.__decorate([
inversify_1.injectable(),
tslib_1.__metadata("design:paramtypes", [ymir_1.FileSystem, ymir_1.CacheFactory])
], CachedFileSystem);
exports.CachedFileSystem = CachedFileSystem;
function statsToKind(stats) {
return stats.isFile() ? 1 /* File */ : stats.isDirectory() ? 2 /* Directory */ : 3 /* Other */;
}
//# sourceMappingURL=cached-file-system.js.map