Skip to content

BuildFlow

Edgar Mesquita edited this page Feb 9, 2026 · 6 revisions

eQuantic.UI Build Flow

This document describes the eQuantic.UI build flow, demonstrating how the framework maintains zero external dependencies for the consumer.

Visual Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│                           DEVELOPMENT (source tree)                         │
└─────────────────────────────────────────────────────────────────────────────┘

  reconciler.ts, component.ts, etc.
         │
         ▼
  ┌──────────────────────────────────┐
  │  npm run build                   │  (only during development)
  │  (eQuantic.UI.Runtime)           │
  └──────────────────────────────────┘
         │
         ▼
  dist/index.js (compiled runtime)
         │
         │
  boot.ts ──────imports────────┘
         │
         ▼
  ┌──────────────────────────────────┐
  │  dotnet build                    │
  │  (eQuantic.UI.Server)            │
  │                                  │
  │  ResolveBunForServer target:     │
  │  ├─ Looks for Bun in:            │
  │  │  Runtime.Osx64/tools/bun/     │
  │  │  Runtime.Win64/tools/bun/     │
  │  │  Runtime.Linux64/tools/bun/   │
  │  ├─ Extracts from .zip if needed │
  │  └─ chmod +x (Unix)              │
  │                                  │
  │  BundleRuntime target:           │
  │  └─ "$(_BunPath)" build boot.ts  │
  └──────────────────────────────────┘
         │
         ▼
  wwwroot/runtime.js (embedded in Server.dll)
         │
         ▼
  ┌──────────────────────────────────┐
  │  dotnet pack                     │
  │  (eQuantic.UI.Server)            │
  └──────────────────────────────────┘
         │
         ▼
  artifacts/packages/eQuantic.UI.Server.0.1.1.nupkg


