eQuantic.UIeQuantic.UI
Docs
Playground
GitHub
DocsArchitektur
Paketarchitektur
Edit this page
12 min read
Dieses Dokument erklärt die Paketarchitektur von eQuantic.UI und die Prinzipien hinter ihrem eigenständigen Entwurf.
Kernprinzip: eigenständige Pakete
Jedes eQuantic.UI-Paket ist eigenständig und verwaltet seine eigenen Artefakte. Das SDK wirkt als Orchestrator, der die anderen Pakete über die von NuGet selbst erzeugten Eigenschaften referenziert.
Der Grund für diesen Entwurf
Das Antipattern, das der Entwurf ablehnt, nämlich das SDK, das Artefakte anderer Pakete einbettet:
1
2
3
eQuantic.UI.Sdk.nupkg
❌
├─ tools/runtime/runtime.js (aus Server kopiert)
└─ tools/StandardComponents/*.cs (aus Components kopiert)
Warum diese Form scheitert:
•
Enge Kopplung: Das SDK kennt die Innereien von Server und Components direkt
•
Versionskonflikte: Der Konsument stellt eine Server-Version wieder her, und das SDK trägt eine Kopie einer anderen
•
Artefakt-Duplikation: Dieselben Dateien liegen in mehreren Paketen
•
Unflexibel: Server oder Components lassen sich nicht aktualisieren, ohne das SDK neu zu veröffentlichen
Die Architektur: eigenständige Pakete
1
2
3
4
5
6
7
8
9
10
11
12
13
eQuantic.UI.Server.nupkg
✅
├─ lib/net10.0/eQuantic.UI.Server.dll (bettet die Runtime ein, die es auf /_equantic/runtime.js ausliefert)
└─ tools/runtime/runtime.js (dieselben Bytes, damit das SDK sie kopiert)
eQuantic.UI.Components.nupkg
✅
└─ tools/source/*.cs (selbst verwaltet)
eQuantic.UI.Sdk.nupkg
✅
├─ Sdk/Sdk.props (nimmt von selbst Primitives, Code, Components, Charts, Web, Server,
│ das Bun-Paket der Plattform und den Generators-Analyzer auf)
├─ Sdk/Sdk.targets (löst die Artefakte der anderen Pakete über $(Pkg*)-Eigenschaften auf)
├─ tools/net10.0/ (eqc und eqicon, die zwei Build-Werkzeuge, die das SDK ausführt)
└─ tools/netstandard2.0/ (die Tasks, die sie starten, und jedes andere Programm, ohne Shell)
Vorteile:
•
✅ Entkopplung: Jedes Paket besitzt und verwaltet seine Artefakte
•
✅ Korrekte Versionierung: Es wird immer die vom Konsumenten installierte Version verwendet
•
✅ Keine Duplikation: eine einzige Quelle der Wahrheit je Artefakt
•
✅ Unabhängige Entwicklung: Pakete können getrennt aktualisiert werden
•
✅ Klare Schnittstelle: Das SDK nutzt klar definierte NuGet-Eigenschaften
Zuständigkeiten der Pakete
Es gibt kein Paket eQuantic.UI.Core. Es wurde in die Assemblys aufgelöst, die seine Teile besaßen: Die DOM-Oberfläche ging an Web, die Kontraktattribute an Primitives, Metadaten und Assets an Server, der Zustand der Bildoptimierung an Images. Eine Seite, die eQuantic.UI.Core wegen [Page] oder [ServerAction] importierte, löscht die Zeile: Der Namespace, den sie für das Vokabular schon importiert, trägt sie.
eQuantic.UI.Primitives
Zweck: das abstrakte Vokabular und die Kontrakte
Enthält:
•
Das visuelle Vokabular, gegen das eine Komponente geschrieben wird: Knoten, Layout, typisierte Styles, Tokens, Theme
•
Die Komponentenbasen, StatelessComponent und StatefulComponent
•
Die Kontraktattribute: [Page], [ServerAction], [ServerOnly], [Authorize], [AllowAnonymous], [FormModel], [UiFactory]
Paketiert: nur die kompilierte DLL (keine weiteren Artefakte). Null Abhängigkeiten.
eQuantic.UI.Web
Zweck: der Web-Realisierer und die DOM-Notluke
Enthält:
•
Den Realisierer, der die gemeinsamen Komponenten zu DOM und atomarem CSS absenkt
•
Die Notluke: HtmlElement, HtmlNode, HtmlStyle, DynamicElement, IComponent, RenderContext, ClassBuilder
Paketiert: nur die kompilierte DLL (keine weiteren Artefakte)
eQuantic.UI.Components
Zweck: die Write-once-Komponentenbibliothek (Button, Input, Card usw.), gegen Primitives geschrieben und je Ziel realisiert
Enthält:
•
Die kompilierte DLL für die Laufzeitnutzung
•
Quelldateien (tools/source/*.cs) für die Typauflösung des Compilers, mit dem eigenen .resx des SDK neben seinem Designer
Paketierung:
1
2
3
4
5
<!-- eQuantic.UI.Components.csproj -->
<ItemGroup>
<Content Include="**\*.cs" Exclude="obj\**;bin\**" PackagePath="tools\source\" />
<Content Include="SdkResources*.resx" PackagePath="tools\source\" />
</ItemGroup>
Warum Quelldateien?
Der Compiler (eqc.dll) baut ein semantisches Modell der Seite, die er transpiliert, und dieses Modell braucht die Definitionen der Komponenten: Wenn eine Seite Button(…) komponiert, liest der Compiler die Klasse Button aus den Components-Quellen. Das JavaScript der Bibliothek wird NICHT je Anwendung kompiliert: Die transpilierten Module reisen in runtime.js mit, und eqc leitet die Imports von using eQuantic.UI.Components dorthin.
Nutzung durch das SDK:
1
2
3
4
5
6
7
8
<!-- Sdk/Sdk.targets, Target CompileEQuanticUI -->
<_StandardComponentsDir Condition="'$(PkgeQuantic_UI_Components)' != ''">$(PkgeQuantic_UI_Components)/tools/source</_StandardComponentsDir>
<_EqcSourceDirs>$(MSBuildProjectDirectory);$(_StandardComponentsDir)</_EqcSourceDirs>
<!-- Dem Compiler in einer Antwortdatei übergeben, ein Argument je Zeile, mit genau den Assemblys,
gegen die csc kompiliert (refs). eqc wird mit RunProgram gestartet, dem Compiler und dieser
Datei als Argumentliste, also liest unterwegs keine Shell einen Pfad oder einen Namen -->
<RunProgram Program="dotnet" Arguments="@(_EqcStartArgument)" />
eQuantic.UI.Runtime
Zweck: das TypeScript der Browser-Runtime (virtuelles DOM, Reconciler, Zustandsverwaltung) und ihr Beweis
Paketiert: nichts. Das Projekt wird nicht gepackt (IsPackable=false): Das Bundle, das eine Anwendung ausführt, schreibt eQuantic.UI.Server aus boot.ts, das dieses TypeScript importiert, und jenes Paket trägt es (siehe unten). Was das Projekt ausführt, ist der Beweis der Runtime, über das eingebettete Bun: dotnet build src/eQuantic.UI.Runtime -t:TestRuntime installiert die Abhängigkeiten aus bun.lock wie festgenagelt, prüft die Typen mit tsc und lässt vitest einmal laufen.
eQuantic.UI.Runtime.{Os}{Arch}
Zweck: das eingebettete Bun, ein Paket je Betriebssystem und Architektur: Osx64, OsxArm64, Win64, WinArm64, Linux64, LinuxArm64
Enthält:
•
Die Bun-Binary, gezippt (tools/bun/bun-darwin.zip, bun-linux.zip oder bun.exe.zip); das SDK entpackt sie beim ersten Build
•
Nichts weiter. Die Pakete deklarieren keine Abhängigkeiten
Warum getrennte Pakete?
•
Sdk.props referenziert genau EINES von ihnen, gewählt über [MSBuild]::IsOSPlatform und die OSArchitecture der Maschine, also lädt ein Konsument eine Binary herunter, nie sechs
•
Klare Trennung zwischen Runtime-Logik und Build-Werkzeugen
•
Version und SHA-256 jedes Zips sind in bun-toolchain.json festgenagelt und werden von den Tests geprüft. Siehe Build-Ablauf
eQuantic.UI.Server
Zweck: die ASP.NET-Core-Integration
Enthält:
•
Serverseitiges Rendering (SSR)
•
Das RPC-System der Server Actions
•
Middleware und Routing
•
Metadaten (SeoBuilder, MetaTags, IHandleMetadata) und Assets (IRequireAssets, AssetBuilder)
•
Die eingebettete runtime.js, ausgeliefert auf /_equantic/runtime.js
•
tools/runtime/runtime.js: dieselben Bytes als Datei, damit das SDK sie neben die Module einer Anwendung kopiert
Hinweis: Der Server ist der einzige Schreiber der Runtime. Sein Target BundleRuntime bündelt boot.ts vor jedem Build in wwwroot/runtime.js; der Compiler bettet diese Datei als EmbeddedResource ein, und das ist, was /_equantic/runtime.js beantwortet, und das Pack-Target CollectServedRuntime legt dieselbe Datei nach tools/runtime/runtime.js. Die Kopie, die der Build des Konsumenten verwendet, ist diese Datei, Byte für Byte.
eQuantic.UI.Generators
Zweck: der Source-Generator, der die deklarative Factory-Oberfläche für die Komponenten der ANWENDUNG selbst schreibt
Enthält:
•
Nur den Generator. Sdk.props liefert ihn als Analyzer (PrivateAssets="all"), er ist also eine Abhängigkeit zur Build-Zeit, und nichts von ihm geht mit der Anwendung aus
•
Abschaltbar mit <EQuanticGenerateFactorySurface>false</EQuanticGenerateFactorySurface>. Siehe Deklarative Oberfläche
eQuantic.UI.Sdk
Zweck: der Orchestrator des MSBuild-SDK
Enthält:
•
Sdk.props - nimmt von selbst Primitives, Code, Components, Charts, Web, Server, das Bun-Paket der Plattform und den Generators-Analyzer auf
•
Sdk.targets - die MSBuild-Targets für die Kompilierung, das Kopieren von runtime.js, den Vektorkatalog und das Icon der Web-Anwendung
•
tools/net10.0/eqc.dll - die Compiler-Binary (Projekt eQuantic.Build)
•
tools/net10.0/eqicon.dll - das Icon-Werkzeug (Projekt eQuantic.UI.Native.Build): Assets/**/*.svg → Vectors.g.cs, und das Anwendungs-Icon → wwwroot/_equantic/icons
•
tools/netstandard2.0/eQuantic.UI.Sdk.Tasks.dll - die MSBuild-Tasks, mit denen das SDK jedes Programm startet (Projekt eQuantic.UI.Sdk.Tasks): RunProgram, ein Programm und eine Argumentliste ohne Shell, und LinkDirectory, der node_modules-Link, den ein BunPackage braucht. netstandard2.0 ist das, was jedes MSBuild lädt, das des .NET-SDK und das von Visual Studio (Seit 0.2.0-preview.62)
Die zwei Werkzeuge und die Tasks erreichen tools/ als Kanten des eigenen Build-Graphen des SDK-Projekts: ProjectReference-Items mit ReferenceOutputAssembly="false" und PrivateAssets="All", deren Ausgabe das Pack-Target CollectBuildOutput einsammelt. Nichts linkt gegen sie, und das Paket gewinnt keine Abhängigkeit.
Enthält NICHT:
•
❌ Runtime-Artefakte (es referenziert das Server-Paket)
•
❌ die Components-Quellen (es referenziert das Components-Paket)
Zuständigkeiten:
1.
Paketverwaltung: nimmt die benötigten Pakete über Sdk.props von selbst auf
2.
Build-Orchestrierung: koordiniert Kompilierung, Bündelung und Asset-Erzeugung
3.
Artefaktauflösung: löst runtime.js und die Komponentenquellen aus ihren Paketen auf
4.
Werkzeuge: stellt den Compiler (eqc.dll) und das Icon-Werkzeug (eqicon.dll) bereit
eQuantic.UI.Sdk.Native
Zweck: dasselbe SDK für Geräte (macOS, iOS, Android). Siehe Photon
Es beantwortet die zwei Fragen von oben genauso: Die Geschwisterpakete lösen sich auf die Version des SDK selbst auf, und das Werkzeug, das es ausführt (eqicon, für das Anwendungs-Icon, das Bundle und das Fähigkeiten-Manifest), ist entweder das, was der Quellbaum gerade gebaut hat, oder das, was das Paket unter tools/net10.0/ ausgeliefert hat.
eQuantic.UI.Lucide / Heroicons / ...
Zweck: Icon-Kataloge
Enthält:
•
Ein generiertes IconGlyph je Icon, als Eigenschaft, damit der Trimmer die wegwerfen kann, die niemand nennt: Write-once-Daten, im Web und auf Native gleichermaßen genutzt
•
Einen EQuanticIconPack.marker in der Quellwurzel. Das SDK entdeckt ein referenziertes Pack an diesem Marker und übergibt seine Quellen eqc als Referenzquellen, sodass eine Glyphe an der Verwendungsstelle eingesetzt wird und kein JavaScript-Modul je Pack ausgeliefert wird
•
Kein Registrierungsaufruf: Das Paket zu referenzieren ist die ganze Einrichtung. Siehe Icons
eQuantic.UI.Charts.ChartJs / ApexCharts
Zweck: spezialisierte Diagrammkomponenten
Enthält:
•
Diagramm-Hüllen
•
Asset-Deklarationen über IRequireAssets
•
Fluent API: Es registriert sich selbst über die Erweiterungen UseChartJs() / UseApexCharts() auf UIOptions.
NuGet-Paketeigenschaften
NuGet erzeugt eine $(Pkg*)-Eigenschaft für ein installiertes Paket NUR dann, wenn seine PackageReference GeneratePathProperty="true" trägt. Sdk.props setzt das genau auf den Paketen, deren INHALT die Targets lesen, nämlich Components, Charts, Server und das Bun-Paket der Plattform:
1
2
3
4
5
6
7
<!-- Automatisch erzeugt in obj/*.nuget.g.props -->
<PropertyGroup>
<PkgeQuantic_UI_Components>/Users/name/.nuget/packages/equantic.ui.components/<version></PkgeQuantic_UI_Components>
<PkgeQuantic_UI_Charts>/Users/name/.nuget/packages/equantic.ui.charts/<version></PkgeQuantic_UI_Charts>
<PkgeQuantic_UI_Server>/Users/name/.nuget/packages/equantic.ui.server/<version></PkgeQuantic_UI_Server>
<PkgeQuantic_UI_Runtime_OsxArm64>/Users/name/.nuget/packages/equantic.ui.runtime.osxarm64/<version></PkgeQuantic_UI_Runtime_OsxArm64>
</PropertyGroup>
Das SDK nutzt sie, um Artefakte aufzulösen:
1
2
3
4
5
6
7
<!-- Sdk/Sdk.targets -->
<Target Name="CopyEQuanticRuntime">
<PropertyGroup>
<_RuntimeSourcePath>$(PkgeQuantic_UI_Server)/tools/runtime/runtime.js</_RuntimeSourcePath>
</PropertyGroup>
<Copy SourceFiles="$(_RuntimeSourcePath)" DestinationFiles="..." />
</Target>
Vorteile:
•
✅ Das SDK muss die Strukturdetails des Pakets nicht kennen
•
✅ Funktioniert mit jeder Paketversion, die der Konsument installiert
•
✅ Automatische Cache-Auflösung durch NuGet
•
✅ Keine hart verdrahteten Pfade
Versionsverwaltung
Eine Version
Jedes Paket wird aus EINER Zeile in der Directory.Build.props des Repositorys versioniert, und ein Release-Tag veröffentlicht sie alle in dieser Version. Die aktuelle Nummer wird hier mit Absicht nicht genannt: Sie ändert sich mit jedem Release, und eine veraltete Nummer in der Dokumentation ist die Art, wie eine Leserin irregeführt wird. Lesen Sie die Datei.
Das SDK nagelt seine Geschwister auf seine eigene Version
Sdk.props liest die Version aus dem nuspec des installierten SDK-Pakets selbst und fordert jedes Geschwister in dieser Version an. Der Rückfall ist ein Platzhalter, also scheitert ein Build nie am Fehlen einer Nummer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- Sdk/Sdk.props -->
<PropertyGroup Condition="'$(EQuanticUIVersion)' == '' AND Exists('$(_EQuanticSdkNuspecPath)')">
<EQuanticUIVersion>$([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(_EQuanticSdkNuspecPath)')),
'&lt;version&gt;(.+?)&lt;/version&gt;').Groups[1].Value)</EQuanticUIVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(EQuanticUIVersion)' == ''">
<EQuanticUIVersion>*</EQuanticUIVersion>
</PropertyGroup>
<ItemGroup Condition="'$(IsEQuanticDevMode)' != 'true'">
<PackageReference Include="eQuantic.UI.Primitives" Version="$(EQuanticUIVersion)" />
<PackageReference Include="eQuantic.UI.Code" Version="$(EQuanticUIVersion)" />
<PackageReference Include="eQuantic.UI.Components" Version="$(EQuanticUIVersion)" GeneratePathProperty="true" />
<PackageReference Include="eQuantic.UI.Charts" Version="$(EQuanticUIVersion)" GeneratePathProperty="true" />
<PackageReference Include="eQuantic.UI.Web" Version="$(EQuanticUIVersion)" />
<!-- Der Server trägt die Runtime, die er ausliefert, auch als Datei (tools/runtime/runtime.js). -->
<PackageReference Include="eQuantic.UI.Server" Version="$(EQuanticUIVersion)" GeneratePathProperty="true" />
<!-- dazu das EINE eQuantic.UI.Runtime.{Os}{Arch}-Paket für diese Maschine -->
</ItemGroup>
So nennt der Konsument die Version EINMAL, im msbuild-sdks-Eintrag der global.json, den die Vorlage einstempelt, und die Geschwister folgen. Das native SDK macht dasselbe mit einem *-*-Rückfall, damit ein Preview-SDK seine eigenen Preview-Geschwister wiederherstellen kann.
Man kann sie überschreiben:
1
2
3
<PropertyGroup>
<EQuanticUIVersion>die-gewuenschte-version</EQuanticUIVersion>
</PropertyGroup>
Die Eigenschaft bewegt alle Geschwister zusammen; die Bedingungen oben weisen sie nur zu, wenn sie leer ist.
Entwicklungs- gegen Konsumentenszenario
Konsumentenszenario (NuGet-Pakete)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
MeineApp.csproj: <Project Sdk="eQuantic.UI.Sdk">
global.json: { "msbuild-sdks": { "eQuantic.UI.Sdk": "<version>" } }
dotnet restore
↓ Sdk.props: kein Quellbaum daneben → PackageReferences auf $(EQuanticUIVersion)
↓ NuGet installiert die Pakete in den Cache
~/.nuget/packages/equantic.ui.server/<version>/
~/.nuget/packages/equantic.ui.components/<version>/
↓ NuGet erzeugt die $(Pkg*)-Eigenschaften
obj/MeineApp.csproj.nuget.g.props
↓ das SDK löst die Artefakte auf
Sdk.targets nutzt $(PkgeQuantic_UI_Server)/tools/runtime/runtime.js
↓ und führt die Werkzeuge aus, die es ausgeliefert hat
<Sdk-Paket>/tools/net10.0/eqc.dll, eqicon.dll
Entwicklungsszenario (Quellbaum)
Ein Build aus dem eigenen Baum des Repositorys (die Beispiele, die Website, ein Scaffold im Test) geht durch KEIN Paket. Dieselben Sdk.props und Sdk.targets erkennen den Baum neben sich und schalten auf Projektreferenzen um; es gibt keinen lokalen Feed, keinen Cache zu leeren und keinen Pack-Schritt zwischen einer Änderung und ihrer Wirkung. dotnet build samples/DefaultUIDashboard ist die ganze Schleife.
Ein Beispiel importiert die zwei Dateien des SDK direkt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- samples/DefaultUIDashboard/DefaultUIDashboard.csproj -->
<Project>
<Import Project="../../src/eQuantic.UI.Sdk/Sdk/Sdk.props" />
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<!-- Icon-Packs sind OPT-IN: referenzieren Sie die Sätze, die Sie nutzen. -->
<ItemGroup>
<ProjectReference Include="../../src/eQuantic.UI.Lucide/eQuantic.UI.Lucide.csproj" />
</ItemGroup>
<Import Project="../../src/eQuantic.UI.Sdk/Sdk/Sdk.targets" />
</Project>
Sdk.props entscheidet zwischen Paketen und Projekten mit EINER Eigenschaft, und im Entwicklungsmodus deklariert es zusätzlich die zwei Build-Werkzeuge als Kanten des Build-Graphen:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- Sdk/Sdk.props -->
<IsEQuanticDevMode Condition="Exists('$(_DevRootDir)eQuantic.UI.Primitives/eQuantic.UI.Primitives.csproj')">true</IsEQuanticDevMode>
<ItemGroup Condition="'$(IsEQuanticDevMode)' == 'true'">
<!-- Die BUILD-WERKZEUGE, die dieses Projekt ausführt, als Graphkanten: Nichts linkt gegen sie,
sie müssen nur EXISTIEREN, bevor unsere Targets laufen, und genau das garantiert eine
Kante. -->
<ProjectReference Include="$(_DevRootDir)eQuantic.Build\eQuantic.Build.csproj"
ReferenceOutputAssembly="false" Private="false" SkipGetTargetFrameworkProperties="true"
GlobalPropertiesToRemove="$(_EqToolGlobalsToRemove)" />
<ProjectReference Include="$(_DevRootDir)eQuantic.UI.Native.Build\eQuantic.UI.Native.Build.csproj"
ReferenceOutputAssembly="false" Private="false" SkipGetTargetFrameworkProperties="true"
GlobalPropertiesToRemove="$(_EqToolGlobalsToRemove)" />
<ProjectReference Include="$(_DevRootDir)eQuantic.UI.Primitives\eQuantic.UI.Primitives.csproj" />
<ProjectReference Include="$(_DevRootDir)eQuantic.UI.Code\eQuantic.UI.Code.csproj" />
<ProjectReference Include="$(_DevRootDir)eQuantic.UI.Components\eQuantic.UI.Components.csproj" />
<ProjectReference Include="$(_DevRootDir)eQuantic.UI.Charts\eQuantic.UI.Charts.csproj" />
<ProjectReference Include="$(_DevRootDir)eQuantic.UI.Web\eQuantic.UI.Web.csproj" />
<ProjectReference Include="$(_DevRootDir)eQuantic.UI.Server\eQuantic.UI.Server.csproj" />
<!-- dazu das EINE eQuantic.UI.Runtime.{Os}{Arch}-Projekt, und eQuantic.UI.Generators als Analyzer -->
</ItemGroup>
Warum Kanten und keine <MSBuild>-Task inline: Ein Inline-Build führte eine ZWEITE Instanz jedes Werkzeugprojekts unter eigenen globalen Eigenschaften aus, im Wettlauf mit dem Build der Solution in dasselbe bin/, und jedes Target, das das Werkzeug ausführte, war durch Exists(eqicon.dll) abgesichert, das bei einem kalten Build "nein" und beim zweiten "ja" antwortete. Es ging Monate lang durch, aus dem Zufall, wo die Projekte in der .sln standen. Eine Kante wird von MSBuild selbst geordnet und serialisiert. Private="false" hält Apphost und json einer exe aus dem bin/ der Anwendung; GlobalPropertiesToRemove verhindert, dass ein Gerätekopf einem einfachen net10.0-Werkzeug sein Framework oder seinen Runtime-Identifier mitgibt.
Sdk.targets stellt dann EINE Frage (gibt es einen Quellbaum neben mir?), um zu entscheiden, wo die beiden Werkzeuge liegen und woher die Artefakte kommen, wenn keine $(Pkg*)-Eigenschaft gesetzt ist:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- Sdk/Sdk.targets -->
<_SdkToolsDir>$(MSBuildThisFileDirectory)../tools/net10.0/</_SdkToolsDir>
<_EqSourceTree Condition="Exists('$(MSBuildThisFileDirectory)../../eQuantic.Build/eQuantic.Build.csproj')">true</_EqSourceTree>
<!-- Ein Quellbaum führt aus, was seine Kanten gerade gebaut haben; ein Paket führt aus, was es
unter tools/ ausgeliefert hat. Ein EqcCliPath, den das Projekt nennt, gewinnt über beide. -->
<EqcCliPath Condition="'$(EqcCliPath)' == '' AND '$(_EqSourceTree)' == 'true'">$(MSBuildThisFileDirectory)../../eQuantic.Build/bin/$(Configuration)/net10.0/eqc.dll</EqcCliPath>
<EqcCliPath Condition="'$(EqcCliPath)' == ''">$(_SdkToolsDir)eqc.dll</EqcCliPath>
<_EqIconToolDir Condition="'$(_EqSourceTree)' == 'true'">$(MSBuildThisFileDirectory)../../eQuantic.UI.Native.Build/bin/$(Configuration)/net10.0/</_EqIconToolDir>
<_EqIconToolDir Condition="'$(_EqIconToolDir)' == ''">$(_SdkToolsDir)</_EqIconToolDir>
<!-- Artefakte: zuerst das $(Pkg*) des Pakets, sonst der Baum neben dem SDK -->
<_StandardComponentsDir Condition="'$(_StandardComponentsDir)' == '' And Exists('$(_DevComponentsDir)')">$(_DevComponentsDir)</_StandardComponentsDir>
<_RuntimeSourcePath Condition="'$(_RuntimeSourcePath)' == '' And Exists('$(MSBuildThisFileDirectory)../../eQuantic.UI.Server/wwwroot/runtime.js')">$(MSBuildThisFileDirectory)../../eQuantic.UI.Server/wwwroot/runtime.js</_RuntimeSourcePath>
<BunZipPath Condition="'$(BunZipPath)' == '' and Exists('$(MSBuildThisFileDirectory)../../$(BunRuntimePackageId)/tools/bun/$(_BunZipName)')">$(MSBuildThisFileDirectory)../../$(BunRuntimePackageId)/tools/bun/$(_BunZipName)</BunZipPath>
Ein Werkzeug, das ohnehin fehlt, scheitert mit seinem Namen, bevor irgendetwas läuft: EQ4002 für eqc, EQ4001 für eqicon: ein kaputter Graph in einem Quellbaum, ein kaputtes Paket aus NuGet, ein falscher Pfad unter einem ausdrücklichen EqcCliPath. Keiner davon lässt sich dadurch beheben, dass man zweimal baut. Siehe Diagnose.
Das Verzeichnis artifacts/packages, das die nuget.config des Repositorys noch als lokale Quelle deklariert, ist NICHT Teil dieses Ablaufs: Nichts in einem Build aus dem Quellbaum schreibt dorthin oder liest daraus. Um eine Änderung über den ECHTEN Konsumentenweg zu prüfen (Pakete, global.json, Restore), nutzen Sie das Rezept für den lokalen Feed in Build-Ablauf.
Vorteile:
•
Wer das Framework entwickelt, arbeitet direkt mit dem Quellcode; eine Änderung ist beim nächsten dotnet build sichtbar
•
Kein Pack, kein Restore, kein Cache-Schritt zwischen einer Änderung und dem Beispiel, das sie zeigt
•
Dieselben Sdk.props und Sdk.targets funktionieren in beiden Szenarien: Der Konsumentenweg ist nie ein anderer Codeweg, nur eine andere Antwort auf "gibt es einen Baum neben mir?"
Bewährte Praktiken
✅ Tun
1.
Lassen Sie jedes Paket seine eigenen Artefakte verwalten
•
Server paketiert die runtime.js, die es ausliefert
•
Components paketiert Quelldateien
2.
Referenzieren Sie über NuGet-Eigenschaften
•
Nutzen Sie $(PkgeQuantic_UI_*) statt hart verdrahteter Pfade
3.
Sehen Sie Rückfälle für die Entwicklung vor
•
Erlauben Sie dem SDK, die Artefakte im Quellbaum zu finden, wenn das Framework entwickelt wird
4.
Scheitern Sie mit Namen
•
Ein fehlendes Werkzeug oder Artefakt ist ein Fehler, der sagt, welches Paket oder welche Kante fehlt, nie eine Warnung und ein Build, der ohne JavaScript weiterläuft
❌ Nicht tun
1.
Betten Sie keine Artefakte anderer Pakete ein
•
Das SDK sollte die runtime.js des Servers NICHT in sich kopieren
2.
Nutzen Sie keine relativen Pfade über Pakete hinweg
•
Schlecht: $(MSBuildThisFileDirectory)../../../Server/tools/runtime/
•
Gut: $(PkgeQuantic_UI_Server)/tools/runtime/
3.
Legen Sie kein Werkzeug von Hand neben das SDK
•
Eine Kopie, die ein früherer Build dort gelassen hat, ist von Bauart veraltet; das SDK führt aus, was seine Kanten gerade gebaut haben, oder was das Paket ausgeliefert hat
4.
Erzeugen Sie keine zyklischen Abhängigkeiten
•
Pakete sollten einen klaren Abhängigkeitsgraphen haben
Fehlersuche
runtime.js nicht gefunden
1
Error: eQuantic.UI: the served runtime was not found. It ships inside the eQuantic.UI.Server package (version …) at tools/runtime/runtime.js; try 'dotnet restore --force'.
Ursache: $(PkgeQuantic_UI_Server) ist leer, und es gibt neben dem SDK keinen Quellbaum, dessen Server-Projekt wwwroot/runtime.js geschrieben hat (diese Datei ist die Build-Ausgabe des Servers)
Behebung:
1
2
3
dotnet restore --force
# Prüfen, ob das Server-Paket die Runtime trägt
ls ~/.nuget/packages/equantic.ui.server/<version>/tools/runtime/
Components-Quellen nicht gefunden
1
Error: eQuantic.UI: Standard components not found. Ensure the eQuantic.UI.Components package (version …) is installed.
Ursache: $(PkgeQuantic_UI_Components) ist leer und es gibt neben dem SDK keinen Quellbaum
Behebung:
1
2
3
dotnet restore --force
# Prüfen, ob das Components-Paket Quelldateien hat
ls ~/.nuget/packages/equantic.ui.components/<version>/tools/source/
EQ4002 / EQ4001: ein Build-Werkzeug fehlt
1
error EQ4002: The eQuantic compiler (eqc) is missing at '…/tools/net10.0/eqc.dll'. …
Ursache: In einem Quellbaum hat das Projekt des Werkzeugs nicht vor diesem gebaut (ein kaputter Build-Graph, lesen Sie die Fehler ÜBER diesem); aus einem Paket ist das Paket ohne tools/net10.0/ ausgeliefert worden; mit einem ausdrücklichen EqcCliPath nennt der Pfad eine Datei, die nicht da ist
Behebung: Keiner davon lässt sich dadurch beheben, dass man zweimal baut. Reparieren Sie den Graphen, leeren Sie die eQuantic-Pakete aus dem Cache und stellen Sie neu wieder her, oder korrigieren Sie den Pfad. Siehe Diagnose
Falsche Version im Einsatz
Symptom: Der Build nutzt eine alte runtime.js trotz eines neuen Server-Pakets
Ursache: Der NuGet-Cache wurde nicht geleert
Behebung:
1
2
3
4
5
# Dieses eine Paket aus dem Cache leeren
rm -rf ~/.nuget/packages/equantic.ui.server/<version>
# Restore erzwingen
dotnet restore --force --no-cache
Aus dem Framework-Checkout entfernt dotnet msbuild -t:ClearEQuanticCache alle equantic.*-Pakete auf einmal aus dem Cache.
Verwandte Dokumentation
•
Build-Ablauf - die vollständige Build-Pipeline, und das Rezept für den lokalen Feed
•
Runtime - die Architektur der Runtime und ihre Verteilung
•
Compiler - wie der Compiler von C# nach JS arbeitet
•
Diagnose - jedes EQxxxx, das SDK und Compiler melden