淘先锋技术网

首页 1 2 3 4 5 6 7

https://zhuanlan.zhihu.com/p/340615580

在这里插入图片描述

示例:
为生成router.js代码写一个单元测试

import { Router } from '../tranform/vue/router';
import { PageParser } from '../index';


const babelParser = require('@babel/parser');
const generate = require('@babel/generator').default;
const traverse = require('@babel/traverse').default; // 访问ast对象,查找和替换使用, https://babeljs.io/docs/en/babel-traverse

describe('测试router', () => {
  const routerDsl = [{
    pageid: 'form', // nouse
    title: `form.name`,
    pageName: 'form',// nouse
    meta: {
        icon: 'fin-icon-s-home',
        breadcrumb: false,
    },
    children: [
        {
            pageid: 'detail', // nouse
            title: `form.detail`,
            pageName: 'detail',// nouse
            meta: {
                icon: 'fin-icon-s-home',
                breadcrumb: false,
            }, 
        },
    ],
  }];
  const pageDsl = {
    fileName: 'index',
    id: 'form',
    shareData: [],
    action: {},
    components: [],
    apiConfigs: [],
    apis: [],
  };
  const pageParser = new PageParser(pageDsl)
  const router = new Router(pageParser, routerDsl); 

  test('test generateRouterCode', () => {
    const res = router.toString();
    console.log('res', res);
    console.log('success');
  })
})

生成router.js源码:

import { ct } from '../../utils/createType';
import { PageParser } from '../../parser/page';
import generate from '@babel/generator';

const babelParser = require('@babel/parser');
const traverse = require('@babel/traverse').default; // 访问ast对象,查找和替换使用, https://babeljs.io/docs/en/babel-traverse

export class Router {
  private parser: any;
  private routerConf: any;
  private baseCode: string = `
      import basicLayout from '@/layouts/basic-layout';
      const sort = 1;
      const routers = [];
      export default routers;
  `;

  constructor(parser: any, routerConf: any) {
    this.parser = parser;
    this.routerConf = routerConf;
  }

  /**
   * 从main.dsl中获取模块的router配置,如果main.dsl没有,返回默认配置
   * @returns object
   */
  private getRouterConfig() {
    // mainParser: pageid代表模块名称,parser:id代表模块名称
    const curRouterConfig = this.routerConf.find((item) => item.pageid === this.parser.dsl.id);
    if (curRouterConfig) {
      return curRouterConfig;
    }

    const routerName: string = this.parser.dsl.fileName || 'index';
    const moduleName: string = this.parser.dsl.id || 'module';
    // 如果mainDal中没有配置,就从page.dsl中取fileName,id为默认值
    return {
        title: routerName,
        pageid: moduleName,
        pageName: routerName,
        meta: {
          icon: 'fin-icon-s-home',
          breadcrumb: false,
        },
        children: [],
    }
  }

  /**
   * 获取所有需要导入的组件名称
   * @params router 路由配置
   * @returns array
   */
  // private getComponents(router: any[]) {
  //   // 获取所有需要导入的组件名称
  //   const comArr: any[] = [];
    
