Skip to content

ExternalTypeResolution pt BR

Edgar Mesquita edited this page Aug 13, 2026 · 1 revision

Resolução de tipos externos no compilador do eQuantic.UI

🌐 Esta página em: English · Português

Visão geral

O compilador do eQuantic.UI resolve tipos definidos em arquivos externos dentro do mesmo projeto. Os componentes referenciam modelos, DTOs e outras classes sem exigir que elas estejam no mesmo arquivo: o compilador trabalha contra a Compilation Roslyn completa do projeto, que inclui todos os arquivos de código e as referências.

// Models/User.cs
public class User
{
    public string Name { get; set; }
    public string Email { get; set; }
}

// Pages/UserProfile.cs
[Page("/profile")]
public class UserProfile : StatefulComponent
{
    private User _currentUser; // ✅ Tipo 'User' totalmente resolvido

    protected override HtmlNode Render()
    {
        return Text(_currentUser.Name); // ✅ Converte para: this._currentUser.name
    }
}

Um semantic model completo importa porque a conversão de acesso a membro depende de saber o tipo do receptor: com ele, _currentUser.Name rebaixa para this._currentUser.name; sem ele, o compilador não consegue distinguir uma propriedade de uma variável local nem escolher o mapeamento de método certo.

Uso da API

Opção 1: usando o MSBuildWorkspace (recomendado)

using eQuantic.UI.Compiler;
using eQuantic.UI.Compiler.Services;

// Obtém a compilação completa do projeto
var compilation = await ProjectCompilationHelper
    .GetProjectCompilationAsync("path/to/MyApp.csproj");

// Cria o compilador e define a compilação do projeto
var compiler = new ComponentCompiler();
compiler.SetProjectCompilation(compilation);

// Agora compile os componentes: os tipos externos serão resolvidos
var results = compiler.CompileFile("Pages/UserProfile.cs");

Opção 2: compilação manual a partir das fontes

Útil em tasks MSBuild onde o MSBuildWorkspace pode não estar disponível:

using eQuantic.UI.Compiler.Services;

// Obtém todos os arquivos .cs do projeto
var sourceFiles = ProjectCompilationHelper
    .GetProjectSourceFiles("path/to/MyApp");

// Obtém as referências de assembly
var assemblyPaths = new[]
{
    "path/to/eQuantic.UI.Core.dll",
    "path/to/other-dependencies.dll"
};

// Cria a compilação a partir das fontes
var compilation = ProjectCompilationHelper.CreateCompilationFromSources(
    sourceFiles,
    assemblyPaths,
    "MyApp");

var compiler = new ComponentCompiler();
compiler.SetProjectCompilation(compilation);

Opção 3: limpando a compilação do projeto

Volta ao modo de compilação mínima (arquivos isolados):

compiler.ClearProjectCompilation();

Como funciona

Arquitetura

┌─────────────────────────────────────────┐
│  Compilação do projeto no MSBuild       │
│  - Todos os .cs do projeto              │
│  - Todos os assemblies referenciados    │
│  - Informação de tipo completa          │
└────────────┬────────────────────────────┘
             │
             ↓ SetProjectCompilation()
┌────────────────────────────────────────┐
│  SemanticModelProvider                 │
│  - Guarda a compilação do projeto      │
│  - Devolve um SemanticModel por arquivo│
└────────────┬───────────────────────────┘
             │
             ↓ GetSemanticModel(tree)
┌────────────────────────────────────────┐
│  CSharpToJsConverter                   │
│  - Usa o SemanticModel para os tipos   │
│  - Converte _currentUser.Name → name   │
└────────────────────────────────────────┘

Classes-chave

  1. SemanticModelProvider (SemanticModelProvider.cs)

    • SetProjectCompilation(Compilation) - define a compilação completa do projeto
    • GetSemanticModel(SyntaxTree) - devolve o semantic model com informação de tipo completa
  2. ProjectCompilationHelper (ProjectCompilationHelper.cs)

    • GetProjectCompilationAsync(string) - carrega a partir do .csproj
    • CreateCompilationFromSources(...) - constrói a partir dos arquivos de código
    • GetProjectSourceFiles(string) - encontra todos os arquivos .cs
  3. ComponentCompiler (ComponentCompiler.cs)

    • SetProjectCompilation(Compilation) - liga a resolução de tipos externos
    • ClearProjectCompilation() - volta ao modo isolado

