@dawnjs/cli
Version:
186 lines (168 loc) • 5.69 kB
JavaScript
/**
* Copyright (c) 2016-present Alibaba Group Holding Limited
* @author Houfeng <admin@xhou.net>
*/
const path = require('path');
const middleware = require('./middleware');
const template = require('./template');
const utils = require('./common/utils');
const fs = require('fs');
const unhandled = require('./middlewares/unhandled');
const pkg = require('../package');
const console = require('./common/console');
const configs = require('./configs');
const mod = require('./mod');
const EventEmitter = require('events');
const stp = require('stp');
const debug = require('debug')('context');
class Context extends EventEmitter {
constructor(cli, opts) {
super();
opts = opts || {};
utils.copy(opts, this);
utils.copy(cli.params, this);
this.cli = cli;
this.cli.pkg = pkg;
this.middlewareMgr = middleware;
this.templateMgr = template;
this.pipeline = opts.pipeline || [];
this.command = this.cli.get('command');
this.cwd = process.cwd();
this.console = console;
this.utils = utils;
this.inquirer = utils.inquirer;
this.configName = './.dawn';
this.configPath = path.resolve(this.cwd, this.configName);
this.conf = configs;
this.mod = mod;
}
async configIsExists() {
debug('config name', this.configName);
const files = await utils.globby([`${this.configName}/**/*.*`, `${this.configName}.*`]);
debug('config files', files);
return files.length > 0;
}
get project() {
const pkgFile = path.normalize(`${this.cwd}/package.json`);
if (!fs.existsSync(pkgFile)) return {};
try {
const text = fs.readFileSync(pkgFile).toString();
return JSON.parse(text);
} catch (err) {
return this.console.error(err);
}
}
async loadLocalConfigs() {
const localConfigs = utils.confman.load(this.configPath);
debug('local configs', localConfigs);
return localConfigs || {};
}
async _loadLocalAllPipeline() {
const localConfigs = await this.loadLocalConfigs();
return localConfigs.pipe || {};
}
async _loadLocalePipeline(command) {
if (!(await this.configIsExists())) return [];
command = command || this.command;
const pipelines = await this._loadLocalAllPipeline();
return pipelines[command] || [];
}
async _mergeRemotePipeline(command, list) {
command = command || this.command;
if (!(await this.configIsExists())) return [];
const pipe = (await configs.getRemoteConf('pipe')) || {};
list = list || [];
pipe.before = pipe.before || [];
pipe.after = pipe.after || [];
const beforeList = pipe.before[command] || [];
beforeList.reverse();
beforeList.forEach(item => {
if (list.some(i => i.name == item.name && !item.force)) return;
list.unshift(item);
});
const afterList = pipe.after[command] || [];
afterList.forEach(item => {
if (list.some(i => i.name == item.name && !item.force)) return;
list.push(item);
});
return list;
}
async loadPipeline(command) {
command = command || this.command;
if (!(await this.configIsExists())) return [];
const list = await this._loadLocalePipeline(command);
return this._mergeRemotePipeline(command, list);
}
async _installProjectDeps() {
if (!(await this.configIsExists())) return;
const modulesPath = path.resolve(this.cwd, './node_modules');
const packagePath = path.resolve(this.cwd, './package.json');
if (fs.existsSync(modulesPath) || !fs.existsSync(packagePath)) return;
await mod.install();
}
async _execQueue(middlewares, args, onFail) {
const middleware = middlewares.shift();
if (!middleware) return;
const handler = await this.load(middleware);
const next = args => {
if (next.__result) return next.__result;
next.__result = this._execQueue(middlewares, args, onFail).catch(err => onFail(err));
return next.__result;
};
return handler.call(this, next, this, args);
}
unescapeExpr(str) {
if (!str) return str;
return str.replace(/\\\{/, '{').replace('/\\}/', '}');
}
_parseOpts(opts) {
utils.each(opts, (name, value) => {
if (utils.isString(name)) {
delete opts[name];
name = this.unescapeExpr(stp(name)(this) || name);
}
if (utils.isString(value)) {
opts[name] = this.unescapeExpr(stp(value)(this));
} else if (utils.isObject(value)) {
opts[name] = this._parseOpts(value);
} else {
opts[name] = value;
}
});
return opts;
}
async load(opts) {
if (utils.isFunction(opts)) return opts;
if (!opts || !opts.name) {
throw new Error('Invalid pipeline config');
}
opts = this._parseOpts(opts);
const modFactory = opts.location
? require(path.resolve(this.cwd, opts.location))
: await middleware.require(opts.name, this.cwd);
if (!utils.isFunction(modFactory)) {
throw new Error(`Invalid middleware '${opts.name}'`);
}
return modFactory.call(this, opts, this);
}
async exec(middlewares, initailArgs) {
if (!utils.isArray(middlewares)) middlewares = [middlewares];
return new Promise((resolve, reject) => {
middlewares.push((next, ctx, args) => {
resolve(args);
});
this._execQueue(middlewares, initailArgs, reject).catch(err => reject(err));
});
}
async run() {
await this._installProjectDeps();
if (!this.pipeline || this.pipeline.length < 1) {
this.pipeline = await this.loadPipeline();
}
if (this.command != 'init' && this.pipeline.length < 1) {
this.pipeline.push(unhandled);
}
return this.exec(this.pipeline);
}
}
module.exports = Context;