代码分析工具 自定义插件——检查any类型的代码
本文是基于掘金小册《前端依赖治理:代码分析工具开发实战》
转载于我的掘金博客,并非抄袭
gitHub仓库:https://github.com/huang-jaskld/code-analysis/tree/master
逻辑步骤
源代码过于庞大,以下步骤只提取主要代码
扫描文件
我们首先需要在analysis.config.js
文件中进行配置需要扫描的文件路径
我们会根据不同的文件类型,扫描不同的文件:
// sitem 为文件目录
if (type === CODEFILETYPE.VUE) {tempEntry = scanFileVue(sitem);
} else if (type === CODEFILETYPE.TS) {tempEntry = scanFileTs(sitem);
}
在扫描文件过程中,我们使用到了glob
依赖进行扫描
示例:该文件目录下所有ts
和tsx
文件都会被获取到,不论中间文件层级
// 扫描TS文件
function scanFileTs(scanPath) {const tsFiles = glob.sync(path.join(process.cwd(), `${scanPath}/**/*.ts`));const tsxFiles = glob.sync(path.join(process.cwd(), `${scanPath}/**/*.tsx`));return tsFiles.concat(tsxFiles);
}
解析代码生成
// 解析ts文件代码,获取ast,checker
exports.parseTS = function (fileName) {// 创建Program// fileNames参数表示文件路径列表,是一个数组,可以只传1个文件// options参数是编译选项,可以理解成tsconfigconst program = tsCompiler.createProgram([fileName], {});const ast = program.getSourceFile(fileName);const checker = program.getTypeChecker();return {ast,checker,};
};
处理AST树
首先,我们需要分析any
类型的特征:
node.kind === tsCompiler.SyntaxKind.AnyKeyword // 节点的类型为AnyKeyword
然后我们需要获取any
类型所在的代码,获取到代码块:
我们调用了上下文(CodeAnalysis
)中的_getFullCode
方法,进行获取
let fullCode = context._getFullCode(tsCompiler, node);
具体实现:
_getFullCode(tsCompiler, node) {if (node.parent && !tsCompiler.isSourceFile(node.parent)) {return this._getFullCode(tsCompiler, node.parent);} else {return node.getFullText();}}
逐级向上查找,如果该节点的父节点是SourceFile
类型,那么该节点已是代码块的根节点(除了File节点),我们可以通过getFullText()
方法进行获取
获取节点行数
const line =ast.getLineAndCharacterOfPosition(node.getStart()).line + baseLine + 1; // 获取节点所在行
完整代码
exports.anyTypePlugin = function (analysisContext) {const mapName = "anyTypeMap";// 在分析实例上下文挂载副作用analysisContext[mapName] = {};function isAnyTypeCheck(context,tsCompiler,checker,node,depth,apiName,filePath,projectName,httpRepo,line) {try {if (node.kind === tsCompiler.SyntaxKind.AnyKeyword) {let fullCode = context._getFullCode(tsCompiler, node);if (!context[mapName][filePath]) {// 记录信息context[mapName][filePath] = [];let temp = {};temp.code = fullCode;temp.line = line;temp.filePath = filePath;temp.pos = node.pos;temp.end = node.end;context[mapName][filePath].push(temp);} else {let temp = {};temp.code = fullCode;temp.line = line;temp.filePath = filePath;temp.pos = node.pos;temp.end = node.end;context[mapName][filePath].push(temp);}}} catch (e) {const info = {projectName: projectName,apiName: apiName,httpRepo: httpRepo + filePath.split("&")[1] + "#L" + line,file: filePath.split("&")[1],line: line,stack: e.stack,};context.addDiagnosisInfo(info);return false;}}return false;
};return {mapName: mapName,checkFun: isAnyTypeCheck,afterHook: null,
};
不足
当前的插件只能检查类型中明显存在any
的情况,比如Array<any>
、any
之类的
但是对于复杂类型来说是检查不到的,比如以下情况:
interface IProps {name:any,age:number
}let info : IProps = {name:1,age:12
}