eQuantic.UIeQuantic.UI
Docs
Playground
GitHub
DocsArquitectura
Arquitectura de paquetes
Edit this page
13 min read
Este documento explica la arquitectura de paquetes de eQuantic.UI y los principios detrás de su diseño autocontenido.
Principio central: paquetes autocontenidos
Cada paquete de eQuantic.UI es autocontenido y gestiona sus propios artefactos. El SDK actúa como un orquestador que referencia a los demás paquetes mediante las propiedades que NuGet genera por sí mismo.
La razón del diseño
El antipatrón que el diseño rechaza, el SDK incrustando artefactos de otros paquetes:
1
2
3
eQuantic.UI.Sdk.nupkg
❌
├─ tools/runtime/runtime.js (copiado de Server)
└─ tools/StandardComponents/*.cs (copiado de Components)
Por qué esa forma falla:
•
Acoplamiento fuerte: el SDK tiene conocimiento directo de las tripas de Server y Components
•
Conflictos de versión: el consumidor restaura una versión de Server y el SDK lleva una copia de otra
•
Duplicación de artefactos: los mismos archivos existen en varios paquetes
•
Inflexible: no se puede actualizar Server ni Components sin republicar el SDK
La arquitectura: paquetes autocontenidos
1
2
3
4
5
6
7
8
9
10
11
12
13
eQuantic.UI.Server.nupkg
✅
├─ lib/net10.0/eQuantic.UI.Server.dll (incrusta el runtime que sirve en /_equantic/runtime.js)
└─ tools/runtime/runtime.js (los mismos bytes, para que el SDK los copie)
eQuantic.UI.Components.nupkg
✅
└─ tools/source/*.cs (autogestionado)
eQuantic.UI.Sdk.nupkg
✅
├─ Sdk/Sdk.props (incluye solo Primitives, Code, Components, Charts, Web, Server,
│ el paquete de bun de la plataforma y el analizador Generators)
├─ Sdk/Sdk.targets (resuelve los artefactos de los demás paquetes vía propiedades $(Pkg*))
├─ tools/net10.0/ (eqc y eqicon, las dos herramientas de build que el SDK ejecuta)
└─ tools/netstandard2.0/ (las tareas que las arrancan, y todo otro programa, sin shell)
Beneficios:
•
✅ Desacoplamiento: cada paquete es dueño de sus artefactos y los gestiona
•
✅ Versionado correcto: siempre se usa la versión instalada por el consumidor
•
✅ Sin duplicación: una única fuente de verdad por artefacto
•
✅ Evolución independiente: los paquetes se pueden actualizar por separado
•
✅ Interfaz clara: el SDK usa propiedades de NuGet bien definidas
Responsabilidades de cada paquete
No existe un paquete eQuantic.UI.Core. Se disolvió en los ensamblados que eran dueños de sus partes: la superficie del DOM fue a Web, los atributos de contrato a Primitives, los metadatos y los assets a Server, el estado de optimización de imágenes a Images. Una página que importaba eQuantic.UI.Core por [Page] o [ServerAction] borra la línea: el namespace que ya importa para el vocabulario los lleva.
eQuantic.UI.Primitives
Propósito: el vocabulario abstracto y los contratos
Contiene:
•
El vocabulario visual contra el que se escribe un componente: nodos, layout, estilos tipados, tokens, tema
•
Las bases de componente, StatelessComponent y StatefulComponent
•
Los atributos de contrato: [Page], [ServerAction], [ServerOnly], [Authorize], [AllowAnonymous], [FormModel], [UiFactory]
Empaqueta: solo la DLL compilada (sin artefactos adicionales). Cero dependencias.
eQuantic.UI.Web
Propósito: el realizador web, y la vía de escape al DOM
Contiene:
•
El realizador que baja los componentes compartidos a DOM y CSS atómico
•
La vía de escape: HtmlElement, HtmlNode, HtmlStyle, DynamicElement, IComponent, RenderContext, ClassBuilder
Empaqueta: solo la DLL compilada (sin artefactos adicionales)
eQuantic.UI.Components
Propósito: la biblioteca de componentes write-once (Button, Input, Card, etc.), escrita contra Primitives y realizada por destino
Contiene:
•
La DLL compilada para el uso en ejecución
•
Archivos fuente (tools/source/*.cs) para la resolución de tipos del compilador, con el .resx propio del SDK junto a su Designer
Empaquetado:
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>
¿Por qué archivos fuente?
El compilador (eqc.dll) construye un modelo semántico de la página que transpila, y ese modelo necesita las definiciones de los componentes: cuando una página compone Button(…), el compilador lee la clase Button de las fuentes de Components. El JavaScript de la biblioteca NO se compila por aplicación: los módulos transpilados viajan dentro de runtime.js, y eqc encamina allí los imports de using eQuantic.UI.Components.
Uso desde el SDK:
1
2
3
4
5
6
7
8
9
<!-- Sdk/Sdk.targets, target CompileEQuanticUI -->
<_StandardComponentsDir Condition="'$(PkgeQuantic_UI_Components)' != ''">$(PkgeQuantic_UI_Components)/tools/source</_StandardComponentsDir>
<_EqcSourceDirs>$(MSBuildProjectDirectory);$(_StandardComponentsDir)</_EqcSourceDirs>
<!-- Entregado al compilador en un archivo de respuesta, un argumento por línea, con los
ensamblados exactos contra los que compila csc (refs). eqc se arranca con RunProgram, el
compilador y ese archivo como lista de argumentos, así que ningún shell lee una ruta ni un
nombre en el camino -->
<RunProgram Program="dotnet" Arguments="@(_EqcStartArgument)" />
eQuantic.UI.Runtime
Propósito: el TypeScript del runtime del navegador (DOM virtual, reconciliador, gestión de estado) y su prueba
Empaqueta: nada. El proyecto no se empaqueta (IsPackable=false): el bundle que ejecuta una aplicación lo escribe eQuantic.UI.Server desde boot.ts, que importa este TypeScript, y ese paquete es el que lo lleva (más abajo). Lo que el proyecto ejecuta es la prueba del runtime, a través del bun embebido: dotnet build src/eQuantic.UI.Runtime -t:TestRuntime instala las dependencias de bun.lock tal como están fijadas, verifica tipos con tsc y corre vitest una vez.
eQuantic.UI.Runtime.{Os}{Arch}
Propósito: el Bun embebido, un paquete por SO y arquitectura: Osx64, OsxArm64, Win64, WinArm64, Linux64, LinuxArm64
Contiene:
•
El ejecutable de Bun, comprimido (tools/bun/bun-darwin.zip, bun-linux.zip o bun.exe.zip); el SDK lo extrae en el primer build
•
Nada más. Los paquetes no declaran dependencias
¿Por qué paquetes separados?
•
Sdk.props referencia exactamente UNO de ellos, elegido por [MSBuild]::IsOSPlatform y la OSArchitecture de la máquina, así que un consumidor descarga un binario, nunca seis
•
Separación limpia entre la lógica de runtime y las herramientas de build
•
La versión y el SHA-256 de cada zip están fijados en bun-toolchain.json y comprobados por los tests. Vea Flujo de build
eQuantic.UI.Server
Propósito: la integración con ASP.NET Core
Contiene:
•
El renderizado en servidor (SSR)
•
El sistema RPC de Server Actions
•
Middleware y routing
•
Metadatos (SeoBuilder, MetaTags, IHandleMetadata) y assets (IRequireAssets, AssetBuilder)
•
El runtime.js embebido, servido en /_equantic/runtime.js
•
tools/runtime/runtime.js: los mismos bytes como archivo, para que el SDK lo copie junto a los módulos de una aplicación
Nota: el Server es el único escritor del runtime. Su target BundleRuntime empaqueta boot.ts en wwwroot/runtime.js antes de cada build; el compilador incrusta ese archivo como EmbeddedResource, que es lo que responde /_equantic/runtime.js, y el target de empaquetado CollectServedRuntime pone el mismo archivo en tools/runtime/runtime.js. La copia que el build del consumidor usa es ese archivo, byte por byte.
eQuantic.UI.Generators
Propósito: el generador de código que escribe la superficie declarativa de factories para los componentes de la PROPIA aplicación
Contiene:
•
Solo el generador. Sdk.props lo entrega como analizador (PrivateAssets="all"), así que es una dependencia de tiempo de build y nada de él se entrega con la aplicación
•
Se desactiva con <EQuanticGenerateFactorySurface>false</EQuanticGenerateFactorySurface>. Vea Superficie declarativa
eQuantic.UI.Sdk
Propósito: el orquestador del SDK de MSBuild
Contiene:
•
Sdk.props - incluye por sí mismo Primitives, Code, Components, Charts, Web, Server, el paquete de bun de la plataforma y el analizador Generators
•
Sdk.targets - los targets de MSBuild para la compilación, la copia de runtime.js, el catálogo de vectores y el icono de la aplicación web
•
tools/net10.0/eqc.dll - el ejecutable del compilador (proyecto eQuantic.Build)
•
tools/net10.0/eqicon.dll - la herramienta de iconos (proyecto eQuantic.UI.Native.Build): Assets/**/*.svg → Vectors.g.cs, y el icono de la aplicación → wwwroot/_equantic/icons
•
tools/netstandard2.0/eQuantic.UI.Sdk.Tasks.dll - las tareas de MSBuild con las que el SDK arranca cada programa (proyecto eQuantic.UI.Sdk.Tasks): RunProgram, un programa y una lista de argumentos sin shell, y LinkDirectory, el enlace a node_modules que necesita un BunPackage. netstandard2.0 es lo que carga cualquier MSBuild, el del SDK de .NET y el de Visual Studio (Desde 0.2.0-preview.62)
Las dos herramientas y las tareas llegan a tools/ como aristas del propio grafo de build del proyecto del SDK: ítems ProjectReference con ReferenceOutputAssembly="false" y PrivateAssets="All", cuya salida recoge el target de empaquetado CollectBuildOutput. Nada enlaza contra ellas y el paquete no gana ninguna dependencia.
NO contiene:
•
❌ Artefactos de runtime (referencia al paquete Server)
•
❌ Fuentes de Components (referencia al paquete Components)
Responsabilidades:
1.
Gestión de paquetes: incluye por sí mismo los paquetes necesarios vía Sdk.props
2.
Orquestación del build: coordina la compilación, el empaquetado y la generación de assets
3.
Resolución de artefactos: resuelve runtime.js y las fuentes de los componentes desde sus paquetes
4.
Herramientas: provee el compilador (eqc.dll) y la herramienta de iconos (eqicon.dll)
eQuantic.UI.Sdk.Native
Propósito: el mismo SDK para dispositivos (macOS, iOS, Android). Vea Photon
Responde a las dos preguntas de arriba igual: los paquetes hermanos se resuelven a la versión del propio SDK, y la herramienta que ejecuta (eqicon, para el icono de la aplicación, el bundle y el manifest de capacidades) es o lo que el árbol de código acaba de construir o lo que el paquete entregó bajo tools/net10.0/.
eQuantic.UI.Lucide / Heroicons / ...
Propósito: catálogos de iconos
Contiene:
•
Un IconGlyph generado por icono, como propiedad para que el trimmer pueda descartar los que nadie nombra: datos write-once, usados igual en web y en nativo
•
Un EQuanticIconPack.marker en la raíz de las fuentes. El SDK descubre un pack referenciado por ese marcador y entrega sus fuentes a eqc como fuentes de referencia, así que un glifo se inlinea en el sitio de uso y no se entrega ningún módulo JavaScript por pack
•
Ninguna llamada de registro: referenciar el paquete es toda la configuración. Vea Iconos
eQuantic.UI.Charts.ChartJs / ApexCharts
Propósito: componentes de gráficos especializados
Contiene:
•
Envoltorios de gráficos
•
Declaraciones de assets vía IRequireAssets
•
API fluida: se registra solo mediante las extensiones UseChartJs() / UseApexCharts() sobre UIOptions.
Propiedades de paquete de NuGet
NuGet genera una propiedad $(Pkg*) para un paquete instalado SOLO cuando su PackageReference lleva GeneratePathProperty="true". Sdk.props lo pone exactamente en los paquetes cuyo CONTENIDO leen los targets, a saber Components, Charts, Server y el paquete de bun de la plataforma:
1
2
3
4
5
6
7
<!-- Autogenerado en 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>
El SDK las usa para resolver artefactos:
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>
Beneficios:
•
✅ El SDK no necesita conocer los detalles de la estructura del paquete
•
✅ Funciona con cualquier versión de paquete que instale el consumidor
•
✅ Resolución automática de la caché por NuGet
•
✅ Ninguna ruta escrita a mano
Gestión de versiones
Una versión
Cada paquete se versiona desde UNA línea en el Directory.Build.props del repositorio, y una etiqueta de release los publica todos en esa versión. El número actual no se cita aquí a propósito: cambia en cada release, y un número viejo en la documentación es como se engaña a quien lee. Lea el archivo.
El SDK fija a sus hermanos en su propia versión
Sdk.props lee la versión del nuspec del propio paquete del SDK instalado y pide cada hermano a esa versión. La alternativa es un comodín, así que un build nunca falla por falta de un número:
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)" />
<!-- El Server lleva el runtime que sirve también como archivo (tools/runtime/runtime.js). -->
<PackageReference Include="eQuantic.UI.Server" Version="$(EQuanticUIVersion)" GeneratePathProperty="true" />
<!-- más el ÚNICO paquete eQuantic.UI.Runtime.{Os}{Arch} para esta máquina -->
</ItemGroup>
Así el consumidor declara la versión UNA vez, en la entrada msbuild-sdks del global.json que sella la plantilla, y los hermanos siguen. El SDK nativo hace lo mismo con una alternativa *-*, para que un SDK preview pueda restaurar sus propios hermanos preview.
Quien lo usa puede sobreescribirla:
1
2
3
<PropertyGroup>
<EQuanticUIVersion>la-version-que-quieras</EQuanticUIVersion>
</PropertyGroup>
La propiedad mueve a todos los hermanos juntos; las condiciones de arriba solo la asignan cuando está vacía.
Escenario de desarrollo frente al de consumo
Escenario de consumo (paquetes de NuGet)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
MiApp.csproj: <Project Sdk="eQuantic.UI.Sdk">
global.json: { "msbuild-sdks": { "eQuantic.UI.Sdk": "<version>" } }
dotnet restore
↓ Sdk.props: no hay árbol de código al lado → PackageReferences a $(EQuanticUIVersion)
↓ NuGet instala los paquetes en la caché
~/.nuget/packages/equantic.ui.server/<version>/
~/.nuget/packages/equantic.ui.components/<version>/
↓ NuGet genera las propiedades $(Pkg*)
obj/MiApp.csproj.nuget.g.props
↓ el SDK resuelve los artefactos
Sdk.targets usa $(PkgeQuantic_UI_Server)/tools/runtime/runtime.js
↓ y ejecuta las herramientas que entregó
<paquete del Sdk>/tools/net10.0/eqc.dll, eqicon.dll
Escenario de desarrollo (árbol de código)
Un build desde el propio árbol del repositorio (los ejemplos, el sitio, un scaffold bajo prueba) no pasa por NINGÚN paquete. Los mismos Sdk.props y Sdk.targets detectan el árbol a su lado y cambian a referencias de proyecto; no hay feed local, ni caché que limpiar, ni paso de empaquetado entre una edición y su efecto. dotnet build samples/DefaultUIDashboard es todo el ciclo.
Un ejemplo importa los dos archivos del SDK directamente:
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>
<!-- Los packs de iconos son OPT-IN: referencia los conjuntos que uses. -->
<ItemGroup>
<ProjectReference Include="../../src/eQuantic.UI.Lucide/eQuantic.UI.Lucide.csproj" />
</ItemGroup>
<Import Project="../../src/eQuantic.UI.Sdk/Sdk/Sdk.targets" />
</Project>
Sdk.props decide entre paquetes y proyectos con UNA propiedad, y en modo de desarrollo declara además las dos herramientas de build como aristas del grafo de build:
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'">
<!-- Las HERRAMIENTAS de build que este proyecto ejecuta, como aristas del grafo: nada enlaza
contra ellas, solo tienen que EXISTIR antes de que corran nuestros targets, que es
exactamente lo que garantiza una arista. -->
<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" />
<!-- más el ÚNICO proyecto eQuantic.UI.Runtime.{Os}{Arch}, y eQuantic.UI.Generators como analizador -->
</ItemGroup>
Por qué aristas y no una tarea <MSBuild> en línea: un build en línea ejecutaba una SEGUNDA instancia de cada proyecto de herramienta bajo sus propias propiedades globales, compitiendo con el build de la solución por el mismo bin/, y cada target que ejecutaba la herramienta estaba guardado por Exists(eqicon.dll), que respondía "no" en un build en frío y "sí" en el segundo. Pasó durante meses por el accidente de dónde estaban los proyectos en el .sln. Una arista la ordena y serializa MSBuild mismo. Private="false" mantiene el apphost y el json de un exe fuera del bin/ de la aplicación; GlobalPropertiesToRemove evita que una cabecera de dispositivo entregue a una herramienta de net10.0 plano su framework o su identificador de runtime.
Sdk.targets hace entonces UNA pregunta (¿hay un árbol de código a mi lado?) para decidir dónde están las dos herramientas, y de dónde vienen los artefactos cuando no hay ninguna propiedad $(Pkg*) puesta:
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>
<!-- Un árbol de código ejecuta lo que sus aristas acaban de construir; un paquete ejecuta lo que
entregó bajo tools/. Un EqcCliPath que declare el proyecto gana a los dos. -->
<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>
<!-- Artefactos: primero el $(Pkg*) del paquete, el árbol junto al SDK en caso contrario -->
<_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>
Una herramienta que falte de todos modos falla por su nombre antes de que corra nada: EQ4002 para eqc, EQ4001 para eqicon: un grafo roto en un árbol de código, un paquete roto venido de NuGet, una ruta equivocada bajo un EqcCliPath explícito. Ninguno de ellos se arregla construyendo dos veces. Vea Diagnósticos.
El directorio artifacts/packages que el nuget.config del repositorio todavía declara como fuente local NO es parte de este flujo: nada en un build desde el árbol de código escribe en él ni lo lee. Para validar un cambio por el camino REAL del consumidor (paquetes, global.json, restore), use la receta de feed local en Flujo de build.
Beneficios:
•
Quien desarrolla el framework trabaja directamente con el código; una edición es visible en el siguiente dotnet build
•
Ningún pack, ningún restore, ningún paso de caché entre una edición y el ejemplo que la muestra
•
Los mismos Sdk.props y Sdk.targets funcionan en los dos escenarios: el camino del consumidor nunca es otro código, solo otra respuesta a "¿hay un árbol a mi lado?"
Buenas prácticas
✅ Haz
1.
Deja que cada paquete gestione sus propios artefactos
•
Server empaqueta el runtime.js que sirve
•
Components empaqueta archivos fuente
2.
Referencia vía propiedades de NuGet
•
Usa $(PkgeQuantic_UI_*) en lugar de rutas escritas a mano
3.
Provee alternativas para el desarrollo
•
Permite que el SDK encuentre los artefactos en el árbol de código cuando se desarrolla el framework
4.
Falla por el nombre
•
Una herramienta o un artefacto que falta es un error que dice qué paquete o qué arista falta, nunca una advertencia y un build que sigue sin JavaScript
❌ No hagas
1.
No incrustes artefactos de otros paquetes
•
El SDK NO debería copiar el runtime.js del Server dentro de sí
2.
No uses rutas relativas entre paquetes
•
Mal: $(MSBuildThisFileDirectory)../../../Server/tools/runtime/
•
Bien: $(PkgeQuantic_UI_Server)/tools/runtime/
3.
No dejes una herramienta junto al SDK a mano
•
Una copia que dejó un build anterior está vieja por construcción; el SDK ejecuta lo que sus aristas acaban de construir, o lo que entregó el paquete
4.
No crees dependencias circulares
•
Los paquetes deben tener un grafo de dependencias claro
Resolución de problemas
No se encuentra runtime.js
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'.
Causa: $(PkgeQuantic_UI_Server) está vacía, y no hay un árbol de código junto al SDK cuyo proyecto Server haya escrito wwwroot/runtime.js (ese archivo es la salida del build del Server)
Arreglo:
1
2
3
dotnet restore --force
# Comprueba que el paquete Server lleva el runtime
ls ~/.nuget/packages/equantic.ui.server/<version>/tools/runtime/
No se encuentran las fuentes de Components
1
Error: eQuantic.UI: Standard components not found. Ensure the eQuantic.UI.Components package (version …) is installed.
Causa: $(PkgeQuantic_UI_Components) está vacía y no hay un árbol de código junto al SDK
Arreglo:
1
2
3
dotnet restore --force
# Verifica que el paquete Components tiene archivos fuente
ls ~/.nuget/packages/equantic.ui.components/<version>/tools/source/
EQ4002 / EQ4001: falta una herramienta de build
1
error EQ4002: The eQuantic compiler (eqc) is missing at '…/tools/net10.0/eqc.dll'. …
Causa: en un árbol de código, el proyecto de la herramienta no se construyó antes que este (un grafo de build roto, lea los errores ARRIBA de este); desde un paquete, el paquete salió sin tools/net10.0/; con un EqcCliPath explícito, la ruta nombra un archivo que no está ahí
Arreglo: ninguno de ellos se arregla construyendo dos veces. Arregle el grafo, limpie los paquetes de eQuantic de la caché y restaure de nuevo, o arregle la ruta. Vea Diagnósticos
Se usa la versión equivocada
Síntoma: el build usa un runtime.js viejo a pesar de un paquete Server nuevo
Causa: la caché de NuGet no se limpió
Arreglo:
1
2
3
4
5
# Limpia ese paquete concreto de la caché
rm -rf ~/.nuget/packages/equantic.ui.server/<version>
# Fuerza el restore
dotnet restore --force --no-cache
Desde el checkout del framework, dotnet msbuild -t:ClearEQuanticCache quita de la caché todos los paquetes equantic.* de una vez.
Documentación relacionada
•
Flujo de build - la pipeline de build completa, y la receta del feed local
•
Runtime - la arquitectura del runtime y su distribución
•
Compilador - cómo funciona el compilador de C# a JS
•
Diagnósticos - cada EQxxxx que reportan el SDK y el compilador