All files Core.ts

18.09% Statements 17/94
5.56% Branches 1/18
21.43% Functions 3/14
29.63% Lines 16/54
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 1202x 2x           2x 1x 1x 1x 1x               1x     2x 1x     2x                                               2x                                   2x                               2x                           2x                     2x                    
import * as ReactDOM from 'react-dom';
import { log } from '@ae.sdp.nd/utils/src/debug';
 
type ModuleConfig = any;
type ModuleParser = any;
type ModuleRender = any;
 
export default class Core {
  public modules: ModuleConfig[] = [];
  public parsers: ModuleParser[] = [];
  public renderers: ModuleRender[] = [];
  public container: HTMLElement | string = '#app';
  public parsedModules: ModuleConfig[];
  public renderedModules: React.ReactElement<any>;
 
  // for extends
  [key: string]: any;
 
  constructor(options?: any) {
    this.setProps(options);
  }
 
  public setProps(Eoptions = {}) {
    (<any>Object).assign(this, options);
  }
 
  public async mount() {
    const { container } = this;
    const renderedModules = await this.getRenderedModules();
    // 挂载
    const mountingElement: HTMLElement = typeof container === 'string'
      ? document.querySelector(container)
      : container;
    if (process.env.NODE_ENV !== 'production') log('挂载组件树 %O', mountingElement);
    ReactDOM.render(renderedModules, mountingElement);
  }
 
  /**
   * 递归处理模块,顺序执行 parser
   * 暴露出去,给动态模块用(Creator,Ref)
   * @todo 动态注册的模块,是否需要合并到现有模块树?
   * @todo 支持使用 express/koa 类的 next 来控制顺序?
   * @example
   * // modules: [m1, m2]
   * // parsers: [p1, p2]
   * p1.pre(m1) -> p2.pre(m1) -> p2.post(m1) -> p1.post(m1)
   * p1.pre(m2) -> p2.pre(m2) -> p2.post(m2) -> p1.post(m2)
   * // 如果有子模块(深度优先)
   * p1.pre(m1) -> p2.pre(m1) -> (子模块流程,同父模块) -> p2.post(m1) -> p1.post(m1)
   */
  public async parseModules(modules: any, parent = {}) {
    const { parsers } = this;
    // 并行处理各模块
    return (<any>Promise).all(modules.map(async (mc: any) => {
      mc = await parsers.reduce(async (_mc, parser) => {
        return parser.pre ? parser.pre(await interopDefaultExports(_mc), parent) : _mc;
      }, mc);
      // 子模块在 post 之前处理掉
      if (mc.modules) {
        mc.modules = await this.parseModules(mc.modules, mc);
      }
      mc = await parsers.reduceRight(async (_mc, parser) => {
        return parser.post ? parser.post(await interopDefaultExports(_mc), parent) : _mc;
      }, mc);
      return mc;
    }));
  }
 
  private async getRenderedModules() {
    if (!this.renderedModules) {
      const parsedModules = await this.getParsedModules();
      if (process.env.NODE_ENV !== 'production') log('渲染模块树 开始');
      this.renderedModules = await this.renderModules(parsedModules);
      if (process.env.NODE_ENV !== 'production') log('渲染模块树 结束 %O', this.renderedModules);
    }
    return this.renderedModules;
  }
 
  /**
   * 渲染模块树
   * @todo 支持使用 express/koa 类的 next 来控制顺序?
   * @param   {Object[]}  parsedModules  Parsers 解析后的原始模块树
   * @return  {any}
   */
  private async renderModules(parsedModules) {
    const { renderers } = this;
    let renderedModules = parsedModules;
    // 前置处理
    renderedModules = await renderers.reduce(async (modules, renderer) => {
      return renderer.pre ? renderer.pre(await modules) : modules;
    }, renderedModules);
    // 后置处理
    renderedModules = await renderers.reduceRight(async (modules, renderer) => {
      return renderer.post ? renderer.post(await modules) : modules;
    }, renderedModules);
    return renderedModules;
  }
 
  private async getParsedModules() {
    if (!this.parsedModules) {
      if (process.env.NODE_ENV !== 'production') log('解析模块树 开始');
      this.parsedModules = await this.parseModules(this.modules);
      if (process.env.NODE_ENV !== 'production') log('解析模块树 结束 %O', this.parsedModules);
      // 清理内存?
      this.modules = null;
      this.parsers = null;
    }
    return this.parsedModules;
  }
}
 
// 对于使用 import() 引入的模块,需要转换
async function interopDefaultExports(m: any) {
  const _ = await m;
  if ('default' in _) {
    return _.default;
  }
  return _;
}