oak-domain/lib/compiler/identifier.js

161 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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));
}