  //   const getCom = (router) => {
  //     router.forEach((item) => {
  //       const { pageName, children=[] } = item;
  //       if (pageName) {
  //         comArr.push(pageName);
  //       }
  //       if (children.length) {
  //         // 递归获取组件名称
  //         getCom(children);
  //       }
  //     });
  //   }
  //   getCom(router);
  //   return comArr;
  // }
  /**
   * 生成children:[]数组中内容
   * @params arr children数组
   * @returns array
   */
  private genChildConf(arr) {
    const routerConfig = this.getRouterConfig();
    const {
      pageid: moduleName,
      pageName: routerName,
    } = routerConfig;
    const childrenConf = [];
    arr.forEach((item, index) => {
      const {
        pageid: childId=`${moduleName}${index}`,
        pageName: childName=`${routerName}${index}`,
        title:subTitle = `${routerName}${index}`,
        meta={},
        children:sub = [],
      } = item;
      meta.title = subTitle;
      let path: string = '';
      // 一级菜单的路径默认为模块名
      if (childId === moduleName) {
        path = `/${moduleName}`;
      } else {
        path = `/${moduleName}/${childId}`;
      }
      const importStr = `/* webpackChunkName: ${moduleName} */ '../view/${childName}.vue'`;
      childrenConf.push(
        ct.objectExpression([
          ct.objectProperty(ct.identifier('path'),ct._convert(path)),
          ct.objectProperty(ct.identifier('component'),
            ct.arrowFunctionExpression(
              [],
              ct.callExpression(
                ct.identifier('import'),
                [ct.identifier(importStr)]
              )
            )
          ),
          ct.objectProperty(ct.identifier('meta'),ct._convert(meta)),
          // 如果有children递归调用
          ct.objectProperty(ct.identifier('children'), this.genChildConf(sub)),
        ])
      );
    });
    return ct.arrayExpression(childrenConf);
  }

  /**
   * 生成routers = []中内容
   * @params router 路由配置
   * @returns string
   */
  private generateRouterArr() {
    const routerConfig = this.getRouterConfig();
    const {
      title = '' ,
      pageid: moduleName,
      pageName: routerName,
      meta = {},
    } = routerConfig;
    // path固定为模块名
    const path = `/${moduleName}`;
    // title放入meta中
    meta.title = title;
    const children = [{
      title,
      pageid: moduleName,
      pageName: routerName,
      meta,
      children: routerConfig.children,
    }];

    return ct.variableDeclaration('const', [
      ct.variableDeclarator(
        ct.identifier('routers'),
        ct.arrayExpression([
          ct.objectExpression([
            ct.objectProperty(ct.identifier('sort'),ct.identifier('sort')),
            ct.objectProperty(ct.identifier('path'),ct._convert(path)),
            ct.objectProperty(ct.identifier('component'),ct.identifier('basicLayout')), // 默认为模板工程的basiclayout
            ct.objectProperty(ct.identifier('meta'),ct._convert(meta)),
            ct.objectProperty(ct.identifier('children'), this.genChildConf(children)),
          ])
        ])
      )
    ]
  );
  }

  /**
   * 生成模块router.js
   * @returns string
   */
  private generateRouterCode() {
    const routerConfig = this.getRouterConfig();
    const {
      pageid: moduleName,
      sort = 1,
    } = routerConfig;    
    const ast = babelParser.parse(this.baseCode, { sourceType: 'module' });

    // // import basiclayout
    // const body_0 = ct.importDeclaration([
    //   ct.importDefaultSpecifier(ct.identifier('basicLayout'))
    // ], ct.stringLiteral('@/layouts/basic-layout'));
    // const body_1 = ct._define('const', 'sort', sort);
    // // 声明moduleName,固定为main.dsl中的pageid
    // const body_2 = ct._define('const', 'module', moduleName);

    // 拼接ast
    // ast.program.body = [
    //   body_0, body_1, body_2,
    //   this.generateRouterArr(),
    //   ct.exportDefaultDeclaration(ct.identifier('routers')),
    // ];
    const routerArr = this.generateRouterArr();
    const astVistor = {
      enter(path) {
        if (ct.isVariableDeclaration(path)) {
          switch (path.node.declarations[0].id.name) {
            case 'sort':
              path.node.declarations[0].init.value = sort;
              break;
            // case 'module':
            //   path.node.declarations[0].init.value = moduleName;
            //   break;
            case 'routers':
              path.node.declarations = routerArr.declarations;
              break;
            default: break; 
          }
        }
      },
    }

    // 遍历ast
    traverse(ast, astVistor);

    // 生成code
    const newScript = generate(ast, {
      jsescOption: { "minimal" : true } // 支持中文字符编码
    }, this.baseCode);
    return newScript.code;
  }

  public toString() {
    if (this.parser instanceof PageParser) {
      return this.generateRouterCode();
    }
    return '';
  }
}