Résolution des types externes dans le compilateur eQuantic.UILe compilateur eQuantic.UI résout les types définis dans des fichiers externes au sein du même projet. Les composants référencent des modèles, des DTO et d'autres classes sans qu'elles aient à se trouver dans le même fichier : le compilateur travaille contre la Compilation Roslyn complète du projet, qui comprend tous les fichiers source et les références.
public string Name { get; set; }
public string Email { get; set; }
public class UserProfile : StatefulComponent
private User _currentUser; // ✅
Le type 'User' est entièrement résolu protected override HtmlNode Render()
return Text(_currentUser.Name); // ✅
Se convertit en : this._currentUser.name Un modèle sémantique complet compte parce que la conversion de l'accès aux membres dépend de la connaissance du type du receveur : avec lui, _currentUser.Name s'abaisse en this._currentUser.name ; sans lui, le compilateur ne peut pas distinguer une propriété d'une variable locale ni choisir le bon mappage de méthode.
Option 1 : utiliser MSBuildWorkspace (recommandé)
using eQuantic.UI.Compiler;
using eQuantic.UI.Compiler.Services;
// Obtenir la compilation complète du projet
var compilation = await ProjectCompilationHelper
.GetProjectCompilationAsync("path/to/MyApp.csproj");
// Créer le compilateur et poser la compilation du projet
var compiler = new ComponentCompiler();
compiler.SetProjectCompilation(compilation);
// Maintenant compilez les composants : les types externes seront résolus
var results = compiler.CompileFile("Pages/UserProfile.cs");
Option 2 : compilation manuelle depuis les sources
Utile dans des tâches MSBuild où MSBuildWorkspace peut ne pas être disponible :
using eQuantic.UI.Compiler.Services;
// Obtenir tous les fichiers .cs du projet
var sourceFiles = ProjectCompilationHelper
.GetProjectSourceFiles("path/to/MyApp");
// Obtenir les références d'assemblys
var assemblyPaths = new[]
"path/to/eQuantic.UI.Primitives.dll",
"path/to/other-dependencies.dll"
// Créer la compilation depuis les sources
var compilation = ProjectCompilationHelper.CreateCompilationFromSources(
var compiler = new ComponentCompiler();
compiler.SetProjectCompilation(compilation);
Option 3 : effacer la compilation du projet
Revenir au mode de compilation minimale (fichiers isolés) :
compiler.ClearProjectCompilation();
┌─────────────────────────────────────────┐
│ Compilation du projet MSBuild │
│ - Tous les .cs du projet │
│ - Tous les assemblys référencés │
│ - Information de types complète │
└────────────┬────────────────────────────┘
↓ SetProjectCompilation()
┌────────────────────────────────────────┐
│ SemanticModelProvider │
│ - Garde la compilation du projet │
│ - Renvoie un SemanticModel par fichier│
└────────────┬───────────────────────────┘
┌────────────────────────────────────────┐
│ - Utilise le SemanticModel pour les types │
│ - Convertit _currentUser.Name → name │
└────────────────────────────────────────┘
•
SetProjectCompilation(Compilation) - pose la compilation complète du projet
•
GetSemanticModel(SyntaxTree) - renvoie le modèle sémantique avec l'information de types complète
•
GetProjectCompilationAsync(string) - charge depuis le .csproj
•
CreateCompilationFromSources(...) - construit depuis des fichiers source
•
GetProjectSourceFiles(string) - trouve tous les fichiers .cs
•
SetProjectCompilation(Compilation) - active la résolution des types externes
•
ClearProjectCompilation() - revient au mode isolé
Exemple 1 : composant avec un modèle externe
Models/Product.cs :
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public bool InStock { get; set; }
Pages/ProductCard.cs :
public class ProductCard : StatelessComponent
public Product Item { get; set; }
protected override HtmlNode Build()
Text($"${Item.Price:F2}"),
Text(Item.InStock ? "In Stock" : "Out of Stock")
JavaScript généré (avec la compilation du projet) :
class ProductCard extends StatelessComponent {
Text(`$${this.item.price.toFixed(2)}`),
Text(this.item.inStock ? 'In Stock' : 'Out of Stock')
Exemple 2 : plusieurs types externes
Models/Address.cs :
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
Models/Customer.cs :
public string Name { get; set; }
public string Email { get; set; }
public Address ShippingAddress { get; set; }
Pages/CheckoutPage.cs :
public class CheckoutPage : StatefulComponent
private Customer _customer;
protected override HtmlNode Render()
Text($"Customer: {_customer.Name}"),
Text($"Email: {_customer.Email}"),
Text($"Shipping: {_customer.ShippingAddress.City}, {_customer.ShippingAddress.ZipCode}")
Tous les accès aux propriétés sont correctement résolus et convertis en JavaScript.
Les tests vérifient que la résolution des types externes marche correctement :
public void SemanticModel_WithProjectCompilation_CanResolveExternalTypes()
// Créer la compilation avec User et le composant
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);
// Affirmer : le type User est résolu
var userType = semanticModel.Compilation.GetTypeByMetadataName("User");
userType.Should().NotBeNull();
•
Sans SetProjectCompilation() : compilation minimale, donc chaque fichier est résolu isolément
•
Avec SetProjectCompilation() : résolution des types de tout le projet
Surcoût minimal :
•
La compilation du projet est créée une fois, au build
•
Elle est partagée par tous les fichiers de composants
•
Aucune pénalité de compilation par fichier
Économe en mémoire :
•
Une seule instance de compilation
•
Réutilise l'infrastructure Roslyn existante
1.
Demande une compilation avant la compilation des composants : le projet doit être compilé (ou au moins parsé) avant de lancer le compilateur de composants.
2.
Disponibilité de MSBuildWorkspace : GetProjectCompilationAsync demande les API MSBuild, qui peuvent ne pas être disponibles dans tous les contextes. Utilisez CreateCompilationFromSources en repli.
3.
Code généré : les fichiers auto-générés dans obj/ sont exclus pour éviter les conflits.
•
Les composants référencent des classes d'autres fichiers ; l'information de types complète est disponible pendant la compilation, donc la génération de JavaScript pour les membres de types externes est correcte.
•
Le build du SDK alimente le compilateur avec les vraies références d'assemblys du projet (--refs), donc les types de receveur se résolvent même par-delà les frontières de paquets.
var compilation = await ProjectCompilationHelper.GetProjectCompilationAsync(projectPath);
compiler.SetProjectCompilation(compilation);