reboost
Version:
A super fast dev server for rapid web development
229 lines (228 loc) • 10.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.initCache = void 0;
const tslib_1 = require("tslib");
const md5_file_1 = (0, tslib_1.__importDefault)(require("md5-file"));
const chalk_1 = (0, tslib_1.__importDefault)(require("chalk"));
// @ts-expect-error No need to install declaration
const hash_sum_1 = (0, tslib_1.__importDefault)(require("hash-sum"));
const fs_1 = (0, tslib_1.__importDefault)(require("fs"));
const path_1 = (0, tslib_1.__importDefault)(require("path"));
const utils_1 = require("./utils");
const initCache = (config, plugins, log) => {
let unsavedCacheInfos = {};
const noop = () => { };
const getCurrentVersion = () => {
const { version } = JSON.parse(fs_1.default.readFileSync(path_1.default.resolve(__dirname, '../../package.json'), 'utf8'));
return version;
};
const versionFilePath = () => path_1.default.join(config.cacheDir, 'version');
const cacheIDsFilePath = () => path_1.default.join(config.cacheDir, 'cache_id_map.json');
const dependentsDataFilePath = () => path_1.default.join(config.cacheDir, 'dependents_data.json');
const memoized = {
cacheVersion: undefined,
cacheIDs: undefined,
dependentsData: undefined
};
const needsSave = {
cacheVersion: false,
cacheIDs: false,
dependentsData: false,
};
const it = {
getFilesDir: () => path_1.default.join(config.cacheDir, 'files'),
cacheFilePath: (cacheID) => path_1.default.join(it.getFilesDir(), cacheID + (config.debugMode ? '.js' : '')),
sourceMapPath: (cacheID) => path_1.default.join(it.getFilesDir(), cacheID + (config.debugMode ? '.js' : '') + '.map'),
cacheInfoFilePath: (cacheID) => path_1.default.join(it.getFilesDir(), cacheID + '.json'),
getCurrentPlugins: () => {
return plugins
.filter((p) => p && p.name)
.map((p) => {
let id = p.name;
if (typeof p.getCacheKey === 'function') {
const cacheKey = p.getCacheKey({ serializeObject: utils_1.serializeObject }) + '';
id += '@' + (cacheKey.length > 6 ? (0, hash_sum_1.default)(cacheKey) : cacheKey);
}
else {
log('info', chalk_1.default.yellow(`Required hook "getCacheKey" is not implemented in ${p.name}`));
}
return id;
})
.join(' && ');
},
get version() {
if (!memoized.cacheVersion) {
memoized.cacheVersion = fs_1.default.existsSync(versionFilePath())
? fs_1.default.readFileSync(versionFilePath(), 'utf8')
: getCurrentVersion();
needsSave.cacheVersion = true;
}
return memoized.cacheVersion;
},
get cacheIDs() {
if (!memoized.cacheIDs) {
const cacheIDs = fs_1.default.existsSync(cacheIDsFilePath())
? JSON.parse(fs_1.default.readFileSync(cacheIDsFilePath(), 'utf8'))
: {};
memoized.cacheIDs = (0, utils_1.observable)(cacheIDs, () => {
needsSave.cacheIDs = true;
});
}
return memoized.cacheIDs;
},
get dependentsData() {
if (!memoized.dependentsData) {
const dependentsData = fs_1.default.existsSync(dependentsDataFilePath())
? JSON.parse(fs_1.default.readFileSync(dependentsDataFilePath(), 'utf8'))
: {};
memoized.dependentsData = (0, utils_1.observable)(dependentsData, () => {
needsSave.dependentsData = true;
});
}
return memoized.dependentsData;
},
cacheInfo: new Proxy({}, {
get: (cacheInfos, cacheID) => {
if (!cacheInfos[cacheID]) {
const cacheInfo = JSON.parse(fs_1.default.readFileSync(it.cacheInfoFilePath(cacheID), 'utf8'));
cacheInfos[cacheID] = (0, utils_1.observable)(cacheInfo, () => {
unsavedCacheInfos[cacheID] = cacheInfo;
});
}
return cacheInfos[cacheID];
},
set: (cacheInfos, cacheID, cacheInfo) => {
if (typeof cacheInfo === 'undefined') {
cacheInfos[cacheID] = undefined;
return true;
}
cacheInfos[cacheID] = (0, utils_1.observable)(cacheInfo, () => {
unsavedCacheInfos[cacheID] = cacheInfo;
});
unsavedCacheInfos[cacheID] = cacheInfo;
return true;
}
}),
saveData: () => {
const stringify = (json) => JSON.stringify(json, null, config.debugMode ? 2 : 0);
if (needsSave.cacheVersion) {
fs_1.default.writeFileSync(versionFilePath(), it.version);
needsSave.cacheVersion = false;
}
if (needsSave.cacheIDs) {
fs_1.default.writeFileSync(cacheIDsFilePath(), stringify(it.cacheIDs));
needsSave.cacheIDs = false;
}
if (needsSave.dependentsData) {
fs_1.default.writeFileSync(dependentsDataFilePath(), stringify(it.dependentsData));
needsSave.dependentsData = false;
}
Object.keys(unsavedCacheInfos).forEach((cacheID) => {
fs_1.default.writeFileSync(it.cacheInfoFilePath(cacheID), stringify(unsavedCacheInfos[cacheID]));
});
unsavedCacheInfos = {};
},
removeFile: (file) => {
const cacheID = it.cacheIDs[file];
if (cacheID) {
it.cacheIDs[file] = undefined;
it.cacheInfo[cacheID] = undefined;
fs_1.default.unlink(it.cacheFilePath(cacheID), noop);
fs_1.default.unlink(it.cacheInfoFilePath(cacheID), noop);
if (fs_1.default.existsSync(it.sourceMapPath(cacheID))) {
fs_1.default.unlink(it.sourceMapPath(cacheID), noop);
}
}
},
removeDependents: (dependency) => {
const dependentsData = it.dependentsData;
const dependents = dependentsData[dependency];
if (dependents) {
dependentsData[dependency] = undefined;
dependents.forEach((dependent) => it.removeFile(dependent));
}
},
verifyFiles: () => {
if (fs_1.default.existsSync(it.getFilesDir())) {
const files = Object.keys(it.cacheIDs);
files.forEach((file) => {
if (!fs_1.default.existsSync(file))
it.removeFile(file);
});
const dependencies = Object.keys(it.dependentsData);
dependencies.forEach((dependency) => {
if (!fs_1.default.existsSync(dependency))
it.removeDependents(dependency);
});
}
it.saveData();
},
hasDependenciesChanged: async (file) => {
const deps = it.cacheInfo[it.cacheIDs[file]].dependencies;
if (!deps)
return false;
for (const dependency in deps) {
try {
const dependencyMeta = deps[dependency];
const currentMtime = Math.floor(fs_1.default.statSync(dependency).mtimeMs);
if (dependencyMeta.mtime === currentMtime)
continue;
const currentHash = await (0, md5_file_1.default)(dependency);
if (dependencyMeta.hash !== currentHash)
return true;
}
catch (e) {
// Probably the `fs.statSync` function caused error if
// the dependency file is unavailable
return true;
}
}
return false;
},
updateDependencies: async (filePath, dependencies, firstTime = false) => {
const dependentsData = it.dependentsData;
const cacheInfo = it.cacheInfo[it.cacheIDs[filePath]];
let added;
let removed;
if (firstTime) {
added = dependencies;
removed = [];
}
else {
const prevDeps = Object.keys(cacheInfo.dependencies || {});
({ added, removed } = (0, utils_1.diff)(prevDeps, dependencies));
}
added.forEach((dependency) => {
const dependents = dependentsData[dependency] || [];
if (!dependents.includes(filePath))
dependents.push(filePath);
dependentsData[dependency] = dependents;
});
removed.forEach((dependency) => {
if (dependentsData[dependency]) {
const dependents = dependentsData[dependency] || [];
// Remove current file from dependents
const filtered = dependents.filter((dependent) => dependent !== filePath);
dependentsData[dependency] = filtered.length ? filtered : undefined;
}
});
if (dependencies.length === 0)
return cacheInfo.dependencies = undefined;
const depsData = {};
await Promise.all(dependencies.map(async (dependency) => {
if (fs_1.default.existsSync(dependency)) {
depsData[dependency] = {
hash: await (0, md5_file_1.default)(dependency),
mtime: Math.floor(fs_1.default.statSync(dependency).mtimeMs)
};
}
else {
depsData[dependency] = {};
}
}));
cacheInfo.dependencies = depsData;
}
};
return it;
};
exports.initCache = initCache;