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);
|
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) {
|
function dealWithActionDefInitializer(moduleName, initializer, program) {
|
||||||
if (ts.isIdentifier(initializer) || ts.isCallExpression(initializer)) {
|
if (ts.isIdentifier(initializer) || ts.isCallExpression(initializer)) {
|
||||||
// 是从别处的引用,注入到mportActionDefFrom
|
// 是从别处的引用,注入到importActionDefFrom
|
||||||
const checker = program.getTypeChecker();
|
const checker = program.getTypeChecker();
|
||||||
const identifier = ts.isIdentifier(initializer) ? initializer : initializer.expression;
|
const identifier = ts.isIdentifier(initializer) ? initializer : initializer.expression;
|
||||||
(0, assert_1.default)(ts.isIdentifier(identifier), "ActionDef的initializer不是一个Identifier");
|
(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");
|
(0, assert_1.default)(ts.isImportSpecifier(declaration), "ActionDef的initializer不是一个ImportSpecifier");
|
||||||
const importDeclartion = declaration.parent.parent.parent;
|
const importDeclartion = declaration.parent.parent.parent;
|
||||||
addImportedFrom(moduleName, identifier.text, importDeclartion);
|
addImportedFrom(moduleName, identifier.text, importDeclartion);
|
||||||
// todo,要去分析外部引用的actionDef中的is,目前只有src/action/action.ts中的makeAbleActionDef,这种情况无法在编译时确定is是否为空,统一设置为空
|
// 如果是函数调用(CallExpression),无法在编译时确定is,直接返回false
|
||||||
// 分析外部引用的actionDef中的is
|
if (ts.isCallExpression(initializer)) {
|
||||||
const aliasedSymbol = checker.getAliasedSymbol(symbol);
|
return false;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// 如果是直接引用(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;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -494,6 +505,71 @@ function dealWithActionDefInitializer(moduleName, initializer, program) {
|
||||||
return false;
|
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'这种形式
|
* entity的引用一定要以 import { Schema as XXX } from '..../XXX'这种形式
|
||||||
* @param declaration
|
* @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) {
|
function dealWithActionDefInitializer(moduleName: string, initializer: ts.Expression, program: ts.Program) {
|
||||||
if (ts.isIdentifier(initializer) || ts.isCallExpression(initializer)) {
|
if (ts.isIdentifier(initializer) || ts.isCallExpression(initializer)) {
|
||||||
// 是从别处的引用,注入到mportActionDefFrom
|
// 是从别处的引用,注入到importActionDefFrom
|
||||||
const checker = program.getTypeChecker();
|
const checker = program.getTypeChecker();
|
||||||
const identifier = ts.isIdentifier(initializer) ? initializer : initializer.expression;
|
const identifier = ts.isIdentifier(initializer) ? initializer : initializer.expression;
|
||||||
assert(ts.isIdentifier(identifier), "ActionDef的initializer不是一个Identifier");
|
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);
|
addImportedFrom(moduleName, identifier.text, importDeclartion as ts.ImportDeclaration);
|
||||||
|
|
||||||
// todo,要去分析外部引用的actionDef中的is,目前只有src/action/action.ts中的makeAbleActionDef,这种情况无法在编译时确定is是否为空,统一设置为空
|
// 如果是函数调用(CallExpression),无法在编译时确定is,直接返回false
|
||||||
// 分析外部引用的actionDef中的is
|
if (ts.isCallExpression(initializer)) {
|
||||||
const aliasedSymbol = checker.getAliasedSymbol(symbol!);
|
return false;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果是直接引用(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;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
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'这种形式
|
* entity的引用一定要以 import { Schema as XXX } from '..../XXX'这种形式
|
||||||
* @param declaration
|
* @param declaration
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue