AST实践案例-统计项目接口情况

382 阅读1分钟

目标

export const searchBank = async (payload) => {
  const url = `/api/hello/${payload.name}/_fuzzy`
  
  // const url = '/api/v1/hello/' + encodeURIComponent('/123');
  // const url = '/api/v1/hello/';
  // const url = `/api/v1/hello?jwt=${payload.channelId}`;
  try {
    const response = await request({
      method: 'GET',
      // url: `https://apis.hello.com`,
      url: `/api/hello?jwt=${jwt}`
    });
    return response.data;
  } catch (error) {
    console.log(error);
    return false;
  }
}

export const fetchTagsLists = () => axios.get(`/api/hello`);

// 暂不支持
//export const fetchTagsLists = () => axios.get('/api/hello');

如上代码,我们的目标就是:

  1. 接口请求方法内的 url 变量(因地制宜,变量命名可以更改,但变量的意义是请求 url 即可);
  2. 直接使用 模版字符串 且包含特定字符串(这个只是筛选条件)的

也就是以下5种情况:

{url: ''}
{url: ``}
var url = '';
var url = ``
`/api/xxx`

为什么要使用 AST

因为 AST 是基于语法词法分析目标代码得到的一个抽象语法树对象,可以准确地找到以上5种情况对应的内容,且可修改(当然,我们现在只是为了统计,并不做修改)。

代码

// babel-core plugin
const getVariableStatement = function (opts, {filename}) {
  const _path = path;
  if (filename === undefined) {
    throw Error('filename 必须填入');
  }
  return {
    visitor: {
      VariableDeclaration(path) {
        const node = path.node;
        if (node.declarations && node.declarations.length > 0) {
          const declarations = node.declarations;
          const id = declarations[0].id;
          const init = declarations[0].init;
          const type = init.type;
          let name = '';
          if ((id && id.name) && (type && type === 'ArrowFunctionExpression')) {
            name = id.name;
            const basename = _path.basename(filename);
            if (result[basename] === undefined) {
              result[basename] = {};
            }
            result[basename][name] = '未知,请确认'
            path.traverse({
              /* 
                寻找目标,对应:
                `/api/xxx`
              */
              TemplateLiteral(path) {
                console.log('TemplateLiteral---');
                const node = path.node;
                if (node.quasis && node.quasis.length > 0) {
                  let expressions = node.expressions;
                  const quasis = node.quasis;
                  let string = '';
                  if (/(^\/api)/.test(quasis[0].value.raw)) {
                    quasis.forEach(function(item, index) {
                      string += item.value.raw;
                      if (expressions.length >= index + 1) {
                        if (expressions[index].name) {
                          string += '${' + expressions[index].name + '}'
                        } else if (expressions[index].object){
                          string += '${' + expressions[index].object.name + '.' + expressions[index].property.name + '}'
                        }
                        
                      }
                    })
                    result[basename][name] = string;
                  }
                }
                return ;
              },
              /* 
                寻找目标,对应:
                url = '' || url = ``
              */
              VariableDeclaration(path) {
                console.log('---VariableDeclaration')
                const node = path.node;
                const declarations = node.declarations;
                if (declarations.length > 0 && declarations[0].id.name === 'url') {
                  const init = declarations[0].init;
                  let string = '';
                  if (init.value) {
                    string = init.value;
                    result[basename][name] = string;
                  }
                  if (init.quasis && init.quasis.length > 0) {
                    let expressions = init.expressions;
                    const quasis = init.quasis;
                    let string = '';
                    if (/(^\/api)/.test(quasis[0].value.raw)) {
                      quasis.forEach(function(item, index) {
                        string += item.value.raw;
                        if (expressions.length >= index + 1) {
                          if (expressions[index].name) {
                            string += '${' + expressions[index].name + '}'
                          } else if (expressions[index].object){
                            string += '${' + expressions[index].object.name + '.' + expressions[index].property.name + '}'
                          }
                          
                        }
                      })
                      result[basename][name] = string;
                    }
                  }
                } else {
                  return ;
                }
              },
              /* 
                寻找目标,对应:
                url: '' || url: ``
              */
              ObjectExpression(path) {
                console.log('---ObjectExpression')
                const node = path.node;
                let string = '';
                if (node.properties.length > 0) {
                  node.properties.forEach(function(item) {
                    if (item.key && item.key.name === 'url') {
                      let value = item.value;
                      if (value.value) {
                        string = value.value;
                      } else if (value.quasis) {
                        const expressions = value.expressions;
                        value.quasis.forEach(function(item, index) {
                          string += item.value.raw;
                          if (expressions.length >= index + 1) {
                            string += '${' + expressions[index].name + '}'
                          }
                        })
                      }
                      if (string) {
                        result[basename][name] = string;
                      }
                    }
                  })
                }
              }
            })
          }
        }
        return ;
      }   
    }
  }
}
// 省略其他不相关代码
// plugin 使用
babel.transform(code, {
  filename: filePath,
  // 如读取的文件是 ts 类型的,则需要添加 @babel/preset-typescript
  presets: ["@babel/preset-typescript"],
  plugins: [
    [getVariableStatement, {
      filename: filePath
    }]
  ]
})

以上,我们就很简单地实现了一个 统计接口情况AST 的实践案例。

辅助

astexplorer.net/