UNPKG

st-bundle

Version:

CLI for watching and bundling SpringType projects.

314 lines (313 loc) 11.4 kB
"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;