cli-engine
Version:
Generic CLI Framework
195 lines (158 loc) • 4.97 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
require('cli-engine-config');
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _yarn = require('./yarn');
var _yarn2 = _interopRequireDefault(_yarn);
var _klawSync = require('klaw-sync');
var _klawSync2 = _interopRequireDefault(_klawSync);
var _fsExtra = require('fs-extra');
var _fsExtra2 = _interopRequireDefault(_fsExtra);
var _manager = require('./manager');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function touch(f) {
_fsExtra2.default.utimesSync(f, new Date(), new Date());
}
class LinkedPlugins extends _manager.Manager {
constructor({ out, config, cache }) {
super({ out, config, cache });
try {
this._data = _fsExtra2.default.readJSONSync(this.file);
this._data.updated_at = new Date(this._data.updated_at || 0);
} catch (err) {
if (err.code !== 'ENOENT') throw err;
this._data = {
version: '1',
updated_at: new Date(),
plugins: []
};
}
}
/**
* adds a linked plugin
* @param {string} p - path of plugin
*/
async add(p) {
if (!this.config.debug) this.out.action.start(`Running prepare script for ${p}`);
await this.prepare(p);
let m = require(p);
if (!m.commands) throw new Error(`${p} does not appear to be a CLI plugin`);
this._data.plugins.push(p);
this._save();
this.out.action.stop();
}
/**
* removes a linked plugin
* @param {string} p - path of plugin
*/
remove(p) {
this._data.plugins = this._data.plugins.filter(q => q !== p);
this._save();
}
/**
* list linked plugins
* @returns {PluginPath[]}
*/
async list() {
try {
await this.load();
return this._data.plugins.map(p => {
return new _manager.PluginPath({ output: this.out, type: 'link', path: p });
});
} catch (err) {
this.out.warn(err, 'Error loading linked plugins');
return [];
}
}
async load() {
if (this.loaded) return;
await this.refresh();
this.loaded = true;
}
/**
* runs prepare() on all linked plugins
*/
async refresh() {
let paths = [];
for (let plugin of this._data.plugins) {
try {
if (await this.prepare(plugin)) {
paths.push(plugin);
}
} catch (err) {
this.out.warn(`Error refreshing ${plugin}`);
this.out.warn(err);
}
}
if (paths.length > 0) {
this.cache.deletePlugin(...paths);
this._data.updated_at = new Date();
this._save();
}
}
/**
* installs plugin dependencies and runs npm prepare if needed
*/
async prepare(p) {
let pjson = this._pjson(p);
await this._install(p);
if (!pjson.main) throw new Error(`No main script specified in ${_path2.default.join(p, 'package.json')}`);
let main = _path2.default.join(p, pjson.main);
if (!this._needsPrepare(p, main)) return false;
if (pjson.scripts && pjson.scripts.prepare) {
if (!this.config.debug) this.out.action.start(`Running prepare script for ${p}`);
let yarn = new _yarn2.default(this.out, p);
await yarn.exec(['run', 'prepare']);
this.out.action.stop();
}
return true;
}
_save() {
_fsExtra2.default.outputJsonSync(this.file, this._data, { spaces: 2 });
}
_needsInstall(p) {
let modules = _path2.default.join(p, 'node_modules');
if (!_fsExtra2.default.existsSync(modules)) return true;
let modulesInfo = _fsExtra2.default.statSync(modules);
let pjsonInfo = _fsExtra2.default.statSync(_path2.default.join(p, 'package.json'));
return modulesInfo.mtime < pjsonInfo.mtime;
}
_needsPrepare(p, main) {
if (!_fsExtra2.default.existsSync(main)) return true;
return !!(0, _klawSync2.default)(p, {
noRecurseOnFailedFilter: true,
filter: f => !['.git', 'node_modules', 'flow-typed'].includes(_path2.default.basename(f.path))
}).filter(f => f.path.endsWith('.js')).find(f => f.stats.mtime > this._data.updated_at);
}
async _install(p, force = false) {
if (!force && !this._needsInstall(p)) return;
if (!this.config.debug) this.out.action.start(`Installing dependencies for ${p}`);
let yarn = new _yarn2.default(this.out, p);
await yarn.exec();
touch(_path2.default.join(p, 'node_modules'));
this.out.action.stop();
}
async handleNodeVersionChange() {
for (let p of this._data.plugins) {
try {
await this._install(p, true);
} catch (err) {
this.out.warn(err);
}
}
}
checkLinked(p) {
if (this._data.plugins.includes(p)) throw new Error(`${p} is already linked`);
return this._pjson(p).name;
}
_pjson(p) {
return require(_path2.default.join(p, 'package.json'));
}
get file() {
return _path2.default.join(this.config.dataDir, 'linked_plugins.json');
}
}
exports.default = LinkedPlugins;