astroboy
Version:
Astroboy(阿童木)is a Nodejs SFB(Separation of Front and Back ends) framework, built on koa2.
296 lines • 8.76 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
// @ts-ignore no types matched
const pathMatching = require("path-matching");
const path = require("path");
const lodash = require("lodash");
const util_1 = require("./lib/util");
const Loader_1 = require("./Loader");
/**
* ## Core Loader
* - decide how to build application.
*
* @author Big Mogician
* @export
* @class CoreLoader
* @extends {Loader<F, A>}
* @template F
* @template A
*/
class CoreLoader extends Loader_1.Loader {
constructor(options = {}) {
super(options);
this.middlewareList = [];
this.options = options || {};
this.astroboy = this.options.astroboy;
this.app = this.options.app || this.app;
this.NODE_ENV = this.app.NODE_ENV || 'development';
this.patterns = Object.assign({}, this.defaultPatterns);
}
get defaultPatterns() {
return {
loaderPattern: `/loader/*.(js|ts)`,
pluginPattern: `/config/plugin.(default|${this.NODE_ENV}).(js|ts)`,
loaderConfigPattern: `/config/loader.(default|${this.NODE_ENV}).(js|ts)`,
};
}
/**
* ### `init` hook
*
* @virtual
* @author Big Mogician
* @protected
* @memberof CoreLoader
*/
async init() {
await this.loadCoreDirs(this.app.ROOT_PATH);
await this.loadPluginConfig();
await this.loadFullDirs();
await this.loadLoaderQueue();
await this.loadLoaders();
await this.runLoaders();
this.useMiddlewares();
}
/**
* ### `load` hook
*
* @virtual
* @author Big Mogician
* @memberof CoreLoader
*/
async load() {
await this.init();
}
/**
* ### 加载核心目录,包括 app、framework,但不包括 plugin
*
* @author Big Mogician
* @protected
* @param {string} baseDir
* @memberof CoreLoader
*/
async loadCoreDirs(baseDir) {
const coreDirs = [
{
baseDir: baseDir,
type: 'app',
name: path.basename(baseDir),
},
];
let proto = this.astroboy;
while (proto) {
proto = Object.getPrototypeOf(proto);
if (proto) {
const newBaseDir = proto[Symbol.for('BASE_DIR')];
if (newBaseDir) {
coreDirs.push({
baseDir: newBaseDir,
type: 'framework',
name: path.basename(newBaseDir),
});
}
}
}
this.coreDirs = coreDirs.reverse();
await util_1.outputJsonAsync(`${this.app.ROOT_PATH}/run/coreDirs.json`, this.coreDirs);
}
/**
* ### 获取插件配置
*
* @author Big Mogician
* @protected
* @memberof CoreLoader
*/
async loadPluginConfig() {
let pluginConfig = {};
for (const item of this.coreDirs) {
const entries = await this.globDir(item.baseDir, this.patterns.pluginPattern);
pluginConfig = entries.reduce((a, b) => {
const content = require(b);
return lodash.merge(a, content);
}, pluginConfig);
}
this.pluginConfig = pluginConfig;
await util_1.outputJsonAsync(`${this.app.ROOT_PATH}/run/pluginConfig.json`, pluginConfig);
}
/**
* ### 获取遍历目录
*
* @author Big Mogician
* @protected
* @memberof CoreLoader
*/
async loadFullDirs() {
let dirs = [];
for (const item of this.coreDirs) {
dirs = dirs.concat((await this.getPluginDirs(item.baseDir)).reverse());
dirs.push(item);
}
this.dirs = dirs;
await util_1.outputJsonAsync(`${this.app.ROOT_PATH}/run/dirs.json`, dirs);
}
/**
* ### 获取需要遍历的插件目录
*
* @author Big Mogician
* @protected
* @param {string} baseDir
* @returns
* @memberof CoreLoader
*/
async getPluginDirs(baseDir) {
const config = await this.getPluginConfig(baseDir);
const ret = [];
if (lodash.isPlainObject(config)) {
for (let name in config) {
if (this.pluginConfig[name].enable) {
const baseDir = this.getPluginPath(config[name]);
ret.push({
baseDir: baseDir,
type: 'plugin',
name: path.basename(baseDir),
});
}
}
}
return ret;
}
/**
* ### 获取需要遍历的插件配置
*
* @author Big Mogician
* @protected
* @param {string} baseDir
* @returns
* @memberof CoreLoader
*/
async getPluginConfig(baseDir) {
let config = {};
const entries = await this.globDir(baseDir, this.patterns.pluginPattern);
config = entries.reduce((a, b) => {
return lodash.merge(a, require(b));
}, {});
return config;
}
/**
* ### 获取加载器执行队列
*
* @author Big Mogician
* @protected
* @memberof CoreLoader
*/
async loadLoaderQueue() {
let loaderConfig = {};
const entries = await this.globDirs(this.patterns.loaderConfigPattern);
loaderConfig = entries.reduce((previousValue, currentValue) => {
return lodash.merge(previousValue, require(currentValue));
}, loaderConfig);
let queue = [];
Object.keys(loaderConfig).forEach(item => {
queue.push(Object.assign({
priority: 300,
name: item,
}, loaderConfig[item]));
});
queue = queue.sort((a, b) => {
return a.priority - b.priority;
});
this.loaderQueue = queue;
}
/**
* ### 获取加载器
*
* @author Big Mogician
* @protected
* @memberof CoreLoader
*/
async loadLoaders() {
let loaders = {};
const entries = await this.globDirs(this.patterns.loaderPattern);
entries.forEach(entry => {
const key = this.resolveExtensions(path.basename(entry));
loaders[key] = require(entry);
});
this.loaders = loaders;
}
/**
* ### 执行加载器
*
* @author Big Mogician
* @protected
* @memberof CoreLoader
*/
async runLoaders() {
const app = this.app;
const loaders = this.loaders;
for (const item of this.loaderQueue) {
if (loaders[item.name]) {
const loader = new loaders[item.name]({
dirs: this.dirs,
config: item.options,
app,
});
if (!(util_1.isLoader(loader))) {
throw new Error(`Loader ${item.name} must extend Loader.`);
}
await loader.load();
}
else {
throw new Error(`Loader ${item.name} is not found.`);
}
}
}
/**
* ### Use Middlewares
*
* @author Big Mogician
* @protected
* @memberof CoreLoader
*/
useMiddlewares() {
const app = this.app;
const middlewares = app.middlewares;
const self = this;
app.middlewareQueue.forEach(item => {
if (middlewares[item.name]) {
let fn = middlewares[item.name](item.options, app);
// 定义扩宽,match和ignore实际上只有一个存在并生效
if (item.match || item.ignore) {
fn = this.wrapMiddleware(fn, item);
}
if (fn) {
app.use(fn);
self.middlewareList.push(fn);
}
}
else {
throw new Error(`middleware ${item.name} is not found.`);
}
});
}
/**
* ### Wrap Middlewares
*
* @author Big Mogician
* @protected
* @param {NormalizedMiddleware} middleware
* @param {IPriority} options
* @returns
* @memberof CoreLoader
*/
wrapMiddleware(middleware, options) {
const match = pathMatching(options);
let fn = async function (ctx, next) {
if (match(ctx)) {
await middleware(ctx, next);
}
else {
await next();
}
};
fn._name = `wrap-${middleware.name || middleware._name}`;
return fn;
}
}
exports.CoreLoader = CoreLoader;
//# sourceMappingURL=CoreLoader.js.map