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.
┌─────────────────────────────────────────────────────────────────────────────┐
│ 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─────────┘
┌──────────────────────────────────┐
│ Target ResolveBunForServer: │
│ │ Runtime.Osx64/tools/bun/ │
│ │ Runtime.Win64/tools/bun/ │
│ │ Runtime.Linux64/tools/bun/ │
│ ├─ Extrai do .zip se preciso │
│ Target BundleRuntime: │
│ └─ "$(_BunPath)" build boot.ts │
└──────────────────────────────────┘
wwwroot/runtime.js (embarcado no Server.dll)
┌──────────────────────────────────┐
└──────────────────────────────────┘
artifacts/packages/eQuantic.UI.Server.0.1.1.nupkg
┌─────────────────────────────────────────────────────────────────────────────┐
│ CONSUMIDOR (projeto cliente) │
└─────────────────────────────────────────────────────────────────────────────┘
├─ Sdk="eQuantic.UI.Sdk/0.1.1"
└─ PackageReference: eQuantic.UI.Server, eQuantic.UI.Runtime
┌──────────────────────────────────┐
│ O NuGet instala os pacotes: │
│ ├─ eQuantic.UI.Server │
│ ├─ eQuantic.UI.Runtime │
│ └─ 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.
┌──────────────────────────────────┐
│ O SDK.targets executa: │
│ └─ $(PkgeQuantic_UI_Runtime_ │
│ Osx64)/tools/bun/*.zip │
│ 2. EnsureBunExtracted │
│ ├─ Descompacta se preciso │
│ 4. InstallBunPackages │
│ ├─ <BunPackage> → bun add │
│ └─ Symlink node_modules │
│ └─ dotnet eqc.dll ... --bun │
│ 6. CopyEQuanticRuntime │ ◄── entrega do runtime.js
│ └─ Copia do pacote Runtime │
│ para wwwroot/_equantic/ │
└──────────────────────────────────┘
├─ runtime.js (do pacote Runtime)
└─ *.js (componentes compilados)
┌──────────────────────────────────┐
│ ├─ runtime.js (do Server.dll) │
│ └─ *.js (de wwwroot/_equantic) │
└──────────────────────────────────┘
O browser carrega a aplicação
Origem do Bun por componente
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)
O consumidor só precisa de:
•
dotnet restore + dotnet build
Nenhuma instalação de Node.js, npm ou Bun global é necessária.
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)
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):
├─ 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):
└─ tools/runtime/runtime.js (autocontido)
└─ tools/source/*.cs (autocontido)
└─ Referencia os outros pacotes via $(PkgeQuantic_UI_*)
Benefícios: desacoplamento, versionamento correto, sem duplicação
1. Empacotamento (desenvolvimento)
Durante o dotnet pack do eQuantic.UI.Runtime:
<!-- eQuantic.UI.Runtime.csproj -->
<Content Include="dist\index.js" PackagePath="tools\runtime\runtime.js"
Condition="Exists('dist\index.js')" />
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:
<Target Name="CopyEQuanticRuntime" AfterTargets="CompileEQuanticUI"
Condition="'$(EnableEQuanticUICompilation)' == 'true'">
<!-- Resolve a partir do pacote Runtime pela propriedade do NuGet -->
<_RuntimeSourcePath Condition="'$(PkgeQuantic_UI_Runtime)' != ''">
$(PkgeQuantic_UI_Runtime)/tools/runtime/runtime.js
<!-- Alternativa pela árvore de código (só em desenvolvimento) -->
<_RuntimeSourcePath Condition="'$(_RuntimeSourcePath)' == ''">
$(MSBuildThisFileDirectory)../../eQuantic.UI.Runtime/dist/index.js
<_RuntimeDestPath>$(MSBuildProjectDirectory)/$(EQuanticOutputPath)runtime.js</_RuntimeDestPath>
<Error Text="eQuantic.UI: Runtime not found. Ensure eQuantic.UI.Runtime package is installed."
Condition="'$(_RuntimeSourcePath)' == '' Or !Exists('$(_RuntimeSourcePath)')" />
<Copy SourceFiles="$(_RuntimeSourcePath)" DestinationFiles="$(_RuntimeDestPath)" />
Entrega das fontes do Components
1. Empacotamento (desenvolvimento)
Durante o dotnet pack do eQuantic.UI.Components:
<!-- eQuantic.UI.Components.csproj -->
<Content Include="**\*.cs" Exclude="obj\**;bin\**" PackagePath="tools\source\" />
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:
<Target Name="CompileEQuanticUI" BeforeTargets="Build">
<!-- 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>
<Error Text="eQuantic.UI: Standard components not found. Ensure eQuantic.UI.Components package is installed."
Condition="'$(_StandardComponentsDir)' == '' Or !Exists('$(_StandardComponentsDir)')" />
<Exec Command="dotnet $(EqcCliPath) "$(MSBuildProjectDirectory);$(_StandardComponentsDir)" ..." />
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({
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.