Skip to content

BuildFlow pt BR

Edgar Mesquita edited this page Aug 13, 2026 · 1 revision

Fluxo de build do eQuantic.UI

🌐 Esta página em: English · Português

Este documento descreve o fluxo de build do eQuantic.UI, demonstrando como o framework mantém zero dependências externas para o consumidor.

Fluxo visual

┌─────────────────────────────────────────────────────────────────────────────┐
│                    DESENVOLVIMENTO (árvore de código)                       │
└─────────────────────────────────────────────────────────────────────────────┘

  reconciler.ts, component.ts, etc.
         │
         ▼
  ┌──────────────────────────────────┐
  │  npm run build                   │  (só durante o desenvolvimento)
  │  (eQuantic.UI.Runtime)           │
  └──────────────────────────────────┘
         │
         ▼
  dist/index.js (runtime compilado)
         │
         │
  boot.ts ──────importa─────────┘
         │
         ▼
  ┌──────────────────────────────────┐
  │  dotnet build                    │
  │  (eQuantic.UI.Server)            │
  │                                  │
  │  Target ResolveBunForServer:     │
  │  ├─ Procura o Bun em:            │
  │  │  Runtime.Osx64/tools/bun/     │
  │  │  Runtime.Win64/tools/bun/     │
  │  │  Runtime.Linux64/tools/bun/   │
  │  ├─ Extrai do .zip se preciso    │
  │  └─ chmod +x (Unix)              │
  │                                  │
  │  Target BundleRuntime:           │
  │  └─ "$(_BunPath)" build boot.ts  │
  └──────────────────────────────────┘
         │
         ▼
  wwwroot/runtime.js (embarcado no Server.dll)
         │
         ▼
  ┌──────────────────────────────────┐
  │  dotnet pack                     │
  │  (eQuantic.UI.Server)            │
  └──────────────────────────────────┘
         │
         ▼
  artifacts/packages/eQuantic.UI.Server.0.1.1.nupkg


┌─────────────────────────────────────────────────────────────────────────────┐
│                      CONSUMIDOR (projeto cliente)                           │
└─────────────────────────────────────────────────────────────────────────────┘

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

         │
         ▼
  ┌──────────────────────────────────┐
  │  dotnet restore                  │
  │                                  │
  │  O NuGet instala os pacotes:     │
  │  ├─ eQuantic.UI.Sdk              │
  │  ├─ eQuantic.UI.Server           │
  │  ├─ eQuantic.UI.Runtime          │
  │  │  └─ (metapacote)              │
  │  └─ eQuantic.UI.Runtime.Osx64    │  ◄── o Bun vem embarcado aqui!
  │     └─ tools/bun/bun-darwin.zip  │
  └──────────────────────────────────┘

