161 lines
5.3 KiB
JavaScript
161 lines
5.3 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.isTCall = void 0;
|
||
const tslib_1 = require("tslib");
|
||
const ts = tslib_1.__importStar(require("typescript"));
|
||
// 缓存结构:WeakMap<Node, Map<modulesKey, boolean>>
|
||
const isTCallCache = new WeakMap();
|
||
/**
|
||
* 生成 modules 的缓存 key
|
||
*/
|
||
function getModulesCacheKey(modules) {
|
||
return modules.sort().join('|');
|
||
}
|
||
/**
|
||
* 判断节点是否为 t 函数调用
|
||
* @param node ts.Node
|
||
* @param typeChecker ts.TypeChecker
|
||
* @returns boolean
|
||
* t: (key: string, params?: object | undefined) => string
|
||
*/
|
||
const isTCall = (node, typeChecker, modules) => {
|
||
// 缓存查询
|
||
const modulesCacheKey = getModulesCacheKey(modules);
|
||
let nodeCache = isTCallCache.get(node);
|
||
if (nodeCache) {
|
||
const cachedResult = nodeCache.get(modulesCacheKey);
|
||
if (cachedResult !== undefined) {
|
||
return cachedResult;
|
||
}
|
||
}
|
||
if (!(ts.isCallExpression(node) && ts.isIdentifier(node.expression))) {
|
||
// 缓存 false 结果
|
||
if (!nodeCache) {
|
||
nodeCache = new Map();
|
||
isTCallCache.set(node, nodeCache);
|
||
}
|
||
nodeCache.set(modulesCacheKey, false);
|
||
return false;
|
||
}
|
||
const result = doIsTCallDirect(node, typeChecker, modules);
|
||
// 缓存结果
|
||
if (!nodeCache) {
|
||
nodeCache = new Map();
|
||
isTCallCache.set(node, nodeCache);
|
||
}
|
||
nodeCache.set(modulesCacheKey, result);
|
||
return result;
|
||
};
|
||
exports.isTCall = isTCall;
|
||
const doIsTCallDirect = (node, typeChecker, modules) => {
|
||
if (!(ts.isCallExpression(node) && ts.isIdentifier(node.expression))) {
|
||
return false;
|
||
}
|
||
const symbol = typeChecker.getSymbolAtLocation(node.expression);
|
||
if (!symbol) {
|
||
return false;
|
||
}
|
||
const declarations = symbol.getDeclarations();
|
||
if (!declarations || declarations.length === 0) {
|
||
return false;
|
||
}
|
||
for (const decl of declarations) {
|
||
const declType = typeChecker.getTypeAtLocation(decl);
|
||
const signatures = declType.getCallSignatures();
|
||
for (const sig of signatures) {
|
||
// 检查这个类型是否来自指定模块
|
||
if (!isTypeFromModules(declType, modules)) {
|
||
continue;
|
||
}
|
||
// 检查返回类型是否为 string
|
||
const returnType = typeChecker.getReturnTypeOfSignature(sig);
|
||
if ((returnType.flags & ts.TypeFlags.String) === 0) {
|
||
continue;
|
||
}
|
||
const params = sig.getParameters();
|
||
if (params.length === 0) {
|
||
continue;
|
||
}
|
||
// 检查第一个参数是否为 string
|
||
const firstParamType = typeChecker.getTypeOfSymbolAtLocation(params[0], decl);
|
||
if ((firstParamType.flags & ts.TypeFlags.String) === 0) {
|
||
continue;
|
||
}
|
||
// 如果只有一个参数,符合 (key: string) => string
|
||
if (params.length === 1) {
|
||
return true;
|
||
}
|
||
// 检查第二个参数
|
||
if (params.length >= 2) {
|
||
const secondParam = params[1];
|
||
const secondParamType = typeChecker.getTypeOfSymbolAtLocation(secondParam, decl);
|
||
// 检查第二个参数是否为 object 或 object | undefined
|
||
if (isObjectOrObjectUnion(secondParamType)) {
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
};
|
||
/**
|
||
* 检查类型是否为 object 或 object | undefined
|
||
* @param type ts.Type
|
||
* @returns boolean
|
||
*/
|
||
function isObjectOrObjectUnion(type) {
|
||
// 如果是联合类型
|
||
if (type.isUnion()) {
|
||
// 检查联合类型中是否包含 object 类型
|
||
// 允许 object | undefined 的组合
|
||
let hasObject = false;
|
||
let hasOnlyObjectAndUndefined = true;
|
||
for (const t of type.types) {
|
||
if ((t.flags & ts.TypeFlags.Object) !== 0 || (t.flags & ts.TypeFlags.NonPrimitive) !== 0) {
|
||
hasObject = true;
|
||
}
|
||
else if ((t.flags & ts.TypeFlags.Undefined) === 0) {
|
||
// 如果既不是 object 也不是 undefined,则不符合
|
||
hasOnlyObjectAndUndefined = false;
|
||
}
|
||
}
|
||
return hasObject && hasOnlyObjectAndUndefined;
|
||
}
|
||
// 如果是单一类型,检查是否为 object
|
||
return (type.flags & ts.TypeFlags.Object) !== 0 || (type.flags & ts.TypeFlags.NonPrimitive) !== 0;
|
||
}
|
||
/**
|
||
* 检查声明是否来自指定的模块
|
||
* @param decl ts.Declaration
|
||
* @param modules 模块名称列表
|
||
* @returns boolean
|
||
*/
|
||
function isFromModules(decl, modules) {
|
||
const sourceFile = decl.getSourceFile();
|
||
if (!sourceFile) {
|
||
return false;
|
||
}
|
||
const fileName = sourceFile.fileName;
|
||
// 检查文件路径是否包含指定的模块
|
||
return modules.some(moduleName => {
|
||
return fileName.includes(moduleName);
|
||
});
|
||
}
|
||
/**
|
||
* 检查类型是否来自指定模块
|
||
* @param type ts.Type
|
||
* @param modules 模块名称列表
|
||
* @returns boolean
|
||
*/
|
||
function isTypeFromModules(type, modules) {
|
||
const symbol = type.getSymbol();
|
||
if (!symbol) {
|
||
return false;
|
||
}
|
||
const declarations = symbol.getDeclarations();
|
||
if (!declarations || declarations.length === 0) {
|
||
return false;
|
||
}
|
||
return declarations.some(decl => isFromModules(decl, modules));
|
||
}
|