添加ReactUMG插件

搭建TS环境
This commit is contained in:
2024-12-13 15:34:14 +08:00
parent 1d64e61b15
commit bed8becdf4
26 changed files with 1180 additions and 1 deletions
@@ -0,0 +1,281 @@
// 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<DelegatePropertyMacro>() || InProperty->IsA<MulticastDelegatePropertyMacro>()
#if ENGINE_MINOR_VERSION >= 23
|| InProperty->IsA<MulticastInlineDelegatePropertyMacro>() || InProperty->IsA<MulticastSparseDelegatePropertyMacro>()
#endif
;
}
static bool HasObjectParam(UFunction* InFunction)
{
for (TFieldIterator<PropertyMacro> ParamIt(InFunction); ParamIt; ++ParamIt)
{
auto Property = *ParamIt;
if (Property->IsA<ObjectPropertyBaseMacro>())
{
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<T> "
"= UE.TArray<T>;\n type "
"TSet<T> = UE.TSet<T>;\n type TMap<TKey, TValue> = UE.TMap<TKey, TValue>;\n\n";
Output << " type RecursivePartial<T> = {\n"
<< " [P in keyof T]?:\n"
<< " T[P] extends (infer U)[] ? RecursivePartial<U>[] :\n"
<< " T[P] extends object ? RecursivePartial<T[P]> :\n"
<< " T[P];\n"
<< " };\n\n";
for (TObjectIterator<UClass> 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<UPanelSlot>())
Gen(Class);
}
Output << " "
<< "export interface Props {\n";
Output << " "
<< " Slot ? : PanelSlot;\n";
Output << " "
<< "}\n\n";
for (TObjectIterator<UClass> 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<UWidget>())
{
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<ObjectPropertyMacro>(Property) || CastFieldMacro<ClassPropertyMacro>(Property) ||
CastFieldMacro<WeakObjectPropertyMacro>(Property) || CastFieldMacro<SoftObjectPropertyMacro>(Property) ||
CastFieldMacro<LazyObjectPropertyMacro>(Property))
return false;
if (auto ArrayProperty = CastFieldMacro<ArrayPropertyMacro>(Property))
{
return IsReactSupportProperty(ArrayProperty->Inner);
}
else if (auto DelegateProperty = CastFieldMacro<DelegatePropertyMacro>(Property))
{
return !HasObjectParam(DelegateProperty->SignatureFunction);
}
else if (auto MulticastDelegateProperty = CastFieldMacro<MulticastDelegatePropertyMacro>(Property))
{
return !HasObjectParam(MulticastDelegateProperty->SignatureFunction);
}
return true;
}
void FReactDeclarationGenerator::GenClass(UClass* Class)
{
if (!Class->IsChildOf<UPanelSlot>() && !Class->IsChildOf<UWidget>())
return;
bool IsWidget = Class->IsChildOf<UWidget>();
FStringBuffer StringBuffer{"", ""};
StringBuffer << " "
<< "interface " << SafeName(Class->GetName());
if (IsWidget)
StringBuffer << "Props";
auto Super = Class->GetSuperStruct();
if (Super && (Super->IsChildOf<UPanelSlot>() || Super->IsChildOf<UWidget>()))
{
Gen(Super);
StringBuffer << " extends " << SafeName(Super->GetName());
if (Super->IsChildOf<UWidget>())
StringBuffer << "Props";
}
else if (IsWidget)
{
StringBuffer << " extends Props";
}
StringBuffer << " {\n";
for (TFieldIterator<PropertyMacro> 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<UObject*> RefTypesTmp;
if (!IsWidget && IsDelegate(Property)) // UPanelSlot skip delegate
{
continue;
}
if (CastFieldMacro<StructPropertyMacro>(Property))
{
TmpBuff << "RecursivePartial<";
}
if (!GenTypeDecl(TmpBuff, Property, RefTypesTmp, false, true))
{
continue;
}
if (CastFieldMacro<StructPropertyMacro>(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();
}
@@ -0,0 +1,17 @@
#include "IReactDeclarationGenerator.h"
#include "Features/IModularFeatures.h"
class FReactDeclarationGeneratorModule : public IReactDeclarationGenerator
{
private:
public:
void StartupModule() override
{
}
void ShutdownModule() override
{
}
};
IMPLEMENT_MODULE(FReactDeclarationGeneratorModule, ReactDeclarationGenerator)
@@ -0,0 +1,31 @@
/*
* Tencent is pleased to support the open source community by making Puerts available.
* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may
* be subject to their corresponding license terms. This file is subject to the terms and conditions defined in file 'LICENSE',
* which is part of this source code package.
*/
#pragma once
#include <cstdio>
#if ENGINE_MAJOR_VERSION < 5 || ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION < 2
#include "IScriptGeneratorPluginInterface.h"
#endif
#include "Modules/ModuleManager.h"
#include "CoreMinimal.h"
class IReactDeclarationGenerator : public IModuleInterface
{
public:
static inline IReactDeclarationGenerator& Get()
{
return FModuleManager::LoadModuleChecked<IReactDeclarationGenerator>("ReactDeclarationGenerator");
}
static inline bool IsAvailable()
{
return FModuleManager::Get().IsModuleLoaded("ReactDeclarationGenerator");
}
};
@@ -0,0 +1,24 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CodeGenerator.h"
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "ReactDeclarationGenerator.generated.h"
/**
*
*/
UCLASS()
class REACTDECLARATIONGENERATOR_API UReactDeclarationGenerator : public UObject, public ICodeGenerator
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintNativeEvent)
void Gen() const;
virtual void Gen_Implementation() const override;
};
@@ -0,0 +1,40 @@
/*
* Tencent is pleased to support the open source community by making Puerts available.
* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms.
* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package.
*/
using System;
using System.IO;
using UnrealBuildTool;
public class ReactDeclarationGenerator : ModuleRules
{
public ReactDeclarationGenerator(ReadOnlyTargetRules Target) : base(Target)
{
PublicIncludePaths.AddRange(
new string[] {
"Programs/UnrealHeaderTool/Public",
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"UMG",
"UnrealEd",
"LevelEditor",
"Engine",
"Slate",
"SlateCore",
"EditorStyle",
"InputCore",
"Projects",
"DeclarationGenerator",
"JsEnv",
}
);
}
}