> **Qual Bun?** O `src/eQuantic.UI.Runtime/bun-toolchain.json` guarda a versão e um SHA-256 por
> plataforma, conferidos a cada execução dos testes. Os digests provam que os bytes comitados são os
> que o release publicou; a versão é verificada extraindo o binário do host e rodando ele, porque um
> manifesto que ninguém compara com aquilo que ele descreve acaba se afastando dele.
>
> Os seis pacotes de plataforma (`Osx64`, `OsxArm64`, `Win64`, `WinArm64`, `Linux64`, `LinuxArm64`)
> carregam o mesmo build; um Bun diferente por arquitetura é como se consegue um bundle que só falha
> na máquina de uma pessoa.
>
> Para atualizar: troque os arquivos `.zip`, rode os testes uma vez com `EQ_UPDATE_BUN_MANIFEST=1`, e
> leia o diff. Uma versão que mudou com digests que não mudaram é um erro, e o contrário também.

         │
         ▼
  ┌──────────────────────────────────┐
  │  dotnet build                    │
  │                                  │
  │  O SDK.targets executa:          │
  │                                  │
  │  1. ResolveBunZipPath            │
  │     └─ $(PkgeQuantic_UI_Runtime_ │
  │        Osx64)/tools/bun/*.zip    │
  │                                  │
  │  2. EnsureBunExtracted           │
  │     ├─ Descompacta se preciso    │
  │     └─ chmod +x (Unix)           │
  │                                  │
  │  3. ResolveBunPath               │
  │     └─ Define $(BunPath)         │
  │                                  │
  │  4. InstallBunPackages           │
  │     ├─ <BunPackage> → bun add   │
  │     └─ Symlink node_modules      │
  │                                  │
  │  5. CompileEQuanticUI            │
  │     └─ dotnet eqc.dll ... --bun  │
  │        "$(BunPath)"              │
  │                                  │
  │  6. CopyEQuanticRuntime          │  ◄── entrega do runtime.js
  │     └─ Copia do pacote Runtime   │
  │        para wwwroot/_equantic/   │
  │                                  │
  │     └─ "$(BunPath)" x            │
  └──────────────────────────────────┘
         │
         ▼
  wwwroot/_equantic/
  ├─ runtime.js (do pacote Runtime)
  └─ *.js (componentes compilados)
         │
         ▼
  ┌──────────────────────────────────┐
  │  dotnet run                      │
  │                                  │
  │  O servidor serve:               │
  │  ├─ runtime.js (do Server.dll)   │
  │  └─ *.js (de wwwroot/_equantic)  │
  └──────────────────────────────────┘
         │
         ▼
  O browser carrega a aplicação

Origem do Bun por componente

Componente Origem do Bun
Server (build do pacote) eQuantic.UI.Runtime.{OS}/tools/bun/ (árvore de código)
SDK (consumidor) $(PkgeQuantic_UI_Runtime_{OS})/tools/bun/ (cache do NuGet)

Requisitos do consumidor

O consumidor só precisa de:

  • .NET SDK 10.0
  • dotnet restore + dotnet build

Nenhuma instalação de Node.js, npm ou Bun global é necessária.

Arquivos-chave

Arquivo Responsabilidade
Sdk/Sdk.targets Resolve o Bun, instala os itens <BunPackage>, compila os componentes
Server.csproj Resolve o Bun na árvore de código, empacota o runtime.js
Runtime.{OS}.csproj Empacota o executável do Bun para cada plataforma

Targets do MSBuild (ordem de execução)

No SDK (consumidor)

  1. ResolveBunZipPath - encontra o .zip do Bun no cache do NuGet
  2. EnsureBunExtracted - extrai o executável se preciso
  3. ResolveBunPath - define $(BunPath) para uso posterior
  4. InstallBunPackages - instala os itens <BunPackage> via bun add (veja BunPackage)
  5. CompileEQuanticUI - transpila C# → TypeScript → JavaScript
  6. CopyEQuanticRuntime - copia o runtime.js do pacote Runtime para wwwroot/_equantic/

No Server (desenvolvimento)

  1. ResolveBunForServer - encontra o Bun na árvore de código
  2. BundleRuntime - compila boot.ts → runtime.js

Arquitetura de pacotes e autocontenção

O eQuantic.UI segue uma arquitetura de pacotes autocontidos, em que cada pacote gerencia os próprios artefatos. O SDK age como orquestrador, referenciando os outros pacotes pelas propriedades $(Pkg*) do NuGet.

Princípios da arquitetura

Antes (problemático):

Pacote SDK ❌
├─ runtime.js embarcado (copiado do Runtime)
└─ arquivos *.cs embarcados (copiados do Components)

Problemas: acoplamento forte, conflitos de versão, duplicação de artefatos

Depois (correto):

Pacote Runtime ✅
└─ tools/runtime/runtime.js (autocontido)

Pacote Components ✅
└─ tools/source/*.cs (autocontido)

Pacote SDK ✅
└─ Referencia os outros pacotes via $(PkgeQuantic_UI_*)

Benefícios: desacoplamento, versionamento correto, sem duplicação

Entrega do runtime.js

1. Empacotamento (desenvolvimento)

Durante o dotnet pack do eQuantic.UI.Runtime:

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

O pacote Runtime embarca o próprio artefato JavaScript compilado.

2. Entrega (build do consumidor)

Durante o dotnet build, o target CopyEQuanticRuntime do SDK executa:

<!-- Sdk/Sdk.targets -->
<Target Name="CopyEQuanticRuntime" AfterTargets="CompileEQuanticUI"
        Condition="'$(EnableEQuanticUICompilation)' == 'true'">
  <PropertyGroup>
    <!-- Resolve a partir do pacote Runtime pela propriedade do NuGet -->
    <_RuntimeSourcePath Condition="'$(PkgeQuantic_UI_Runtime)' != ''">
      $(PkgeQuantic_UI_Runtime)/tools/runtime/runtime.js
    </_RuntimeSourcePath>

    <!-- Alternativa pela árvore de código (só em desenvolvimento) -->
    <_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>

Entrega das fontes do Components

1. Empacotamento (desenvolvimento)

Durante o dotnet pack do eQuantic.UI.Components:

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

O pacote Components embarca os próprios arquivos de código C# para a resolução de tipos do compilador.

2. Compilação (build do consumidor)

Durante o dotnet build, o target CompileEQuanticUI do SDK executa:

<!-- Sdk/Sdk.targets -->
<Target Name="CompileEQuanticUI" BeforeTargets="Build">
  <PropertyGroup>
    <!-- Resolve a partir do pacote Components pela propriedade do NuGet -->
    <_StandardComponentsDir Condition="'$(PkgeQuantic_UI_Components)' != ''">
      $(PkgeQuantic_UI_Components)/tools/source
    </_StandardComponentsDir>

    <!-- Alternativa pela árvore de código (só em desenvolvimento) -->
    <_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>

Principais benefícios da arquitetura

  • Desacoplamento: o SDK não embarca artefatos de outros pacotes
  • Versionamento correto: o consumidor pode usar Runtime 0.1.3 + SDK 0.1.2 de forma independente
  • Sem duplicação: cada artefato existe só no pacote de origem dele
  • Flexibilidade: os pacotes evoluem independentemente, sem acoplamento forte
  • Interface clara: o SDK referencia os pacotes por propriedades bem definidas do NuGet ($(Pkg*))
  • Alternativa em desenvolvimento: os caminhos da árvore de código funcionam para o desenvolvimento do framework

Estratégia de bundle único do runtime

O Runtime usa a configuração inlineDynamicImports: true do Vite para criar um bundle único:

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

Por que um bundle único?

  • Entrega simplificada: só um arquivo para copiar (runtime.js)
  • Sem gestão de chunks: evita problemas com chunks separados de logger-.js, error-overlay-.js
  • Distribuição confiável: garante que todos os recursos do runtime (logger, overlay de erro) estejam incluídos
  • Tamanho pequeno: ~49KB minificado com todos os recursos incluídos

Sem isso, o Vite criaria chunks separados para os imports dinâmicos, e o SDK precisaria copiar vários arquivos com nomes baseados em hash.

Clone this wiki locally