┌─────────────────────────────────────────────────────────────────────────────┐
│                        CONSUMER (client project)                            │
└─────────────────────────────────────────────────────────────────────────────┘

  MyApp.csproj
  ├─ Sdk="eQuantic.UI.Sdk/0.1.1"
  └─ PackageReference: eQuantic.UI.Server, eQuantic.UI.Runtime

         │
         ▼
  ┌──────────────────────────────────┐
  │  dotnet restore                  │
  │                                  │
  │  NuGet installs packages:        │
  │  ├─ eQuantic.UI.Sdk              │
  │  ├─ eQuantic.UI.Server           │
  │  ├─ eQuantic.UI.Runtime          │
  │  │  └─ (meta-package)            │
  │  └─ eQuantic.UI.Runtime.Osx64    │  ◄── Bun embedded here!
  │     └─ tools/bun/bun-darwin.zip  │
  └──────────────────────────────────┘
         │
         ▼
  ┌──────────────────────────────────┐
  │  dotnet build                    │
  │                                  │
  │  SDK.targets executes:           │
  │                                  │
  │  1. ResolveBunZipPath            │
  │     └─ $(PkgeQuantic_UI_Runtime_ │
  │        Osx64)/tools/bun/*.zip    │
  │                                  │
  │  2. EnsureBunExtracted           │
  │     ├─ Unzip if needed           │
  │     └─ chmod +x (Unix)           │
  │                                  │
  │  3. ResolveBunPath               │
  │     └─ Defines $(BunPath)        │
  │                                  │
  │  4. InstallBunPackages           │
  │     ├─ <BunPackage> → bun add   │
  │     └─ Symlink node_modules      │
  │                                  │
  │  5. CompileEQuanticUI            │
  │     └─ dotnet eqc.dll ... --bun  │
  │        "$(BunPath)"              │
  │                                  │
  │  6. CopyEQuanticRuntime          │  ◄── Runtime.js deployment
  │     └─ Copy from Runtime package │
  │        to wwwroot/_equantic/     │
  │                                  │
  │  7. BuildCSS (Tailwind)          │
  │     └─ "$(BunPath)" x            │
  │        @tailwindcss/cli ...      │
  └──────────────────────────────────┘
         │
         ▼
  wwwroot/_equantic/
  ├─ runtime.js (from Runtime package)
  └─ *.js (compiled components)
         │
         ▼
  ┌──────────────────────────────────┐
  │  dotnet run                      │
  │                                  │
  │  Server serves:                  │
  │  ├─ runtime.js (from Server.dll) │
  │  └─ *.js (from wwwroot/_equantic)│
  └──────────────────────────────────┘
         │
         ▼
  Browser loads application

Bun Source by Component

Component Bun Source
Server (package build) eQuantic.UI.Runtime.{OS}/tools/bun/ (source tree)
SDK (consumer) $(PkgeQuantic_UI_Runtime_{OS})/tools/bun/ (NuGet cache)
Tailwind Uses $(BunPath) resolved by SDK

Consumer Requirements

The consumer only needs:

  • .NET SDK 8.0
  • dotnet restore + dotnet build

No Node.js, npm, or global Bun installation required.

Key Files

File Responsibility
Sdk/Sdk.targets Resolves Bun, installs <BunPackage> items, compiles components
Server.csproj Resolves Bun from source tree, bundles runtime.js
Tailwind.targets Uses $(BunPath) to generate CSS
Runtime.{OS}.csproj Packages Bun executable for each platform

MSBuild Targets (Execution Order)

In SDK (consumer)

  1. ResolveBunZipPath - Finds the Bun .zip in NuGet cache
  2. EnsureBunExtracted - Extracts the executable if needed
  3. ResolveBunPath - Defines $(BunPath) for later use
  4. InstallBunPackages - Installs <BunPackage> items via bun add (see BunPackage)
  5. CompileEQuanticUI - Transpiles C# → TypeScript → JavaScript
  6. CopyEQuanticRuntime - Copies runtime.js from Runtime package to wwwroot/_equantic/
  7. BuildCSS (Tailwind) - Generates CSS with Tailwind CLI

In Server (development)

  1. ResolveBunForServer - Finds Bun in source tree
  2. BundleRuntime - Compiles boot.ts → runtime.js

Package Architecture & Self-Containment

eQuantic.UI follows a self-contained package architecture where each package manages its own artifacts. The SDK acts as an orchestrator, referencing other packages via NuGet's $(Pkg*) properties.

Architecture Principles

Before (Problematic):

SDK Package ❌
├─ Embedded runtime.js (copied from Runtime)
└─ Embedded *.cs files (copied from Components)

Problems: tight coupling, version conflicts, artifact duplication

After (Correct):

Runtime Package ✅
└─ tools/runtime/runtime.js (self-contained)

Components Package ✅
└─ tools/source/*.cs (self-contained)

SDK Package ✅
└─ References other packages via $(PkgeQuantic_UI_*)

Benefits: decoupling, correct versioning, no duplication

Runtime.js Deployment

1. Packaging (Development)

During dotnet pack of eQuantic.UI.Runtime:

<!-- eQuantic.UI.Runtime.csproj -->
<ItemGroup>
  <Content Include="dist\index.js" PackagePath="tools\runtime\runtime.js"
           Condition="Exists('dist\index.js')" />
</ItemGroup>

The Runtime package embeds its own compiled JavaScript artifact.

2. Deployment (Consumer Build)

During dotnet build, the SDK's CopyEQuanticRuntime target executes:

<!-- Sdk/Sdk.targets -->
<Target Name="CopyEQuanticRuntime" AfterTargets="CompileEQuanticUI"
        Condition="'$(EnableEQuanticUICompilation)' == 'true'">
  <PropertyGroup>
    <!-- Resolve from Runtime package via NuGet property -->
    <_RuntimeSourcePath Condition="'$(PkgeQuantic_UI_Runtime)' != ''">
      $(PkgeQuantic_UI_Runtime)/tools/runtime/runtime.js
    </_RuntimeSourcePath>

    <!-- Fallback to source tree (development only) -->
    <_RuntimeSourcePath Condition="'$(_RuntimeSourcePath)' == ''">
      $(MSBuildThisFileDirectory)../../eQuantic.UI.Runtime/dist/index.js
    </_RuntimeSourcePath>

    <_RuntimeDestPath>$(MSBuildProjectDirectory)/$(EQuanticOutputPath)runtime.js</_RuntimeDestPath>
  </PropertyGroup>

  <Error Text="eQuantic.UI: Runtime not found. Ensure eQuantic.UI.Runtime package is installed."
         Condition="'$(_RuntimeSourcePath)' == '' Or !Exists('$(_RuntimeSourcePath)')" />

  <Copy SourceFiles="$(_RuntimeSourcePath)" DestinationFiles="$(_RuntimeDestPath)" />
</Target>

Components Source Deployment

1. Packaging (Development)

During dotnet pack of eQuantic.UI.Components:

<!-- eQuantic.UI.Components.csproj -->
<ItemGroup>
  <Content Include="**\*.cs" Exclude="obj\**;bin\**" PackagePath="tools\source\" />
</ItemGroup>

The Components package embeds its own C# source files for compiler type resolution.

2. Compilation (Consumer Build)

During dotnet build, the SDK's CompileEQuanticUI target executes:

<!-- Sdk/Sdk.targets -->
<Target Name="CompileEQuanticUI" BeforeTargets="Build">
  <PropertyGroup>
    <!-- Resolve from Components package via NuGet property -->
    <_StandardComponentsDir Condition="'$(PkgeQuantic_UI_Components)' != ''">
      $(PkgeQuantic_UI_Components)/tools/source
    </_StandardComponentsDir>

    <!-- Fallback to source tree (development only) -->
    <_StandardComponentsDir Condition="'$(_StandardComponentsDir)' == ''">
      $(MSBuildThisFileDirectory)../../eQuantic.UI.Components
    </_StandardComponentsDir>
  </PropertyGroup>

  <Error Text="eQuantic.UI: Standard components not found. Ensure eQuantic.UI.Components package is installed."
         Condition="'$(_StandardComponentsDir)' == '' Or !Exists('$(_StandardComponentsDir)')" />

  <Exec Command="dotnet $(EqcCliPath) &quot;$(MSBuildProjectDirectory);$(_StandardComponentsDir)&quot; ..." />
</Target>

Key Architectural Benefits

  • Decoupling: SDK doesn't embed artifacts from other packages
  • Correct Versioning: Consumer can use Runtime 0.1.3 + SDK 0.1.2 independently
  • No Duplication: Each artifact exists only in its source package
  • Flexibility: Packages evolve independently without tight coupling
  • Clear Interface: SDK references packages via well-defined NuGet properties ($(Pkg*))
  • Development Fallback: Source tree paths work for framework development

Runtime Single Bundle Strategy

The Runtime uses Vite's inlineDynamicImports: true configuration to create a single bundle:

// eQuantic.UI.Runtime/vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        inlineDynamicImports: true,  // ← Creates single bundle
      },
    },
  },
});

Why Single Bundle?

  • Simplified Deployment: Only one file to copy (runtime.js)
  • No Chunk Management: Avoids issues with separate logger-.js, error-overlay-.js chunks
  • Reliable Distribution: Guaranteed that all runtime features (logger, error overlay) are included
  • Small Size: ~49KB minified with all features included

Without this, Vite would create separate chunks for dynamic imports, and the SDK would need to copy multiple files with hash-based names.

Clone this wiki locally