Repository navigation
ExternalTypeResolution es
🌐 Esta página en: English · Português · Español · Français · Deutsch
El compilador de eQuantic.UI resuelve los tipos definidos en archivos externos dentro del mismo proyecto. Los componentes referencian modelos, DTOs y otras clases sin que tengan que estar en el mismo archivo: el compilador trabaja contra la Compilation completa de Roslyn del proyecto, que incluye todos los archivos fuente y las referencias.
// 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; // ✅ El tipo 'User' se resuelve por completo
protected override HtmlNode Render()
{
return Text(_currentUser.Name); // ✅ Se convierte en: this._currentUser.name
}
}Un modelo semántico completo importa porque la conversión del acceso a miembros depende de conocer el tipo del receptor: con él, _currentUser.Name baja a this._currentUser.name; sin él, el compilador no puede distinguir una propiedad de una variable local ni elegir el mapeo de método correcto.
using eQuantic.UI.Compiler;
using eQuantic.UI.Compiler.Services;
// Obtener la compilación completa del proyecto
var compilation = await ProjectCompilationHelper
.GetProjectCompilationAsync("path/to/MyApp.csproj");
// Crear el compilador y fijar la compilación del proyecto
var compiler = new ComponentCompiler();
compiler.SetProjectCompilation(compilation);
// Ahora compila los componentes: los tipos externos se resolverán
var results = compiler.CompileFile("Pages/UserProfile.cs");Útil en tareas de MSBuild donde MSBuildWorkspace puede no estar disponible:
using eQuantic.UI.Compiler.Services;
// Obtener todos los archivos .cs del proyecto
var sourceFiles = ProjectCompilationHelper
.GetProjectSourceFiles("path/to/MyApp");
// Obtener las referencias de ensamblados
var assemblyPaths = new[]
{
"path/to/eQuantic.UI.Primitives.dll",
"path/to/other-dependencies.dll"
};
// Crear la compilación a partir de las fuentes
var compilation = ProjectCompilationHelper.CreateCompilationFromSources(
sourceFiles,
assemblyPaths,
"MyApp");
var compiler = new ComponentCompiler();
compiler.SetProjectCompilation(compilation);Volver al modo de compilación mínima (archivos aislados):
compiler.ClearProjectCompilation();┌─────────────────────────────────────────┐
│ Compilación del proyecto MSBuild │
│ - Todos los .cs del proyecto │
│ - Todos los ensamblados referenciados │
│ - Información de tipos completa │
└────────────┬────────────────────────────┘
│
↓ SetProjectCompilation()
┌────────────────────────────────────────┐
│ SemanticModelProvider │
│ - Guarda la compilación del proyecto │
│ - Devuelve un SemanticModel por archivo│
└────────────┬───────────────────────────┘
│
↓ GetSemanticModel(tree)
┌────────────────────────────────────────┐
│ CSharpToJsConverter │
│ - Usa el SemanticModel para los tipos │
│ - Convierte _currentUser.Name → name │
└────────────────────────────────────────┘
-
SemanticModelProvider(SemanticModelProvider.cs)-
SetProjectCompilation(Compilation)- fija la compilación completa del proyecto -
GetSemanticModel(SyntaxTree)- devuelve el modelo semántico con la información de tipos completa
-
-
ProjectCompilationHelper(ProjectCompilationHelper.cs)-
GetProjectCompilationAsync(string)- carga desde el .csproj -
CreateCompilationFromSources(...)- construye a partir de archivos fuente -
GetProjectSourceFiles(string)- encuentra todos los archivos .cs
-
-
ComponentCompiler(ComponentCompiler.cs)-
SetProjectCompilation(Compilation)- habilita la resolución de tipos externos -
ClearProjectCompilation()- vuelve al modo aislado
-
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 generado (con la compilación del proyecto):
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')
]);
}
}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 los accesos a propiedades se resuelven correctamente y se convierten a JavaScript.
Las pruebas verifican que la resolución de tipos externos funciona correctamente:
[Fact]
public void SemanticModel_WithProjectCompilation_CanResolveExternalTypes()
{
// Crear la compilación con User y el 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);
// Afirmar: el tipo User se resuelve
var userType = semanticModel.Compilation.GetTypeByMetadataName("User");
userType.Should().NotBeNull();
}Vea ExternalTypeResolutionTests.cs para la suite de pruebas completa.
- Sin
SetProjectCompilation(): compilación mínima, así que cada archivo se resuelve de forma aislada - Con
SetProjectCompilation(): resolución de tipos de todo el proyecto
Sobrecoste mínimo:
- La compilación del proyecto se crea una vez, en tiempo de build
- Se comparte entre todos los archivos de componente
- Ninguna penalización de compilación por archivo
Eficiente en memoria:
- Una única instancia de compilación
- Reutiliza la infraestructura de Roslyn existente
-
Requiere compilar antes de compilar los componentes: el proyecto tiene que estar compilado (o al menos parseado) antes de ejecutar el compilador de componentes.
-
Disponibilidad de MSBuildWorkspace:
GetProjectCompilationAsyncrequiere las APIs de MSBuild, que pueden no estar disponibles en todos los contextos. UsaCreateCompilationFromSourcescomo alternativa. -
Código generado: los archivos autogenerados en
obj/se excluyen para evitar conflictos.
- Los componentes referencian clases de otros archivos; la información de tipos completa está disponible durante la compilación, así que la generación de JavaScript para los miembros de tipos externos es correcta.
- El build del SDK alimenta al compilador con las referencias de ensamblado reales del proyecto (
--refs), así que los tipos del receptor se resuelven incluso entre fronteras de paquete.
var compilation = await ProjectCompilationHelper.GetProjectCompilationAsync(projectPath);
compiler.SetProjectCompilation(compilation);🏁 Empieza aquí
🏗️ Arquitectura
- Visión general de la arquitectura
- Componentes write-once
- Superficie declarativa
- Arquitectura de paquetes
- Componentes
- Estilos
- Localización
- Analytics y GTM
📱 Write-once
- Motor Photon
- Design System
- Capacidades
- Almacenamiento
- Formularios
- Editor de código
- Markdown
- Mermaid
- Renderizado de email
⚙️ Compilación
⚡ Runtime
🔌 Servidor
🎨 Ecosistema
🚀 Desarrollo