cli-engine
Version:
Generic CLI Framework
187 lines (153 loc) • 5.59 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
require('cli-engine-config');
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _fsExtra = require('fs-extra');
var _fsExtra2 = _interopRequireDefault(_fsExtra);
var _manager = require('./manager');
var _namespaces = require('../namespaces');
var _namespaces2 = _interopRequireDefault(_namespaces);
var _yarn = require('./yarn');
var _yarn2 = _interopRequireDefault(_yarn);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
class UserPluginPath extends _manager.PluginPath {
constructor({ output, type, path, tag, userPlugins }) {
super({ output, type, path, tag });
this.repairAttempted = false;
this.userPlugins = userPlugins;
}
async repair(err) {
if (err.code !== 'MODULE_NOT_FOUND') return false;
if (this.repairAttempted) return false;
this.out.warn(err);
this.out.action.start(`Repairing plugin ${this.path}`);
this.repairAttempted = true;
await this.userPlugins.installForce();
this.out.action.stop();
return true;
}
}
class UserPlugins extends _manager.Manager {
constructor({ out, config, cache }) {
super({ out, config, cache });
this.yarn = new _yarn2.default(this.out, this.userPluginsDir);
/**
* There is a bug with snappy & node-gyp & semver that causes issues when
* we rebuild. What happens is that semver is downgraded from 5.3.0 to
* 4.3.2 which has a bug in it that causes node-gyp to fail
*
* I was going to try and make this an add to package.json to save some
* time but that causes 4.3.2 to install into node-gyp/node_modules for
* reasons I do not understand but `add` does the right thing
*
* I set version to ~5.3.0 to match the current dependency from node-gyp
* under the assumption that it is the most likely to work properly
*
* https://github.com/nodejs/node-gyp/blob/75cfae290fee1791a23fa68820ae5dd841e93e14/package.json#L34
*/
this.hardcodedDepFixes = {
semver: '~5.3.0'
};
}
/**
* list user plugins
* @returns {PluginPath[]}
*/
async list() {
try {
const pjson = this.userPluginsPJSON;
return entries(pjson.dependencies || {}).filter(([name, tag]) => {
return !this.hardcodedDepFixes[name];
}).map(([name, tag]) => {
return new UserPluginPath({ output: this.out, type: 'user', path: this.userPluginPath(name), tag: tag, userPlugins: this });
});
} catch (err) {
this.out.warn(err, 'error loading user plugins');
return [];
}
}
get userPluginsPJSON() {
try {
return _fsExtra2.default.readJSONSync(this.userPluginsPJSONPath);
} catch (err) {
return { dependencies: {} };
}
}
saveUserPluginsPJSON(pjson) {
_fsExtra2.default.writeJSONSync(_path2.default.join(this.userPluginsPJSONPath), pjson);
}
async setupUserPlugins() {
const pjson = _path2.default.join(this.userPluginsDir, 'package.json');
const yarnrc = _path2.default.join(this.userPluginsDir, '.yarnrc');
_fsExtra2.default.mkdirpSync(this.userPluginsDir);
if (!_fsExtra2.default.existsSync(pjson)) _fsExtra2.default.writeFileSync(pjson, JSON.stringify({ private: true }));
if (!_fsExtra2.default.existsSync(yarnrc)) _fsExtra2.default.writeFileSync(yarnrc, 'registry "https://cli-npm.heroku.com/"');
}
async installForce() {
if (_fsExtra2.default.existsSync(_path2.default.join(this.userPluginsDir, 'node_modules'))) {
let dependencies = this.userPluginsPJSON['dependencies'] || {};
for (let mod in this.hardcodedDepFixes) {
if (!dependencies[mod]) {
await this.yarn.exec(['add', `${mod}@${this.hardcodedDepFixes[mod]}`]);
}
}
await this.yarn.exec(['install', '--force']);
}
}
async handleNodeVersionChange() {
try {
await this.installForce();
} catch (err) {
this.out.warn(err);
}
}
async install(name, tag = 'latest') {
await this.setupUserPlugins();
this.addPackageToPJSON(name, tag);
try {
await this.yarn.exec();
let path = this.userPluginPath(name);
_namespaces2.default.throwErrorIfNotPermitted(path, this.config);
let plugin = require(path);
if (!plugin.commands) throw new Error(`${name} does not appear to be a ${this.config.bin} CLI plugin`);
return path;
} catch (err) {
this.removePackageFromPJSON(name);
this.out.error(err);
throw new Error('unreachable');
}
}
async update() {
await this.setupUserPlugins();
await this.yarn.exec(['upgrade']);
}
async remove(name) {
await this.yarn.exec(['remove', name]);
}
addPackageToPJSON(name, version = '*') {
let pjson = this.userPluginsPJSON;
if (!pjson.dependencies) pjson.dependencies = {};
pjson.dependencies[name] = version;
this.saveUserPluginsPJSON(pjson);
}
removePackageFromPJSON(name) {
let pjson = this.userPluginsPJSON;
if (!pjson.dependencies) pjson.dependencies = {};
delete pjson.dependencies[name];
this.saveUserPluginsPJSON(pjson);
}
get userPluginsDir() {
return _path2.default.join(this.config.dataDir, 'plugins');
}
get userPluginsPJSONPath() {
return _path2.default.join(this.userPluginsDir, 'package.json');
}
userPluginPath(name) {
return _path2.default.join(this.userPluginsDir, 'node_modules', name);
}
}
exports.default = UserPlugins;
const entries = o => Object.keys(o).map(k => [k, o[k]]);