UNPKG

jgb-cli

Version:

```shell npm i -g jgb-cli #全局安装 ```

294 lines 19.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var fs = require("fs"); var fsExtra = require("fs-extra"); var Logger_1 = require("jgb-shared/lib/Logger"); var utils_1 = require("jgb-shared/lib/utils"); var mkdir = require("mkdirp"); var path = require("path"); var util_1 = require("util"); var VError = require("verror"); var pkg = require("../package.json"); // These keys can affect the output, so if they differ, the cache should not match var OPTION_KEYS = [ 'alias', 'target', 'source', 'presets', 'plugins', 'minify', 'outDir', 'sourceDir', 'rootDir' ]; var mkdirp = util_1.promisify(mkdir); var statp = util_1.promisify(fs.stat); var FSCache = /** @class */ (function () { function FSCache(options) { this.invalidated = new Set(); this.dir = path.resolve(options.cacheDir || '.cache'); var hash = OPTION_KEYS.reduce(function (p, k) { return ((p[k] = options[k]), p); }, { version: pkg.version }); this.optionsHash = utils_1.objectHash(hash); this.ensureDirExists(); } FSCache.prototype.ensureDirExists = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { var task; var _this = this; return tslib_1.__generator(this, function (_a) { if (this.dirExistsPromise) { return [2 /*return*/, this.dirExistsPromise]; } task = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { var i; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, mkdirp(this.dir)]; case 1: _a.sent(); i = 0; _a.label = 2; case 2: if (!(i < 256)) return [3 /*break*/, 5]; return [4 /*yield*/, mkdirp(path.join(this.dir, ('00' + i.toString(16)).slice(-2)))]; case 3: _a.sent(); _a.label = 4; case 4: i++; return [3 /*break*/, 2]; case 5: return [2 /*return*/]; } }); }); }; this.dirExistsPromise = task(); return [2 /*return*/]; }); }); }; FSCache.prototype.getCacheFile = function (filename) { return tslib_1.__awaiter(this, void 0, void 0, function () { var hash, _a; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: _a = utils_1.md5; return [4 /*yield*/, this.optionsHash]; case 1: hash = _a.apply(void 0, [(_b.sent()) + filename]); return [2 /*return*/, path.join(this.dir, hash.slice(0, 2), hash.slice(2) + '.json')]; } }); }); }; FSCache.prototype.getLastModifiedSync = function (filename) { var stat = fs.statSync(filename); return stat.mtime.getTime(); }; FSCache.prototype.getLastModified = function (filename) { return tslib_1.__awaiter(this, void 0, void 0, function () { var stat; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, statp(filename)]; case 1: stat = _a.sent(); return [2 /*return*/, stat.mtime.getTime()]; } }); }); }; FSCache.prototype.writeDepMtimes = function (data) { return tslib_1.__awaiter(this, void 0, void 0, function () { var e_1, _a, dependencies, _b, _c, dep; return tslib_1.__generator(this, function (_d) { dependencies = data.dependencies; if (dependencies instanceof Map) { // @ts-ignore dependencies = dependencies.values(); } try { for (_b = tslib_1.__values(new Set(dependencies)), _c = _b.next(); !_c.done; _c = _b.next()) { dep = _c.value; if (dep && dep.includedInParent) { try { dep.mtime = this.getLastModifiedSync(dep.name); } catch (error) { Logger_1.logger.error(VError.fullStack(error)); } } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } return [2 /*return*/]; }); }); }; FSCache.prototype.write = function (filename, data) { return tslib_1.__awaiter(this, void 0, void 0, function () { var cacheFile, err_1; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 5, , 6]); return [4 /*yield*/, this.ensureDirExists()]; case 1: _a.sent(); return [4 /*yield*/, this.getCacheFile(filename)]; case 2: cacheFile = _a.sent(); if (data.dependencies instanceof Map) { data.dependencies = tslib_1.__spread(data.dependencies).map(function (_a) { var _b = tslib_1.__read(_a, 2), fileName = _b[0], asset = _b[1]; return [fileName, tslib_1.__assign({}, asset, { asset: null })]; }); } else if (Array.isArray(data.dependencies)) { data.dependencies = tslib_1.__spread(data.dependencies).map(function (asset) { return [asset.name, tslib_1.__assign({}, asset, { asset: null })]; }); } return [4 /*yield*/, this.writeDepMtimes(data)]; case 3: _a.sent(); return [4 /*yield*/, fsExtra.writeFile(cacheFile, JSON.stringify(data))]; case 4: _a.sent(); this.invalidated.delete(filename); return [3 /*break*/, 6]; case 5: err_1 = _a.sent(); err_1.name = 'FSCache Error write cache'; Logger_1.logger.error(VError.fullStack(err_1)); return [3 /*break*/, 6]; case 6: return [2 /*return*/]; } }); }); }; FSCache.prototype.checkDepMtimes = function (data) { return tslib_1.__awaiter(this, void 0, void 0, function () { var e_2, _a, _b, _c, _d, assetName, dep, e_2_1; return tslib_1.__generator(this, function (_e) { switch (_e.label) { case 0: if (!Array.isArray(data.dependencies)) return [3 /*break*/, 8]; _e.label = 1; case 1: _e.trys.push([1, 6, 7, 8]); _b = tslib_1.__values(new Set(data.dependencies)), _c = _b.next(); _e.label = 2; case 2: if (!!_c.done) return [3 /*break*/, 5]; _d = tslib_1.__read(_c.value, 2), assetName = _d[0], dep = _d[1]; if (!(dep && dep.includedInParent)) return [3 /*break*/, 4]; return [4 /*yield*/, this.getLastModified(dep.name)]; case 3: if ((_e.sent()) > dep.mtime) { return [2 /*return*/, false]; } _e.label = 4; case 4: _c = _b.next(); return [3 /*break*/, 2]; case 5: return [3 /*break*/, 8]; case 6: e_2_1 = _e.sent(); e_2 = { error: e_2_1 }; return [3 /*break*/, 8]; case 7: try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_2) throw e_2.error; } return [7 /*endfinally*/]; case 8: return [2 /*return*/, true]; } }); }); }; FSCache.prototype.read = function (filename) { return tslib_1.__awaiter(this, void 0, void 0, function () { var cacheFile, stats, cacheStats, json, data, err_2; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: if (this.invalidated.has(filename)) { return [2 /*return*/, null]; } return [4 /*yield*/, this.getCacheFile(filename)]; case 1: cacheFile = _a.sent(); _a.label = 2; case 2: _a.trys.push([2, 7, , 8]); return [4 /*yield*/, statp(filename)]; case 3: stats = _a.sent(); return [4 /*yield*/, statp(cacheFile)]; case 4: cacheStats = _a.sent(); if (stats.mtime > cacheStats.mtime) { return [2 /*return*/, null]; } return [4 /*yield*/, util_1.promisify(fs.readFile)(cacheFile, { encoding: 'utf-8' })]; case 5: json = _a.sent(); data = JSON.parse(json); return [4 /*yield*/, this.checkDepMtimes(data)]; case 6: if (!(_a.sent())) { return [2 /*return*/, null]; } return [2 /*return*/, data]; case 7: err_2 = _a.sent(); // logger.error(`cannot found ${filename}'s cacheFile: ${cacheFile}`); return [2 /*return*/, null]; case 8: return [2 /*return*/]; } }); }); }; FSCache.prototype.invalidate = function (filename) { this.invalidated.add(filename); }; FSCache.prototype.delete = function (filename) { return tslib_1.__awaiter(this, void 0, void 0, function () { var cacheFile, err_3; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 3, , 4]); return [4 /*yield*/, this.getCacheFile(filename)]; case 1: cacheFile = _a.sent(); return [4 /*yield*/, util_1.promisify(fs.unlink)(cacheFile)]; case 2: _a.sent(); this.invalidated.delete(filename); return [3 /*break*/, 4]; case 3: err_3 = _a.sent(); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }; return FSCache; }()); exports.default = FSCache; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRlNDYWNoZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9GU0NhY2hlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLHVCQUF5QjtBQUN6QixrQ0FBb0M7QUFHcEMsZ0RBQStDO0FBQy9DLDhDQUF1RDtBQUN2RCw4QkFBZ0M7QUFDaEMsMkJBQTZCO0FBQzdCLDZCQUFpQztBQUNqQywrQkFBaUM7QUFDakMscUNBQXVDO0FBSXZDLGtGQUFrRjtBQUNsRixJQUFNLFdBQVcsR0FBaUI7SUFDaEMsT0FBTztJQUNQLFFBQVE7SUFDUixRQUFRO0lBQ1IsU0FBUztJQUNULFNBQVM7SUFDVCxRQUFRO0lBQ1IsUUFBUTtJQUNSLFdBQVc7SUFDWCxTQUFTO0NBQ1YsQ0FBQztBQUVGLElBQU0sTUFBTSxHQUFHLGdCQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDaEMsSUFBTSxLQUFLLEdBQUcsZ0JBQVMsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUM7QUFFakM7SUFNRSxpQkFBWSxPQUFxQjtRQUhqQyxnQkFBVyxHQUFHLElBQUksR0FBRyxFQUFFLENBQUM7UUFJdEIsSUFBSSxDQUFDLEdBQUcsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxRQUFRLElBQUksUUFBUSxDQUFDLENBQUM7UUFDdEQsSUFBTSxJQUFJLEdBQUcsV0FBVyxDQUFDLE1BQU0sQ0FBQyxVQUFDLENBQU0sRUFBRSxDQUFDLElBQUssT0FBQSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUF4QixDQUF3QixFQUFFO1lBQ3ZFLE9BQU8sRUFBRSxHQUFHLENBQUMsT0FBTztTQUNyQixDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsV0FBVyxHQUFHLGtCQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFcEMsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO0lBQ3pC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