// Fill out your copyright notice in the Description page of Project Settings. #include "ReactDeclarationGenerator.h" #include "TypeScriptDeclarationGenerator.h" #include "Misc/Paths.h" #include "CoreUObject.h" #include "TypeScriptDeclarationGenerator.h" #include "Components/PanelSlot.h" #include "Components/Widget.h" #include "Interfaces/IPluginManager.h" static FString SafeName(const FString& Name) { auto Ret = Name.Replace(TEXT(" "), TEXT("")) .Replace(TEXT("-"), TEXT("_")) .Replace(TEXT("/"), TEXT("_")) .Replace(TEXT("("), TEXT("_")) .Replace(TEXT(")"), TEXT("_")) .Replace(TEXT("?"), TEXT("$")) .Replace(TEXT(","), TEXT("_")); if (Ret.Len() > 0) { auto FirstChar = Ret[0]; if ((TCHAR) '0' <= FirstChar && FirstChar <= (TCHAR) '9') { return TEXT("_") + Ret; } } return Ret; } static bool IsDelegate(PropertyMacro* InProperty) { return InProperty->IsA() || InProperty->IsA() #if ENGINE_MINOR_VERSION >= 23 || InProperty->IsA() || InProperty->IsA() #endif ; } static bool HasObjectParam(UFunction* InFunction) { for (TFieldIterator ParamIt(InFunction); ParamIt; ++ParamIt) { auto Property = *ParamIt; if (Property->IsA()) { return true; } } return false; } struct FReactDeclarationGenerator : public FTypeScriptDeclarationGenerator { void Begin(FString Namespace) override; void GenReactDeclaration(); void GenClass(UClass* Class) override; void GenStruct(UStruct* Struct) override; void GenEnum(UEnum* Enum) override; void End() override; virtual ~FReactDeclarationGenerator() { } }; //--- FSlotDeclarationGenerator begin --- void FReactDeclarationGenerator::Begin(FString Namespace) { } // do nothing void FReactDeclarationGenerator::End() { } // do nothing void FReactDeclarationGenerator::GenReactDeclaration() { FString Components = TEXT("exports.lazyloadComponents = {};\n"); Output << "declare module \"react-umg\" {\n"; Output << " " << "import * as React from 'react';\n import * as UE from 'ue';\n import * as cpp from 'cpp';\n type TArray " "= UE.TArray;\n type " "TSet = UE.TSet;\n type TMap = UE.TMap;\n\n"; Output << " type RecursivePartial = {\n" << " [P in keyof T]?:\n" << " T[P] extends (infer U)[] ? RecursivePartial[] :\n" << " T[P] extends object ? RecursivePartial :\n" << " T[P];\n" << " };\n\n"; for (TObjectIterator It; It; ++It) { UClass* Class = *It; checkfSlow(Class != nullptr, TEXT("Class name corruption!")); if (Class->GetName().StartsWith("SKEL_") || Class->GetName().StartsWith("REINST_") || Class->GetName().StartsWith("TRASHCLASS_") || Class->GetName().StartsWith("PLACEHOLDER_")) { continue; } if (Class->IsChildOf()) Gen(Class); } Output << " " << "export interface Props {\n"; Output << " " << " Slot ? : PanelSlot;\n"; Output << " " << "}\n\n"; for (TObjectIterator It; It; ++It) { UClass* Class = *It; checkfSlow(Class != nullptr, TEXT("Class name corruption!")); if (Class->GetName().StartsWith("SKEL_") || Class->GetName().StartsWith("REINST_") || Class->GetName().StartsWith("TRASHCLASS_") || Class->GetName().StartsWith("PLACEHOLDER_")) { continue; } if (Class->IsChildOf()) { Gen(Class); Components += "exports." + SafeName(Class->GetName()) + " = '" + SafeName(Class->GetName()) + "';\n"; if (!(Class->ClassFlags & CLASS_Native)) { Components += "exports.lazyloadComponents." + SafeName(Class->GetName()) + " = '" + Class->GetPathName() + "';\n"; } } } Output << R"( interface Root { removeFromViewport() : void; getWidget(): any; } interface TReactUMG { render(element: React.ReactElement) : Root; init(world: any) : void; } var ReactUMG : TReactUMG; } )"; FFileHelper::SaveStringToFile(ToString(), *(FPaths::ProjectDir() / TEXT("Typing/react-umg/index.d.ts")), FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM); FFileHelper::SaveStringToFile(Components, *(FPaths::ProjectContentDir() / TEXT("JavaScript/react-umg/components.js")), FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM); } static bool IsReactSupportProperty(PropertyMacro* Property) { if (CastFieldMacro(Property) || CastFieldMacro(Property) || CastFieldMacro(Property) || CastFieldMacro(Property) || CastFieldMacro(Property)) return false; if (auto ArrayProperty = CastFieldMacro(Property)) { return IsReactSupportProperty(ArrayProperty->Inner); } else if (auto DelegateProperty = CastFieldMacro(Property)) { return !HasObjectParam(DelegateProperty->SignatureFunction); } else if (auto MulticastDelegateProperty = CastFieldMacro(Property)) { return !HasObjectParam(MulticastDelegateProperty->SignatureFunction); } return true; } void FReactDeclarationGenerator::GenClass(UClass* Class) { if (!Class->IsChildOf() && !Class->IsChildOf()) return; bool IsWidget = Class->IsChildOf(); FStringBuffer StringBuffer{"", ""}; StringBuffer << " " << "interface " << SafeName(Class->GetName()); if (IsWidget) StringBuffer << "Props"; auto Super = Class->GetSuperStruct(); if (Super && (Super->IsChildOf() || Super->IsChildOf())) { Gen(Super); StringBuffer << " extends " << SafeName(Super->GetName()); if (Super->IsChildOf()) StringBuffer << "Props"; } else if (IsWidget) { StringBuffer << " extends Props"; } StringBuffer << " {\n"; for (TFieldIterator PropertyIt(Class, EFieldIteratorFlags::ExcludeSuper); PropertyIt; ++PropertyIt) { auto Property = *PropertyIt; if (!IsReactSupportProperty(Property)) continue; //UE.27上遇到一个问题, //引擎内置蓝图DefaultBurnIn有个FLinearColor类型的变量Foreground Color, //并且父类UUserWidget有个FSlateColor类型的变量ForegroundColor //此处SafeName去掉空格导致interface写入了父类变量名字,类型对不上,有报错,仅此一例 //PuerTS插件DeclarationGenerator.cpp中的SafeName存在同样隐患 FString PropertyNameSafe = SafeName(Property->GetName()); UClass* SuperClass = Class->GetSuperClass(); if (PropertyNameSafe != Property->GetName() && SuperClass != nullptr && SuperClass->FindPropertyByName(*PropertyNameSafe) != nullptr) { UE_LOG(LogTemp, Warning, TEXT("ReactUMG, invalid property name, origin name: %s, safe name %s in super class!"), *Property->GetName(), *PropertyNameSafe); continue; } FStringBuffer TmpBuff; TmpBuff << " " << SafeName(Property->GetName()) << "?: "; TArray RefTypesTmp; if (!IsWidget && IsDelegate(Property)) // UPanelSlot skip delegate { continue; } if (CastFieldMacro(Property)) { TmpBuff << "RecursivePartial<"; } if (!GenTypeDecl(TmpBuff, Property, RefTypesTmp, false, true)) { continue; } if (CastFieldMacro(Property)) { TmpBuff << ">"; } for (auto Type : RefTypesTmp) { Gen(Type); } StringBuffer << " " << TmpBuff.Buffer << ";\n"; } StringBuffer << " " << "}\n\n"; if (IsWidget) { StringBuffer << " " << "class " << SafeName(Class->GetName()) << " extends React.Component<" << SafeName(Class->GetName()) << "Props> {\n" << " nativePtr: " << GetNameWithNamespace(Class) << ";\n }\n\n"; } Output << StringBuffer; } void FReactDeclarationGenerator::GenStruct(UStruct* Struct) { } void FReactDeclarationGenerator::GenEnum(UEnum* Enum) { } //--- FSlotDeclarationGenerator end --- void UReactDeclarationGenerator::Gen_Implementation() const { FReactDeclarationGenerator ReactDeclarationGenerator; ReactDeclarationGenerator.GenReactDeclaration(); }