eQuantic.UIeQuantic.UI
Docs
Playground
GitHub
DocsArchitecture
Package Architecture
Edit this page
4 min read
🌐 This page in: English · Português
This document explains eQuantic.UI's package architecture and the principles behind its self-contained design.
Core Principle: Self-Contained Packages
Each eQuantic.UI package is self-contained and manages its own artifacts. The SDK acts as an orchestrator that references other packages via NuGet's auto-generated properties.
The Design Rationale
Anti-pattern the design rejects, the SDK embedding other packages' artifacts:
1
2
3
eQuantic.UI.Sdk.nupkg ❌
├─ tools/runtime/runtime.js (copied from Runtime)
└─ tools/StandardComponents/*.cs (copied from Components)
Why that shape fails:
Tight Coupling: SDK has direct knowledge of Runtime and Components internals
Version Conflicts: Consumer installs Runtime 0.1.3 but SDK contains embedded 0.1.2 artifacts
Artifact Duplication: Same files exist in multiple packages
Inflexible: Can't independently update Runtime or Components without republishing SDK
The Architecture: Self-Contained Packages
1
2
3
4
5
6
7
8
9
eQuantic.UI.Runtime.nupkg ✅
└─ tools/runtime/runtime.js (self-managed)
eQuantic.UI.Components.nupkg ✅
└─ tools/source/*.cs (self-managed)
eQuantic.UI.Sdk.nupkg ✅
├─ Sdk/Sdk.props (auto-includes Core, Components, Server, Runtime)
└─ Sdk/Sdk.targets (references packages via $(Pkg*) properties)
Benefits:
Decoupling: Each package owns and manages its artifacts
Correct Versioning: Consumer's installed version is always used
No Duplication: Single source of truth per artifact
Independent Evolution: Packages can be updated separately
Clear Interface: SDK uses well-defined NuGet properties
Package Responsibilities
eQuantic.UI.Core
Purpose: Core abstractions and types
Contains:
IComponent interface
HtmlNode, HtmlElement base types
Component lifecycle abstractions
Render context
Packages: Only compiled DLL (no additional artifacts)
eQuantic.UI.Components
Purpose: Standard component library (Button, Input, Container, etc.)
Contains:
Compiled DLL for runtime usage
Source files (tools/source/*.cs) for compiler type resolution
Packaging:
1
2
3
4
5
<!-- eQuantic.UI.Components.csproj -->
<ItemGroup>
<Content Include="**\*.cs" Exclude="obj\**;bin\**"
PackagePath="tools\source\" />
</ItemGroup>
Why Source Files?
The compiler (eqc.dll) needs access to component source code to resolve external types during compilation. When a consumer uses <Button>, the compiler looks up the Button class definition from the Components package.
SDK Usage:
1
2
3
4
5
6
7
<!-- Resolved automatically by NuGet -->
<PropertyGroup>
<_ComponentsSource>$(PkgeQuantic_UI_Components)/tools/source</_ComponentsSource>
</PropertyGroup>
<!-- Passed to compiler -->
<Exec Command="dotnet eqc.dll &quot;$(ProjectDir);$(_ComponentsSource)&quot; ..." />
eQuantic.UI.Runtime
Purpose: Browser runtime (Virtual DOM, reconciler, state management)
Contains:
TypeScript/JavaScript runtime compiled with Vite
runtime.js (tools/runtime/runtime.js) - single bundled file (~49KB)
Packaging:
1
2
3
4
5
6
<!-- eQuantic.UI.Runtime.csproj -->
<ItemGroup>
<Content Include="dist\index.js"
PackagePath="tools\runtime\runtime.js"
Condition="Exists('dist\index.js')" />
</ItemGroup>
Build Process:
1.
TypeScript source → npm run build (Vite + tsc)
2.
Output: dist/index.js (single bundle via inlineDynamicImports)
3.
Packaged: eQuantic.UI.Runtime.nupkg/tools/runtime/runtime.js
SDK Usage:
1
2
3
4
5
6
7
8
<!-- Resolved automatically by NuGet -->
<PropertyGroup>
<_RuntimeSource>$(PkgeQuantic_UI_Runtime)/tools/runtime/runtime.js</_RuntimeSource>
</PropertyGroup>
<!-- Copied to consumer's wwwroot -->
<Copy SourceFiles="$(_RuntimeSource)"
DestinationFiles="wwwroot/_equantic/runtime.js" />
eQuantic.UI.Runtime.{Platform}
Purpose: Platform-specific Bun executables (Osx64, Win64, Linux64)
Contains:
Bun executable (zipped) for bundling
Platform-specific binaries (~60MB each)
Why Separate Packages?
Consumers only download the executable for their platform
Reduces package size (no need for all 3 platforms)
Clean separation of runtime logic vs. build tools
eQuantic.UI.Server
Purpose: ASP.NET Core integration
Contains:
Server-side rendering (SSR)
Server Actions RPC system
Middleware and routing
Embedded runtime.js served at /_equantic/runtime.js
Note: Server embeds its own copy of runtime.js as an EmbeddedResource for serving via HTTP. This is separate from the consumer's build-time copy.
eQuantic.UI.Sdk
Purpose: MSBuild SDK orchestrator
Contains:
Sdk.props - Auto-includes Core, Components, Server, Runtime packages
Sdk.targets - MSBuild targets for compilation, bundling, CSS generation
tools/net10.0/eqc.dll - The compiler executable
Does NOT Contain:
❌ Runtime artifacts (references Runtime package)
❌ Components source (references Components package)
Responsibilities:
1.
Package Management: Auto-includes required packages via Sdk.props
2.
Build Orchestration: Coordinates compilation, bundling, CSS generation
3.
Artifact Resolution: Resolves runtime.js and component sources from their packages
4.
Tooling: Provides compiler (eqc.dll) and build infrastructure
eQuantic.UI.Lucide / Heroicons / ...
Purpose: Icon set providers
Contains:
SVG resolution logic
Specialized icon components
IIconProvider implementation
Fluent API: Registers itself via Use{Name}Icons() extension on UIOptions.
eQuantic.UI.Charts.ChartJs / ApexCharts
Purpose: Specialized chart components
Contains:
Chart wrappers
Asset declarations via IRequireAssets
Fluent API: Registers itself via UseChartJs() / UseApexCharts() extensions on UIOptions.
NuGet Package Properties
NuGet automatically generates $(Pkg*) properties for installed packages:
1
2
3
4
5
6
7
<!-- Auto-generated in obj/*.nuget.g.props -->
<PropertyGroup>
<PkgeQuantic_UI_Core>/Users/name/.nuget/packages/equantic.ui.core/0.1.2</PkgeQuantic_UI_Core>
<PkgeQuantic_UI_Components>/Users/name/.nuget/packages/equantic.ui.components/0.1.2</PkgeQuantic_UI_Components>
<PkgeQuantic_UI_Runtime>/Users/name/.nuget/packages/equantic.ui.runtime/0.1.2</PkgeQuantic_UI_Runtime>
<PkgeQuantic_UI_Runtime_Osx64>/Users/name/.nuget/packages/equantic.ui.runtime.osx64/0.1.2</PkgeQuantic_UI_Runtime_Osx64>
</PropertyGroup>
SDK uses these to resolve artifacts:
1
2
3
4
5
6
7
<!-- Sdk/Sdk.targets -->
<Target Name="CopyEQuanticRuntime">
<PropertyGroup>
<_RuntimeSource>$(PkgeQuantic_UI_Runtime)/tools/runtime/runtime.js</_RuntimeSource>
</PropertyGroup>
<Copy SourceFiles="$(_RuntimeSource)" DestinationFiles="..." />
</Target>
Benefits:
✅ SDK doesn't need to know package structure details
✅ Works with any package version consumer installs
✅ Automatic cache resolution by NuGet
✅ No hardcoded paths
Version Management
Independent Versioning
Packages can evolve independently:
1
2
3
4
<!-- Consumer can mix versions -->
<PackageReference Include="eQuantic.UI.Core" Version="0.1.3" />
<PackageReference Include="eQuantic.UI.Runtime" Version="0.1.4" />
<PackageReference Include="eQuantic.UI.Sdk" Version="0.1.2" />
The SDK will use:
Runtime 0.1.4's runtime.js (not 0.1.2's embedded copy)
Components 0.1.3's source files (not 0.1.2's embedded copy)
Coordinated Versioning
For simplicity, the SDK's Sdk.props defines a default version:
1
2
3
4
5
6
7
8
9
10
11
<!-- Sdk/Sdk.props -->
<PropertyGroup>
<EQuanticUIVersion>0.1.2</EQuanticUIVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="eQuantic.UI.Core" Version="$(EQuanticUIVersion)" />
<PackageReference Include="eQuantic.UI.Components" Version="$(EQuanticUIVersion)" />
<PackageReference Include="eQuantic.UI.Runtime" Version="$(EQuanticUIVersion)" />
<PackageReference Include="eQuantic.UI.Server" Version="$(EQuanticUIVersion)" />
</ItemGroup>
Users can override:
1
2
3
<PropertyGroup>
<EQuanticUIVersion>0.1.5</EQuanticUIVersion>
</PropertyGroup>
Or manually specify versions:
1
2
3
<ItemGroup>
<PackageReference Include="eQuantic.UI.Runtime" Version="0.1.6" />
</ItemGroup>
Development vs. Consumer Scenarios
Consumer Scenario (NuGet Packages)
1
2
3
4
5
6
7
8
dotnet restore
↓ NuGet installs packages to cache
~/.nuget/packages/equantic.ui.runtime/0.1.2/
~/.nuget/packages/equantic.ui.components/0.1.2/
↓ NuGet generates $(Pkg*) properties
obj/project.nuget.g.props
↓ SDK resolves artifacts
Sdk.targets uses $(PkgeQuantic_UI_Runtime)/tools/runtime/runtime.js
Development Scenario (Source Tree)
1
2
3
4
5
6
7
8
9
10
Source tree at: /Users/name/equantic-ui/
↓ Local packages in: artifacts/packages/
↓ NuGet.config prioritizes local
<packageSources>
<add key="local" value="../../artifacts/packages" />
</packageSources>
↓ Fallback paths in Sdk.targets
<_RuntimeSource Condition="'$(PkgeQuantic_UI_Runtime)' == ''">
$(MSBuildThisFileDirectory)../../eQuantic.UI.Runtime/dist/index.js
</_RuntimeSource>
Benefits:
Framework developers can work directly with source
No need to pack/restore during every change
Same targets work for both scenarios
Best Practices
✅ Do
1.
Let packages manage their own artifacts
Runtime packages runtime.js
Components packages source files
2.
Reference via NuGet properties
Use $(PkgeQuantic_UI_*) instead of hardcoded paths
3.
Provide development fallbacks
Allow SDK to find artifacts in source tree when developing the framework
4.
Include clear error messages
Tell users which package is missing when artifacts not found
❌ Don't
1.
Don't embed other packages' artifacts
SDK should NOT copy Runtime's runtime.js into itself
2.
Don't use relative paths across packages
Bad: $(MSBuildThisFileDirectory)../../../Runtime/dist/
Good: $(PkgeQuantic_UI_Runtime)/tools/runtime/
3.
Don't assume package versions match
Consumer may use Runtime 0.1.3 + SDK 0.1.2
4.
Don't create circular dependencies
Packages should have clear dependency graph
Troubleshooting
Runtime.js not found
1
Error: eQuantic.UI: Runtime not found. Ensure eQuantic.UI.Runtime package is installed.
Cause: $(PkgeQuantic_UI_Runtime) is empty
Fix:
1
2
3
dotnet restore --force
# Check that Runtime package is installed
ls ~/.nuget/packages/equantic.ui.runtime/0.1.2/
Components source not found
1
Error: eQuantic.UI: Standard components not found. Ensure eQuantic.UI.Components package is installed.
Cause: $(PkgeQuantic_UI_Components) is empty
Fix:
1
2
3
dotnet restore --force
# Verify Components package has source files
ls ~/.nuget/packages/equantic.ui.components/0.1.2/tools/source/
Using wrong version
Symptom: Build uses old runtime.js despite updating Runtime package
Cause: NuGet cache not cleared
Fix:
1
2
3
4
5
# Clear specific package from cache
rm -rf ~/.nuget/packages/equantic.ui.runtime/0.1.2
# Force restore
dotnet restore --force --no-cache
Related Documentation
Build Flow - Complete build pipeline
Runtime - Runtime architecture and distribution
Compiler - How the C# to JS compiler works