node_modules/egg-core/lib/loader/mixin/config.js

'use strict';

const debug = require('debug')('egg-core:config');
const path = require('path');
const extend = require('extend2');
const assert = require('assert');
const { Console } = require('console');

const SET_CONFIG_META = Symbol('Loader#setConfigMeta');

module.exports = {

  /**
   * Load config/config.js
   *
   * Will merge config.default.js 和 config.${env}.js
   *
   * @function EggLoader#loadConfig
   * @since 1.0.0
   */
  loadConfig() {
    this.timing.start('Load Config');
    this.configMeta = {};

    const target = {};

    // Load Application config first
    const appConfig = this._preloadAppConfig();

    //   plugin config.default
    //     framework config.default
    //       app config.default
    //         plugin config.{env}
    //           framework config.{env}
    //             app config.{env}
    for (const filename of this.getTypeFiles('config')) {
      for (const unit of this.getLoadUnits()) {
        const isApp = unit.type === 'app';
        const config = this._loadConfig(unit.path, filename, isApp ? undefined : appConfig, unit.type);

        if (!config) {
          continue;
        }

        debug('Loaded config %s/%s, %j', unit.path, filename, config);
        extend(true, target, config);
      }
    }

    // You can manipulate the order of app.config.coreMiddleware and app.config.appMiddleware in app.js
    target.coreMiddleware = target.coreMiddlewares = target.coreMiddleware || [];
    target.appMiddleware = target.appMiddlewares = target.middleware || [];

    this.config = target;
    this.timing.end('Load Config');
  },

  _preloadAppConfig() {
    const names = [
      'config.default',
      `config.${this.serverEnv}`,
    ];
    const target = {};
    for (const filename of names) {
      const config = this._loadConfig(this.options.baseDir, filename, undefined, 'app');
      extend(true, target, config);
    }
    return target;
  },

  _loadConfig(dirpath, filename, extraInject, type) {
    const isPlugin = type === 'plugin';
    const isApp = type === 'app';

    let filepath = this.resolveModule(path.join(dirpath, 'config', filename));
    // let config.js compatible
    if (filename === 'config.default' && !filepath) {
      filepath = this.resolveModule(path.join(dirpath, 'config/config'));
    }
    const config = this.loadFile(filepath, this.appInfo, extraInject);

    if (!config) return null;

    if (isPlugin || isApp) {
      assert(!config.coreMiddleware, 'Can not define coreMiddleware in app or plugin');
    }
    if (!isApp) {
      assert(!config.middleware, 'Can not define middleware in ' + filepath);
    }

    // store config meta, check where is the property of config come from.
    this[SET_CONFIG_META](config, filepath);

    return config;
  },

  [SET_CONFIG_META](config, filepath) {
    config = extend(true, {}, config);
    setConfig(config, filepath);
    extend(true, this.configMeta, config);
  },
};

function setConfig(obj, filepath) {
  for (const key of Object.keys(obj)) {
    const val = obj[key];
    // ignore console
    if (key === 'console' && val && typeof val.Console === 'function' && val.Console === Console) {
      obj[key] = filepath;
      continue;
    }
    if (val && Object.getPrototypeOf(val) === Object.prototype && Object.keys(val).length > 0) {
      setConfig(val, filepath);
      continue;
    }
    obj[key] = filepath;
  }
}