• 项目配置

    编写

    hzero-front 使用生成的方式编写 utils/config.js 文件

    配置说明

    配置是固定字符串

    高级配置

    1. route: init 返回的值 可以 被 routeMap 这个环境变量中对应值覆盖
    2. noChange: 不能在运行中被changeRoute更改
    3. deps&get: 该配置依赖于其他配置 deps, 生成方法是 get

    config/apiConfig.js 文件的编写

    固定配置

      const config ={
        VERSION: 'v1',
      };
    

    后端服务路由

      const config ={
        HZERO_HXXX: {
          init: () => {
             return "'/hxxx'";
          },
          route: true,
        },
      };
    

    不可在运行时更改

      const config = {
        CLIENT_ID: {
          init: () => {
            return '`${process.env.CLIENT_ID}`';
          },
          noChange: true,
        },
      };
    

    依赖其他变量

      const config = {
        AUTH_LOGOUT_URL: {
          deps: ['AUTH_HOST'],
          get: (AUTH_HOST) => {
            return `${AUTH_HOST}/logout`;
          },
        },
      };
    

    实际文件

    config/apiConfig.js 编写文件

    /**
     * 配置 utils/config 下的常量
     */
    
    /**
     * deps 和 get 必须同时出现
     * get 和 init 是互斥的
     * 如果有 route, 那么必须有init
     *
     * @typedef {Object|string} CONFIG
     * @property {?Function} init - 返回返回字符串包裹的字符串模板; 参照 CLIENT_ID
     * @property {?boolean} noChange - 是否能改变 false/undefined => 能改变, true => 不能改变; 参照 CLIENT_ID
     * @property {?boolean} route - 是否能被环境变量routeMap改变 false/undefined => 可以改变, true => 不能改变; 参照 HZERO_PLATFORM
     * @property {?string[]} deps - 依赖的CONFIG, 当 依赖的CONFIG 改变时 会同时改变; 参照 AUTH_SELF_URL 的配置
     * @property {?Function} get - 依赖改变生成自己的方法; (...deps: string[]) => string; 参照 AUTH_SELF_URL 的配置
     */
    
    /**
     * @type {{[configId]: CONFIG}}
     */
    const config = {
      CLIENT_ID: {
        init: () => {
          return '`${process.env.CLIENT_ID}`';
        },
        noChange: true,
      },
      WEBSOCKET_URL: {
        init: () => {
          return '`${process.env.WEBSOCKET_HOST}`';
        },
      },
      HZERO_PLATFORM: {
        init: () => {
          return "'/hpfm'";
        },
        route: true,
      },
      HZERO_FILE: {
        deps: ['API_HOST', 'HZERO_HFLE'],
        get: (API_HOST, HZERO_HFLE) => {
          return `${API_HOST}${HZERO_HFLE}`;
        },
      },
      VERSION_IS_OP: {
        deps: ['PLATFORM_VERSION'],
        get: (PLATFORM_VERSION) => {
          return `${PLATFORM_VERSION}` === 'OP';  //  OP版
        },
        init: () => {
          return '`${process.env.PLATFORM_VERSION}`  ===  \'OP\'';  //  OP版
        },
      },
      // ...
    };
    
    const prefix = `/**
     * 不要直接修改这个文件, 请修改 config/apiConfig 文件
     * Config - 全局统一配置
     * @date: 2018-6-20
     * @author: niujiaqing <njq.niu@hand-china.com>
     * @version: 0.0.1
     * @copyright Copyright (c) 2018, Hand
     */
    `;
    
    const suffix = '';
    
    module.exports = {
      config,
      prefix,
      suffix,
    };
    
    

    生成方法文件 scripts/genConfig.js

    /**
     * TODO: 已经可以直接通过 changeRoute('/iam', '/iam-xxxxx') 来在运行时改变配置了, 需要将 .route 相关的代码注释取消 和注释之下的代码, 需要注意单引号问题, 已知问题, 不能有重复的 配置名
     * 通过 ../config/apiConfig 生成 utils/config 文件
     * @author WY <yang.wang06@hand-china.com>
     * @date 2019-05-22
     * @copyright ® HAND
     */
    const {config, prefix, suffix} = require('../config/apiConfig');
    
    const helpFuncMap = {};
    const initLang = [];
    const changeRouteSwitch = new Map(); // 存储 case 代码
    const configChangeFunc = [];
    const exportConfig = [];
    // 不需要从 'HZERO_PLATFORM' -> '/hpfm' 的映射
    // '/hpfm' -> 'HZERO_PLATFORM' 的映射
    // const routeMapVar = {};
    
    Object.entries(config).forEach(([key, value]) => {
    
      // #region exportConfig
      exportConfig.push(key);
      // #endregion
    
      // #region initConfig
      let varType = 'let';
      if (value.noChange) {
        varType = 'const';
      }
    
      //  init  lang
      if (value.init) {
        if (value.route) {
          // 路由 不能直接被环境变量更改, 所有 route: true 的 init 是直接返回可用的变量
          const routePath = value.init();
          // if (routeMapVar[routePath]) {
          //   throw new Error(formatString('route define has two config %12s, %12s has same value %8s', routeMapVar[routePath], key, routePath));
          // } else {
          //   routeMapVar[routePath] = key;
          // }
          initLang.push(formatString('%8s %16s = %s;', varType, key, ` routeMap[${routePath}] || ${routePath}`));
        } else {
          // not route
          initLang.push(formatString('%8s %16s = %s;', varType, key, value.init()));
        }
    
      } else if (value.get) {
        //  get  config  return  body;
        // TODO: 初始化时也调用方法
        // const retBody = value.get.toString().match(/return ([\S\s]+);/);
        // initLang.push(`${varType} ${key} = ${(retBody && retBody[1] || '')};`);
        initLang.push(formatString('%8s %16s = %s;', varType, key, `change${key}(${value.deps.join(', ')})`));
      } else {
        initLang.push(formatString('%8s %16s = %s;', varType, key, `'${value}'`));
      }
      // #endregion
    
      if (value.noChange) {
        // noChange 不能在运行时更改
        return;
      }
    
      // #region help  func
      // const helpKey = value.route ? value.init() : key;
      const helpKey = key;
      const curHelp = helpFuncMap[helpKey] || {changeConfig: [], depBy: []};
      helpFuncMap[helpKey] = curHelp;
      if (value.deps) {
        value.deps.forEach((depKey) => {
          // const depHelpKey = (config[depKey] && config[depKey].route) ? config[depKey].init() : depKey;
          const depHelpKey = depKey;
          const depHelp = helpFuncMap[depHelpKey] || {changeConfig: [], depBy: []};
          helpFuncMap[depHelpKey] = depHelp;
          depHelp.changeConfig.push(helpKey);
          curHelp.depBy.push(helpKey);
        });
      }
      curHelp.changeConfig.push(helpKey);
      // #endregion
    
      // #region change  route  func
      // const caseKey = value.route ? value.init() : key;
      const caseKey = key;
      const curCase = changeRouteSwitch.get(caseKey) || [];
      changeRouteSwitch.set(caseKey, curCase);
      if (value.deps) {
        //  if  have  deps,  must  have  get
        value.deps.forEach((depKey, depIndex) => {
          const depCase = changeRouteSwitch.get(depKey) || [];
          changeRouteSwitch.set(depKey, depCase);
          const newDep = [...value.deps];
          newDep[depIndex] = 'value';
          depCase.push(`${key} = change${key}(${newDep.join(', ')});`);
        });
        const changeFuncLines = value.get.toString().split('\n');
        changeFuncLines[0] = `function change${key} (${value.deps.join(', ')}) {`;
        const lastLineNo = changeFuncLines.length - 1;
        configChangeFunc.push(
          changeFuncLines.map((line, lineNo) => {
            if (lineNo === 0 || lineNo === lastLineNo) {
              return line.trim();
            } else {
              return `  ${line.trim()}`;
            }
          }).join('\n'));
      }
      // every config can change it self
      curCase.push(`${key} = value;`);
      // #endregion
    
    });
    
    function renderChangeRouteSwitch() {
      const strs = [
        'window.changeRoute = function changeRoute(key, value)  {',
        '  if(key && value)  {',
        '    switch(key)  {',
      ];
      for ([key, value] of changeRouteSwitch.entries()) {
        if (value.noChange) {
          return;
        }
        const curCase = (value || []);
        strs.push(`      case '${key}':`,
          curCase.map(str => {
            return `        ${str}`;
          }).join('\n'),
          '        break;',
        );
      }
      strs.push('      default:');
      strs.push('        console.error(`${key} is not exists`);');
      strs.push('        helpMethod();');
      strs.push('        break;');
      strs.push('',
        '    }',
        '  } else {',
        '    helpMethod(key);',
        '  }',
        '};',
      );
      return strs.join('\n');
    }
    
    function renderHelpMethod() {
      const strs = [];
      strs.push(`const helpMethodAssist = ${JSON.stringify(helpFuncMap)};`,
        'function helpMethod(key) {',
        '  if(key && helpMethodAssist[key]) {',
        `     console.error(\`\${key} 会更改: [\${helpMethodAssist[key].changeConfig.join(', ')}], 被级连更改: [\${helpMethodAssist[key].depBy.join(', ')}]\`)`,
        '  } else {',
        `    console.error('使用 changeRoute() 查看可以更改的参数');`,
        `    console.error('使用 changeRoute("参数") 查看具体改变');`,
        `    console.error('使用 changeRoute("参数", "参数值") 更改参数');`,
        `    console.error(\`可以更改的配置: [\${Object.keys(helpMethodAssist).join(', ')}]\`);`,
        '  }',
        '}');
      return strs.join('\n');
    }
    
    function renderExportsConfig() {
      return ['export {', exportConfig.join(',\n  '), '};'].join('\n');
    }
    
    const configData = [
      '/* eslint-disable no-shadow,camelcase,import/no-mutable-exports,no-console */',
      '// TODO: 自动生成的 src/utils/config 禁用了部分 eslint, 请查看 scripts/genConfig.js',
      prefix,
      'const routeMap = JSON.parse(process.env.routeMap || \'{}\');',
      // `const routeMapVar = ${JSON.stringify(routeMapVar)};`,
      '// #region initConfig',
      initLang.join('\n'),
      '// #endregion',
      '',
      '// #region changeConfig Funcs',
      configChangeFunc.join('\n'),
      '// #endregion',
      '',
      '// #region changeRoute',
      renderChangeRouteSwitch(),
      '// #endregion',
      '',
      '// #region helpMethod',
      renderHelpMethod(),
      '// #endregion',
      '',
      '// #regioin exportsConfig',
      renderExportsConfig(),
      '// #endregion',
      suffix,
    ];
    
    require('fs').writeFileSync(
      require('path').resolve(__dirname, '../src/utils/config.js'),
      configData.join('\n'),
    );
    
    
    function formatString(format, ...args) {
      let argIndex = 0;
      return format.replace(/%(\d*)s/g, (testStr, match/* , start, all */) => {
        const padNum = +match;
        let ret;
        if (!padNum) {
          ret = args[argIndex]
        } else {
          ret = ("" + args[argIndex]).padEnd(padNum, ' ');
        }
        argIndex++;
        return ret;
      });
    }
    

    生成的 src/config.js 文件

    /* eslint-disable no-shadow,camelcase,import/no-mutable-exports,no-console */
    // TODO: 自动生成的 src/utils/config 禁用了部分 eslint, 请查看 scripts/genConfig.js
    /**
     * 不要直接修改这个文件, 请修改 config/apiConfig 文件
     * Config - 全局统一配置
     * @date: 2018-6-20
     * @author: niujiaqing <njq.niu@hand-china.com>
     * @version: 0.0.1
     * @copyright Copyright (c) 2018, Hand
     */
    
    const routeMap = JSON.parse(process.env.routeMap || '{}');
    // #region initConfig
    const CLIENT_ID = `${process.env.CLIENT_ID}`;
    let WEBSOCKET_URL = `${process.env.WEBSOCKET_HOST}`;
    let HZERO_PLATFORM = routeMap['/hpfm'] || '/hpfm';
    let HZERO_HFLE = routeMap['/hfle'] || '/hfle';
    let HZERO_FILE = changeHZERO_FILE(API_HOST, HZERO_HFLE);
    let VERSION_IS_OP = `${process.env.PLATFORM_VERSION}` === 'OP';
    // #endregion
    
    // #region changeConfig Funcs
    function changeHZERO_FILE(API_HOST, HZERO_HFLE) {
      return `${API_HOST}${HZERO_HFLE}`;
    }
    function changeVERSION_IS_OP(PLATFORM_VERSION) {
      return `${PLATFORM_VERSION}` === 'OP'; //  OP版
    }
    // #endregion
    
    // #region changeRoute
    window.changeRoute = function changeRoute(key, value) {
      if (key && value) {
        switch (key) {
          case 'WEBSOCKET_URL':
            WEBSOCKET_URL = value;
            break;
          case 'HZERO_PLATFORM':
            HZERO_PLATFORM = value;
            break;
          case 'HZERO_HFLE':
            HZERO_HFLE = value;
            HZERO_FILE = changeHZERO_FILE(API_HOST, value);
            break;
          case 'HZERO_FILE':
            HZERO_FILE = value;
            break;
          case 'VERSION_IS_OP':
            VERSION_IS_OP = value;
            break;
          case 'PLATFORM_VERSION':
            VERSION_IS_OP = changeVERSION_IS_OP(value);
            break;
          default:
            console.error(`${key} is not exists`);
            helpMethod();
            break;
        }
      } else {
        helpMethod(key);
      }
    };
    // #endregion
    
    // #region helpMethod
    const helpMethodAssist = {
      WEBSOCKET_URL: { changeConfig: ['WEBSOCKET_URL'], depBy: [] },
      HZERO_PLATFORM: { changeConfig: ['HZERO_PLATFORM'], depBy: [] },
      HZERO_HFLE: { changeConfig: ['HZERO_HFLE', 'HZERO_FILE'], depBy: [] },
      HZERO_FILE: { changeConfig: ['HZERO_FILE'], depBy: ['HZERO_FILE', 'HZERO_FILE'] },
      CLIENT_ID: { changeConfig: ['AUTH_URL'], depBy: [] },
      VERSION_IS_OP: { changeConfig: ['VERSION_IS_OP'], depBy: ['VERSION_IS_OP'] },
      PLATFORM_VERSION: { changeConfig: ['VERSION_IS_OP'], depBy: [] },
    };
    function helpMethod(key) {
      if (key && helpMethodAssist[key]) {
        console.error(
          `${key} 会更改: [${helpMethodAssist[key].changeConfig.join(
            ', '
          )}], 被级连更改: [${helpMethodAssist[key].depBy.join(', ')}]`
        );
      } else {
        console.error('使用 changeRoute() 查看可以更改的参数');
        console.error('使用 changeRoute("参数") 查看具体改变');
        console.error('使用 changeRoute("参数", "参数值") 更改参数');
        console.error(`可以更改的配置: [${Object.keys(helpMethodAssist).join(', ')}]`);
      }
    }
    // #endregion
    
    // #regioin exportsConfig
    export {
      CLIENT_ID,
      WEBSOCKET_URL,
      HZERO_PLATFORM,
      HZERO_HFLE,
      HZERO_FILE,
      VERSION_IS_OP,
    };
    // #endregion