Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Can't export interface using JSExport for node api usage

I have a C# library. I am trying to write a nodejs addon using Microsoft.JavaScript.NodeApi. I have the following interface :

using ClassReflectionKit.Models;

namespace ClassReflectionKit.Helpers;

public interface IClassReflectionKitHelper
{
    TemplateClassInfo? GetClassInfo(string ns, string className);
    //TemplateClassInfo? GetClassInfo(string ns, string className, ProcessClassInfo procDelegate);
    string[] GetNameSpaces();
    IEnumerable<TemplateClassInfo> GetNSClasses(string ns);
    IEnumerable<TemplateClassInfo> GetNSClasses(string ns, ProcessNSClasses procDelegate);
    IEnumerable<TemplateClassInfo> GetNSClasses(string ns, ProcessNSClasses procDelegate, ProcessClassInfo procClassDelegate);
    void InitializeFromCodeInputs(params string[] codeItems);
    void InitializeFromFilePaths(params string[] filePaths);

    bool IsCodeSnippetValid(string codeSnippet);
}

What it actually does, it to allow defining helper classes that can manipulate existing C# code and get metadata information.

I have a class that implements that interface

[JSExport]
public class ClassReflectionKitHelper : IClassReflectionKitHelper
{
    private Dictionary<string, List<SyntaxTree>> NamespaceSyntaxTrees { get; set; } = new();

    private SyntaxTree GetFileSyntaxTree(string filePath)
    {
    }

    public void InitializeFromFilePaths(params string[] filePaths)
    {
    }

    public void InitializeFromCodeInputs(params string[] codeItems)
    {
    }


    public string[] GetNameSpaces()
    {
    }

    public virtual TemplateClassInfo? GetClassInfo(string ns, string className)
    {
    }

    public virtual TemplateClassInfo? GetClassInfo(string ns, string className, ProcessClassInfo procDelegate)
    {
    }

    public virtual IEnumerable<TemplateClassInfo> GetNSClasses(string ns)
    {
    }

    public virtual IEnumerable<TemplateClassInfo> GetNSClasses(string ns, ProcessNSClasses procDelegate)
    {
    }

    public virtual IEnumerable<TemplateClassInfo> GetNSClasses(string ns, ProcessNSClasses procDelegate, ProcessClassInfo procClassDelegate)
    {
    }

    bool IClassReflectionKitHelper.IsCodeSnippetValid(string codeSnippet)
    {
    }
}


(I have removed the actual implementation for my class and left just the schema)

So my Issue is this. Currently If i build the project , the typescript definitions for ClassReflectionKitHelper are correctly generated .

ProcessNSClasses and ProcessClassInfo are Delegates .

Here is their definition

namespace ClassReflectionKit.Models;
[JSExport("ProcessClassInfo")]
public delegate TemplateClassInfo? ProcessClassInfo(TemplateClassInfo? classInfo);

[JSExport("ProcessNSClasses")]
public delegate IEnumerable<TemplateClassInfo> ProcessNSClasses(IEnumerable<TemplateClassInfo> classes);

If I try to export the interface also using JSExport attribute I get the following error

ClassReflectionKit failed with 3 error(s) (0,8s)
    Microsoft.JavaScript.NodeApi.Generator.ModuleGenerator\ClassReflectionKit.NodeApi.g.cs(693,80): error CS0103: The name 'from_ClassReflectionKit_Models_ProcessNSClasses' does not exist in the current context
    Microsoft.JavaScript.NodeApi.Generator\Microsoft.JavaScript.NodeApi.Generator.ModuleGenerator\ClassReflectionKit.NodeApi.g.cs(706,80): error CS0103: The name 'from_ClassReflectionKit_Models_ProcessNSClasses' does not exist in the current context
     error CS0103: The name 'from_ClassReflectionKit_Models_ProcessClassInfo' does not exist in the current context

Any ideas what am I missing ?

like image 356
Manos Kounelakis Avatar asked Dec 01 '25 10:12

Manos Kounelakis


1 Answers

The root of the errors you’re seeing is that the Node-API code generator only knows how to emit marshaling support (the “from_” and “to_” helpers) for concrete types that it’s been told to export—and interfaces aren’t concrete, nor do they get their own generated glue code. In practice that means:

  1. Don’t put [JSExport] on your interface. Remove

    [JSExport]
    public interface IClassReflectionKitHelper { … }
    

    The generator will never produce the necessary from_ClassReflectionKit_Models_ProcessNSClasses helpers for an interface.

  2. Keep your delegates [JSExport]-ed and use them only on exported classes. You already have this right:

    namespace ClassReflectionKit.Models
    {
      [JSExport("ProcessClassInfo")]
      public delegate TemplateClassInfo? ProcessClassInfo(TemplateClassInfo? classInfo);
    
      [JSExport("ProcessNSClasses")]
      public delegate IEnumerable<TemplateClassInfo> ProcessNSClasses(IEnumerable<TemplateClassInfo> classes);
    }
    

    Just make sure that both delegates actually appear as parameters or return types on your exported class.

  3. Export only the concrete implementation

    using Microsoft.JavaScript.NodeApi;
    using ClassReflectionKit.Models;
    
    [JSExport]
    public class ClassReflectionKitHelper /*: IClassReflectionKitHelper — you can still implement the interface for your own code structure */
    {
      // … your existing implementation …
    
      // Example exported methods that use your delegates:
      [JSExport]
      public TemplateClassInfo? GetClassInfo(string ns, string className)
        => /* … */;
    
      [JSExport]
      public TemplateClassInfo? GetClassInfo(string ns, string className, ProcessClassInfo proc)
        => proc(GetClassInfo(ns, className));
    
      [JSExport]
      public IEnumerable<TemplateClassInfo> GetNSClasses(string ns)
        => /* … */;
    
      [JSExport]
      public IEnumerable<TemplateClassInfo> GetNSClasses(string ns, ProcessNSClasses proc)
        => proc(GetNSClasses(ns));
    
      // And so on…
    }
    

    By decorating only the class and its methods with [JSExport], and by using your delegates in those methods, the generator will emit the correct marshalling code (from_ProcessNSClasses, etc.) and you will avoid the “name ‘from_X’ does not exist” errors.


like image 79
a4arpan Avatar answered Dec 03 '25 00:12

a4arpan