fix: 处理外部dts和js引入的actionDef问题
This commit is contained in:
parent
a36a565d4e
commit
7fa85577c1
|
|
@ -437,16 +437,9 @@ function dealWithActionTypeNode(moduleName, filename, actionTypeNode, program, s
|
|||
});
|
||||
pushStatementIntoActionAst(moduleName, factory.createVariableStatement([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("actions"), undefined, undefined, factory.createArrayLiteralExpression(actionTexts.map(ele => factory.createStringLiteral(ele)), false))], ts.NodeFlags.Const)), sourceFile);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param moduleName
|
||||
* @param initializer
|
||||
* @param program
|
||||
* @returns 返回是否显式定义了is
|
||||
*/
|
||||
function dealWithActionDefInitializer(moduleName, initializer, program) {
|
||||
if (ts.isIdentifier(initializer) || ts.isCallExpression(initializer)) {
|
||||
// 是从别处的引用,注入到mportActionDefFrom
|
||||
// 是从别处的引用,注入到importActionDefFrom
|
||||
const checker = program.getTypeChecker();
|
||||
const identifier = ts.isIdentifier(initializer) ? initializer : initializer.expression;
|
||||
(0, assert_1.default)(ts.isIdentifier(identifier), "ActionDef的initializer不是一个Identifier");
|
||||
|
|
@ -455,31 +448,49 @@ function dealWithActionDefInitializer(moduleName, initializer, program) {
|
|||
(0, assert_1.default)(ts.isImportSpecifier(declaration), "ActionDef的initializer不是一个ImportSpecifier");
|
||||
const importDeclartion = declaration.parent.parent.parent;
|
||||
addImportedFrom(moduleName, identifier.text, importDeclartion);
|
||||
// todo,要去分析外部引用的actionDef中的is,目前只有src/action/action.ts中的makeAbleActionDef,这种情况无法在编译时确定is是否为空,统一设置为空
|
||||
// 分析外部引用的actionDef中的is
|
||||
const aliasedSymbol = checker.getAliasedSymbol(symbol);
|
||||
const aliasedDeclaration = aliasedSymbol.getDeclarations()[0];
|
||||
(0, assert_1.default)(ts.isVariableDeclaration(aliasedDeclaration), "ActionDef的aliasedDeclaration不是一个VariableDeclaration");
|
||||
const { initializer: aliDecInit } = aliasedDeclaration;
|
||||
if (aliDecInit) {
|
||||
// 如果是函数调用(CallExpression),默认返回false
|
||||
if (ts.isCallExpression(initializer)) {
|
||||
// 检查被调用的函数是否是箭头函数或函数表达式
|
||||
if (ts.isArrowFunction(aliDecInit) || ts.isFunctionExpression(aliDecInit)) {
|
||||
// 这是一个函数调用,无法在编译时确定is,返回false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 如果是对象字面量,检查is属性
|
||||
if (ts.isObjectLiteralExpression(aliDecInit)) {
|
||||
console.log('使用外部引用的ActionDef时,可能无法确定is属性,请确认');
|
||||
const { properties } = aliDecInit;
|
||||
const isProp = properties.find((ele) => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'is' && ts.isStringLiteral(ele.initializer));
|
||||
if (isProp) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 如果是函数调用(CallExpression),无法在编译时确定is,直接返回false
|
||||
if (ts.isCallExpression(initializer)) {
|
||||
return false;
|
||||
}
|
||||
// 如果是直接引用(Identifier),尝试分析外部引用的actionDef中的is
|
||||
const aliasedSymbol = checker.getAliasedSymbol(symbol);
|
||||
const aliasedDeclaration = aliasedSymbol?.getDeclarations()?.[0];
|
||||
// 如果无法获取到声明,返回false(可能是第三方库)
|
||||
if (!aliasedDeclaration || !ts.isVariableDeclaration(aliasedDeclaration)) {
|
||||
return false;
|
||||
}
|
||||
const { initializer: aliDecInit } = aliasedDeclaration;
|
||||
// 如果没有初始化器,可能是.d.ts声明文件,尝试从对应的.js文件读取
|
||||
if (!aliDecInit) {
|
||||
const sourceFile = aliasedDeclaration.getSourceFile();
|
||||
const sourceFileName = sourceFile.fileName;
|
||||
// 检查是否是.d.ts文件
|
||||
if (sourceFileName.endsWith('.d.ts')) {
|
||||
const jsFileName = sourceFileName.replace(/\.d\.ts$/, '.js');
|
||||
// 尝试读取对应的.js文件
|
||||
if ((0, fs_1.existsSync)(jsFileName)) {
|
||||
try {
|
||||
const jsContent = require('fs').readFileSync(jsFileName, 'utf-8');
|
||||
const exportName = identifier.text;
|
||||
// 尝试从JS文件中解析is属性
|
||||
const hasIs = tryParseIsFromJsFile(jsContent, exportName);
|
||||
return hasIs;
|
||||
}
|
||||
catch (error) {
|
||||
console.warn(`无法解析JS文件 ${jsFileName}:`, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// 如果是对象字面量,检查is属性
|
||||
if (ts.isObjectLiteralExpression(aliDecInit)) {
|
||||
const { properties } = aliDecInit;
|
||||
const isProp = properties.find((ele) => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'is' && ts.isStringLiteral(ele.initializer));
|
||||
return !!isProp;
|
||||
}
|
||||
// 其他情况返回false
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
|
|
@ -494,6 +505,71 @@ function dealWithActionDefInitializer(moduleName, initializer, program) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 尝试从JavaScript文件内容中解析导出对象的is属性
|
||||
* @param jsContent JavaScript文件内容
|
||||
* @param exportName 导出的变量名
|
||||
* @returns 是否包含is属性
|
||||
*/
|
||||
function tryParseIsFromJsFile(jsContent, exportName) {
|
||||
// 创建一个临时的SourceFile来解析JavaScript
|
||||
const jsSourceFile = ts.createSourceFile('temp.js', jsContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
|
||||
let hasIs = false;
|
||||
// 遍历AST查找导出
|
||||
const visit = (node) => {
|
||||
// 处理 exports.xxx = {...} 或 module.exports.xxx = {...}
|
||||
if (ts.isExpressionStatement(node)) {
|
||||
const expr = node.expression;
|
||||
if (ts.isBinaryExpression(expr) && expr.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
||||
const left = expr.left;
|
||||
const right = expr.right;
|
||||
// 检查是否是 exports.exportName 或 module.exports.exportName
|
||||
if (ts.isPropertyAccessExpression(left)) {
|
||||
const propertyName = left.name.text;
|
||||
if (propertyName === exportName) {
|
||||
// 检查右侧是否是对象字面量
|
||||
if (ts.isObjectLiteralExpression(right)) {
|
||||
hasIs = checkObjectHasIsProperty(right);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理 const xxx = {...}; exports.xxx = xxx;
|
||||
if (ts.isVariableStatement(node)) {
|
||||
const declarations = node.declarationList.declarations;
|
||||
for (const decl of declarations) {
|
||||
if (ts.isIdentifier(decl.name) && decl.name.text === exportName) {
|
||||
if (decl.initializer && ts.isObjectLiteralExpression(decl.initializer)) {
|
||||
hasIs = checkObjectHasIsProperty(decl.initializer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
};
|
||||
visit(jsSourceFile);
|
||||
return hasIs;
|
||||
}
|
||||
/**
|
||||
* 检查对象字面量是否包含is属性
|
||||
*/
|
||||
function checkObjectHasIsProperty(objLiteral) {
|
||||
const { properties } = objLiteral;
|
||||
const isProp = properties.find((ele) => {
|
||||
if (ts.isPropertyAssignment(ele)) {
|
||||
const name = ele.name;
|
||||
if (ts.isIdentifier(name) && name.text === 'is') {
|
||||
// 检查值是否是字符串字面量
|
||||
return ts.isStringLiteral(ele.initializer);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return !!isProp;
|
||||
}
|
||||
/**
|
||||
* entity的引用一定要以 import { Schema as XXX } from '..../XXX'这种形式
|
||||
* @param declaration
|
||||
|
|
|
|||
|
|
@ -646,16 +646,9 @@ function dealWithActionTypeNode(moduleName: string, filename: string, actionType
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param moduleName
|
||||
* @param initializer
|
||||
* @param program
|
||||
* @returns 返回是否显式定义了is
|
||||
*/
|
||||
function dealWithActionDefInitializer(moduleName: string, initializer: ts.Expression, program: ts.Program) {
|
||||
if (ts.isIdentifier(initializer) || ts.isCallExpression(initializer)) {
|
||||
// 是从别处的引用,注入到mportActionDefFrom
|
||||
// 是从别处的引用,注入到importActionDefFrom
|
||||
const checker = program.getTypeChecker();
|
||||
const identifier = ts.isIdentifier(initializer) ? initializer : initializer.expression;
|
||||
assert(ts.isIdentifier(identifier), "ActionDef的initializer不是一个Identifier");
|
||||
|
|
@ -667,36 +660,60 @@ function dealWithActionDefInitializer(moduleName: string, initializer: ts.Expres
|
|||
|
||||
addImportedFrom(moduleName, identifier.text, importDeclartion as ts.ImportDeclaration);
|
||||
|
||||
// todo,要去分析外部引用的actionDef中的is,目前只有src/action/action.ts中的makeAbleActionDef,这种情况无法在编译时确定is是否为空,统一设置为空
|
||||
// 分析外部引用的actionDef中的is
|
||||
const aliasedSymbol = checker.getAliasedSymbol(symbol!);
|
||||
const aliasedDeclaration = aliasedSymbol.getDeclarations()![0]!;
|
||||
assert(ts.isVariableDeclaration(aliasedDeclaration), "ActionDef的aliasedDeclaration不是一个VariableDeclaration");
|
||||
const { initializer: aliDecInit } = aliasedDeclaration;
|
||||
|
||||
if (aliDecInit) {
|
||||
// 如果是函数调用(CallExpression),默认返回false
|
||||
if (ts.isCallExpression(initializer)) {
|
||||
// 检查被调用的函数是否是箭头函数或函数表达式
|
||||
if (ts.isArrowFunction(aliDecInit) || ts.isFunctionExpression(aliDecInit)) {
|
||||
// 这是一个函数调用,无法在编译时确定is,返回false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是对象字面量,检查is属性
|
||||
if (ts.isObjectLiteralExpression(aliDecInit)) {
|
||||
console.log('使用外部引用的ActionDef时,可能无法确定is属性,请确认');
|
||||
const { properties } = aliDecInit;
|
||||
const isProp = properties.find(
|
||||
(ele) => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'is' && ts.isStringLiteral(ele.initializer)
|
||||
);
|
||||
if (isProp) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 如果是函数调用(CallExpression),无法在编译时确定is,直接返回false
|
||||
if (ts.isCallExpression(initializer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果是直接引用(Identifier),尝试分析外部引用的actionDef中的is
|
||||
const aliasedSymbol = checker.getAliasedSymbol(symbol!);
|
||||
const aliasedDeclaration = aliasedSymbol?.getDeclarations()?.[0];
|
||||
|
||||
// 如果无法获取到声明,返回false(可能是第三方库)
|
||||
if (!aliasedDeclaration || !ts.isVariableDeclaration(aliasedDeclaration)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { initializer: aliDecInit } = aliasedDeclaration;
|
||||
|
||||
// 如果没有初始化器,可能是.d.ts声明文件,尝试从对应的.js文件读取
|
||||
if (!aliDecInit) {
|
||||
const sourceFile = aliasedDeclaration.getSourceFile();
|
||||
const sourceFileName = sourceFile.fileName;
|
||||
|
||||
// 检查是否是.d.ts文件
|
||||
if (sourceFileName.endsWith('.d.ts')) {
|
||||
const jsFileName = sourceFileName.replace(/\.d\.ts$/, '.js');
|
||||
|
||||
// 尝试读取对应的.js文件
|
||||
if (existsSync(jsFileName)) {
|
||||
try {
|
||||
const jsContent = require('fs').readFileSync(jsFileName, 'utf-8');
|
||||
const exportName = identifier.text;
|
||||
|
||||
// 尝试从JS文件中解析is属性
|
||||
const hasIs = tryParseIsFromJsFile(jsContent, exportName);
|
||||
return hasIs;
|
||||
} catch (error) {
|
||||
console.warn(`无法解析JS文件 ${jsFileName}:`, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果是对象字面量,检查is属性
|
||||
if (ts.isObjectLiteralExpression(aliDecInit)) {
|
||||
const { properties } = aliDecInit;
|
||||
const isProp = properties.find(
|
||||
(ele) => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'is' && ts.isStringLiteral(ele.initializer)
|
||||
);
|
||||
return !!isProp;
|
||||
}
|
||||
|
||||
// 其他情况返回false
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
|
|
@ -716,6 +733,87 @@ function dealWithActionDefInitializer(moduleName: string, initializer: ts.Expres
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试从JavaScript文件内容中解析导出对象的is属性
|
||||
* @param jsContent JavaScript文件内容
|
||||
* @param exportName 导出的变量名
|
||||
* @returns 是否包含is属性
|
||||
*/
|
||||
function tryParseIsFromJsFile(jsContent: string, exportName: string): boolean {
|
||||
// 创建一个临时的SourceFile来解析JavaScript
|
||||
const jsSourceFile = ts.createSourceFile(
|
||||
'temp.js',
|
||||
jsContent,
|
||||
ts.ScriptTarget.Latest,
|
||||
true,
|
||||
ts.ScriptKind.JS
|
||||
);
|
||||
|
||||
let hasIs = false;
|
||||
|
||||
// 遍历AST查找导出
|
||||
const visit = (node: ts.Node) => {
|
||||
// 处理 exports.xxx = {...} 或 module.exports.xxx = {...}
|
||||
if (ts.isExpressionStatement(node)) {
|
||||
const expr = node.expression;
|
||||
if (ts.isBinaryExpression(expr) && expr.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
||||
const left = expr.left;
|
||||
const right = expr.right;
|
||||
|
||||
// 检查是否是 exports.exportName 或 module.exports.exportName
|
||||
if (ts.isPropertyAccessExpression(left)) {
|
||||
const propertyName = left.name.text;
|
||||
if (propertyName === exportName) {
|
||||
// 检查右侧是否是对象字面量
|
||||
if (ts.isObjectLiteralExpression(right)) {
|
||||
hasIs = checkObjectHasIsProperty(right);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 const xxx = {...}; exports.xxx = xxx;
|
||||
if (ts.isVariableStatement(node)) {
|
||||
const declarations = node.declarationList.declarations;
|
||||
for (const decl of declarations) {
|
||||
if (ts.isIdentifier(decl.name) && decl.name.text === exportName) {
|
||||
if (decl.initializer && ts.isObjectLiteralExpression(decl.initializer)) {
|
||||
hasIs = checkObjectHasIsProperty(decl.initializer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ts.forEachChild(node, visit);
|
||||
};
|
||||
|
||||
visit(jsSourceFile);
|
||||
return hasIs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查对象字面量是否包含is属性
|
||||
*/
|
||||
function checkObjectHasIsProperty(objLiteral: ts.ObjectLiteralExpression): boolean {
|
||||
const { properties } = objLiteral;
|
||||
const isProp = properties.find(
|
||||
(ele) => {
|
||||
if (ts.isPropertyAssignment(ele)) {
|
||||
const name = ele.name;
|
||||
if (ts.isIdentifier(name) && name.text === 'is') {
|
||||
// 检查值是否是字符串字面量
|
||||
return ts.isStringLiteral(ele.initializer);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
return !!isProp;
|
||||
}
|
||||
|
||||
/**
|
||||
* entity的引用一定要以 import { Schema as XXX } from '..../XXX'这种形式
|
||||
* @param declaration
|
||||
|
|
|
|||
Loading…
Reference in New Issue