oak-domain/lib/compiler/tscBuilder.js

1190 lines
50 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.build = exports.OAK_IGNORE_TAGS = void 0;
const tslib_1 = require("tslib");
const ts = tslib_1.__importStar(require("typescript"));
const path = tslib_1.__importStar(require("path"));
const fs = tslib_1.__importStar(require("fs"));
const verboseLogging = false;
const log = (...args) => {
if (verboseLogging) {
console.log('[tscBuilder]', ...args);
}
};
exports.OAK_IGNORE_TAGS = [
'@oak-ignore',
'@oak-ignore-asynccontext',
];
// ANSI 颜色代码
const colors = {
reset: '\x1b[0m',
cyan: '\x1b[36m',
red: '\x1b[91m',
yellow: '\x1b[93m',
gray: '\x1b[90m'
};
// 解析命令行参数
function parseArgs(pargs) {
const args = pargs.slice(2);
let configPath = 'tsconfig.json';
for (let i = 0; i < args.length; i++) {
if (args[i] === '-p' || args[i] === '--project') {
if (i + 1 < args.length) {
configPath = args[i + 1];
break;
}
else {
console.error('error: option \'-p, --project\' argument missing');
process.exit(1);
}
}
}
return configPath;
}
// 读取自定义配置
function readCustomConfig(configPath) {
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
if (configFile.error) {
return {}; // 返回默认配置
}
const config = configFile.config;
// 返回自定义配置,如果不存在则返回默认值
return config.oakBuildChecks || {};
}
function printDiagnostic(diagnostic, index) {
if (diagnostic.file && diagnostic.start !== undefined) {
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
const isError = diagnostic.category === ts.DiagnosticCategory.Error;
const category = isError ? 'error' : 'warning';
const categoryColor = isError ? colors.red : colors.yellow;
console.log(`${index + 1}.→ ${colors.cyan}${diagnostic.file.fileName}${colors.reset}:${colors.yellow}${line + 1}${colors.reset}:${colors.yellow}${character + 1}${colors.reset} - ${categoryColor}${category}${colors.reset} ${colors.gray}TS${diagnostic.code}${colors.reset}: ${message}`);
}
else {
const isError = diagnostic.category === ts.DiagnosticCategory.Error;
const category = isError ? 'error' : 'warning';
const categoryColor = isError ? colors.red : colors.yellow;
console.log(`${index + 1}.→ ${categoryColor}${category}${colors.reset} ${colors.gray}TS${diagnostic.code}${colors.reset}: ${ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}`);
}
}
function performCustomChecks(program, typeChecker, customConfig) {
const diagnostics = [];
// 如果自定义检查被禁用,直接返回
if (!customConfig.context?.checkAsyncContext) {
console.log('Custom AsyncContext checks are disabled.');
return diagnostics;
}
// 数据结构定义
const functionsWithContextCalls = new Set();
const functionDeclarations = new Map();
const functionCallGraph = new Map(); // 谁调用了谁
const directContextCalls = new Map(); // 函数内的直接 context 调用
const allContextCalls = []; // 所有 context 调用点
// 缓存:函数符号 -> 参数索引 -> 是否被处理
const functionParameterHandling = new Map();
// 缓存:变量符号 -> 是否被正确处理
const variableHandlingCache = new Map();
// 正在检查的变量集合(防止循环)
const checkingVariables = new Set();
const typeCache = new Map();
const addDiagnostic = (node, messageText, code) => {
const sourceFile = node.getSourceFile();
diagnostics.push({
file: sourceFile,
start: node.getStart(sourceFile),
length: node.getWidth(sourceFile),
messageText,
category: ts.DiagnosticCategory.Warning,
code
});
};
const shouldReportUnawaitedCall = (callNode, sourceFile) => {
// 跳过特定场景
if (shouldSkipCheck(callNode)) {
return false;
}
// 检查忽略注释
if (hasIgnoreComment(callNode, sourceFile)) {
return false;
}
// 检查是否已 await
if (isAwaited(callNode)) {
return false;
}
// 检查是否赋值给变量并正确处理
if (isCallAssignedAndHandled(callNode)) {
return false;
}
return true;
};
const isCallAssignedAndHandled = (callNode) => {
const parent = callNode.parent;
if (ts.isVariableDeclaration(parent) && parent.initializer === callNode) {
if (parent.name && ts.isIdentifier(parent.name)) {
const variableSymbol = typeChecker.getSymbolAtLocation(parent.name);
if (variableSymbol) {
// 如果变量被 return则在 shouldSkipCheck 中已经处理,这里不需要再检查
if (isVariableReturned(variableSymbol, parent)) {
return true; // 被 return 的变量,认为已处理
}
// 否则检查是否在作用域内被正确处理
if (isPromiseProperlyHandled(variableSymbol, parent)) {
return true;
}
}
}
}
// 检查是否作为参数传递给会处理 Promise 的函数
if (isCallPassedToHandlingFunction(callNode)) {
return true;
}
return false;
};
const resolveModuleName = (moduleName, containingFile) => {
const compilerOptions = program.getCompilerOptions();
const resolvedModule = ts.resolveModuleName(moduleName, containingFile, compilerOptions, ts.sys);
return resolvedModule.resolvedModule?.resolvedFileName;
};
const pathCache = new Map();
const normalizeModulePath = (filePath) => {
if (pathCache.has(filePath)) {
return pathCache.get(filePath);
}
const normalized = filePath
.replace(/\\/g, '/')
.replace(/\.(ts|tsx|js|jsx|d\.ts)$/, '');
pathCache.set(filePath, normalized);
return normalized;
};
const hasIgnoreComment = (node, sourceFile) => {
const fullText = sourceFile.getFullText();
// 检查当前节点及其父节点向上查找最多3层
let currentNode = node;
let depth = 0;
const maxDepth = 5;
while (currentNode && depth < maxDepth) {
// 检查前导注释
const nodeFullStart = currentNode.getFullStart();
const leadingComments = ts.getLeadingCommentRanges(fullText, nodeFullStart);
if (leadingComments && leadingComments.length > 0) {
for (const comment of leadingComments) {
const commentText = fullText.substring(comment.pos, comment.end);
if (isIgnoreComment(commentText)) {
return true;
}
}
}
// 检查尾随注释
const nodeEnd = currentNode.getEnd();
const trailingComments = ts.getTrailingCommentRanges(fullText, nodeEnd);
if (trailingComments && trailingComments.length > 0) {
for (const comment of trailingComments) {
const commentText = fullText.substring(comment.pos, comment.end);
if (isIgnoreComment(commentText)) {
return true;
}
}
}
// 向上查找父节点
currentNode = currentNode.parent;
depth++;
// 如果遇到函数边界,停止查找
if (currentNode && (ts.isFunctionDeclaration(currentNode) ||
ts.isFunctionExpression(currentNode) ||
ts.isArrowFunction(currentNode) ||
ts.isMethodDeclaration(currentNode))) {
break;
}
}
return false;
};
const isIgnoreComment = (commentText) => {
return exports.OAK_IGNORE_TAGS.some(tag => commentText.includes(tag));
};
const isAsyncContextType = (type, modules) => {
if (typeCache.has(type)) {
return typeCache.get(type);
}
const result = checkIsAsyncContextType(type, modules);
typeCache.set(type, result);
return result;
};
const checkIsAsyncContextType = (type, modules) => {
// 检查类型本身
if (checkTypeSymbol(type, modules)) {
return true;
}
// 检查联合类型(例如 RuntimeCxt = FRC | BRC
if (type.isUnion()) {
return type.types.some(t => isAsyncContextType(t, modules));
}
// 检查交叉类型
if (type.isIntersection()) {
return type.types.some(t => isAsyncContextType(t, modules));
}
// 检查基类型(继承关系)
const baseTypes = type.getBaseTypes?.() || [];
for (const baseType of baseTypes) {
if (isAsyncContextType(baseType, modules)) {
return true;
}
}
return false;
};
const checkTypeSymbol = (type, modules) => {
const symbol = type.getSymbol();
if (!symbol) {
return false;
}
const declarations = symbol.getDeclarations();
if (!declarations || declarations.length === 0) {
return false;
}
for (const declaration of declarations) {
const sourceFile = declaration.getSourceFile();
const fileName = sourceFile.fileName;
const normalizedFileName = normalizeModulePath(fileName);
// 检查是否来自目标模块
for (const moduleName of modules) {
// 直接路径匹配(处理已解析的路径)
if (normalizedFileName.includes(moduleName.replace(/\\/g, '/'))) {
return true;
}
// 尝试解析模块别名
const resolvedPath = resolveModuleName(moduleName, sourceFile.fileName);
if (resolvedPath) {
const normalizedResolvedPath = normalizeModulePath(resolvedPath);
if (normalizedFileName === normalizedResolvedPath ||
normalizedFileName.includes(normalizedResolvedPath)) {
return true;
}
}
// 检查模块说明符(从 import 语句中获取)
const importDeclarations = sourceFile.statements.filter(ts.isImportDeclaration);
for (const importDecl of importDeclarations) {
if (importDecl.moduleSpecifier && ts.isStringLiteral(importDecl.moduleSpecifier)) {
const importPath = importDecl.moduleSpecifier.text;
if (importPath === moduleName || importPath.includes(moduleName)) {
// 检查当前符号是否来自这个 import
const importClause = importDecl.importClause;
if (importClause) {
const importSymbol = typeChecker.getSymbolAtLocation(importDecl.moduleSpecifier);
if (importSymbol && isSymbolRelated(symbol, importSymbol)) {
return true;
}
}
}
}
}
}
}
return false;
};
const isSymbolRelated = (symbol, moduleSymbol) => {
// 检查符号是否与模块相关
const exports = typeChecker.getExportsOfModule(moduleSymbol);
return exports.some(exp => exp === symbol || exp.name === symbol.name);
};
const isAwaited = (node) => {
let parent = node.parent;
// 向上遍历父节点,查找 await 表达式
while (parent) {
// 直接被 await 修饰
if (ts.isAwaitExpression(parent) && parent.expression === node) {
return true;
}
// 在 await 表达式的子表达式中
if (ts.isAwaitExpression(parent)) {
const current = parent.expression;
if (isNodeInSubtree(current, node)) {
return true;
}
}
// 如果遇到这些节点,停止向上查找
if (ts.isFunctionDeclaration(parent) ||
ts.isFunctionExpression(parent) ||
ts.isArrowFunction(parent) ||
ts.isMethodDeclaration(parent)) {
break;
}
parent = parent.parent;
}
return false;
};
const isPromiseType = (type) => {
// 检查类型符号
const symbol = type.getSymbol();
if (symbol) {
const name = symbol.getName();
if (name === 'Promise') {
return true;
}
}
// 检查类型字符串表示
const typeString = typeChecker.typeToString(type);
if (typeString.startsWith('Promise<') || typeString === 'Promise') {
return true;
}
// 检查联合类型(例如 Promise<T> | undefined
if (type.isUnion()) {
return type.types.some(t => isPromiseType(t));
}
// 检查基类型
const baseTypes = type.getBaseTypes?.() || [];
for (const baseType of baseTypes) {
if (isPromiseType(baseType)) {
return true;
}
}
return false;
};
const isNodeInSubtree = (root, target) => {
if (root === target) {
return true;
}
let found = false;
ts.forEachChild(root, (child) => {
if (found)
return;
if (child === target || isNodeInSubtree(child, target)) {
found = true;
}
});
return found;
};
// 辅助函数:获取函数调用的名称
const getFunctionCallName = (node, sourceFile) => {
if (ts.isIdentifier(node.expression)) {
return node.expression.getText(sourceFile);
}
else if (ts.isPropertyAccessExpression(node.expression)) {
return node.expression.name.getText(sourceFile);
}
return node.expression.getText(sourceFile);
};
// 辅助函数:获取声明的符号
const getSymbolOfDeclaration = (declaration) => {
// 处理有名称的声明(函数声明、方法声明等)
if ('name' in declaration && declaration.name) {
return typeChecker.getSymbolAtLocation(declaration.name);
}
// 处理箭头函数:尝试从父节点(变量声明)获取符号
if (ts.isArrowFunction(declaration) || ts.isFunctionExpression(declaration)) {
const parent = declaration.parent;
if (ts.isVariableDeclaration(parent) && parent.name) {
return typeChecker.getSymbolAtLocation(parent.name);
}
// 处理作为属性值的情况
if (ts.isPropertyAssignment(parent) && parent.name) {
return typeChecker.getSymbolAtLocation(parent.name);
}
}
return undefined;
};
const collectInformation = (sourceFile) => {
let currentFunction;
const visit = (node) => {
// 记录函数声明
if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) ||
ts.isArrowFunction(node) || ts.isMethodDeclaration(node)) {
const symbol = getSymbolOfDeclaration(node);
if (symbol) {
functionDeclarations.set(symbol, node);
const previousFunction = currentFunction;
currentFunction = symbol;
ts.forEachChild(node, visit);
currentFunction = previousFunction;
return;
}
}
// 记录 context 方法调用
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
const objectType = typeChecker.getTypeAtLocation(node.expression.expression);
const targetModules = customConfig.context?.targetModules ||
['@project/context/BackendRuntimeContext'];
if (isAsyncContextType(objectType, targetModules)) {
allContextCalls.push(node);
// 记录到当前函数
if (currentFunction) {
if (!directContextCalls.has(currentFunction)) {
directContextCalls.set(currentFunction, []);
}
directContextCalls.get(currentFunction).push(node);
}
}
}
// 记录函数调用关系
if (ts.isCallExpression(node) && currentFunction) {
const signature = typeChecker.getResolvedSignature(node);
if (signature) {
const declaration = signature.getDeclaration();
if (declaration) {
const calledSymbol = getSymbolOfDeclaration(declaration);
if (calledSymbol && calledSymbol !== currentFunction) {
if (!functionCallGraph.has(currentFunction)) {
functionCallGraph.set(currentFunction, new Set());
}
functionCallGraph.get(currentFunction).add(calledSymbol);
}
}
}
}
ts.forEachChild(node, visit);
};
visit(sourceFile);
};
const propagateContextMarks = () => {
// 初始标记:直接包含 context 调用的函数
const markedFunctions = new Set(directContextCalls.keys());
// 迭代传播标记
let changed = true;
while (changed) {
changed = false;
for (const [caller, callees] of functionCallGraph.entries()) {
if (markedFunctions.has(caller))
continue;
// 如果调用了任何标记的函数,则标记当前函数
for (const callee of callees) {
if (markedFunctions.has(callee)) {
markedFunctions.add(caller);
functionsWithContextCalls.add(caller);
changed = true;
break;
}
}
}
}
// 更新全局标记
markedFunctions.forEach(symbol => functionsWithContextCalls.add(symbol));
};
const checkCallSites = () => {
// 检查所有直接的 context 调用
for (const callNode of allContextCalls) {
if (shouldSkipCheck(callNode))
continue;
const signature = typeChecker.getResolvedSignature(callNode);
if (!signature)
continue;
const returnType = typeChecker.getReturnTypeOfSignature(signature);
if (!isPromiseType(returnType))
continue;
// 检查是否直接 await
const sourceFile = callNode.getSourceFile();
if (shouldReportUnawaitedCall(callNode, sourceFile)) {
const propertyAccess = callNode.expression;
const methodName = propertyAccess.name.getText(sourceFile);
const objectName = propertyAccess.expression.getText(sourceFile);
addDiagnostic(callNode, `未await的context调用可能导致事务不受控: ${objectName}.${methodName}() 需要使用 await 或通过 instanceof Promise 判断后处理`, 9100);
}
}
// 检查所有标记函数的调用点
for (const sourceFile of program.getSourceFiles()) {
if (sourceFile.isDeclarationFile || sourceFile.fileName.includes('node_modules')) {
continue;
}
checkFunctionCallsInFile(sourceFile);
}
};
const checkFunctionCallsInFile = (sourceFile) => {
const visit = (node) => {
if (ts.isCallExpression(node)) {
if (shouldSkipCheck(node)) {
ts.forEachChild(node, visit);
return;
}
const signature = typeChecker.getResolvedSignature(node);
if (signature) {
const declaration = signature.getDeclaration();
if (declaration) {
const symbol = getSymbolOfDeclaration(declaration);
// 检查是否调用了标记的函数
if (symbol && functionsWithContextCalls.has(symbol)) {
const returnType = typeChecker.getReturnTypeOfSignature(signature);
if (!isPromiseType(returnType)) {
ts.forEachChild(node, visit);
return;
}
if (shouldReportUnawaitedCall(node, sourceFile)) {
const functionName = getFunctionCallName(node, sourceFile);
addDiagnostic(node, `未await的函数调用可能导致事务不受控: ${functionName}() 内部包含 context 调用,需要使用 await 或通过 instanceof Promise 判断后处理`, 9101);
}
}
}
}
}
ts.forEachChild(node, visit);
};
visit(sourceFile);
};
const shouldSkipCheck = (node) => {
// 直接在 return 语句中
if (ts.isReturnStatement(node.parent)) {
return true;
}
// 箭头函数的直接返回值
if (ts.isArrowFunction(node.parent) && node.parent.body === node) {
return true;
}
// 检查是否赋值给变量,然后该变量被 return
if (ts.isVariableDeclaration(node.parent) && node.parent.initializer === node) {
const variableDecl = node.parent;
if (variableDecl.name && ts.isIdentifier(variableDecl.name)) {
const variableSymbol = typeChecker.getSymbolAtLocation(variableDecl.name);
if (variableSymbol && isVariableReturned(variableSymbol, variableDecl)) {
return true;
}
}
}
return false;
};
// 检查变量是否在其作用域内被 return
const isVariableReturned = (symbol, declarationNode) => {
let scope = declarationNode;
while (scope && !ts.isFunctionDeclaration(scope) &&
!ts.isFunctionExpression(scope) &&
!ts.isArrowFunction(scope) &&
!ts.isMethodDeclaration(scope) &&
!ts.isSourceFile(scope)) {
scope = scope.parent;
}
if (!scope || ts.isSourceFile(scope)) {
return false;
}
let found = false;
const checkIfContainsVariable = (node) => {
if (ts.isIdentifier(node)) {
const nodeSymbol = typeChecker.getSymbolAtLocation(node);
return nodeSymbol === symbol;
}
let contains = false;
ts.forEachChild(node, (child) => {
if (contains)
return;
if (checkIfContainsVariable(child)) {
contains = true;
}
});
return contains;
};
const visit = (node) => {
if (found)
return;
// 检查 return 语句
if (ts.isReturnStatement(node) && node.expression) {
// 检查 return 的表达式中是否包含该变量
if (checkIfContainsVariable(node.expression)) {
found = true;
return;
}
}
// 不进入嵌套函数
if (ts.isFunctionDeclaration(node) ||
ts.isFunctionExpression(node) ||
ts.isArrowFunction(node)) {
return;
}
ts.forEachChild(node, visit);
};
const funcLike = scope;
if (funcLike.body) {
visit(funcLike.body);
}
return found;
};
// 检查变量是否通过 instanceof Promise 判断
// 检查变量是否通过 instanceof Promise 判断
const hasInstanceOfPromiseCheck = (symbol, scope) => {
let found = false;
const visit = (node) => {
if (found)
return;
// 检查 instanceof Promise
if (ts.isBinaryExpression(node) &&
node.operatorToken.kind === ts.SyntaxKind.InstanceOfKeyword) {
const left = node.left;
if (ts.isIdentifier(left)) {
const leftSymbol = typeChecker.getSymbolAtLocation(left);
if (leftSymbol === symbol) {
const right = node.right;
// 修改:检查右侧是否是 Promise 标识符
if (ts.isIdentifier(right) && right.text === 'Promise') {
found = true;
return;
}
// 也检查类型(兼容其他情况)
const rightType = typeChecker.getTypeAtLocation(right);
const rightSymbol = rightType.getSymbol();
if (rightSymbol && rightSymbol.name === 'PromiseConstructor') {
found = true;
return;
}
}
}
}
ts.forEachChild(node, visit);
};
visit(scope);
return found;
};
// 辅助函数:检查是否有 return 语句返回 Promise 处理结果
const hasReturnWithPromiseHandling = (node, symbol) => {
let found = false;
const visit = (n) => {
if (found)
return;
if (ts.isReturnStatement(n) && n.expression) {
// 检查返回的表达式是否是对该变量的 Promise 方法调用
if (ts.isCallExpression(n.expression) &&
ts.isPropertyAccessExpression(n.expression.expression)) {
const object = n.expression.expression.expression;
if (ts.isIdentifier(object)) {
const objSymbol = typeChecker.getSymbolAtLocation(object);
if (objSymbol === symbol) {
const methodName = n.expression.expression.name.text;
if (methodName === 'then' || methodName === 'catch' || methodName === 'finally') {
found = true;
return;
}
}
}
}
}
// 不进入嵌套函数
if (ts.isFunctionDeclaration(n) ||
ts.isFunctionExpression(n) ||
ts.isArrowFunction(n)) {
return;
}
ts.forEachChild(n, visit);
};
visit(node);
return found;
};
// 检查变量是否在作用域内被 await
const isVariableAwaitedInScope = (symbol, scope) => {
let found = false;
const visit = (node) => {
if (found)
return;
// 检查 await 表达式
if (ts.isAwaitExpression(node)) {
const expression = node.expression;
if (ts.isIdentifier(expression)) {
const exprSymbol = typeChecker.getSymbolAtLocation(expression);
if (exprSymbol === symbol) {
found = true;
return;
}
}
}
// 不进入嵌套函数
if (ts.isFunctionDeclaration(node) ||
ts.isFunctionExpression(node) ||
ts.isArrowFunction(node)) {
return;
}
ts.forEachChild(node, visit);
};
visit(scope);
return found;
};
// 检查变量是否通过 Promise 方法处理(.then, .catch, Promise.all 等)
const isVariableHandledByPromiseMethod = (symbol, scope) => {
let found = false;
const visit = (node) => {
if (found)
return;
// 检查 .then() 或 .catch() 调用
if (ts.isCallExpression(node) &&
ts.isPropertyAccessExpression(node.expression)) {
const object = node.expression.expression;
if (ts.isIdentifier(object)) {
const objSymbol = typeChecker.getSymbolAtLocation(object);
if (objSymbol === symbol) {
const methodName = node.expression.name.text;
if (methodName === 'then' || methodName === 'catch' ||
methodName === 'finally') {
found = true;
return;
}
}
}
}
// 检查 Promise.all, Promise.race 等
if (ts.isCallExpression(node) &&
ts.isPropertyAccessExpression(node.expression)) {
const object = node.expression.expression;
if (ts.isIdentifier(object) && object.text === 'Promise') {
const methodName = node.expression.name.text;
if (methodName === 'all' || methodName === 'race' ||
methodName === 'allSettled' || methodName === 'any') {
// 检查参数中是否包含该变量
for (const arg of node.arguments) {
if (containsVariable(arg, symbol)) {
found = true;
return;
}
}
}
}
}
ts.forEachChild(node, visit);
};
visit(scope);
return found;
};
// 检查节点中是否包含指定变量
const containsVariable = (node, symbol) => {
if (ts.isIdentifier(node)) {
const nodeSymbol = typeChecker.getSymbolAtLocation(node);
return nodeSymbol === symbol;
}
let found = false;
ts.forEachChild(node, (child) => {
if (found)
return;
if (containsVariable(child, symbol)) {
found = true;
}
});
return found;
};
const isVariableHandledInCallback = (symbol, scope, depth = 0) => {
// 深度限制
if (depth > 2) {
return false;
}
let found = false;
const visit = (node) => {
if (found)
return;
// 检查是否作为回调参数传递
if (ts.isCallExpression(node)) {
for (const arg of node.arguments) {
if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
// 检查回调函数体内是否处理了该变量
if (arg.body && containsVariable(arg.body, symbol)) {
// 使用简化版本检查,避免深度递归
if (hasInstanceOfPromiseCheck(symbol, arg.body) ||
isVariableAwaitedInScope(symbol, arg.body) ||
isVariableHandledByPromiseMethod(symbol, arg.body)) {
found = true;
return;
}
}
}
}
}
ts.forEachChild(node, visit);
};
visit(scope);
return found;
};
// 检查变量是否被传递给会处理 Promise 的函数参数
const isVariablePassedToHandlingFunction = (symbol, scope, depth = 0) => {
// 深度限制,防止过深的递归
if (depth > 2) {
return false;
}
log('[DEBUG] isVariablePassedToHandlingFunction: checking', symbol.name, 'depth:', depth);
log('[DEBUG] Scope kind:', ts.SyntaxKind[scope.kind]);
let found = false;
let callCount = 0;
const visit = (node) => {
if (found)
return;
log('[DEBUG] Visiting node:', ts.SyntaxKind[node.kind]);
// 检查函数调用
if (ts.isCallExpression(node)) {
callCount++;
log('[DEBUG] Found call expression #', callCount);
// 遍历所有参数
node.arguments.forEach((arg, argIndex) => {
if (found)
return;
log('[DEBUG] Checking argument', argIndex, 'kind:', ts.SyntaxKind[arg.kind]);
// 检查参数是否是我们要找的变量
if (ts.isIdentifier(arg)) {
log('[DEBUG] Argument is identifier:', arg.text);
const argSymbol = typeChecker.getSymbolAtLocation(arg);
if (argSymbol === symbol) {
log('[DEBUG] ✓ Found variable', symbol.name, 'passed as argument at index:', argIndex);
// 获取被调用函数的签名
const signature = typeChecker.getResolvedSignature(node);
if (!signature) {
log('[DEBUG] ✗ No signature found');
return;
}
const declaration = signature.getDeclaration();
if (!declaration) {
log('[DEBUG] ✗ No declaration found');
return;
}
const calledSymbol = getSymbolOfDeclaration(declaration);
if (!calledSymbol) {
log('[DEBUG] ✗ No called symbol found');
return;
}
log('[DEBUG] Called function:', calledSymbol.name);
// 检查该函数是否会处理这个参数位置的 Promise
const paramHandling = getFunctionParameterHandling(calledSymbol);
log('[DEBUG] Parameter handling map:', Array.from(paramHandling.entries()));
log('[DEBUG] Parameter handling for index', argIndex, ':', paramHandling.get(argIndex));
if (paramHandling.get(argIndex) === true) {
log('[DEBUG] ✓✓✓ Parameter is handled!');
found = true;
return;
}
else {
log('[DEBUG] ✗ Parameter is NOT handled');
}
// 处理剩余参数的情况
if (declaration && 'parameters' in declaration) {
const params = declaration.parameters;
if (params.length > 0) {
const lastParam = params[params.length - 1];
if (lastParam && lastParam.dotDotDotToken && argIndex >= params.length - 1) {
log('[DEBUG] Checking rest parameter at index', params.length - 1);
if (paramHandling.get(params.length - 1) === true) {
log('[DEBUG] ✓✓✓ Rest parameter is handled!');
found = true;
return;
}
}
}
}
}
else {
log('[DEBUG] Argument symbol mismatch:', argSymbol?.name, 'vs', symbol.name);
}
}
});
}
// 不进入嵌套函数的内部
if (ts.isFunctionDeclaration(node) ||
ts.isFunctionExpression(node) ||
ts.isArrowFunction(node) ||
ts.isMethodDeclaration(node)) {
log('[DEBUG] Skipping nested function body');
return; // 不调用 ts.forEachChild直接返回
}
ts.forEachChild(node, visit);
};
// 关键修改:从函数体开始遍历,而不是从函数声明开始
let startNode;
if (ts.isFunctionDeclaration(scope) || ts.isFunctionExpression(scope) ||
ts.isArrowFunction(scope) || ts.isMethodDeclaration(scope)) {
const funcLike = scope;
startNode = funcLike.body;
log('[DEBUG] Starting from function body');
}
else if (ts.isSourceFile(scope)) {
startNode = scope;
log('[DEBUG] Starting from source file');
}
else {
startNode = scope;
log('[DEBUG] Starting from scope directly');
}
if (startNode) {
visit(startNode);
}
log('[DEBUG] isVariablePassedToHandlingFunction result for', symbol.name, ':', found, '(checked', callCount, 'calls)');
return found;
};
// 综合检查Promise 是否被正确处理
const isPromiseProperlyHandled = (symbol, declarationNode) => {
// 检查缓存
if (variableHandlingCache.has(symbol)) {
return variableHandlingCache.get(symbol);
}
// 检查是否正在检查这个变量(防止循环)
if (checkingVariables.has(symbol)) {
return false;
}
checkingVariables.add(symbol);
try {
log('[DEBUG] isPromiseProperlyHandled: variable', symbol.name, 'declarationNode kind:', ts.SyntaxKind[declarationNode.kind]);
// 找到包含该声明的函数作用域
let scope = declarationNode;
let depth = 0;
while (scope && !ts.isFunctionDeclaration(scope) &&
!ts.isFunctionExpression(scope) &&
!ts.isArrowFunction(scope) &&
!ts.isMethodDeclaration(scope) &&
!ts.isSourceFile(scope)) {
log('[DEBUG] Moving up from', ts.SyntaxKind[scope.kind], 'to parent');
scope = scope.parent;
depth++;
if (depth > 20) {
log('[DEBUG] Too deep, breaking');
break;
}
}
if (!scope) {
log('[DEBUG] No scope found');
variableHandlingCache.set(symbol, false);
return false;
}
log('[DEBUG] Found scope:', ts.SyntaxKind[scope.kind]);
// 打印 scope 的一些信息
if (ts.isFunctionDeclaration(scope) || ts.isMethodDeclaration(scope)) {
const funcDecl = scope;
if (funcDecl.name) {
log('[DEBUG] Scope function name:', funcDecl.name.getText());
}
}
log('[DEBUG] Checking variable:', symbol.name, 'in scope');
// 检查各种处理方式
const hasInstanceOf = hasInstanceOfPromiseCheck(symbol, scope);
const isAwaited = isVariableAwaitedInScope(symbol, scope);
const hasPromiseMethod = isVariableHandledByPromiseMethod(symbol, scope);
const inCallback = isVariableHandledInCallback(symbol, scope);
const passedToFunction = isVariablePassedToHandlingFunction(symbol, scope);
log('[DEBUG] Check results:', {
hasInstanceOf,
isAwaited,
hasPromiseMethod,
inCallback,
passedToFunction
});
const result = hasInstanceOf || isAwaited || hasPromiseMethod || inCallback || passedToFunction;
variableHandlingCache.set(symbol, result);
return result;
}
finally {
checkingVariables.delete(symbol);
}
};
// 分析函数参数是否在函数体内被正确处理
const analyzeParameterHandling = (functionSymbol, declaration) => {
const parameterHandling = new Map();
if (!declaration.parameters || declaration.parameters.length === 0) {
return parameterHandling;
}
// 获取函数体
const body = declaration.body;
if (!body) {
return parameterHandling;
}
// 分析每个参数
declaration.parameters.forEach((param, index) => {
if (!param.name) {
parameterHandling.set(index, false);
return;
}
// 处理标识符参数
if (ts.isIdentifier(param.name)) {
const paramSymbol = typeChecker.getSymbolAtLocation(param.name);
if (paramSymbol) {
const isHandled = isPromiseProperlyHandled(paramSymbol, body);
parameterHandling.set(index, isHandled);
return;
}
}
// 处理解构参数 - 保守处理,标记为未处理
// TODO: 可以进一步分析解构后的变量是否被处理
parameterHandling.set(index, false);
});
return parameterHandling;
};
// 获取函数参数处理信息(带缓存)
const getFunctionParameterHandling = (functionSymbol) => {
if (functionParameterHandling.has(functionSymbol)) {
return functionParameterHandling.get(functionSymbol);
}
const declaration = functionDeclarations.get(functionSymbol);
if (!declaration) {
return new Map();
}
const handling = analyzeParameterHandling(functionSymbol, declaration);
functionParameterHandling.set(functionSymbol, handling);
return handling;
};
// 检查调用是否作为参数传递给会处理 Promise 的函数
const isCallPassedToHandlingFunction = (callNode) => {
const parent = callNode.parent;
// 检查是否作为函数调用的参数
if (ts.isCallExpression(parent)) {
// 找到当前调用在参数列表中的位置
const argIndex = parent.arguments.indexOf(callNode);
if (argIndex === -1) {
return false;
}
// 获取被调用函数的签名
const signature = typeChecker.getResolvedSignature(parent);
if (!signature) {
return false;
}
const declaration = signature.getDeclaration();
if (!declaration) {
return false;
}
const calledSymbol = getSymbolOfDeclaration(declaration);
if (!calledSymbol) {
return false;
}
if (declaration && 'parameters' in declaration) {
const params = declaration.parameters;
const lastParam = params[params.length - 1];
// 如果最后一个参数是剩余参数,且当前参数索引超出普通参数范围
if (lastParam && lastParam.dotDotDotToken && argIndex >= params.length - 1) {
// 检查剩余参数是否被处理
const paramHandling = getFunctionParameterHandling(calledSymbol);
return paramHandling.get(params.length - 1) === true;
}
}
// 检查该函数是否会处理这个参数位置的 Promise
const paramHandling = getFunctionParameterHandling(calledSymbol);
return paramHandling.get(argIndex) === true;
}
// 检查是否作为数组元素传递给 Promise.all 等
if (ts.isArrayLiteralExpression(parent)) {
const grandParent = parent.parent;
if (ts.isCallExpression(grandParent) &&
ts.isPropertyAccessExpression(grandParent.expression)) {
const object = grandParent.expression.expression;
if (ts.isIdentifier(object) && object.text === 'Promise') {
const methodName = grandParent.expression.name.text;
if (['all', 'race', 'allSettled', 'any'].includes(methodName)) {
return true;
}
}
}
}
return false;
};
// 信息收集
for (const sourceFile of program.getSourceFiles()) {
if (sourceFile.isDeclarationFile || sourceFile.fileName.includes('node_modules')) {
continue;
}
collectInformation(sourceFile);
}
// 预分析所有函数的参数处理情况
for (const [symbol, declaration] of functionDeclarations.entries()) {
getFunctionParameterHandling(symbol);
}
// 标记传播
propagateContextMarks();
// 检查调用点
checkCallSites();
return diagnostics;
}
const compile = (configPath) => {
// 读取自定义配置
const customConfig = readCustomConfig(configPath);
// 读取 tsconfig.json
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
if (configFile.error) {
console.error(ts.formatDiagnostic(configFile.error, {
getCanonicalFileName: (f) => f,
getCurrentDirectory: process.cwd,
getNewLine: () => '\n'
}));
process.exit(1);
}
// 解析配置
const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configPath));
if (parsedConfig.errors.length > 0) {
parsedConfig.errors.forEach((diagnostic) => {
console.error(ts.formatDiagnostic(diagnostic, {
getCanonicalFileName: (f) => f,
getCurrentDirectory: process.cwd,
getNewLine: () => '\n'
}));
});
process.exit(1);
}
// 创建编译程序
let program;
if (parsedConfig.options.incremental || parsedConfig.options.composite) {
const host = ts.createIncrementalCompilerHost(parsedConfig.options);
const incrementalProgram = ts.createIncrementalProgram({
rootNames: parsedConfig.fileNames,
options: parsedConfig.options,
host: host,
configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics(parsedConfig),
});
program = incrementalProgram.getProgram();
}
else {
program = ts.createProgram({
rootNames: parsedConfig.fileNames,
options: parsedConfig.options,
});
}
// 获取类型检查器
const typeChecker = program.getTypeChecker();
// 执行自定义检查(传入自定义配置)
const customDiagnostics = performCustomChecks(program, typeChecker, customConfig);
if (customDiagnostics.length > 0) {
log(`Found ${customDiagnostics.length} custom diagnostics.`);
console.log(`${colors.yellow}Oak-Compiler 发现了相关问题,如果确定逻辑正确,可以使用注释标签来忽略:${colors.reset}`);
exports.OAK_IGNORE_TAGS.forEach(tag => {
console.log(` ${colors.cyan}// ${tag}${colors.reset}`);
});
console.log('');
customDiagnostics.forEach(printDiagnostic);
}
// 执行编译
const emitResult = program.emit();
// 获取诊断信息
const allDiagnostics = [
...ts.getPreEmitDiagnostics(program),
...emitResult.diagnostics,
];
// 输出诊断信息
allDiagnostics.forEach(printDiagnostic);
// 输出编译统计
const errorCount = allDiagnostics.filter(d => d.category === ts.DiagnosticCategory.Error).length;
const warningCount = allDiagnostics.filter(d => d.category === ts.DiagnosticCategory.Warning).length;
if (errorCount > 0 || warningCount > 0) {
if (allDiagnostics.length > 0) {
console.log('');
}
const parts = [];
if (errorCount > 0) {
parts.push(`${errorCount} error${errorCount !== 1 ? 's' : ''}`);
}
if (warningCount > 0) {
parts.push(`${warningCount} warning${warningCount !== 1 ? 's' : ''}`);
}
console.log(`Found ${parts.join(' and ')}.`);
}
if (errorCount > 0) {
process.exit(1);
}
console.log('Compilation completed successfully.');
};
const build = (pwd, args) => {
// 执行编译
const configPathArg = parseArgs(args);
let configPath;
// 判断参数是目录还是文件
if (fs.existsSync(configPathArg)) {
const stat = fs.statSync(configPathArg);
if (stat.isDirectory()) {
configPath = path.resolve(configPathArg, 'tsconfig.json');
}
else {
configPath = path.resolve(configPathArg);
}
}
else {
configPath = path.resolve(pwd, configPathArg);
if (!fs.existsSync(configPath)) {
const dirPath = path.resolve(pwd, configPathArg);
if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
configPath = path.join(dirPath, 'tsconfig.json');
}
}
}
if (!fs.existsSync(configPath)) {
console.error(`error TS5058: The specified path does not exist: '${configPath}'.`);
process.exit(1);
}
compile(configPath);
};
exports.build = build;