Exemplos

Exemplo 1: componente com modelo externo

Models/Product.cs:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public bool InStock { get; set; }
}

Pages/ProductCard.cs:

[Component]
public class ProductCard : StatelessComponent
{
    public Product Item { get; set; }

    protected override HtmlNode Build()
    {
        return Container(
            Heading(Item.Name),
            Text($"${Item.Price:F2}"),
            Text(Item.InStock ? "In Stock" : "Out of Stock")
        );
    }
}

JavaScript gerado (com a compilação do projeto):

class ProductCard extends StatelessComponent {
    build() {
        return Container([
            Heading(this.item.name),
            Text(`$${this.item.price.toFixed(2)}`),
            Text(this.item.inStock ? 'In Stock' : 'Out of Stock')
        ]);
    }
}

Exemplo 2: vários tipos externos

Models/Address.cs:

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string ZipCode { get; set; }
}

Models/Customer.cs:

public class Customer
{
    public string Name { get; set; }
    public string Email { get; set; }
    public Address ShippingAddress { get; set; }
}

Pages/CheckoutPage.cs:

[Page("/checkout")]
public class CheckoutPage : StatefulComponent
{
    private Customer _customer;

    protected override HtmlNode Render()
    {
        return Container(
            Text($"Customer: {_customer.Name}"),
            Text($"Email: {_customer.Email}"),
            Text($"Shipping: {_customer.ShippingAddress.City}, {_customer.ShippingAddress.ZipCode}")
        );
    }
}

Todos os acessos a propriedade são resolvidos corretamente e convertidos para JavaScript.

Testes

Os testes verificam que a resolução de tipos externos funciona corretamente:

[Fact]
public void SemanticModel_WithProjectCompilation_CanResolveExternalTypes()
{
    // Cria a compilação com User e o componente
    var userTree = CSharpSyntaxTree.ParseText("public class User { ... }");
    var componentTree = CSharpSyntaxTree.ParseText("public class UserProfile { ... }");

    var compilation = CSharpCompilation.Create("Test", new[] { userTree, componentTree }, ...);

    var provider = new SemanticModelProvider();
    provider.SetProjectCompilation(compilation);

    var semanticModel = provider.GetSemanticModel(componentTree);

    // Afirma: o tipo User está resolvido
    var userType = semanticModel.Compilation.GetTypeByMetadataName("User");
    userType.Should().NotBeNull();
}

Veja o ExternalTypeResolutionTests.cs para a suíte de testes completa.

Modos

  • Sem SetProjectCompilation(): compilação mínima, então cada arquivo é resolvido isoladamente
  • Com SetProjectCompilation(): resolução completa de tipos do projeto

Performance

Custo mínimo:

  • A compilação do projeto é criada uma vez, em tempo de build
  • Compartilhada entre todos os arquivos de componente
  • Sem penalidade de compilação por arquivo

Eficiente em memória:

  • Uma única instância de compilação
  • Reaproveita a infraestrutura existente do Roslyn

Limitações

  1. Exige compilação antes da compilação dos componentes: o projeto tem que estar compilado (ou pelo menos parseado) antes de rodar o compilador de componentes.

  2. Disponibilidade do MSBuildWorkspace: o GetProjectCompilationAsync precisa das APIs do MSBuild, que podem não estar disponíveis em todos os contextos. Use o CreateCompilationFromSources como alternativa.

  3. Código gerado: arquivos autogerados em obj/ são excluídos para evitar conflitos.

Resumo

  • Componentes referenciam classes de outros arquivos; a informação de tipo completa está disponível durante a compilação, então a geração de JavaScript para membros de tipos externos sai correta.
  • O build do SDK entrega ao compilador as referências de assembly reais do projeto (--refs), então os tipos de receptor resolvem mesmo atravessando fronteiras de pacote.
var compilation = await ProjectCompilationHelper.GetProjectCompilationAsync(projectPath);
compiler.SetProjectCompilation(compilation);

Clone this wiki locally