leetcode-cli
Version:
A cli tool to enjoy leetcode!
217 lines (180 loc) • 5.23 kB
JavaScript
var cp = require('child_process');
var fs = require('fs');
var path = require('path');
var _ = require('underscore');
var request = require('request');
var h = require('./helper');
var file = require('./file');
var cache = require('./cache');
var config = require('./config');
var log = require('./log');
var Queue = require('./queue');
function Plugin(id, name, ver, desc, deps) {
this.id = id;
this.name = name;
this.ver = ver || 'default';
this.desc = desc || '';
this.enabled = true;
this.deleted = false;
this.missing = (this.ver === 'missing');
this.builtin = (this.ver === 'default');
// only need deps for current platform
this.deps = _.chain(deps || [])
.filter(x => ! x.includes(':') || x.includes(':' + process.platform))
.map(x => x.split(':')[0])
.value();
}
Plugin.prototype.init = function() {
this.config = config.plugins[this.name] || {};
this.next = null;
};
Plugin.prototype.setNext = function(next) {
Object.setPrototypeOf(this, next);
this.next = next;
};
Plugin.prototype.delete = function() {
if (!this.missing) {
try {
const fullpath = file.pluginFile(this.file);
file.rm(fullpath);
} catch(e) {
return log.error(e.message);
}
}
this.deleted = true;
};
Plugin.prototype.save = function() {
const stats = cache.get(h.KEYS.plugins) || {};
if (this.deleted) delete stats[this.name];
else if (this.missing) return;
else stats[this.name] = this.enabled;
cache.set(h.KEYS.plugins, stats);
};
Plugin.prototype.install = function(cb) {
if (this.deps.length === 0) return cb();
const cmd = 'npm install --save ' + this.deps.join(' ');
log.debug(cmd);
const spin = h.spin(cmd);
cp.exec(cmd, {cwd: file.codeDir()}, function(e) {
spin.stop();
return cb(e);
});
};
Plugin.prototype.help = function() {};
Plugin.plugins = [];
Plugin.init = function(head) {
log.trace('initializing all plugins');
head = head || require('./core');
const stats = cache.get(h.KEYS.plugins) || {};
// 1. find installed plugins
let installed = [];
for (let f of file.listCodeDir('lib/plugins')) {
const p = f.data;
if (!p) continue;
log.trace('found plugin: ' + p.name + '=' + p.ver);
p.file = f.file;
p.enabled = stats[p.name];
if (!(p.name in stats)) {
if (p.builtin) {
log.trace('new builtin plugin, enable by default');
p.enabled = true;
} else {
log.trace('new 3rd party plugin, disable by default');
p.enabled = false;
}
}
installed.push(p);
}
// the one with bigger `id` comes first
installed = _.sortBy(installed, x => -x.id);
// 2. init all in reversed order
for (let i = installed.length - 1; i >= 0; --i) {
const p = installed[i];
if (p.enabled) {
p.init();
log.trace('inited plugin: ' + p.name);
} else {
log.trace('skipped plugin: ' + p.name);
}
}
// 3. chain together
const plugins = installed.filter(x => x.enabled);
let last = head;
for (let p of plugins) {
last.setNext(p);
last = p;
}
// 4. check missing plugins
const missings = [];
for (let k of _.keys(stats)) {
if (installed.find(x => x.name === k)) continue;
const p = new Plugin(-1, k, 'missing');
p.enabled = stats[k];
missings.push(p);
log.trace('missing plugin:' + p.name);
}
Plugin.plugins = installed.concat(missings);
return missings.length === 0;
};
Plugin.copy = function(src, cb) {
// FIXME: remove local file support?
if (path.extname(src) !== '.js') {
src = config.sys.urls.plugin.replace('$name', src);
}
const dst = file.pluginFile(src);
const srcstream = src.startsWith('https://') ? request(src) : fs.createReadStream(src);
const dststream = fs.createWriteStream(dst);
let error;
srcstream.on('response', function(resp) {
if (resp.statusCode !== 200)
srcstream.emit('error', 'HTTP Error: ' + resp.statusCode);
});
srcstream.on('error', function(e) {
dststream.emit('error', e);
});
dststream.on('error', function(e) {
error = e;
dststream.end();
});
dststream.on('close', function() {
spin.stop();
if (error) file.rm(dst);
return cb(error, dst);
});
log.debug('copying from ' + src);
const spin = h.spin('Downloading ' + src);
srcstream.pipe(dststream);
};
Plugin.install = function(name, cb) {
Plugin.copy(name, function(e, fullpath) {
if (e) return cb(e);
log.debug('copied to ' + fullpath);
const p = require(fullpath);
p.file = path.basename(fullpath);
p.install(function() {
return cb(null, p);
});
});
};
Plugin.installMissings = function(cb) {
function doTask(plugin, queue, cb) {
Plugin.install(plugin.name, function(e, p) {
if (!e) {
p.enabled = plugin.enabled;
p.save();
p.help();
}
return cb(e, p);
});
}
const missings = Plugin.plugins.filter(x => x.missing);
if (missings.length === 0) return cb();
log.warn('Installing missing plugins, might take a while ...');
const q = new Queue(missings, {}, doTask);
q.run(1, cb);
};
Plugin.save = function() {
for (let p of this.plugins) p.save();
};
module.exports = Plugin;
;