From 9b0666e0f80c337a19c586f17c760fc4f1201580 Mon Sep 17 00:00:00 2001 From: Marc Zwart Date: Sun, 14 Dec 2025 16:33:44 +0100 Subject: [PATCH 01/34] render TS as proxy class in user namespace --- .../Parsing/ModuleHierarchyInfo.cs | 4 +- TypeShim.Generator/Program.cs | 6 +- .../Typescript/TypeScriptMethodRenderer.cs | 38 ++++-- .../Typescript/TypeScriptRenderer.cs | 29 ++--- .../TypeScriptSymbolNameTemplate.cs | 69 ++++++++++ .../Typescript/TypeScriptTypeMapper.cs | 77 +++++------ .../TypeScriptUserClassNamespaceRenderer.cs | 21 +++ .../Typescript/TypescriptClassNameBuilder.cs | 18 --- .../TypescriptInteropInterfaceRenderer.cs | 12 +- ...ypescriptInteropModuleInterfaceRenderer.cs | 6 +- .../TypescriptSymbolNameProvider.cs | 41 ++++++ .../TypescriptUserClassInterfaceRenderer.cs | 32 ----- .../TypescriptUserClassProxyRenderer.cs | 121 +++++++++--------- .../TypescriptUserModuleClassRenderer.cs | 59 +++++---- 14 files changed, 307 insertions(+), 226 deletions(-) create mode 100644 TypeShim.Generator/Typescript/TypeScriptSymbolNameTemplate.cs create mode 100644 TypeShim.Generator/Typescript/TypeScriptUserClassNamespaceRenderer.cs delete mode 100644 TypeShim.Generator/Typescript/TypescriptClassNameBuilder.cs create mode 100644 TypeShim.Generator/Typescript/TypescriptSymbolNameProvider.cs delete mode 100644 TypeShim.Generator/Typescript/TypescriptUserClassInterfaceRenderer.cs diff --git a/TypeShim.Generator/Parsing/ModuleHierarchyInfo.cs b/TypeShim.Generator/Parsing/ModuleHierarchyInfo.cs index f12254f..f77daf4 100644 --- a/TypeShim.Generator/Parsing/ModuleHierarchyInfo.cs +++ b/TypeShim.Generator/Parsing/ModuleHierarchyInfo.cs @@ -9,12 +9,12 @@ internal class ModuleHierarchyInfo private readonly Dictionary _children = []; - internal static ModuleHierarchyInfo FromClasses(IEnumerable classInfos, TypescriptClassNameBuilder classNameBuilder) + internal static ModuleHierarchyInfo FromClasses(IEnumerable classInfos, TypescriptSymbolNameProvider symbolNameProvider) { ModuleHierarchyInfo moduleInfo = new() { ExportedClass = null }; foreach (ClassInfo classInfo in classInfos) { - string[] propertyAccessorParts = [.. classInfo.Namespace.Split('.'), classNameBuilder.GetInteropInterfaceName(classInfo)]; + string[] propertyAccessorParts = [.. classInfo.Namespace.Split('.'), symbolNameProvider.GetInteropInterfaceName(classInfo)]; moduleInfo.Add(propertyAccessorParts, classInfo); } return moduleInfo; diff --git a/TypeShim.Generator/Program.cs b/TypeShim.Generator/Program.cs index 93bb2ac..e26384a 100644 --- a/TypeShim.Generator/Program.cs +++ b/TypeShim.Generator/Program.cs @@ -35,12 +35,12 @@ static void GenerateCSharpInteropCode(ProgramArguments parsedArgs, List classInfos) { TypeScriptTypeMapper typeMapper = new(classInfos); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); + TypescriptSymbolNameProvider symbolNameProvider = new(typeMapper); ModuleInfo moduleInfo = new() { ExportedClasses = classInfos, - HierarchyInfo = ModuleHierarchyInfo.FromClasses(classInfos, classNameBuilder) + HierarchyInfo = ModuleHierarchyInfo.FromClasses(classInfos, symbolNameProvider) }; - TypeScriptRenderer tsRenderer = new(classInfos, moduleInfo, classNameBuilder, typeMapper); + TypeScriptRenderer tsRenderer = new(classInfos, moduleInfo, symbolNameProvider); File.WriteAllText(parsedArgs.TsOutputFilePath, tsRenderer.Render()); } \ No newline at end of file diff --git a/TypeShim.Generator/Typescript/TypeScriptMethodRenderer.cs b/TypeShim.Generator/Typescript/TypeScriptMethodRenderer.cs index 93ff47d..7264f7e 100644 --- a/TypeShim.Generator/Typescript/TypeScriptMethodRenderer.cs +++ b/TypeShim.Generator/Typescript/TypeScriptMethodRenderer.cs @@ -1,32 +1,44 @@ namespace TypeShim.Generator.Typescript; -internal class TypeScriptMethodRenderer(TypeScriptTypeMapper typeMapper) +internal class TypeScriptMethodRenderer(TypescriptSymbolNameProvider symbolNameProvider) { - internal string RenderPropertyGetterSignatureForClass(MethodInfo methodInfo) + internal string RenderProxyPropertyGetterSignature(MethodInfo methodInfo) { - - return $"get {methodInfo.Name.TrimStart("get_")}({RenderMethodParameters(methodInfo)}): {typeMapper.ToTypeScriptType(methodInfo.ReturnType)}"; + string returnType = symbolNameProvider.GetProxyReferenceNameIfExists(methodInfo.ReturnType) ?? symbolNameProvider.GetNakedSymbolReference(methodInfo.ReturnType); + return $"get {methodInfo.Name.TrimStart("get_")}({RenderProxyMethodParameters(methodInfo)}): {returnType}"; } - internal string RenderPropertySetterSignatureForClass(MethodInfo methodInfo) + internal string RenderProxyPropertySetterSignature(MethodInfo methodInfo) { - return $"set {methodInfo.Name.TrimStart("set_")}({RenderMethodParameters(methodInfo)})"; + return $"set {methodInfo.Name.TrimStart("set_")}({RenderProxyMethodParameters(methodInfo)})"; } - internal string RenderMethodSignatureForClass(MethodInfo methodInfo) + internal string RenderProxyMethodSignature(MethodInfo methodInfo) { + string returnType = symbolNameProvider.GetProxyReferenceNameIfExists(methodInfo.ReturnType) ?? symbolNameProvider.GetNakedSymbolReference(methodInfo.ReturnType); + string optionalAsync = methodInfo.ReturnType.IsTaskType ? "async " : string.Empty; - return $"{optionalAsync}{RenderMethodSignatureForInterface(methodInfo)}"; + return $"{optionalAsync}{methodInfo.Name}({RenderProxyMethodParameters(methodInfo)}): {returnType}"; } - internal string RenderMethodSignatureForInterface(MethodInfo methodInfo) + private string RenderProxyMethodParameters(MethodInfo methodInfo) { - return $"{methodInfo.Name}({RenderMethodParameters(methodInfo)}): {typeMapper.ToTypeScriptType(methodInfo.ReturnType)}"; + return string.Join(", ", methodInfo.MethodParameters.Select(p => + { + string returnType = symbolNameProvider.GetProxyReferenceNameIfExists(p.Type) ?? symbolNameProvider.GetNakedSymbolReference(p.Type); + return $"{p.Name}: {returnType}"; + })); } - private string RenderMethodParameters(MethodInfo methodInfo) + internal string RenderInteropMethodSignature(MethodInfo methodInfo) { - return string.Join(", ", methodInfo.MethodParameters - .Select(p => $"{p.Name}: {typeMapper.ToTypeScriptType(p.Type)}")); + string returnType = symbolNameProvider.GetNakedSymbolReference(methodInfo.ReturnType); + return $"{methodInfo.Name}({RenderInteropMethodParameters(methodInfo)}): {returnType}"; + + string RenderInteropMethodParameters(MethodInfo methodInfo) + { + return string.Join(", ", methodInfo.MethodParameters + .Select(p => $"{p.Name}: {symbolNameProvider.GetNakedSymbolReference(p.Type)}")); + } } } \ No newline at end of file diff --git a/TypeShim.Generator/Typescript/TypeScriptRenderer.cs b/TypeShim.Generator/Typescript/TypeScriptRenderer.cs index d0deca0..e63debb 100644 --- a/TypeShim.Generator/Typescript/TypeScriptRenderer.cs +++ b/TypeShim.Generator/Typescript/TypeScriptRenderer.cs @@ -2,10 +2,10 @@ using TypeShim.Generator.Parsing; using TypeShim.Generator.Typescript; -internal class TypeScriptRenderer(IEnumerable classInfos, ModuleInfo moduleInfo, TypescriptClassNameBuilder classNameBuilder, TypeScriptTypeMapper typeMapper) +internal class TypeScriptRenderer(IEnumerable classInfos, ModuleInfo moduleInfo, TypescriptSymbolNameProvider symbolNameProvider) { private readonly StringBuilder sourceBuilder = new(); - private readonly TypeScriptMethodRenderer methodRenderer = new(typeMapper); + private readonly TypeScriptMethodRenderer methodRenderer = new(symbolNameProvider); internal string Render() { @@ -13,15 +13,13 @@ internal string Render() RenderUserModuleClass(); RenderInteropInterfaces(); - RenderUserClassInterfaces(); - - RenderUserClassProxies(); + RenderUserClassNamespaces(); return sourceBuilder.ToString(); } private void RenderInteropModuleInterface() { - TypescriptInteropModuleInterfaceRenderer moduleInterfaceRenderer = new(moduleInfo.HierarchyInfo, classNameBuilder); + TypescriptInteropModuleInterfaceRenderer moduleInterfaceRenderer = new(moduleInfo.HierarchyInfo, symbolNameProvider); sourceBuilder.AppendLine(moduleInterfaceRenderer.Render()); } @@ -29,17 +27,17 @@ private void RenderInteropInterfaces() { foreach (ClassInfo classInfo in classInfos) { - TypescriptInteropInterfaceRenderer interopInterfaceRenderer = new(classInfo, methodRenderer, classNameBuilder); + TypescriptInteropInterfaceRenderer interopInterfaceRenderer = new(classInfo, methodRenderer, symbolNameProvider); sourceBuilder.AppendLine(interopInterfaceRenderer.Render()); } } - private void RenderUserClassInterfaces() + private void RenderUserClassNamespaces() { foreach (ClassInfo classInfo in classInfos.Where(c => !c.IsModule)) { - TypescriptUserClassInterfaceRenderer classInterfaceRenderer = new(classInfo, methodRenderer, typeMapper); - sourceBuilder.AppendLine(classInterfaceRenderer.Render()); + TypeScriptUserClassNamespaceRenderer namespaceRenderer = new(classInfo, methodRenderer, symbolNameProvider); + sourceBuilder.AppendLine(namespaceRenderer.Render()); } } @@ -47,17 +45,8 @@ private void RenderUserModuleClass() { foreach (ClassInfo moduleClassInfo in classInfos.Where(c => c.IsModule)) { - TypescriptUserModuleClassRenderer moduleClassRenderer = new(moduleClassInfo, methodRenderer, classNameBuilder); + TypescriptUserModuleClassRenderer moduleClassRenderer = new(moduleClassInfo, methodRenderer, symbolNameProvider); sourceBuilder.AppendLine(moduleClassRenderer.Render()); } } - - private void RenderUserClassProxies() - { - foreach (ClassInfo classInfo in classInfos.Where(c => !c.IsModule)) - { - TypescriptUserClassProxyRenderer classProxyRenderer = new(classInfo, methodRenderer, classNameBuilder); - sourceBuilder.AppendLine(classProxyRenderer.Render()); - } - } } \ No newline at end of file diff --git a/TypeShim.Generator/Typescript/TypeScriptSymbolNameTemplate.cs b/TypeShim.Generator/Typescript/TypeScriptSymbolNameTemplate.cs new file mode 100644 index 0000000..52d91f7 --- /dev/null +++ b/TypeShim.Generator/Typescript/TypeScriptSymbolNameTemplate.cs @@ -0,0 +1,69 @@ +using TypeShim.Generator.Parsing; + +namespace TypeShim.Generator.Typescript; + +internal sealed class TypeScriptSymbolNameTemplate +{ + internal required string Template { get; init; } + internal required TypeScriptSymbolNameTemplate? InnerTemplate { get; init; } + + private const string InnerPlaceholder = "{INNER_PLACEHOLDER}"; + private const string SuffixPlaceholder = "{SUFFIX_PLACEHOLDER}"; + + internal string Render(string suffix = "") + { + string template = Template; + if (InnerTemplate is not null) + { + string inner = InnerTemplate.Render(suffix); + template = template.Replace(InnerPlaceholder, inner); + } + + return template.Replace(SuffixPlaceholder, suffix); + } + + internal static TypeScriptSymbolNameTemplate ForUserType(InteropTypeInfo typeInfo) + { + return new TypeScriptSymbolNameTemplate + { + Template = $"{typeInfo.CLRTypeSyntax}{SuffixPlaceholder}", + InnerTemplate = null + }; + } + + internal static TypeScriptSymbolNameTemplate ForSimpleType(string typeName) + { + return new TypeScriptSymbolNameTemplate + { + Template = typeName, + InnerTemplate = null + }; + } + + internal static TypeScriptSymbolNameTemplate ForArrayType(TypeScriptSymbolNameTemplate innerTemplate) + { + return new TypeScriptSymbolNameTemplate + { + Template = $"Array<{InnerPlaceholder}>", + InnerTemplate = innerTemplate + }; + } + + internal static TypeScriptSymbolNameTemplate ForPromiseType(TypeScriptSymbolNameTemplate innerTemplate) + { + return new TypeScriptSymbolNameTemplate + { + Template = $"Promise<{InnerPlaceholder}>", + InnerTemplate = innerTemplate + }; + } + + internal static TypeScriptSymbolNameTemplate ForNullableType(TypeScriptSymbolNameTemplate innerTemplate) + { + return new TypeScriptSymbolNameTemplate + { + Template = $"{InnerPlaceholder} | null", + InnerTemplate = innerTemplate + }; + } +} diff --git a/TypeShim.Generator/Typescript/TypeScriptTypeMapper.cs b/TypeShim.Generator/Typescript/TypeScriptTypeMapper.cs index d291f12..32523ec 100644 --- a/TypeShim.Generator/Typescript/TypeScriptTypeMapper.cs +++ b/TypeShim.Generator/Typescript/TypeScriptTypeMapper.cs @@ -4,7 +4,7 @@ namespace TypeShim.Generator.Typescript; -internal class TypeScriptTypeMapper(IEnumerable classInfos) +internal sealed class TypeScriptTypeMapper(IEnumerable classInfos) { private readonly HashSet _userTypeNames = [.. classInfos.Select(ci => ci.Name)]; @@ -13,53 +13,46 @@ public bool IsUserType(InteropTypeInfo typeInfo) return _userTypeNames.Contains(typeInfo.CLRTypeSyntax.ToString()); } - public TypeSyntax ExtractInnerTypeArgument(TypeSyntax typeSyntax) + public TypeScriptSymbolNameTemplate ToTypeScriptType(InteropTypeInfo typeInfo) { - return typeSyntax switch - { - GenericNameSyntax generic => generic.TypeArgumentList.Arguments.SingleOrDefault() - ?? throw new Exception("Generic types are only supported with a single type argument"), - ArrayTypeSyntax array => array.ElementType ?? SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword)), - _ => typeSyntax - }; - } - - public string ToTypeScriptType(InteropTypeInfo typeInfo) - { - if (IsUserType(typeInfo)) return typeInfo.CLRTypeSyntax.ToString(); // write user-defined types to TS as they are defined in C# - - if (typeInfo.IsNullableType) return $"{ToTypeScriptType(typeInfo.TypeArgument ?? throw new ArgumentException("Nullable type must have a type argument"))} | null"; + if (IsUserType(typeInfo)) + return TypeScriptSymbolNameTemplate.ForUserType(typeInfo); - // TODO: handle nullable types properly (i.e. int?[] => Array - // really need to start writing some unit tests... + if (typeInfo.IsNullableType) + return TypeScriptSymbolNameTemplate.ForNullableType(ToTypeScriptType(typeInfo.TypeArgument ?? throw new ArgumentException("Nullable type must have a type argument"))); return typeInfo.ManagedType switch { - KnownManagedType.None => "undefined", - KnownManagedType.Void => "void", - KnownManagedType.Boolean => "boolean", - KnownManagedType.Byte => "number", - KnownManagedType.Char => "string", - KnownManagedType.Int16 => "number", - KnownManagedType.Int32 => "number", - KnownManagedType.Int64 => "number", - KnownManagedType.Double => "number", - KnownManagedType.Single => "number", - KnownManagedType.IntPtr => "number", // JS doesn't have pointers, typically represented as number - KnownManagedType.JSObject or KnownManagedType.Object - => "object", - KnownManagedType.String => "string", - KnownManagedType.Exception => "Error", - KnownManagedType.DateTime => "Date", - KnownManagedType.DateTimeOffset => "Date", + KnownManagedType.None => TypeScriptSymbolNameTemplate.ForSimpleType("undefined"), + KnownManagedType.Void => TypeScriptSymbolNameTemplate.ForSimpleType("void"), + KnownManagedType.JSObject + or KnownManagedType.Object + => TypeScriptSymbolNameTemplate.ForSimpleType("object"), + KnownManagedType.Boolean => TypeScriptSymbolNameTemplate.ForSimpleType("boolean"), + KnownManagedType.Char + or KnownManagedType.String => TypeScriptSymbolNameTemplate.ForSimpleType("string"), + KnownManagedType.Byte + or KnownManagedType.Int16 + or KnownManagedType.Int32 + or KnownManagedType.Int64 + or KnownManagedType.Double + or KnownManagedType.Single + or KnownManagedType.IntPtr + => TypeScriptSymbolNameTemplate.ForSimpleType("number"), + KnownManagedType.Exception => TypeScriptSymbolNameTemplate.ForSimpleType("Error"), + KnownManagedType.DateTime + or KnownManagedType.DateTimeOffset => TypeScriptSymbolNameTemplate.ForSimpleType("Date"), + KnownManagedType.Task => TypeScriptSymbolNameTemplate.ForPromiseType(ToTypeScriptType(typeInfo.TypeArgument ?? typeInfo)), + KnownManagedType.Array + => TypeScriptSymbolNameTemplate.ForArrayType(ToTypeScriptType(typeInfo.TypeArgument ?? typeInfo)), + KnownManagedType.ArraySegment + or KnownManagedType.Span + => throw new NotImplementedException("ArraySegment and Span are not yet supported"), // MemoryView TODO KnownManagedType.Nullable => throw new NotImplementedException("Nullable value-types are not yet supported"), // return "something | null" ? - KnownManagedType.Task => $"Promise<{ToTypeScriptType(typeInfo.TypeArgument ?? typeInfo)}>", - KnownManagedType.Array or KnownManagedType.ArraySegment or KnownManagedType.Span - => $"Array<{ToTypeScriptType(typeInfo.TypeArgument ?? typeInfo)}>", - KnownManagedType.Action => "(() => void)", - KnownManagedType.Function => "Function", // TODO: try map signature ? - KnownManagedType.Unknown => "any", - _ => "any" + KnownManagedType.Action => throw new NotImplementedException("Action is not yet supported"), // "(() => void)" + KnownManagedType.Function => throw new NotImplementedException("Function is not yet supported"), // "Function" + KnownManagedType.Unknown + or _ => TypeScriptSymbolNameTemplate.ForSimpleType("any"), }; } diff --git a/TypeShim.Generator/Typescript/TypeScriptUserClassNamespaceRenderer.cs b/TypeShim.Generator/Typescript/TypeScriptUserClassNamespaceRenderer.cs new file mode 100644 index 0000000..daf66b2 --- /dev/null +++ b/TypeShim.Generator/Typescript/TypeScriptUserClassNamespaceRenderer.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; +using TypeShim.Generator.Parsing; + +namespace TypeShim.Generator.Typescript; + +internal class TypeScriptUserClassNamespaceRenderer(ClassInfo classInfo, TypeScriptMethodRenderer methodRenderer, TypescriptSymbolNameProvider symbolNameProvider) +{ + private readonly StringBuilder sb = new(); + internal string Render() + { + TypescriptUserClassProxyRenderer proxyRenderer = new(classInfo, methodRenderer, symbolNameProvider); + + sb.AppendLine($"// Auto-generated TypeScript namespace for class: {classInfo.Namespace}.{classInfo.Name}"); + sb.AppendLine($"export namespace {symbolNameProvider.GetUserClassNamespace(classInfo)} {{"); + sb.AppendLine(proxyRenderer.Render(depth: 1)); + sb.AppendLine($"}}"); + return sb.ToString(); + } +} diff --git a/TypeShim.Generator/Typescript/TypescriptClassNameBuilder.cs b/TypeShim.Generator/Typescript/TypescriptClassNameBuilder.cs deleted file mode 100644 index ced0285..0000000 --- a/TypeShim.Generator/Typescript/TypescriptClassNameBuilder.cs +++ /dev/null @@ -1,18 +0,0 @@ -using TypeShim.Generator.Parsing; - -namespace TypeShim.Generator.Typescript; - -internal class TypescriptClassNameBuilder(TypeScriptTypeMapper typeMapper) -{ - internal string GetInteropInterfaceName(ClassInfo classInfo) => $"{classInfo.Name}Interop"; - internal string GetUserClassProxyName(ClassInfo classInfo) => $"{classInfo.Name}Proxy"; - internal string? GetUserClassProxyName(InteropTypeInfo typeInfo) - { - InteropTypeInfo targetType = typeInfo.TypeArgument ?? typeInfo; - return typeMapper.IsUserType(targetType) - ? $"{typeMapper.ToTypeScriptType(targetType)}Proxy" - : null; - } - internal string GetUserClassStaticsName(ClassInfo classInfo) => $"{classInfo.Name}Statics"; - internal string GetModuleInteropClassName() => $"AssemblyExports"; -} diff --git a/TypeShim.Generator/Typescript/TypescriptInteropInterfaceRenderer.cs b/TypeShim.Generator/Typescript/TypescriptInteropInterfaceRenderer.cs index a6ebb25..8feca94 100644 --- a/TypeShim.Generator/Typescript/TypescriptInteropInterfaceRenderer.cs +++ b/TypeShim.Generator/Typescript/TypescriptInteropInterfaceRenderer.cs @@ -10,25 +10,25 @@ namespace TypeShim.Generator.Typescript; /// /// /// -/// -internal class TypescriptInteropInterfaceRenderer(ClassInfo classInfo, TypeScriptMethodRenderer methodRenderer, TypescriptClassNameBuilder classNameBuilder) +/// +internal class TypescriptInteropInterfaceRenderer(ClassInfo classInfo, TypeScriptMethodRenderer methodRenderer, TypescriptSymbolNameProvider symbolNameProvider) { private readonly StringBuilder sb = new(); internal string Render() { sb.AppendLine($"// Auto-generated TypeScript interop interface. Source class: {classInfo.Namespace}.{classInfo.Name}"); - sb.AppendLine($"export interface {classNameBuilder.GetInteropInterfaceName(classInfo)} {{"); + sb.AppendLine($"export interface {symbolNameProvider.GetInteropInterfaceName(classInfo)} {{"); foreach (MethodInfo methodInfo in classInfo.Methods) { - sb.AppendLine($" {methodRenderer.RenderMethodSignatureForInterface(methodInfo.WithInteropTypeInfo())};"); + sb.AppendLine($" {methodRenderer.RenderInteropMethodSignature(methodInfo.WithInteropTypeInfo())};"); } foreach (PropertyInfo propertyInfo in classInfo.Properties) { - sb.AppendLine($" {methodRenderer.RenderMethodSignatureForInterface(propertyInfo.GetMethod.WithInteropTypeInfo())};"); + sb.AppendLine($" {methodRenderer.RenderInteropMethodSignature(propertyInfo.GetMethod.WithInteropTypeInfo())};"); if (propertyInfo.SetMethod is MethodInfo setMethod) { - sb.AppendLine($" {methodRenderer.RenderMethodSignatureForInterface(setMethod.WithInteropTypeInfo())};"); + sb.AppendLine($" {methodRenderer.RenderInteropMethodSignature(setMethod.WithInteropTypeInfo())};"); } } sb.AppendLine("}"); diff --git a/TypeShim.Generator/Typescript/TypescriptInteropModuleInterfaceRenderer.cs b/TypeShim.Generator/Typescript/TypescriptInteropModuleInterfaceRenderer.cs index c127f5d..3723325 100644 --- a/TypeShim.Generator/Typescript/TypescriptInteropModuleInterfaceRenderer.cs +++ b/TypeShim.Generator/Typescript/TypescriptInteropModuleInterfaceRenderer.cs @@ -6,14 +6,14 @@ namespace TypeShim.Generator.Typescript; /// Renders a TypeScript interface matching the JSExport-generated WebAssembly module exports /// /// -internal class TypescriptInteropModuleInterfaceRenderer(ModuleHierarchyInfo moduleHierarchyInfo, TypescriptClassNameBuilder classNameBuilder) +internal class TypescriptInteropModuleInterfaceRenderer(ModuleHierarchyInfo moduleHierarchyInfo, TypescriptSymbolNameProvider symbolNameProvider) { private readonly StringBuilder sb = new(); internal string Render() { sb.AppendLine("// Auto-generated TypeScript module exports interface"); - sb.AppendLine($"export interface {classNameBuilder.GetModuleInteropClassName()} {{"); + sb.AppendLine($"export interface {symbolNameProvider.GetModuleInteropClassName()} {{"); RenderModuleInfo(moduleHierarchyInfo, 1); @@ -28,7 +28,7 @@ private void RenderModuleInfo(ModuleHierarchyInfo moduleInfo, int indentLevel) { if (child.Value.ExportedClass != null) { - sb.AppendLine($"{indent}{child.Key}: {classNameBuilder.GetInteropInterfaceName(child.Value.ExportedClass)};"); + sb.AppendLine($"{indent}{child.Key}: {symbolNameProvider.GetInteropInterfaceName(child.Value.ExportedClass)};"); } else { diff --git a/TypeShim.Generator/Typescript/TypescriptSymbolNameProvider.cs b/TypeShim.Generator/Typescript/TypescriptSymbolNameProvider.cs new file mode 100644 index 0000000..d07d3ff --- /dev/null +++ b/TypeShim.Generator/Typescript/TypescriptSymbolNameProvider.cs @@ -0,0 +1,41 @@ +using TypeShim.Generator.Parsing; + +namespace TypeShim.Generator.Typescript; + +internal class TypescriptSymbolNameProvider(TypeScriptTypeMapper typeMapper) +{ + internal string GetModuleInteropClassName() => $"AssemblyExports"; + internal string GetInteropInterfaceName(ClassInfo classInfo) => $"{classInfo.Name}Interop"; + + internal string GetUserClassNamespace(ClassInfo classInfo) + { + return classInfo.Name; + } + + internal string GetProxyDefinitionName() => $"Proxy"; + internal string GetProxyReferenceName(ClassInfo classInfo) => RenderUserClassProxyReferenceName(classInfo.Type); + internal string? GetProxyReferenceNameIfExists(InteropTypeInfo typeInfo) + { + return typeMapper.IsUserType(typeInfo) || (typeInfo.TypeArgument is InteropTypeInfo argTypeInfo && typeMapper.IsUserType(argTypeInfo)) + ? RenderUserClassProxyReferenceName(typeInfo) + : null; + } + private string RenderUserClassProxyReferenceName(InteropTypeInfo typeInfo) => $"{typeMapper.ToTypeScriptType(typeInfo).Render(suffix: $".{GetProxyDefinitionName()}")}"; + + /// + /// returns the TypeScript type name without any proxy or snapshot suffixes. Mostly useful for non-user types like string, int with accomodations for Tasks, arrays etc. + /// + /// + /// + internal string GetNakedSymbolReference(InteropTypeInfo typeInfo) => typeMapper.ToTypeScriptType(typeInfo).Render(suffix: string.Empty); + + + +} + +enum SymbolReferenceType +{ + None, + Proxy, + Snapshot +} diff --git a/TypeShim.Generator/Typescript/TypescriptUserClassInterfaceRenderer.cs b/TypeShim.Generator/Typescript/TypescriptUserClassInterfaceRenderer.cs deleted file mode 100644 index 73b2072..0000000 --- a/TypeShim.Generator/Typescript/TypescriptUserClassInterfaceRenderer.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Text; -using TypeShim.Generator.Parsing; - -namespace TypeShim.Generator.Typescript; - -/// -/// Renders a TypeScript interface matching the signature of the user's C# class annotated with . -/// The interface hides the interop details from the end user through the proxy class rendered by . -/// -/// -/// -internal class TypescriptUserClassInterfaceRenderer(ClassInfo classInfo, TypeScriptMethodRenderer methodRenderer, TypeScriptTypeMapper typeMapper) -{ - private readonly StringBuilder sb = new(); - - internal string Render() - { - sb.AppendLine($"// Auto-generated TypeScript interface. Source class: {classInfo.Namespace}.{classInfo.Name}"); - sb.AppendLine($"export interface {classInfo.Name} {{"); - foreach (MethodInfo methodInfo in classInfo.Methods.Where(m => !m.IsStatic)) // only instance methods - { - sb.AppendLine($" {methodRenderer.RenderMethodSignatureForInterface(methodInfo.WithoutInstanceParameter())};"); - } - foreach (PropertyInfo propertyInfo in classInfo.Properties.Where(p => !p.IsStatic)) - { - bool isReadonly = propertyInfo.SetMethod is null; - sb.AppendLine($" {(isReadonly ? "readonly " : string.Empty)}{propertyInfo.Name}: {typeMapper.ToTypeScriptType(propertyInfo.Type)};"); - } - sb.AppendLine("}"); - return sb.ToString(); - } -} diff --git a/TypeShim.Generator/Typescript/TypescriptUserClassProxyRenderer.cs b/TypeShim.Generator/Typescript/TypescriptUserClassProxyRenderer.cs index 96ca1ef..6045cb7 100644 --- a/TypeShim.Generator/Typescript/TypescriptUserClassProxyRenderer.cs +++ b/TypeShim.Generator/Typescript/TypescriptUserClassProxyRenderer.cs @@ -3,112 +3,123 @@ namespace TypeShim.Generator.Typescript; -internal class TypescriptUserClassProxyRenderer(ClassInfo classInfo, TypeScriptMethodRenderer methodRenderer, TypescriptClassNameBuilder classNameBuilder) +internal class TypescriptUserClassProxyRenderer(ClassInfo classInfo, TypeScriptMethodRenderer methodRenderer, TypescriptSymbolNameProvider symbolNameProvider) { - // PURPOSE: - // - glue between interop interface for a single class instance, enabling dynamic method invocation - private readonly StringBuilder sb = new(); - internal string Render() + internal string Render(int depth) { - string proxyClassName = classNameBuilder.GetUserClassProxyName(classInfo); - string interopInterfaceName = classNameBuilder.GetModuleInteropClassName(); - RenderProxyClass(proxyClassName, interopInterfaceName); - - //BEEPBOOP - //string staticsClassName = classNameBuilder.GetUserClassStaticsName(classInfo); - //RenderStaticsClass(staticsClassName, interopInterfaceName); + string interopInterfaceName = symbolNameProvider.GetModuleInteropClassName(); + RenderProxyClass(interopInterfaceName, depth); return sb.ToString(); } - private void RenderProxyClass(string className, string interopInterfaceName) + private void RenderProxyClass(string interopInterfaceName, int depth) { - string indent = " "; - sb.AppendLine($"// Auto-generated TypeScript proxy class. Source class: {classInfo.Namespace}.{classInfo.Name}"); + string indent = new(' ', depth * 2); + string indent2 = new(' ', (depth + 1) * 2); + string indent3 = new(' ', (depth + 2) * 2); - sb.AppendLine($"class {className} implements {classInfo.Name} {{"); - sb.AppendLine($"{indent}interop: {interopInterfaceName};"); - sb.AppendLine($"{indent}instance: object;"); + sb.AppendLine($"{indent}export class {symbolNameProvider.GetProxyDefinitionName()} {{"); + sb.AppendLine($"{indent2}interop: {interopInterfaceName};"); + sb.AppendLine($"{indent2}instance: object;"); sb.AppendLine(); - sb.AppendLine($"{indent}constructor(instance: object, interop: {interopInterfaceName}) {{"); - sb.AppendLine($"{indent}{indent}this.interop = interop;"); - sb.AppendLine($"{indent}{indent}this.instance = instance;"); - sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent2}constructor(instance: object, interop: {interopInterfaceName}) {{"); + sb.AppendLine($"{indent3}this.interop = interop;"); + sb.AppendLine($"{indent3}this.instance = instance;"); + sb.AppendLine($"{indent2}}}"); sb.AppendLine(); foreach (MethodInfo methodInfo in classInfo.Methods.Where(m => !m.IsStatic)) { - sb.AppendLine($"{indent}public {methodRenderer.RenderMethodSignatureForClass(methodInfo.WithoutInstanceParameter())} {{"); - RenderProxyInstanceExtraction(indent, methodInfo); - RenderInteropInvocation(indent, methodInfo); - sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent2}public {methodRenderer.RenderProxyMethodSignature(methodInfo.WithoutInstanceParameter())} {{"); + RenderProxyInstanceExtraction(indent3, methodInfo); + RenderInteropInvocation(indent3, methodInfo); + sb.AppendLine($"{indent2}}}"); sb.AppendLine(); } foreach (PropertyInfo propertyInfo in classInfo.Properties.Where(p => !p.IsStatic)) { MethodInfo? getter = propertyInfo.GetMethod; - sb.AppendLine($"{indent}public {methodRenderer.RenderPropertyGetterSignatureForClass(getter.WithoutInstanceParameter())} {{"); - RenderInteropInvocation(indent, getter); - sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent2}public {methodRenderer.RenderProxyPropertyGetterSignature(getter.WithoutInstanceParameter())} {{"); + RenderInteropInvocation(indent3, getter); + sb.AppendLine($"{indent2}}}"); sb.AppendLine(); if (propertyInfo.SetMethod is MethodInfo setter) { - sb.AppendLine($"{indent}public {methodRenderer.RenderPropertySetterSignatureForClass(setter.WithoutInstanceParameter())} {{"); - RenderProxyInstanceExtraction(indent, setter); - RenderInteropInvocation(indent, setter); - sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent2}public {methodRenderer.RenderProxyPropertySetterSignature(setter.WithoutInstanceParameter())} {{"); + RenderProxyInstanceExtraction(indent3, setter); + RenderInteropInvocation(indent3, setter); + sb.AppendLine($"{indent2}}}"); sb.AppendLine(); } } - sb.AppendLine($"}}"); + sb.AppendLine($"{indent}}}"); } private void RenderProxyInstanceExtraction(string indent, MethodInfo methodInfo) { foreach (MethodParameterInfo param in methodInfo.MethodParameters.Where(p => p.Type.RequiresCLRTypeConversion && !p.IsInjectedInstanceParameter)) { - if (classNameBuilder.GetUserClassProxyName(param.Type) is not string proxyClassName) + InteropTypeInfo paramType = param.Type; + InteropTypeInfo paramTargetType = param.Type.TypeArgument ?? param.Type; // we are concerned with the element type for arrays/tasks or inner type of nullables + if (symbolNameProvider.GetProxyReferenceNameIfExists(paramTargetType) is not string proxyClassName) { throw new ArgumentException("All type conversion-requiring types should be user class proxies."); } - if (param.Type.IsArrayType || param.Type.IsTaskType) + if (paramType.IsArrayType || paramType.IsTaskType) { - string transformFunction = param.Type.IsArrayType ? "map" : "then"; - sb.AppendLine($"{indent}{indent}const {GetInteropInvocationVariable(param)} = {param.Name}.{transformFunction}(item => item instanceof {proxyClassName} ? item.instance : item);"); + string transformFunction = paramType.IsArrayType ? "map" : "then"; + sb.AppendLine($"{indent}const {GetInteropInvocationVariable(param)} = {param.Name}.{transformFunction}(item => item instanceof {proxyClassName} ? item.instance : item);"); } else { // simple or nullable types - sb.AppendLine($"{indent}{indent}const {GetInteropInvocationVariable(param)} = {param.Name} instanceof {proxyClassName} ? {param.Name}.instance : {param.Name};"); + sb.AppendLine($"{indent}const {GetInteropInvocationVariable(param)} = {param.Name} instanceof {proxyClassName} ? {param.Name}.instance : {param.Name};"); } - } } private void RenderInteropInvocation(string indent, MethodInfo methodInfo) { string interopInvoke = RenderMethodCallParametersWithInstanceParameterExpression(methodInfo, "this.instance"); // note: instance parameter will be unused for static methods - if (classNameBuilder.GetUserClassProxyName(methodInfo.ReturnType) is string proxyClassName) // user class return type, wrap in proxy + + InteropTypeInfo returnType = methodInfo.ReturnType; + InteropTypeInfo returnTargetType = methodInfo.ReturnType.TypeArgument ?? methodInfo.ReturnType; // we are concerned with the element type for arrays/tasks or inner type of nullables + + if (symbolNameProvider.GetProxyReferenceNameIfExists(returnTargetType) is string proxyClassName) { - string optionalAwait = methodInfo.ReturnType.IsTaskType ? "await " : string.Empty; - sb.AppendLine($"{indent}{indent}const res = {optionalAwait}this.interop.{ResolveInteropMethodAccessor(classInfo, methodInfo)}({interopInvoke});"); + // user class return type, wrap in proxy + string optionalAwait = returnType.IsTaskType ? "await " : string.Empty; + sb.AppendLine($"{indent}const res = {optionalAwait}this.interop.{ResolveInteropMethodAccessor(classInfo, methodInfo)}({interopInvoke});"); - if (methodInfo.ReturnType.IsArrayType) + if (returnType.IsArrayType) { - sb.AppendLine($"{indent}{indent}return res.map(item => {GetNewProxyExpression(methodInfo.ReturnType, proxyClassName, "item")});"); + sb.AppendLine($"{indent}return res.map(item => {GetNewProxyExpression(returnType, proxyClassName, "item")});"); } else { - sb.AppendLine($"{indent}{indent}return {GetNewProxyExpression(methodInfo.ReturnType, proxyClassName, "res")};"); + sb.AppendLine($"{indent}return {GetNewProxyExpression(returnType, proxyClassName, "res")};"); } } else // primitive return type or void { - string optionalReturn = methodInfo.ReturnType.ManagedType == KnownManagedType.Void ? string.Empty : "return "; - sb.AppendLine($"{indent}{indent}{optionalReturn}this.interop.{ResolveInteropMethodAccessor(classInfo, methodInfo)}({interopInvoke});"); + string optionalReturn = returnType.ManagedType == KnownManagedType.Void ? string.Empty : "return "; + sb.AppendLine($"{indent}{optionalReturn}this.interop.{ResolveInteropMethodAccessor(classInfo, methodInfo)}({interopInvoke});"); + } + + static string GetNewProxyExpression(InteropTypeInfo returnTypeInfo, string proxyClassName, string instanceName) + { + if (returnTypeInfo.IsNullableType) + { + return $"{instanceName} ? new {proxyClassName}({instanceName}, this.interop) : null"; + } + else + { + return $"new {proxyClassName}({instanceName}, this.interop)"; + } } } @@ -123,18 +134,6 @@ internal string RenderMethodCallParametersWithInstanceParameterExpression(Method return string.Join(", ", methodInfo.MethodParameters.Select(p => p.IsInjectedInstanceParameter ? instanceParameterExpression : GetInteropInvocationVariable(p))); } - private string GetNewProxyExpression(InteropTypeInfo returnTypeInfo, string proxyClassName, string instanceName) - { - if (returnTypeInfo.IsNullableType) - { - return $"{instanceName} ? new {proxyClassName}({instanceName}, this.interop) : null"; - } - else - { - return $"new {proxyClassName}({instanceName}, this.interop)"; - } - } - private string GetInteropInvocationVariable(MethodParameterInfo param) { return param.Type.RequiresCLRTypeConversion ? $"{param.Name}Instance" : param.Name; @@ -142,6 +141,6 @@ private string GetInteropInvocationVariable(MethodParameterInfo param) private string ResolveInteropMethodAccessor(ClassInfo classInfo, MethodInfo methodInfo) { - return $"{classInfo.Namespace}.{classNameBuilder.GetInteropInterfaceName(classInfo)}.{methodInfo.Name}"; + return $"{classInfo.Namespace}.{symbolNameProvider.GetInteropInterfaceName(classInfo)}.{methodInfo.Name}"; } } diff --git a/TypeShim.Generator/Typescript/TypescriptUserModuleClassRenderer.cs b/TypeShim.Generator/Typescript/TypescriptUserModuleClassRenderer.cs index 238ad00..3f41ae6 100644 --- a/TypeShim.Generator/Typescript/TypescriptUserModuleClassRenderer.cs +++ b/TypeShim.Generator/Typescript/TypescriptUserModuleClassRenderer.cs @@ -6,13 +6,13 @@ namespace TypeShim.Generator.Typescript; /// /// Renders the 'TSModule' marked by the user. Can be constructed with the 'AssemblyExports', from here on out the user can interact with their C# code /// -internal class TypescriptUserModuleClassRenderer(ClassInfo moduleClassInfo, TypeScriptMethodRenderer methodRenderer, TypescriptClassNameBuilder classNameBuilder) +internal class TypescriptUserModuleClassRenderer(ClassInfo moduleClassInfo, TypeScriptMethodRenderer methodRenderer, TypescriptSymbolNameProvider symbolNameProvider) { private readonly StringBuilder sb = new(); internal string Render() { - RenderModuleClass(moduleClassInfo.Name, classNameBuilder.GetModuleInteropClassName()); + RenderModuleClass(moduleClassInfo.Name, symbolNameProvider.GetModuleInteropClassName()); return sb.ToString(); } @@ -30,7 +30,7 @@ private void RenderModuleClass(string className, string interopInterfaceName) sb.AppendLine(); foreach (MethodInfo methodInfo in moduleClassInfo.Methods.Where(m => m.IsStatic)) { - sb.AppendLine($"{indent}public {methodRenderer.RenderMethodSignatureForClass(methodInfo)} {{"); + sb.AppendLine($"{indent}public {methodRenderer.RenderProxyMethodSignature(methodInfo)} {{"); RenderProxyInstanceExtraction(indent, methodInfo); RenderInteropInvocation(indent, methodInfo); sb.AppendLine($"{indent}}}"); @@ -40,14 +40,14 @@ private void RenderModuleClass(string className, string interopInterfaceName) foreach (PropertyInfo propertyInfo in moduleClassInfo.Properties.Where(p => p.IsStatic)) { MethodInfo? getter = propertyInfo.GetMethod; - sb.AppendLine($"{indent}public {methodRenderer.RenderPropertyGetterSignatureForClass(getter.WithoutInstanceParameter())} {{"); + sb.AppendLine($"{indent}public {methodRenderer.RenderProxyPropertyGetterSignature(getter.WithoutInstanceParameter())} {{"); RenderInteropInvocation(indent, getter); sb.AppendLine($"{indent}}}"); sb.AppendLine(); if (propertyInfo.SetMethod is MethodInfo setter) { - sb.AppendLine($"{indent}public {methodRenderer.RenderPropertySetterSignatureForClass(setter.WithoutInstanceParameter())} {{"); + sb.AppendLine($"{indent}public {methodRenderer.RenderProxyPropertySetterSignature(setter.WithoutInstanceParameter())} {{"); RenderProxyInstanceExtraction(indent, setter); RenderInteropInvocation(indent, setter); sb.AppendLine($"{indent}}}"); @@ -61,14 +61,17 @@ private void RenderProxyInstanceExtraction(string indent, MethodInfo methodInfo) { foreach (MethodParameterInfo param in methodInfo.MethodParameters.Where(p => p.Type.RequiresCLRTypeConversion && !p.IsInjectedInstanceParameter)) { - if (classNameBuilder.GetUserClassProxyName(param.Type) is not string proxyClassName) + InteropTypeInfo paramType = param.Type; + InteropTypeInfo paramTargetType = param.Type.TypeArgument ?? param.Type; // for arrays tasks nullables, use the element/inner type + + if (symbolNameProvider.GetProxyReferenceNameIfExists(paramTargetType) is not string proxyClassName) { throw new ArgumentException("All type conversion-requiring types should be user class proxies."); } - if (param.Type.IsArrayType || param.Type.IsTaskType) + if (paramType.IsArrayType || paramType.IsTaskType) { - string transformFunction = param.Type.IsArrayType ? "map" : "then"; + string transformFunction = paramType.IsArrayType ? "map" : "then"; sb.AppendLine($"{indent}{indent}const {GetInteropInvocationVariable(param)} = {param.Name}.{transformFunction}(item => item instanceof {proxyClassName} ? item.instance : item);"); } else @@ -83,18 +86,22 @@ private void RenderProxyInstanceExtraction(string indent, MethodInfo methodInfo) private void RenderInteropInvocation(string indent, MethodInfo methodInfo) { string interopInvoke = RenderMethodCallParametersWithInstanceParameterExpression(methodInfo, "this.instance"); // note: instance parameter will be unused for static methods - if (classNameBuilder.GetUserClassProxyName(methodInfo.ReturnType) is string proxyClassName) // user class return type, wrap in proxy + + InteropTypeInfo returnType = methodInfo.ReturnType; + InteropTypeInfo returnTargetType = methodInfo.ReturnType.TypeArgument ?? methodInfo.ReturnType; // for arrays tasks nullables, use the element/inner type + + if (symbolNameProvider.GetProxyReferenceNameIfExists(returnTargetType) is string proxyClassName) // user class return type, wrap in proxy { - string optionalAwait = methodInfo.ReturnType.IsTaskType ? "await " : string.Empty; + string optionalAwait = returnType.IsTaskType ? "await " : string.Empty; sb.AppendLine($"{indent}{indent}const res = {optionalAwait}this.interop.{ResolveInteropMethodAccessor(moduleClassInfo, methodInfo)}({interopInvoke});"); - if (methodInfo.ReturnType.IsArrayType) + if (returnType.IsArrayType) { - sb.AppendLine($"{indent}{indent}return res.map(item => {GetNewProxyExpression(methodInfo.ReturnType, proxyClassName, "item")});"); + sb.AppendLine($"{indent}{indent}return res.map(item => {GetNewProxyExpression(returnType, proxyClassName, "item")});"); } else { - sb.AppendLine($"{indent}{indent}return {GetNewProxyExpression(methodInfo.ReturnType, proxyClassName, "res")};"); + sb.AppendLine($"{indent}{indent}return {GetNewProxyExpression(returnType, proxyClassName, "res")};"); } } else // primitive return type or void @@ -102,6 +109,18 @@ private void RenderInteropInvocation(string indent, MethodInfo methodInfo) string optionalReturn = methodInfo.ReturnType.ManagedType == KnownManagedType.Void ? string.Empty : "return "; sb.AppendLine($"{indent}{indent}{optionalReturn}this.interop.{ResolveInteropMethodAccessor(moduleClassInfo, methodInfo)}({interopInvoke});"); } + + static string GetNewProxyExpression(InteropTypeInfo returnTypeInfo, string proxyClassName, string instanceName) + { + if (returnTypeInfo.IsNullableType) + { + return $"{instanceName} ? new {proxyClassName}({instanceName}, this.interop) : null"; + } + else + { + return $"new {proxyClassName}({instanceName}, this.interop)"; + } + } } /// @@ -115,18 +134,6 @@ internal string RenderMethodCallParametersWithInstanceParameterExpression(Method return string.Join(", ", methodInfo.MethodParameters.Select(p => p.IsInjectedInstanceParameter ? instanceParameterExpression : GetInteropInvocationVariable(p))); } - private string GetNewProxyExpression(InteropTypeInfo returnTypeInfo, string proxyClassName, string instanceName) - { - if (returnTypeInfo.IsNullableType) - { - return $"{instanceName} ? new {proxyClassName}({instanceName}, this.interop) : null"; - } - else - { - return $"new {proxyClassName}({instanceName}, this.interop)"; - } - } - private string GetInteropInvocationVariable(MethodParameterInfo param) { return param.Type.RequiresCLRTypeConversion ? $"{param.Name}Instance" : param.Name; @@ -134,6 +141,6 @@ private string GetInteropInvocationVariable(MethodParameterInfo param) private string ResolveInteropMethodAccessor(ClassInfo classInfo, MethodInfo methodInfo) { - return $"{classInfo.Namespace}.{classNameBuilder.GetInteropInterfaceName(classInfo)}.{methodInfo.Name}"; + return $"{classInfo.Namespace}.{symbolNameProvider.GetInteropInterfaceName(classInfo)}.{methodInfo.Name}"; } } From dffd03cc6d0e0808ab54fd5ca4cc7e52678a6fbf Mon Sep 17 00:00:00 2001 From: Marc Zwart Date: Sun, 14 Dec 2025 16:35:57 +0100 Subject: [PATCH 02/34] fix tests --- .../TypeScriptInteropInterfaceRenderer.cs | 12 +-- .../TypeScriptRendererTests_Properties.cs | 70 +++------------ ...riptUserClassProxyRendererTests_Methods.cs | 87 +++++++++---------- 3 files changed, 58 insertions(+), 111 deletions(-) diff --git a/TypeShim.Generator.Tests/TypeScript/TypeScriptInteropInterfaceRenderer.cs b/TypeShim.Generator.Tests/TypeScript/TypeScriptInteropInterfaceRenderer.cs index c52c3ab..1b79c20 100644 --- a/TypeShim.Generator.Tests/TypeScript/TypeScriptInteropInterfaceRenderer.cs +++ b/TypeShim.Generator.Tests/TypeScript/TypeScriptInteropInterfaceRenderer.cs @@ -46,10 +46,10 @@ public void DoStuff(UserClass? u) {} ClassInfo userClassInfo = new ClassInfoBuilder(userClassSymbol).Build(); TypeScriptTypeMapper typeMapper = new([classInfo, userClassInfo]); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); - TypeScriptMethodRenderer methodRenderer = new(typeMapper); + TypescriptSymbolNameProvider symbolNameProvider = new(typeMapper); + TypeScriptMethodRenderer methodRenderer = new(symbolNameProvider); - string interopClass = new TypescriptInteropInterfaceRenderer(classInfo, methodRenderer, classNameBuilder).Render(); + string interopClass = new TypescriptInteropInterfaceRenderer(classInfo, methodRenderer, symbolNameProvider).Render(); Assert.That(interopClass, Is.EqualTo(""" // Auto-generated TypeScript interop interface. Source class: N1.C1 @@ -86,10 +86,10 @@ public void DoStuff({{csType}} u) {} ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); TypeScriptTypeMapper typeMapper = new([classInfo]); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); - TypeScriptMethodRenderer methodRenderer = new(typeMapper); + TypescriptSymbolNameProvider symbolNameProvider = new(typeMapper); + TypeScriptMethodRenderer methodRenderer = new(symbolNameProvider); - string interopClass = new TypescriptInteropInterfaceRenderer(classInfo, methodRenderer, classNameBuilder).Render(); + string interopClass = new TypescriptInteropInterfaceRenderer(classInfo, methodRenderer, symbolNameProvider).Render(); Assert.That(interopClass, Is.EqualTo(""" // Auto-generated TypeScript interop interface. Source class: N1.C1 diff --git a/TypeShim.Generator.Tests/TypeScript/TypeScriptRendererTests_Properties.cs b/TypeShim.Generator.Tests/TypeScript/TypeScriptRendererTests_Properties.cs index 38613ce..70db961 100644 --- a/TypeShim.Generator.Tests/TypeScript/TypeScriptRendererTests_Properties.cs +++ b/TypeShim.Generator.Tests/TypeScript/TypeScriptRendererTests_Properties.cs @@ -32,15 +32,14 @@ public class C1 ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); TypeScriptTypeMapper typeMapper = new([classInfo]); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); - TypeScriptMethodRenderer methodRenderer = new(typeMapper); + TypescriptSymbolNameProvider symbolNameProvider = new(typeMapper); + TypeScriptMethodRenderer methodRenderer = new(symbolNameProvider); - string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, classNameBuilder).Render(); + string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, symbolNameProvider).Render(0); Assert.That(interopClass, Is.EqualTo(""" -// Auto-generated TypeScript proxy class. Source class: N1.C1 -class C1Proxy implements C1 { +export class Proxy { interop: AssemblyExports; instance: object; @@ -86,15 +85,14 @@ public class C1 ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); TypeScriptTypeMapper typeMapper = new([classInfo]); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); - TypeScriptMethodRenderer methodRenderer = new(typeMapper); + TypescriptSymbolNameProvider symbolNameProvider = new(typeMapper); + TypeScriptMethodRenderer methodRenderer = new(symbolNameProvider); - string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, classNameBuilder).Render(); + string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, symbolNameProvider).Render(0); Assert.That(interopClass, Is.EqualTo(""" -// Auto-generated TypeScript proxy class. Source class: N1.C1 -class C1Proxy implements C1 { +export class Proxy { interop: AssemblyExports; instance: object; @@ -105,50 +103,6 @@ class C1Proxy implements C1 { } -""".Replace("{{typeScriptType}}", typeScriptType))); - } - - [TestCase("string", "string")] - [TestCase("double", "number")] - [TestCase("bool", "boolean")] - public void TypeScriptUserClassInterface_InstanceProperty_GeneratesGetAndSetFunctions(string typeExpression, string typeScriptType) - { - SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(""" - using System; - using System.Threading.Tasks; - namespace N1; - [TSExport] - public class C1 - { - public {{typeExpression}} P1 { get; set; } - public {{typeExpression}} P2 { get; init; } - public {{typeExpression}} P3 { get; } - public {{typeExpression}} P4 => 1 - } - """.Replace("{{typeExpression}}", typeExpression)); - - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; - Assert.That(exportedClasses, Has.Count.EqualTo(1)); - INamedTypeSymbol classSymbol = exportedClasses[0]; - - ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); - - TypeScriptTypeMapper typeMapper = new([classInfo]); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); - TypeScriptMethodRenderer methodRenderer = new(typeMapper); - - string interopClass = new TypescriptUserClassInterfaceRenderer(classInfo, methodRenderer, typeMapper).Render(); - - Assert.That(interopClass, Is.EqualTo(""" -// Auto-generated TypeScript interface. Source class: N1.C1 -export interface C1 { - P1: {{typeScriptType}}; - readonly P2: {{typeScriptType}}; - readonly P3: {{typeScriptType}}; - readonly P4: {{typeScriptType}}; -} - """.Replace("{{typeScriptType}}", typeScriptType))); } @@ -179,16 +133,16 @@ public static class C1 ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); TypeScriptTypeMapper typeMapper = new([classInfo]); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); - TypeScriptMethodRenderer methodRenderer = new(typeMapper); + TypescriptSymbolNameProvider symbolNameProvider = new(typeMapper); + TypeScriptMethodRenderer methodRenderer = new(symbolNameProvider); ModuleInfo moduleInfo = new() { ExportedClasses = [classInfo], - HierarchyInfo = ModuleHierarchyInfo.FromClasses([classInfo], classNameBuilder), + HierarchyInfo = ModuleHierarchyInfo.FromClasses([classInfo], symbolNameProvider), }; - string interopClass = new TypescriptUserModuleClassRenderer(classInfo, methodRenderer, classNameBuilder).Render(); + string interopClass = new TypescriptUserModuleClassRenderer(classInfo, methodRenderer, symbolNameProvider).Render(); Assert.That(interopClass, Is.EqualTo(""" // Auto-generated TypeShim TSModule class. Source class: N1.C1 diff --git a/TypeShim.Generator.Tests/TypeScript/TypeScriptUserClassProxyRendererTests_Methods.cs b/TypeShim.Generator.Tests/TypeScript/TypeScriptUserClassProxyRendererTests_Methods.cs index c9f89f7..5476fe4 100644 --- a/TypeShim.Generator.Tests/TypeScript/TypeScriptUserClassProxyRendererTests_Methods.cs +++ b/TypeShim.Generator.Tests/TypeScript/TypeScriptUserClassProxyRendererTests_Methods.cs @@ -33,15 +33,14 @@ public class C1 ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); TypeScriptTypeMapper typeMapper = new([classInfo]); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); - TypeScriptMethodRenderer methodRenderer = new(typeMapper); + TypescriptSymbolNameProvider symbolNameProvider = new(typeMapper); + TypeScriptMethodRenderer methodRenderer = new(symbolNameProvider); - string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, classNameBuilder).Render(); + string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, symbolNameProvider).Render(0); Assert.That(interopClass, Is.EqualTo(""" -// Auto-generated TypeScript proxy class. Source class: N1.C1 -class C1Proxy implements C1 { +export class Proxy { interop: AssemblyExports; instance: object; @@ -97,15 +96,14 @@ public UserClass[] GetAll() {} ClassInfo userClassInfo = new ClassInfoBuilder(userClassSymbol).Build(); TypeScriptTypeMapper typeMapper = new([classInfo, userClassInfo]); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); - TypeScriptMethodRenderer methodRenderer = new(typeMapper); + TypescriptSymbolNameProvider symbolNameProvider = new(typeMapper); + TypeScriptMethodRenderer methodRenderer = new(symbolNameProvider); - string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, classNameBuilder).Render(); + string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, symbolNameProvider).Render(0); Assert.That(interopClass, Is.EqualTo(""" -// Auto-generated TypeScript proxy class. Source class: N1.C1 -class C1Proxy implements C1 { +export class Proxy { interop: AssemblyExports; instance: object; @@ -114,9 +112,9 @@ class C1Proxy implements C1 { this.instance = instance; } - public GetAll(): Array { + public GetAll(): Array { const res = this.interop.N1.C1Interop.GetAll(this.instance); - return res.map(item => new UserClassProxy(item, this.interop)); + return res.map(item => new UserClass.Proxy(item, this.interop)); } } @@ -162,15 +160,14 @@ public class C1 ClassInfo userClassInfo = new ClassInfoBuilder(userClassSymbol).Build(); TypeScriptTypeMapper typeMapper = new([classInfo, userClassInfo]); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); - TypeScriptMethodRenderer methodRenderer = new(typeMapper); + TypescriptSymbolNameProvider symbolNameProvider = new(typeMapper); + TypeScriptMethodRenderer methodRenderer = new(symbolNameProvider); - string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, classNameBuilder).Render(); + string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, symbolNameProvider).Render(0); Assert.That(interopClass, Is.EqualTo(""" -// Auto-generated TypeScript proxy class. Source class: N1.C1 -class C1Proxy implements C1 { +export class Proxy { interop: AssemblyExports; instance: object; @@ -179,9 +176,9 @@ class C1Proxy implements C1 { this.instance = instance; } - public GetMaybe(): UserClass | null { + public GetMaybe(): UserClass.Proxy | null { const res = this.interop.N1.C1Interop.GetMaybe(this.instance); - return res ? new UserClassProxy(res, this.interop) : null; + return res ? new UserClass.Proxy(res, this.interop) : null; } } @@ -227,14 +224,13 @@ public void DoStuff(UserClass u) {} ClassInfo userClassInfo = new ClassInfoBuilder(userClassSymbol).Build(); TypeScriptTypeMapper typeMapper = new([classInfo, userClassInfo]); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); - TypeScriptMethodRenderer methodRenderer = new(typeMapper); + TypescriptSymbolNameProvider symbolNameProvider = new(typeMapper); + TypeScriptMethodRenderer methodRenderer = new(symbolNameProvider); - string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, classNameBuilder).Render(); + string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, symbolNameProvider).Render(0); Assert.That(interopClass, Is.EqualTo(""" -// Auto-generated TypeScript proxy class. Source class: N1.C1 -class C1Proxy implements C1 { +export class Proxy { interop: AssemblyExports; instance: object; @@ -243,8 +239,8 @@ class C1Proxy implements C1 { this.instance = instance; } - public DoStuff(u: UserClass): void { - const uInstance = u instanceof UserClassProxy ? u.instance : u; + public DoStuff(u: UserClass.Proxy): void { + const uInstance = u instanceof UserClass.Proxy ? u.instance : u; this.interop.N1.C1Interop.DoStuff(this.instance, uInstance); } @@ -291,14 +287,13 @@ public void DoStuff(UserClass? u) {} ClassInfo userClassInfo = new ClassInfoBuilder(userClassSymbol).Build(); TypeScriptTypeMapper typeMapper = new([classInfo, userClassInfo]); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); - TypeScriptMethodRenderer methodRenderer = new(typeMapper); + TypescriptSymbolNameProvider symbolNameProvider = new(typeMapper); + TypeScriptMethodRenderer methodRenderer = new(symbolNameProvider); - string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, classNameBuilder).Render(); + string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, symbolNameProvider).Render(0); Assert.That(interopClass, Is.EqualTo(""" -// Auto-generated TypeScript proxy class. Source class: N1.C1 -class C1Proxy implements C1 { +export class Proxy { interop: AssemblyExports; instance: object; @@ -307,8 +302,8 @@ class C1Proxy implements C1 { this.instance = instance; } - public DoStuff(u: UserClass | null): void { - const uInstance = u instanceof UserClassProxy ? u.instance : u; + public DoStuff(u: UserClass.Proxy | null): void { + const uInstance = u instanceof UserClass.Proxy ? u.instance : u; this.interop.N1.C1Interop.DoStuff(this.instance, uInstance); } @@ -355,14 +350,13 @@ public void DoStuff(UserClass[] u) {} ClassInfo userClassInfo = new ClassInfoBuilder(userClassSymbol).Build(); TypeScriptTypeMapper typeMapper = new([classInfo, userClassInfo]); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); - TypeScriptMethodRenderer methodRenderer = new(typeMapper); + TypescriptSymbolNameProvider symbolNameProvider = new(typeMapper); + TypeScriptMethodRenderer methodRenderer = new(symbolNameProvider); - string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, classNameBuilder).Render(); + string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, symbolNameProvider).Render(0); Assert.That(interopClass, Is.EqualTo(""" -// Auto-generated TypeScript proxy class. Source class: N1.C1 -class C1Proxy implements C1 { +export class Proxy { interop: AssemblyExports; instance: object; @@ -371,8 +365,8 @@ class C1Proxy implements C1 { this.instance = instance; } - public DoStuff(u: Array): void { - const uInstance = u.map(item => item instanceof UserClassProxy ? item.instance : item); + public DoStuff(u: Array): void { + const uInstance = u.map(item => item instanceof UserClass.Proxy ? item.instance : item); this.interop.N1.C1Interop.DoStuff(this.instance, uInstance); } @@ -418,14 +412,13 @@ public void DoStuff(Task u) {} ClassInfo userClassInfo = new ClassInfoBuilder(userClassSymbol).Build(); TypeScriptTypeMapper typeMapper = new([classInfo, userClassInfo]); - TypescriptClassNameBuilder classNameBuilder = new(typeMapper); - TypeScriptMethodRenderer methodRenderer = new(typeMapper); + TypescriptSymbolNameProvider symbolNameProvider = new(typeMapper); + TypeScriptMethodRenderer methodRenderer = new(symbolNameProvider); - string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, classNameBuilder).Render(); + string interopClass = new TypescriptUserClassProxyRenderer(classInfo, methodRenderer, symbolNameProvider).Render(0); Assert.That(interopClass, Is.EqualTo(""" -// Auto-generated TypeScript proxy class. Source class: N1.C1 -class C1Proxy implements C1 { +export class Proxy { interop: AssemblyExports; instance: object; @@ -434,8 +427,8 @@ class C1Proxy implements C1 { this.instance = instance; } - public DoStuff(u: Promise): void { - const uInstance = u.then(item => item instanceof UserClassProxy ? item.instance : item); + public DoStuff(u: Promise): void { + const uInstance = u.then(item => item instanceof UserClass.Proxy ? item.instance : item); this.interop.N1.C1Interop.DoStuff(this.instance, uInstance); } From 78b74ba309c4763a19a57cb1c6780458e6c70739 Mon Sep 17 00:00:00 2001 From: Marc Zwart Date: Sun, 14 Dec 2025 16:36:17 +0100 Subject: [PATCH 03/34] use proxy in sample --- .../src/ArraysDemoComponent.tsx | 6 +- .../src/PrimitivesDemoComponent.tsx | 4 +- .../@typeshim/people-ui/src/PeopleGrid.tsx | 2 +- .../@typeshim/people-ui/src/PeopleList.tsx | 2 +- .../people-ui/src/PeopleRepository.tsx | 10 +- .../@typeshim/people-ui/src/PersonCard.tsx | 4 +- .../@typeshim/wasm-exports/typeshim.ts | 411 ++++++++---------- Sample/TypeShim.Sample/DiagnosticsTest.cs | 29 +- 8 files changed, 219 insertions(+), 249 deletions(-) diff --git a/Sample/TypeShim.Sample.Client/@typeshim/capabilities-ui/src/ArraysDemoComponent.tsx b/Sample/TypeShim.Sample.Client/@typeshim/capabilities-ui/src/ArraysDemoComponent.tsx index d2eca42..a17e345 100644 --- a/Sample/TypeShim.Sample.Client/@typeshim/capabilities-ui/src/ArraysDemoComponent.tsx +++ b/Sample/TypeShim.Sample.Client/@typeshim/capabilities-ui/src/ArraysDemoComponent.tsx @@ -8,13 +8,13 @@ export interface ArraysDemoProps { } type ArraysDemoState = { - instance: ArraysDemo; + instance: ArraysDemo.Proxy; appendValue: number; setValue: number; }; export const ArraysDemoComponent: React.FC = ({ exportsPromise }) => { - const [cap, setCap] = useState(null); + const [cap, setCap] = useState(null); const [demos, setDemos] = useState([]); useEffect(() => { @@ -35,7 +35,7 @@ export const ArraysDemoComponent: React.FC = ({ exportsPromise const createDemo = () => { if (!cap) return; - const instance: ArraysDemo = cap.GetArraysDemo(); + const instance: ArraysDemo.Proxy = cap.GetArraysDemo(); setDemos(prev => [{ instance, appendValue: 0, setValue: 0 }, ...prev]); }; diff --git a/Sample/TypeShim.Sample.Client/@typeshim/capabilities-ui/src/PrimitivesDemoComponent.tsx b/Sample/TypeShim.Sample.Client/@typeshim/capabilities-ui/src/PrimitivesDemoComponent.tsx index 3a117de..4aefe95 100644 --- a/Sample/TypeShim.Sample.Client/@typeshim/capabilities-ui/src/PrimitivesDemoComponent.tsx +++ b/Sample/TypeShim.Sample.Client/@typeshim/capabilities-ui/src/PrimitivesDemoComponent.tsx @@ -8,9 +8,9 @@ export interface PrimitivesDemoProps { } export const PrimitivesDemoComponent: React.FC = ({ exportsPromise }) => { - const [cap, setCap] = useState(null); + const [cap, setCap] = useState(null); const [newBaseInput, setNewBaseInput] = useState('Hello'); - const [demos, setDemos] = useState>([]); + const [demos, setDemos] = useState>([]); useEffect(() => { (async () => { diff --git a/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PeopleGrid.tsx b/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PeopleGrid.tsx index f8a4c8a..26e4f20 100644 --- a/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PeopleGrid.tsx +++ b/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PeopleGrid.tsx @@ -11,7 +11,7 @@ export interface PeopleGridProps { } export const PeopleGrid: React.FC = ({ emptyText = 'No people found.', repository }) => { - const [people, setPeople] = useState([]); + const [people, setPeople] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); diff --git a/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PeopleList.tsx b/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PeopleList.tsx index 70e7d7d..f57fd76 100644 --- a/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PeopleList.tsx +++ b/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PeopleList.tsx @@ -11,7 +11,7 @@ export interface PeopleListProps { } export const PeopleList: React.FC = ({ emptyText = 'No people found.', repository }) => { - const [people, setPeople] = useState([]); + const [people, setPeople] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); diff --git a/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PeopleRepository.tsx b/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PeopleRepository.tsx index ef81e7d..6f7ac12 100644 --- a/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PeopleRepository.tsx +++ b/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PeopleRepository.tsx @@ -8,20 +8,20 @@ export class PeopleRepository { this.wasmModulePromise = this.getInitializedSampleModule(exportsPromise); } - public async getAllPeople(): Promise { + public async getAllPeople(): Promise { const sampleModule: TypeShimSampleModule = await this.wasmModulePromise; - const peopleProvider: PeopleProvider | null = sampleModule.PeopleProvider; + const peopleProvider: PeopleProvider.Proxy | null = sampleModule.PeopleProvider; if (!peopleProvider) { throw new Error("PeopleProvider is null"); } - const people: People = await peopleProvider.FetchPeopleAsync(); + const people: People.Proxy = await peopleProvider.FetchPeopleAsync(); this.PrintAgeMethodUsage(people); return people.All; } - private PrintAgeMethodUsage(people: People) { + private PrintAgeMethodUsage(people: People.Proxy) { console.log("Demonstrating Person.IsOlderThan method:"); - const persons: Person[] = people.All; + const persons: Person.Proxy[] = people.All; const person1 = persons[(Math.random() * persons.length) | 0]; const person2 = persons[(Math.random() * persons.length) | 0]; console.log(person1.Name, person1.Age, "isOlderThan", person2.Name, person2.Age, ":", person1.IsOlderThan(person2)); diff --git a/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PersonCard.tsx b/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PersonCard.tsx index 49dd93a..449259e 100644 --- a/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PersonCard.tsx +++ b/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PersonCard.tsx @@ -2,11 +2,11 @@ import type { Person } from '@typeshim/wasm-exports'; export interface PersonCardProps { - initPerson: Person; + initPerson: Person.Proxy; } export const PersonCard: React.FC = ({ initPerson }) => { - const [wrapper, setPerson] = useState<{person: Person}>({person: initPerson}); + const [wrapper, setPerson] = useState<{person: Person.Proxy}>({person: initPerson}); const person = wrapper.person; const pet = person.Pet; return ( diff --git a/Sample/TypeShim.Sample.Client/@typeshim/wasm-exports/typeshim.ts b/Sample/TypeShim.Sample.Client/@typeshim/wasm-exports/typeshim.ts index ec38104..f862a22 100644 --- a/Sample/TypeShim.Sample.Client/@typeshim/wasm-exports/typeshim.ts +++ b/Sample/TypeShim.Sample.Client/@typeshim/wasm-exports/typeshim.ts @@ -25,9 +25,9 @@ export class CapabilitiesModule { this.interop = interop; } - public GetCapabilitiesProvider(): CapabilitiesProvider { + public GetCapabilitiesProvider(): CapabilitiesProvider.Proxy { const res = this.interop.TypeShim.Sample.Capabilities.CapabilitiesModuleInterop.GetCapabilitiesProvider(); - return new CapabilitiesProviderProxy(res, this.interop); + return new CapabilitiesProvider.Proxy(res, this.interop); } } @@ -40,9 +40,9 @@ export class TypeShimSampleModule { this.interop = interop; } - public get PeopleProvider(): PeopleProvider | null { + public get PeopleProvider(): PeopleProvider.Proxy | null { const res = this.interop.TypeShim.Sample.TypeShimSampleModuleInterop.get_PeopleProvider(); - return res ? new PeopleProviderProxy(res, this.interop) : null; + return res ? new PeopleProvider.Proxy(res, this.interop) : null; } } @@ -120,288 +120,255 @@ export interface TypeShimSampleModuleInterop { get_PeopleProvider(): object | null; } -// Auto-generated TypeScript interface. Source class: TypeShim.Sample.Capabilities.CapabilitiesProvider -export interface CapabilitiesProvider { - GetPrimitivesDemo(baseString: string): PrimitivesDemo; - GetArraysDemo(): ArraysDemo; -} +// Auto-generated TypeScript namespace for class: TypeShim.Sample.Capabilities.CapabilitiesProvider +export namespace CapabilitiesProvider { + export class Proxy { + interop: AssemblyExports; + instance: object; -// Auto-generated TypeScript interface. Source class: TypeShim.Sample.Capabilities.PrimitivesDemo -export interface PrimitivesDemo { - GetStringLength(): number; - ToUpperCase(): string; - Concat(str1: string, str2: string): string; - ContainsUpperCase(): boolean; - ResetBaseString(): void; - MultiplyString(times: number): void; - readonly InitialStringProperty: string; - StringProperty: string; -} + constructor(instance: object, interop: AssemblyExports) { + this.interop = interop; + this.instance = instance; + } -// Auto-generated TypeScript interface. Source class: TypeShim.Sample.Capabilities.ArraysDemo -export interface ArraysDemo { - SumIntArray(): number; - AppendToIntArray(value: number): void; - IntArrayProperty: Array; -} + public GetPrimitivesDemo(baseString: string): PrimitivesDemo.Proxy { + const res = this.interop.TypeShim.Sample.Capabilities.CapabilitiesProviderInterop.GetPrimitivesDemo(this.instance, baseString); + return new PrimitivesDemo.Proxy(res, this.interop); + } -// Auto-generated TypeScript interface. Source class: TypeShim.Sample.People -export interface People { - readonly All: Array; -} + public GetArraysDemo(): ArraysDemo.Proxy { + const res = this.interop.TypeShim.Sample.Capabilities.CapabilitiesProviderInterop.GetArraysDemo(this.instance); + return new ArraysDemo.Proxy(res, this.interop); + } -// Auto-generated TypeScript interface. Source class: TypeShim.Sample.Person -export interface Person { - IsOlderThan(other: Person): boolean; - AdoptPet(): void; - Id: number; - Name: string; - Age: number; - Pet: Dog | null; } -// Auto-generated TypeScript interface. Source class: TypeShim.Sample.Dog -export interface Dog { - Bark(): string; - GetAge(asHumanYears: boolean): number; - Name: string; - Breed: string; - Age: number; } -// Auto-generated TypeScript interface. Source class: TypeShim.Sample.PeopleProvider -export interface PeopleProvider { - FetchPeopleAsync(): Promise; -} +// Auto-generated TypeScript namespace for class: TypeShim.Sample.Capabilities.PrimitivesDemo +export namespace PrimitivesDemo { + export class Proxy { + interop: AssemblyExports; + instance: object; -// Auto-generated TypeScript proxy class. Source class: TypeShim.Sample.Capabilities.CapabilitiesProvider -class CapabilitiesProviderProxy implements CapabilitiesProvider { - interop: AssemblyExports; - instance: object; + constructor(instance: object, interop: AssemblyExports) { + this.interop = interop; + this.instance = instance; + } - constructor(instance: object, interop: AssemblyExports) { - this.interop = interop; - this.instance = instance; - } + public GetStringLength(): number { + return this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.GetStringLength(this.instance); + } - public GetPrimitivesDemo(baseString: string): PrimitivesDemo { - const res = this.interop.TypeShim.Sample.Capabilities.CapabilitiesProviderInterop.GetPrimitivesDemo(this.instance, baseString); - return new PrimitivesDemoProxy(res, this.interop); - } + public ToUpperCase(): string { + return this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.ToUpperCase(this.instance); + } - public GetArraysDemo(): ArraysDemo { - const res = this.interop.TypeShim.Sample.Capabilities.CapabilitiesProviderInterop.GetArraysDemo(this.instance); - return new ArraysDemoProxy(res, this.interop); - } + public Concat(str1: string, str2: string): string { + return this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.Concat(this.instance, str1, str2); + } -} + public ContainsUpperCase(): boolean { + return this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.ContainsUpperCase(this.instance); + } -// Auto-generated TypeScript proxy class. Source class: TypeShim.Sample.Capabilities.PrimitivesDemo -class PrimitivesDemoProxy implements PrimitivesDemo { - interop: AssemblyExports; - instance: object; + public ResetBaseString(): void { + this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.ResetBaseString(this.instance); + } - constructor(instance: object, interop: AssemblyExports) { - this.interop = interop; - this.instance = instance; - } + public MultiplyString(times: number): void { + this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.MultiplyString(this.instance, times); + } - public GetStringLength(): number { - return this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.GetStringLength(this.instance); - } + public get InitialStringProperty(): string { + return this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.get_InitialStringProperty(this.instance); + } - public ToUpperCase(): string { - return this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.ToUpperCase(this.instance); - } + public get StringProperty(): string { + return this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.get_StringProperty(this.instance); + } - public Concat(str1: string, str2: string): string { - return this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.Concat(this.instance, str1, str2); - } + public set StringProperty(value: string) { + this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.set_StringProperty(this.instance, value); + } - public ContainsUpperCase(): boolean { - return this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.ContainsUpperCase(this.instance); - } +} - public ResetBaseString(): void { - this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.ResetBaseString(this.instance); - } +} - public MultiplyString(times: number): void { - this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.MultiplyString(this.instance, times); - } +// Auto-generated TypeScript namespace for class: TypeShim.Sample.Capabilities.ArraysDemo +export namespace ArraysDemo { + export class Proxy { + interop: AssemblyExports; + instance: object; - public get InitialStringProperty(): string { - return this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.get_InitialStringProperty(this.instance); - } + constructor(instance: object, interop: AssemblyExports) { + this.interop = interop; + this.instance = instance; + } - public get StringProperty(): string { - return this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.get_StringProperty(this.instance); - } + public SumIntArray(): number { + return this.interop.TypeShim.Sample.Capabilities.ArraysDemoInterop.SumIntArray(this.instance); + } - public set StringProperty(value: string) { - this.interop.TypeShim.Sample.Capabilities.PrimitivesDemoInterop.set_StringProperty(this.instance, value); - } + public AppendToIntArray(value: number): void { + this.interop.TypeShim.Sample.Capabilities.ArraysDemoInterop.AppendToIntArray(this.instance, value); + } -} + public get IntArrayProperty(): Array { + return this.interop.TypeShim.Sample.Capabilities.ArraysDemoInterop.get_IntArrayProperty(this.instance); + } -// Auto-generated TypeScript proxy class. Source class: TypeShim.Sample.Capabilities.ArraysDemo -class ArraysDemoProxy implements ArraysDemo { - interop: AssemblyExports; - instance: object; + public set IntArrayProperty(value: Array) { + this.interop.TypeShim.Sample.Capabilities.ArraysDemoInterop.set_IntArrayProperty(this.instance, value); + } - constructor(instance: object, interop: AssemblyExports) { - this.interop = interop; - this.instance = instance; - } +} - public SumIntArray(): number { - return this.interop.TypeShim.Sample.Capabilities.ArraysDemoInterop.SumIntArray(this.instance); - } +} - public AppendToIntArray(value: number): void { - this.interop.TypeShim.Sample.Capabilities.ArraysDemoInterop.AppendToIntArray(this.instance, value); - } +// Auto-generated TypeScript namespace for class: TypeShim.Sample.People +export namespace People { + export class Proxy { + interop: AssemblyExports; + instance: object; - public get IntArrayProperty(): Array { - return this.interop.TypeShim.Sample.Capabilities.ArraysDemoInterop.get_IntArrayProperty(this.instance); - } + constructor(instance: object, interop: AssemblyExports) { + this.interop = interop; + this.instance = instance; + } - public set IntArrayProperty(value: Array) { - this.interop.TypeShim.Sample.Capabilities.ArraysDemoInterop.set_IntArrayProperty(this.instance, value); - } + public get All(): Array { + const res = this.interop.TypeShim.Sample.PeopleInterop.get_All(this.instance); + return res.map(item => new Person.Proxy(item, this.interop)); + } } -// Auto-generated TypeScript proxy class. Source class: TypeShim.Sample.People -class PeopleProxy implements People { - interop: AssemblyExports; - instance: object; - - constructor(instance: object, interop: AssemblyExports) { - this.interop = interop; - this.instance = instance; - } - - public get All(): Array { - const res = this.interop.TypeShim.Sample.PeopleInterop.get_All(this.instance); - return res.map(item => new PersonProxy(item, this.interop)); - } - } -// Auto-generated TypeScript proxy class. Source class: TypeShim.Sample.Person -class PersonProxy implements Person { - interop: AssemblyExports; - instance: object; +// Auto-generated TypeScript namespace for class: TypeShim.Sample.Person +export namespace Person { + export class Proxy { + interop: AssemblyExports; + instance: object; - constructor(instance: object, interop: AssemblyExports) { - this.interop = interop; - this.instance = instance; - } + constructor(instance: object, interop: AssemblyExports) { + this.interop = interop; + this.instance = instance; + } - public IsOlderThan(other: Person): boolean { - const otherInstance = other instanceof PersonProxy ? other.instance : other; - return this.interop.TypeShim.Sample.PersonInterop.IsOlderThan(this.instance, otherInstance); - } + public IsOlderThan(other: Person.Proxy): boolean { + const otherInstance = other instanceof Person.Proxy ? other.instance : other; + return this.interop.TypeShim.Sample.PersonInterop.IsOlderThan(this.instance, otherInstance); + } - public AdoptPet(): void { - this.interop.TypeShim.Sample.PersonInterop.AdoptPet(this.instance); - } + public AdoptPet(): void { + this.interop.TypeShim.Sample.PersonInterop.AdoptPet(this.instance); + } - public get Id(): number { - return this.interop.TypeShim.Sample.PersonInterop.get_Id(this.instance); - } + public get Id(): number { + return this.interop.TypeShim.Sample.PersonInterop.get_Id(this.instance); + } - public set Id(value: number) { - this.interop.TypeShim.Sample.PersonInterop.set_Id(this.instance, value); - } + public set Id(value: number) { + this.interop.TypeShim.Sample.PersonInterop.set_Id(this.instance, value); + } - public get Name(): string { - return this.interop.TypeShim.Sample.PersonInterop.get_Name(this.instance); - } + public get Name(): string { + return this.interop.TypeShim.Sample.PersonInterop.get_Name(this.instance); + } - public set Name(value: string) { - this.interop.TypeShim.Sample.PersonInterop.set_Name(this.instance, value); - } + public set Name(value: string) { + this.interop.TypeShim.Sample.PersonInterop.set_Name(this.instance, value); + } - public get Age(): number { - return this.interop.TypeShim.Sample.PersonInterop.get_Age(this.instance); - } + public get Age(): number { + return this.interop.TypeShim.Sample.PersonInterop.get_Age(this.instance); + } - public set Age(value: number) { - this.interop.TypeShim.Sample.PersonInterop.set_Age(this.instance, value); - } + public set Age(value: number) { + this.interop.TypeShim.Sample.PersonInterop.set_Age(this.instance, value); + } - public get Pet(): Dog | null { - const res = this.interop.TypeShim.Sample.PersonInterop.get_Pet(this.instance); - return res ? new DogProxy(res, this.interop) : null; - } + public get Pet(): Dog.Proxy | null { + const res = this.interop.TypeShim.Sample.PersonInterop.get_Pet(this.instance); + return res ? new Dog.Proxy(res, this.interop) : null; + } - public set Pet(value: Dog | null) { - const valueInstance = value instanceof DogProxy ? value.instance : value; - this.interop.TypeShim.Sample.PersonInterop.set_Pet(this.instance, valueInstance); - } + public set Pet(value: Dog.Proxy | null) { + const valueInstance = value instanceof Dog.Proxy ? value.instance : value; + this.interop.TypeShim.Sample.PersonInterop.set_Pet(this.instance, valueInstance); + } } -// Auto-generated TypeScript proxy class. Source class: TypeShim.Sample.Dog -class DogProxy implements Dog { - interop: AssemblyExports; - instance: object; +} - constructor(instance: object, interop: AssemblyExports) { - this.interop = interop; - this.instance = instance; - } +// Auto-generated TypeScript namespace for class: TypeShim.Sample.Dog +export namespace Dog { + export class Proxy { + interop: AssemblyExports; + instance: object; - public Bark(): string { - return this.interop.TypeShim.Sample.DogInterop.Bark(this.instance); - } + constructor(instance: object, interop: AssemblyExports) { + this.interop = interop; + this.instance = instance; + } - public GetAge(asHumanYears: boolean): number { - return this.interop.TypeShim.Sample.DogInterop.GetAge(this.instance, asHumanYears); - } + public Bark(): string { + return this.interop.TypeShim.Sample.DogInterop.Bark(this.instance); + } - public get Name(): string { - return this.interop.TypeShim.Sample.DogInterop.get_Name(this.instance); - } + public GetAge(asHumanYears: boolean): number { + return this.interop.TypeShim.Sample.DogInterop.GetAge(this.instance, asHumanYears); + } - public set Name(value: string) { - this.interop.TypeShim.Sample.DogInterop.set_Name(this.instance, value); - } + public get Name(): string { + return this.interop.TypeShim.Sample.DogInterop.get_Name(this.instance); + } - public get Breed(): string { - return this.interop.TypeShim.Sample.DogInterop.get_Breed(this.instance); - } + public set Name(value: string) { + this.interop.TypeShim.Sample.DogInterop.set_Name(this.instance, value); + } - public set Breed(value: string) { - this.interop.TypeShim.Sample.DogInterop.set_Breed(this.instance, value); - } + public get Breed(): string { + return this.interop.TypeShim.Sample.DogInterop.get_Breed(this.instance); + } - public get Age(): number { - return this.interop.TypeShim.Sample.DogInterop.get_Age(this.instance); - } + public set Breed(value: string) { + this.interop.TypeShim.Sample.DogInterop.set_Breed(this.instance, value); + } - public set Age(value: number) { - this.interop.TypeShim.Sample.DogInterop.set_Age(this.instance, value); - } + public get Age(): number { + return this.interop.TypeShim.Sample.DogInterop.get_Age(this.instance); + } + + public set Age(value: number) { + this.interop.TypeShim.Sample.DogInterop.set_Age(this.instance, value); + } } -// Auto-generated TypeScript proxy class. Source class: TypeShim.Sample.PeopleProvider -class PeopleProviderProxy implements PeopleProvider { - interop: AssemblyExports; - instance: object; +} - constructor(instance: object, interop: AssemblyExports) { - this.interop = interop; - this.instance = instance; - } +// Auto-generated TypeScript namespace for class: TypeShim.Sample.PeopleProvider +export namespace PeopleProvider { + export class Proxy { + interop: AssemblyExports; + instance: object; - public async FetchPeopleAsync(): Promise { - const res = await this.interop.TypeShim.Sample.PeopleProviderInterop.FetchPeopleAsync(this.instance); - return new PeopleProxy(res, this.interop); - } + constructor(instance: object, interop: AssemblyExports) { + this.interop = interop; + this.instance = instance; + } + + public async FetchPeopleAsync(): Promise { + const res = await this.interop.TypeShim.Sample.PeopleProviderInterop.FetchPeopleAsync(this.instance); + return new People.Proxy(res, this.interop); + } + +} } diff --git a/Sample/TypeShim.Sample/DiagnosticsTest.cs b/Sample/TypeShim.Sample/DiagnosticsTest.cs index dc8e8ae..87ffb12 100644 --- a/Sample/TypeShim.Sample/DiagnosticsTest.cs +++ b/Sample/TypeShim.Sample/DiagnosticsTest.cs @@ -1,22 +1,25 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading.Tasks; namespace TypeShim.Sample; //[TSExport] -//[TSModule] -internal class DiagnosticsTest -{ - //public Task X() { return Task.FromResult(new int[] { 1, 2, 3 }); } - //public Task Z() { return Task.FromResult(1); } +////[TSModule] +//internal class DiagnosticsTest +//{ +// public Task X() { return Task.FromResult(new int[] { 1, 2, 3 }); } +// public Task Z() { return Task.FromResult(1); } - //public Span Y() { return new Span(); } - //public Span BA() { return new Span(); } - //public IEnumerable BBA() { return []; } - //public IDictionary C() { return new Dictionary(); } - //public IReadOnlyList E() { return []; } - //public Dictionary D() { return new Dictionary(); } +// public Span Y() { return new Span(); } +// public Span BA() { return new Span(); } +// public IEnumerable BBA() { return []; } +// public IDictionary C() { return new Dictionary(); } +// public IReadOnlyList E() { return []; } +// public Dictionary D() { return new Dictionary(); } - //public ArraySegment W() { return new ArraySegment(new string[] { "a", "b", "c" }); } -} +// public ArraySegment W() { return new ArraySegment(new string[] { "a", "b", "c" }); } + +// public Func F() { return (i) => i.ToString(); } +//} From 6bd7927c855c0a65879ff0aaa7fe0a50d550451f Mon Sep 17 00:00:00 2001 From: Marc Zwart Date: Sun, 14 Dec 2025 17:00:58 +0100 Subject: [PATCH 04/34] setup code + better message on error --- TypeShim.Generator/Parsing/CSharpFileInfo.cs | 1 - TypeShim.Generator/Program.cs | 23 +++++++---- TypeShim.Generator/ProgramArguments.cs | 1 - TypeShim.Generator/SymbolExtractor.cs | 40 ++++++++++++++++++++ 4 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 TypeShim.Generator/SymbolExtractor.cs diff --git a/TypeShim.Generator/Parsing/CSharpFileInfo.cs b/TypeShim.Generator/Parsing/CSharpFileInfo.cs index d6d34f4..8054349 100644 --- a/TypeShim.Generator/Parsing/CSharpFileInfo.cs +++ b/TypeShim.Generator/Parsing/CSharpFileInfo.cs @@ -2,6 +2,5 @@ class CSharpFileInfo { - public string Path { get; set; } = string.Empty; public SyntaxTree SyntaxTree { get; set; } = null!; } diff --git a/TypeShim.Generator/Program.cs b/TypeShim.Generator/Program.cs index e26384a..01cafcd 100644 --- a/TypeShim.Generator/Program.cs +++ b/TypeShim.Generator/Program.cs @@ -9,17 +9,24 @@ ProgramArguments parsedArgs = ProgramArguments.Parse(args); -CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation(parsedArgs.CsFileInfos.Select(csFile => csFile.SyntaxTree)); +try +{ + SymbolExtractor symbolExtractor = new(parsedArgs.CsFileInfos); + List classInfos = [.. symbolExtractor.ExtractAllExportedSymbols() + .Select(classSymbol => new ClassInfoBuilder(classSymbol).Build()) + .Where(ci => ci.Methods.Any() || ci.Properties.Any())]; // dont bother with empty classes -List classInfos = [.. parsedArgs.CsFileInfos - .SelectMany(fileInfo => TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(fileInfo.SyntaxTree), fileInfo.SyntaxTree.GetRoot())) - .Select(classSymbol => new ClassInfoBuilder(classSymbol).Build()) - .Where(ci => ci.Methods.Any() || ci.Properties.Any())]; // dont bother with empty classes + Task generateTS = Task.Run(() => GenerateTypeScriptInteropCode(parsedArgs, classInfos)); + Task generateCS = Task.Run(() => GenerateCSharpInteropCode(parsedArgs, classInfos)); -Task generateTS = Task.Run(() => GenerateTypeScriptInteropCode(parsedArgs, classInfos)); -Task generateCS = Task.Run(() => GenerateCSharpInteropCode(parsedArgs, classInfos)); + await Task.WhenAll(generateTS, generateCS); +} +catch (TypeShimException ex) // known exceptions warrant only an error message +{ + Console.Error.WriteLine($"TypeShim received invalid input. {ex.GetType().Name} {ex.Message}"); + Environment.Exit(-1); +} -await Task.WhenAll(generateTS, generateCS); // End of main program static void GenerateCSharpInteropCode(ProgramArguments parsedArgs, List classInfos) diff --git a/TypeShim.Generator/ProgramArguments.cs b/TypeShim.Generator/ProgramArguments.cs index 513a209..532ced9 100644 --- a/TypeShim.Generator/ProgramArguments.cs +++ b/TypeShim.Generator/ProgramArguments.cs @@ -48,7 +48,6 @@ private static CSharpFileInfo[] ParseCsFilePaths(string arg) fileInfos[i] = new CSharpFileInfo { SyntaxTree = CSharpSyntaxTree.ParseText(code), - Path = csFilePath }; } return fileInfos; diff --git a/TypeShim.Generator/SymbolExtractor.cs b/TypeShim.Generator/SymbolExtractor.cs new file mode 100644 index 0000000..cfd12f7 --- /dev/null +++ b/TypeShim.Generator/SymbolExtractor.cs @@ -0,0 +1,40 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Collections.Generic; +using System.Text; +using TypeShim.Generator.CSharp; +using TypeShim.Generator.Parsing; + +namespace TypeShim.Generator; + +internal class SymbolExtractor(IEnumerable fileInfos) +{ + internal IEnumerable ExtractAllExportedSymbols() + { + CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation(fileInfos.Select(csFile => csFile.SyntaxTree)); + + List classInfos = [.. fileInfos.SelectMany(fileInfo => FindLabelledClassSymbols(compilation.GetSemanticModel(fileInfo.SyntaxTree), fileInfo.SyntaxTree.GetRoot()))]; + return classInfos; + + } + + private static IEnumerable FindLabelledClassSymbols(SemanticModel semanticModel, SyntaxNode root) + { + foreach (var cls in root.DescendantNodes().OfType()) + { + if (semanticModel.GetDeclaredSymbol(cls) is not INamedTypeSymbol symbol) + { + continue; + } + + if (symbol.GetAttributes().Any(attributeData => attributeData.AttributeClass?.Name is "TSExportAttribute" or "TSExport" or "TSModuleAttribute" or "TSModule")) + { + //TODO: add verbosity argument and use with ILogger + //Console.WriteLine($"TsExport: {symbol.ToDisplayString()}"); + yield return symbol; + } + } + } +} From 9419bdcf5bdab611d29e640051a05c83beb74aeb Mon Sep 17 00:00:00 2001 From: Marc Zwart Date: Sun, 14 Dec 2025 17:17:17 +0100 Subject: [PATCH 05/34] use symbolextractor in tests --- ...arpInteropClassRendererTests_Properties.cs | 20 ++++---- ...lassRendererTests_SystemArrayReturnType.cs | 24 +++++----- ...ndererTests_SystemDateTimeParameterType.cs | 8 ++-- ...sRendererTests_SystemDateTimeReturnType.cs | 8 ++-- ...endererTests_SystemNumericParameterType.cs | 12 ++--- ...ssRendererTests_SystemNumericReturnType.cs | 8 ++-- ...RendererTests_SystemStringParameterType.cs | 8 ++-- ...assRendererTests_SystemStringReturnType.cs | 8 ++-- ...ssRendererTests_SystemTaskParameterType.cs | 24 +++++----- ...ClassRendererTests_SystemTaskReturnType.cs | 24 +++++----- .../SyntaxTreeParsingTests_AccessModifiers.cs | 12 ++--- ...SyntaxTreeParsingTests_StaticProperties.cs | 4 +- .../SyntaxTreeParsingTests_UnsupportedType.cs | 12 ++--- .../TsExportAnnotatedClassFinderTests.cs | 15 +++--- .../TypeScriptInteropInterfaceRenderer.cs | 13 ++---- .../TypeScriptRendererTests_Properties.cs | 12 ++--- ...riptUserClassProxyRendererTests_Methods.cs | 46 ++++++------------- TypeShim.Generator/Parsing/CSharpFileInfo.cs | 12 ++++- .../Parsing/TsExportAnnotatedClassFinder.cs | 25 ---------- TypeShim.Generator/ProgramArguments.cs | 5 +- 20 files changed, 127 insertions(+), 173 deletions(-) delete mode 100644 TypeShim.Generator/Parsing/TsExportAnnotatedClassFinder.cs diff --git a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_Properties.cs b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_Properties.cs index f5c8915..c7e988a 100644 --- a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_Properties.cs +++ b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_Properties.cs @@ -23,8 +23,8 @@ public class C1 } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -83,10 +83,10 @@ public class C1 public MyClass P1 { get; set; } } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([userClass, syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; - Assert.That(exportedClasses, Has.Count.EqualTo(1)); - INamedTypeSymbol classSymbol = exportedClasses.Last(); + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; + Assert.That(exportedClasses, Has.Count.EqualTo(2)); + INamedTypeSymbol classSymbol = exportedClasses.First(); ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); string interopClass = new CSharpInteropClassRenderer(classInfo).Render(); @@ -144,10 +144,10 @@ public class C1 public MyClass? P1 { get; set; } } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([userClass, syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; - Assert.That(exportedClasses, Has.Count.EqualTo(1)); - INamedTypeSymbol classSymbol = exportedClasses.Last(); + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; + Assert.That(exportedClasses, Has.Count.EqualTo(2)); + INamedTypeSymbol classSymbol = exportedClasses.First(); ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); string interopClass = new CSharpInteropClassRenderer(classInfo).Render(); diff --git a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemArrayReturnType.cs b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemArrayReturnType.cs index 2ce79e1..4d2aeec 100644 --- a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemArrayReturnType.cs +++ b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemArrayReturnType.cs @@ -29,8 +29,8 @@ public class C1 } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -85,10 +85,10 @@ public static MyClass[] M1() } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([userClass, syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; - Assert.That(exportedClasses, Has.Count.EqualTo(1)); - INamedTypeSymbol classSymbol = exportedClasses.Last(); + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; + Assert.That(exportedClasses, Has.Count.EqualTo(2)); + INamedTypeSymbol classSymbol = exportedClasses.First(); ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); string interopClass = new CSharpInteropClassRenderer(classInfo).Render(); @@ -141,10 +141,10 @@ public Task M1() } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([userClass, syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; - Assert.That(exportedClasses, Has.Count.EqualTo(1)); - INamedTypeSymbol classSymbol = exportedClasses.Last(); + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; + Assert.That(exportedClasses, Has.Count.EqualTo(2)); + INamedTypeSymbol classSymbol = exportedClasses.First(); ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); string interopClass = new CSharpInteropClassRenderer(classInfo).Render(); @@ -185,8 +185,8 @@ public class C1 } } """.Replace("{{typeName}}", typeName).Replace("{{objectCreation}}", objectCreation)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses.Last(); diff --git a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemDateTimeParameterType.cs b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemDateTimeParameterType.cs index 6391a20..739a8f5 100644 --- a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemDateTimeParameterType.cs +++ b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemDateTimeParameterType.cs @@ -23,8 +23,8 @@ public static void M1({{typeName}} p1) } """.Replace("{{typeName}}", typeName)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -65,8 +65,8 @@ public void M1({{typeName}} p1) } """.Replace("{{typeName}}", typeName)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; diff --git a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemDateTimeReturnType.cs b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemDateTimeReturnType.cs index 31c091c..e0a5b47 100644 --- a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemDateTimeReturnType.cs +++ b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemDateTimeReturnType.cs @@ -23,8 +23,8 @@ public class C1 } """.Replace("{{typeName}}", typeName)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -65,8 +65,8 @@ public class C1 } """.Replace("{{typeName}}", typeName)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; diff --git a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemNumericParameterType.cs b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemNumericParameterType.cs index 939ab9d..451e9c6 100644 --- a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemNumericParameterType.cs +++ b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemNumericParameterType.cs @@ -36,8 +36,8 @@ public static void M1({{typeExpression}} arg1) } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -91,8 +91,8 @@ public static void M1({{typeExpression}} arg1) } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -146,8 +146,8 @@ public void M1({{typeExpression}} arg1) } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; diff --git a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemNumericReturnType.cs b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemNumericReturnType.cs index 9cf5e0a..8b7fe07 100644 --- a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemNumericReturnType.cs +++ b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemNumericReturnType.cs @@ -36,8 +36,8 @@ public class C1 } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -91,8 +91,8 @@ public class C1 } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; diff --git a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemStringParameterType.cs b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemStringParameterType.cs index cae034a..73deef4 100644 --- a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemStringParameterType.cs +++ b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemStringParameterType.cs @@ -23,8 +23,8 @@ public static void M1(string p1) } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -65,8 +65,8 @@ public void M1(string p1) } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; diff --git a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemStringReturnType.cs b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemStringReturnType.cs index d43d7a0..88f7844 100644 --- a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemStringReturnType.cs +++ b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemStringReturnType.cs @@ -23,8 +23,8 @@ public static string M1() } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -65,8 +65,8 @@ public string M1() } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; diff --git a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemTaskParameterType.cs b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemTaskParameterType.cs index 68e5946..fe7dd54 100644 --- a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemTaskParameterType.cs +++ b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemTaskParameterType.cs @@ -38,8 +38,8 @@ public static void M1(Task<{{typeExpression}}> task) } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -94,10 +94,10 @@ public static void M1(Task task) } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([userClass, syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; - Assert.That(exportedClasses, Has.Count.EqualTo(1)); - INamedTypeSymbol classSymbol = exportedClasses.Last(); + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; + Assert.That(exportedClasses, Has.Count.EqualTo(2)); + INamedTypeSymbol classSymbol = exportedClasses.First(); ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); string interopClass = new CSharpInteropClassRenderer(classInfo).Render(); @@ -157,10 +157,10 @@ public static void M1(Task task) } } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([userClass, syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; - Assert.That(exportedClasses, Has.Count.EqualTo(1)); - INamedTypeSymbol classSymbol = exportedClasses.Last(); + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; + Assert.That(exportedClasses, Has.Count.EqualTo(2)); + INamedTypeSymbol classSymbol = exportedClasses.First(); ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); string interopClass = new CSharpInteropClassRenderer(classInfo).Render(); @@ -206,8 +206,8 @@ public static void M1(Task<{{typeName}}> task) } } """.Replace("{{typeName}}", typeName).Replace("{{objectCreation}}", objectCreation)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses.Last(); diff --git a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemTaskReturnType.cs b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemTaskReturnType.cs index b130f2b..581e604 100644 --- a/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemTaskReturnType.cs +++ b/TypeShim.Generator.Tests/CSharp/CSharpInteropClassRendererTests_SystemTaskReturnType.cs @@ -37,8 +37,8 @@ public class C1 } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -93,10 +93,10 @@ public static Task M1() } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([userClass, syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; - Assert.That(exportedClasses, Has.Count.EqualTo(1)); - INamedTypeSymbol classSymbol = exportedClasses.Last(); + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; + Assert.That(exportedClasses, Has.Count.EqualTo(2)); + INamedTypeSymbol classSymbol = exportedClasses.First(); ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); string interopClass = new CSharpInteropClassRenderer(classInfo).Render(); @@ -149,10 +149,10 @@ public Task M1() } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([userClass, syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; - Assert.That(exportedClasses, Has.Count.EqualTo(1)); - INamedTypeSymbol classSymbol = exportedClasses.Last(); + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; + Assert.That(exportedClasses, Has.Count.EqualTo(2)); + INamedTypeSymbol classSymbol = exportedClasses.First(); ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); string interopClass = new CSharpInteropClassRenderer(classInfo).Render(); @@ -193,8 +193,8 @@ public class C1 } } """.Replace("{{typeName}}", typeName).Replace("{{objectCreation}}", objectCreation)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses.Last(); diff --git a/TypeShim.Generator.Tests/SyntaxTreeParsingTests_AccessModifiers.cs b/TypeShim.Generator.Tests/SyntaxTreeParsingTests_AccessModifiers.cs index c463685..3650baf 100644 --- a/TypeShim.Generator.Tests/SyntaxTreeParsingTests_AccessModifiers.cs +++ b/TypeShim.Generator.Tests/SyntaxTreeParsingTests_AccessModifiers.cs @@ -43,8 +43,8 @@ private int M2() } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); @@ -69,8 +69,8 @@ public class C1 } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); @@ -91,8 +91,8 @@ public class C1 } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); diff --git a/TypeShim.Generator.Tests/SyntaxTreeParsingTests_StaticProperties.cs b/TypeShim.Generator.Tests/SyntaxTreeParsingTests_StaticProperties.cs index a47e449..c2504ec 100644 --- a/TypeShim.Generator.Tests/SyntaxTreeParsingTests_StaticProperties.cs +++ b/TypeShim.Generator.Tests/SyntaxTreeParsingTests_StaticProperties.cs @@ -20,8 +20,8 @@ public class C1 } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; ClassInfo classInfo = new ClassInfoBuilder(classSymbol).Build(); diff --git a/TypeShim.Generator.Tests/SyntaxTreeParsingTests_UnsupportedType.cs b/TypeShim.Generator.Tests/SyntaxTreeParsingTests_UnsupportedType.cs index 0dd61e9..7cb4fed 100644 --- a/TypeShim.Generator.Tests/SyntaxTreeParsingTests_UnsupportedType.cs +++ b/TypeShim.Generator.Tests/SyntaxTreeParsingTests_UnsupportedType.cs @@ -34,8 +34,8 @@ public class C1 } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; Assert.Throws(() => @@ -70,8 +70,8 @@ public class C1 } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -112,8 +112,8 @@ public class C1 } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; diff --git a/TypeShim.Generator.Tests/TsExportAnnotatedClassFinderTests.cs b/TypeShim.Generator.Tests/TsExportAnnotatedClassFinderTests.cs index 9efb3d3..7e79abe 100644 --- a/TypeShim.Generator.Tests/TsExportAnnotatedClassFinderTests.cs +++ b/TypeShim.Generator.Tests/TsExportAnnotatedClassFinderTests.cs @@ -20,10 +20,8 @@ public static async Task SampleMethod(object param1, int param2) } } "); - - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - IEnumerable exportedClasses = TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot()); - Assert.That(exportedClasses, Is.Empty); + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + Assert.That(symbolExtractor.ExtractAllExportedSymbols(), Is.Empty); } [Test] @@ -41,9 +39,8 @@ public static async Task SampleMethod(object param1, int param2) } } "); - - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); } @@ -70,8 +67,8 @@ public static async Task SampleMethod(object param1, int param2) } "); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(2)); } } diff --git a/TypeShim.Generator.Tests/TypeScript/TypeScriptInteropInterfaceRenderer.cs b/TypeShim.Generator.Tests/TypeScript/TypeScriptInteropInterfaceRenderer.cs index 1b79c20..e69ae89 100644 --- a/TypeShim.Generator.Tests/TypeScript/TypeScriptInteropInterfaceRenderer.cs +++ b/TypeShim.Generator.Tests/TypeScript/TypeScriptInteropInterfaceRenderer.cs @@ -33,11 +33,8 @@ public void DoStuff(UserClass? u) {} } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree, userClass]); - List exportedClasses = [ - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot()), - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(userClass), userClass.GetRoot()), - ]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(2)); INamedTypeSymbol classSymbol = exportedClasses[0]; INamedTypeSymbol userClassSymbol = exportedClasses[1]; @@ -76,10 +73,8 @@ public void DoStuff({{csType}} u) {} } """.Replace("{{csType}}", csType)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [ - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot()), - ]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; diff --git a/TypeShim.Generator.Tests/TypeScript/TypeScriptRendererTests_Properties.cs b/TypeShim.Generator.Tests/TypeScript/TypeScriptRendererTests_Properties.cs index 70db961..c29916e 100644 --- a/TypeShim.Generator.Tests/TypeScript/TypeScriptRendererTests_Properties.cs +++ b/TypeShim.Generator.Tests/TypeScript/TypeScriptRendererTests_Properties.cs @@ -24,8 +24,8 @@ public class C1 } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -77,8 +77,8 @@ public class C1 } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -125,8 +125,8 @@ public static class C1 } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; diff --git a/TypeShim.Generator.Tests/TypeScript/TypeScriptUserClassProxyRendererTests_Methods.cs b/TypeShim.Generator.Tests/TypeScript/TypeScriptUserClassProxyRendererTests_Methods.cs index 5476fe4..443f17b 100644 --- a/TypeShim.Generator.Tests/TypeScript/TypeScriptUserClassProxyRendererTests_Methods.cs +++ b/TypeShim.Generator.Tests/TypeScript/TypeScriptUserClassProxyRendererTests_Methods.cs @@ -25,8 +25,8 @@ public class C1 } """.Replace("{{typeExpression}}", typeExpression)); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree]); - List exportedClasses = [.. TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot())]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(1)); INamedTypeSymbol classSymbol = exportedClasses[0]; @@ -83,11 +83,8 @@ public UserClass[] GetAll() {} } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree, userClass]); - List exportedClasses = [ - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot()), - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(userClass), userClass.GetRoot()), - ]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(2)); INamedTypeSymbol classSymbol = exportedClasses[0]; INamedTypeSymbol userClassSymbol = exportedClasses[1]; @@ -147,11 +144,8 @@ public class C1 } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree, userClass]); - List exportedClasses = [ - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot()), - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(userClass), userClass.GetRoot()), - ]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(2)); INamedTypeSymbol classSymbol = exportedClasses[0]; INamedTypeSymbol userClassSymbol = exportedClasses[1]; @@ -211,11 +205,8 @@ public void DoStuff(UserClass u) {} } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree, userClass]); - List exportedClasses = [ - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot()), - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(userClass), userClass.GetRoot()), - ]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(2)); INamedTypeSymbol classSymbol = exportedClasses[0]; INamedTypeSymbol userClassSymbol = exportedClasses[1]; @@ -274,11 +265,8 @@ public void DoStuff(UserClass? u) {} } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree, userClass]); - List exportedClasses = [ - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot()), - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(userClass), userClass.GetRoot()), - ]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(2)); INamedTypeSymbol classSymbol = exportedClasses[0]; INamedTypeSymbol userClassSymbol = exportedClasses[1]; @@ -337,11 +325,8 @@ public void DoStuff(UserClass[] u) {} } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree, userClass]); - List exportedClasses = [ - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot()), - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(userClass), userClass.GetRoot()), - ]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(2)); INamedTypeSymbol classSymbol = exportedClasses[0]; INamedTypeSymbol userClassSymbol = exportedClasses[1]; @@ -399,11 +384,8 @@ public void DoStuff(Task u) {} } """); - CSharpCompilation compilation = CSharpPartialCompilation.CreatePartialCompilation([syntaxTree, userClass]); - List exportedClasses = [ - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(syntaxTree), syntaxTree.GetRoot()), - ..TSExportAnnotatedClassFinder.FindLabelledClassSymbols(compilation.GetSemanticModel(userClass), userClass.GetRoot()), - ]; + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)]); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; Assert.That(exportedClasses, Has.Count.EqualTo(2)); INamedTypeSymbol classSymbol = exportedClasses[0]; INamedTypeSymbol userClassSymbol = exportedClasses[1]; diff --git a/TypeShim.Generator/Parsing/CSharpFileInfo.cs b/TypeShim.Generator/Parsing/CSharpFileInfo.cs index 8054349..8983bfc 100644 --- a/TypeShim.Generator/Parsing/CSharpFileInfo.cs +++ b/TypeShim.Generator/Parsing/CSharpFileInfo.cs @@ -1,6 +1,14 @@ using Microsoft.CodeAnalysis; -class CSharpFileInfo +internal class CSharpFileInfo { - public SyntaxTree SyntaxTree { get; set; } = null!; + internal SyntaxTree SyntaxTree { get; private init; } = null!; + + internal static CSharpFileInfo Create(SyntaxTree syntaxTree) + { + return new CSharpFileInfo + { + SyntaxTree = syntaxTree + }; + } } diff --git a/TypeShim.Generator/Parsing/TsExportAnnotatedClassFinder.cs b/TypeShim.Generator/Parsing/TsExportAnnotatedClassFinder.cs deleted file mode 100644 index b81a36d..0000000 --- a/TypeShim.Generator/Parsing/TsExportAnnotatedClassFinder.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace TypeShim.Generator.Parsing; - -internal sealed class TSExportAnnotatedClassFinder -{ - internal static IEnumerable FindLabelledClassSymbols(SemanticModel semanticModel, SyntaxNode root) - { - foreach (var cls in root.DescendantNodes().OfType()) - { - if (semanticModel.GetDeclaredSymbol(cls) is not INamedTypeSymbol symbol) - { - continue; - } - - if (symbol.GetAttributes().Any(attributeData => attributeData.AttributeClass?.Name is "TSExportAttribute" or "TSExport" or "TSModuleAttribute" or "TSModule")) - { - //TODO: add verbosity argument and use with ILogger - //Console.WriteLine($"TsExport: {symbol.ToDisplayString()}"); - yield return symbol; - } - } - } -} diff --git a/TypeShim.Generator/ProgramArguments.cs b/TypeShim.Generator/ProgramArguments.cs index 532ced9..a05e6bf 100644 --- a/TypeShim.Generator/ProgramArguments.cs +++ b/TypeShim.Generator/ProgramArguments.cs @@ -45,10 +45,7 @@ private static CSharpFileInfo[] ParseCsFilePaths(string arg) } string code = File.ReadAllText(csFilePath); - fileInfos[i] = new CSharpFileInfo - { - SyntaxTree = CSharpSyntaxTree.ParseText(code), - }; + fileInfos[i] = CSharpFileInfo.Create(CSharpSyntaxTree.ParseText(code)); } return fileInfos; } From 6967437b680cf0861c1523df898617c196120073 Mon Sep 17 00:00:00 2001 From: Marc Zwart Date: Sun, 14 Dec 2025 20:08:11 +0100 Subject: [PATCH 06/34] render snapshot functions and types on TS side --- .../@typeshim/people-ui/src/PersonCard.tsx | 24 +-- .../@typeshim/wasm-exports/typeshim.ts | 78 +++++++- Sample/TypeShim.Sample/PeopleProvider.cs | 6 + ...ypeScriptUserClassSnapshotRendererTests.cs | 174 ++++++++++++++++++ TypeShim.Generator/Parsing/InteropTypeInfo.cs | 25 +-- .../Parsing/InteropTypeInfoBuilder.cs | 15 +- .../TypeScriptUserClassNamespaceRenderer.cs | 13 +- .../TypeScriptUserClassSnapshotRenderer.cs | 73 ++++++++ .../TypescriptSymbolNameProvider.cs | 19 +- 9 files changed, 375 insertions(+), 52 deletions(-) create mode 100644 TypeShim.Generator.Tests/TypeScript/TypeScriptUserClassSnapshotRendererTests.cs create mode 100644 TypeShim.Generator/Typescript/TypeScriptUserClassSnapshotRenderer.cs diff --git a/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PersonCard.tsx b/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PersonCard.tsx index 449259e..9bc42cb 100644 --- a/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PersonCard.tsx +++ b/Sample/TypeShim.Sample.Client/@typeshim/people-ui/src/PersonCard.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import type { Person } from '@typeshim/wasm-exports'; +import { Person } from '@typeshim/wasm-exports'; export interface PersonCardProps { initPerson: Person.Proxy; @@ -7,8 +7,10 @@ export interface PersonCardProps { export const PersonCard: React.FC = ({ initPerson }) => { const [wrapper, setPerson] = useState<{person: Person.Proxy}>({person: initPerson}); - const person = wrapper.person; - const pet = person.Pet; + const personSnapshot = Person.snapshot(wrapper.person); + const petSnapshot = personSnapshot.Pet; + const personProxy = wrapper.person; + const petProxy = personProxy.Pet; return (
= ({ initPerson }) => { flexDirection: 'row', justifyContent: 'space-between', }}> -
-

{person.Name}

-

Age: {person.Age}

+
+

{personSnapshot.Name}

+

Age: {personSnapshot.Age}

- {pet && ( + {petSnapshot && petProxy && (
= ({ initPerson }) => { color: '#1e3a8a', fontSize: '0.75rem', border: '1px solid #cfe0ff' - }} title={`Pet: ${pet.Name} (Breed: ${pet.Breed})`}> - Pet: {pet.Name} {pet.GetAge(false)} years/{pet.GetAge(true)} years ({pet.Breed}) - {pet.Bark()} + }} title={`Pet: ${petSnapshot.Name} (Breed: ${petSnapshot.Breed})`}> + Pet: {petSnapshot.Name} {petProxy.GetAge(false)} years/{petProxy.GetAge(true)} years ({petSnapshot.Breed}) - {petProxy.Bark()}
)} - {!pet && ( + {!petSnapshot && (