st-bundle
Version:
CLI for watching and bundling SpringType projects.
314 lines (313 loc) • 11.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const path = require("path");
const env_1 = require("../env");
const Package_1 = require("../core/Package");
const utils_1 = require("../utils/utils");
function generateValidKey(key) {
return encodeURIComponent(key) + '.cache';
}
const TREE_FILE_KEY = 'tree.json';
class Cache {
constructor(props) {
this.unsynced = new Map();
this.synced = new Map();
const config = props.ctx.config;
this.ctx = props.ctx;
this.rootFolder = path.join(config.cache.root, env_1.env.VERSION, env_1.env.isTest ? '' : utils_1.fastHash(config.homeDir + config.entries.join('')));
this.packageCacheFolder = path.join(this.rootFolder, env_1.env.CACHE.PACKAGES);
this.projectCacheFolder = path.join(this.rootFolder, env_1.env.CACHE.PROJET_FILES);
}
init() {
utils_1.ensureDir(this.packageCacheFolder);
utils_1.ensureDir(this.projectCacheFolder);
}
nukeAll() {
this.clearMemory();
this.clearTree();
utils_1.removeFolder(this.rootFolder);
}
nukeProjectCache() {
this.clearMemory();
this.clearTree();
utils_1.removeFolder(this.projectCacheFolder);
}
nukePackageCache() {
this.clearMemory();
this.clearTree();
utils_1.removeFolder(this.packageCacheFolder);
}
set(key, value) {
key = generateValidKey(key);
this.synced.delete(key);
this.unsynced.set(key, value);
}
unset(key) {
key = generateValidKey(key);
this.synced.delete(key);
this.unsynced.delete(key);
}
forceSyncOnKey(key) {
key = generateValidKey(key);
if (this.synced.get(key)) {
this.unsynced.set(key, this.synced.get(key));
this.synced.delete(key);
}
}
/**
* get dependency tree (here we store all information about cached packages)
*
* @returns
* @memberof FileAdapter
*/
getTree() {
let tree = this.get(TREE_FILE_KEY);
if (!tree) {
tree = {
packages: {},
};
this.set(TREE_FILE_KEY, tree);
}
return tree;
}
get(key, folder) {
key = generateValidKey(key);
if (this.synced.has(key)) {
return this.synced.get(key);
}
if (this.unsynced.has(key)) {
return this.unsynced.get(key);
}
let targetFile;
if (folder) {
targetFile = path.join(folder, key);
}
else {
targetFile = path.join(this.rootFolder, key);
}
if (utils_1.fileExists(targetFile)) {
const contents = utils_1.readFile(targetFile);
this.synced.set(key, JSON.parse(contents));
return this.synced.get(key);
}
}
clearMemory() {
this.unsynced = new Map();
this.synced = new Map();
}
async sync() {
const writers = [];
this.unsynced.forEach((value, key) => {
if (/^pkg_/.test(key)) {
writers.push(utils_1.writeFile(path.join(this.packageCacheFolder, key), JSON.stringify(value)));
}
else if (/^prj_/.test(key)) {
writers.push(utils_1.writeFile(path.join(this.projectCacheFolder, key), JSON.stringify(value)));
}
else {
writers.push(utils_1.writeFile(path.join(this.rootFolder, key), JSON.stringify(value)));
}
// it's synced now
this.synced.set(key, value);
});
await Promise.all(writers);
// reset unsynced
this.unsynced = new Map();
}
clearTree() {
const tree = this.getTree();
tree.packages = {};
}
getPackageKey(pkg) {
return `pkg_${pkg.props.meta.name}-${pkg.props.meta.version}`;
}
getPackage(pkg, userModules) {
const tree = this.getTree();
const meta = pkg.props.meta;
const response = {};
if (!tree.packages[meta.name]) {
response.abort = true;
// we don't want to retreive anything from cache if even one package is missing
this.clearTree();
return response;
}
const dest = tree.packages[meta.name][meta.version];
if (!dest) {
// same here, drop everything
this.clearTree();
response.abort = true;
return response;
}
// checking here if user entries e.g libary/foo.js, library/boo.js
// all present in the cache.
// If a new partial require was spotted and/or used a different entry
let moduleMismatch = false;
let modules = [...pkg.modules];
if (userModules) {
modules = modules.concat(userModules);
}
modules.forEach(item => {
if (!dest.modules.includes(item.props.fuseBoxPath)) {
moduleMismatch = true;
}
});
// retrieve cache for this package
if (moduleMismatch) {
return { abort: true };
}
// if the package isn't aborted (the cache is valid)
// we should collect all the dependencies with its cache
const packageCollection = {};
const collectAllSubDependencies = (list) => {
list.map(item => {
if (!tree.packages[item.name] || !tree.packages[item.name][item.version]) {
response.abort = true;
this.clearTree();
return;
}
// check if it wasn't added before
// to avoid an infinite loop
if (!response.abort) {
const key = `${item.name}-${item.version}`;
const info = tree.packages[item.name][item.version];
if (!packageCollection[key]) {
const targetPackage = Package_1.createPackage({
ctx: this.ctx,
meta: info.meta,
});
const cacheKey = this.getPackageKey(targetPackage);
const cache = this.get(cacheKey, this.packageCacheFolder);
// if one of the cached version is missing - abort
// cache should be reset
if (!cache) {
response.abort = true;
this.clearTree();
return;
}
targetPackage.setCache(cache);
packageCollection[key] = targetPackage;
const targetPackagInfo = tree.packages[item.name][item.version];
if (targetPackagInfo.dependencies) {
collectAllSubDependencies(targetPackagInfo.dependencies);
}
}
}
});
};
collectAllSubDependencies(dest.dependencies);
if (response.abort) {
return response;
}
const cache = this.get(this.getPackageKey(pkg), this.packageCacheFolder);
if (!cache) {
return { abort: true };
}
pkg.setCache(cache);
const dependants = [];
for (const key in packageCollection) {
dependants.push(packageCollection[key]);
}
return {
target: {
moduleMismatch,
pkg,
},
dependants: dependants,
};
}
getModuleCacheKey(module) {
return `prj_${utils_1.fastHash(module.props.fuseBoxPath)}`;
}
saveModule(module, basics) {
const stat = utils_1.fileStat(module.props.absPath);
const mtime = stat.mtime.getTime();
const obj = {
mtime,
fastAnalysis: module.fastAnalysis,
contents: basics.contents,
sourceMap: basics.sourceMap,
absPath: module.props.absPath,
extension: module.props.extension,
fuseBoxPath: module.props.fuseBoxPath,
};
if (module.weakReferences) {
obj.weakReferences = {};
for (const absPath of module.weakReferences) {
const refStat = utils_1.fileStat(absPath);
obj.weakReferences[absPath] = refStat.mtime.getTime();
}
}
if (module.meta) {
obj.meta = module.meta;
}
// if (module.breakDependantsCache) {
// obj.breakDependantsCache = true;
// obj.dependants = module.moduleDependants.map(item => item.props.absPath);
// }
this.set(this.getModuleCacheKey(module), obj);
}
getModuleCacheData(module) {
return this.get(this.getModuleCacheKey(module), this.projectCacheFolder);
}
restoreModule(module) {
const fpath = module.props.absPath;
const cached = this.get(this.getModuleCacheKey(module), this.projectCacheFolder);
if (cached) {
const stat = utils_1.fileStat(fpath);
if (stat.mtime.getTime() !== cached.mtime) {
this.unset(fpath);
return;
}
module.props.extension = cached.extension;
module.props.fuseBoxPath = cached.fuseBoxPath;
module.fastAnalysis = cached.fastAnalysis;
module.meta = cached.meta;
//module.weakReferences = cached.weakReferences;
if (cached.weakReferences) {
// check if weakReferences did't change (usually it's the internal imports in CSS modules)
for (const absPath in cached.weakReferences) {
const refStat = utils_1.fileStat(absPath);
if (refStat.mtime.getTime() !== cached.weakReferences[absPath]) {
return;
}
module.props.ctx.weakReferences.add(absPath, module.props.absPath);
}
}
module.setCache({
//breakDependantsCache: cached.breakDependantsCache,
//dependants: cached.dependants,
contents: cached.contents,
sourceMap: cached.sourceMap,
});
return module;
}
}
savePackage(pkg, basics) {
const tree = this.getTree();
const meta = pkg.props.meta;
if (!tree.packages[meta.name]) {
tree.packages[meta.name] = {};
}
const deps = pkg.externalPackages.map(externalPackage => ({
name: externalPackage.props.meta.name,
version: externalPackage.props.meta.version,
}));
const modules = pkg.modules.map(mod => mod.props.fuseBoxPath);
const obj = {
name: meta.name,
version: meta.version,
dependencies: deps,
meta: meta,
modules: modules,
};
tree.packages[pkg.props.meta.name][meta.version] = obj;
const cache_key = this.getPackageKey(pkg);
this.set(cache_key, basics);
this.forceSyncOnKey(TREE_FILE_KEY);
}
}
exports.Cache = Cache;
function createCache(props) {
return new Cache(props);
}
exports.createCache = createCache;