jgb-cli
Version:
```shell npm i -g jgb-cli #全局安装 ```
294 lines • 19.7 kB
JavaScript
"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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