代码分析工具 自定义插件——检查any类型的代码
本文是基于掘金小册《前端依赖治理:代码分析工具开发实战》
gitHub仓库:github.com/huang-jaskl…
逻辑步骤
源代码过于庞大,以下步骤只提取主要代码
扫描文件
我们首先需要在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参数是编译选项,可以理解成tsconfig
const 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
}