eQuantic.UIeQuantic.UI
Docs
Playground
GitHub
DocsDesenvolvimento
Depuração e ferramentas de desenvolvimento
Editar esta página
11 min de leitura
O eQuantic.UI oferece ferramentas de depuração profissionais parecidas com as do Next.js, com recursos exclusivos de desenvolvimento que ajudam a identificar e corrigir problemas rapidamente.
🔍 Detecção do modo de desenvolvimento
O framework detecta o ambiente automaticamente usando o IWebHostEnvironment.IsDevelopment() e o expõe ao browser por:
1
window.__EQ_DEV__; // true em desenvolvimento, false em produção
Todas as ferramentas de desenvolvimento são carregadas condicionalmente com base nessa flag.
📝 Sistema de logging
O logger fornece logs consistentes e com prefixo, que só saem em modo de desenvolvimento.
Uso
1
2
3
4
5
6
7
8
9
import { logger } from "@equantic/ui-runtime";
// Só em desenvolvimento (silenciado em produção)
logger.debug("Component state:", state);
logger.info("API call completed");
// Sempre loga (mesmo em produção)
logger.warn("Deprecated API used");
logger.error("Failed to load data:", error);
Níveis de log
Método
Saída
Produção
Prefixo
debug()
Console debug
❌ Silenciado
[eQuantic.UI]
info()
Console info
❌ Silenciado
[eQuantic.UI]
warn()
Console warn
✅ Sempre
[eQuantic.UI]
error()
Console error
✅ Sempre
[eQuantic.UI]
Filtrando os logs
No DevTools do browser, você pode filtrar pelo prefixo:
1
[eQuantic.UI]
Implementação
O logger está implementado em src/eQuantic.UI.Runtime/src/utils/logger.ts:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const isDev = typeof window !== "undefined" && window.__EQ_DEV__;
export const logger = {
debug(...args: any[]) {
if (isDev) console.debug("[eQuantic.UI]", ...args);
},
info(...args: any[]) {
if (isDev) console.info("[eQuantic.UI]", ...args);
},
warn(...args: any[]) {
console.warn("[eQuantic.UI]", ...args);
},
error(...args: any[]) {
console.error("[eQuantic.UI]", ...args);
},
};
🚨 Overlay de erro
O overlay de erro fornece uma interface de erro em tela cheia, no estilo do Next.js, que aparece automaticamente quando ocorrem erros em tempo de execução.
Recursos
•
Captura automática: pega erros não tratados e rejeições de promise
•
Stack traces em C# ✨: o overlay é ciente de source map: ele busca o .js.map de cada bundle, decodifica (src/dev/source-map.ts + src/dev/stack-remapper.ts), e reescreve a pilha de chamadas como os frames C# originais, mais um trecho da linha do código C# que falhou. Cai para a visão em JS se não houver mapa disponível. (É isso que torna real a promessa de "0 conhecimento de JS" na hora de depurar: quem desenvolve em C# vê C#, não JavaScript transpilado.)
•
Suporte a teclado: aperte Esc para fechar
•
Só em desenvolvimento: nunca aparece em produção (carregado por um import() dinâmico em dev)
•
UX limpa: cabeçalho vermelho, fonte monoespaçada, conteúdo rolável
Quando ele aparece
O overlay de erro aparece automaticamente para:
1.
Erros não tratados: qualquer exceção não capturada no JavaScript
2.
Rejeições de promise: erros assíncronos não tratados
1
2
3
4
5
6
// Isto dispara o overlay de erro em modo de desenvolvimento
throw new Error("Something went wrong");
Promise.reject("Async error");
await fetch("/api/data"); // Se o fetch falhar e não for capturado
A interface do overlay de erro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌─────────────────────────────────────────────┐
│
⚠️
Build Error Close (Esc)│
├─────────────────────────────────────────────┤
│ │
│ Mensagem de erro aqui │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ Stack trace: │ │
│ │ at MyComponent.render (page.js:42) │ │
│ │ at Reconciler.patch (reconciler.js:12)│ │
│ │ ... │ │
│ └─────────────────────────────────────────┘ │
│ │
├─────────────────────────────────────────────┤
│ Este overlay de erro só aparece em │
│ desenvolvimento. Corrija o erro para seguir.│
└─────────────────────────────────────────────┘
Mostrando erros manualmente
Você pode mostrar erros no overlay manualmente:
1
2
3
4
5
6
7
8
9
import { errorOverlay } from "@equantic/ui-runtime/dev";
if (window.__EQ_DEV__) {
errorOverlay.show({
message: "Custom error message",
stack: error.stack,
componentStack: "Component hierarchy...",
});
}
Limpando o overlay
1
2
3
4
5
6
// Limpeza programática
errorOverlay.clear();
// Ações do usuário
// - Apertar a tecla Esc
// - Clicar no botão "Close"
Implementação
O overlay de erro está implementado em src/eQuantic.UI.Runtime/src/dev/error-overlay.ts:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class ErrorOverlay {
private overlay: HTMLDivElement | null = null;
private errors: ErrorInfo[] = [];
show(error: ErrorInfo) {
if (!window.__EQ_DEV__) return; // Só em dev
this.errors.push(error);
this.render();
}
clear() {
this.errors = [];
if (this.overlay) {
this.overlay.remove();
this.overlay = null;
}
}
private render() {
// Cria o overlay de tela cheia com os detalhes do erro
}
}
export const errorOverlay = new ErrorOverlay();
// Captura automática de erros
if (window.__EQ_DEV__) {
window.addEventListener("error", (event) => {
errorOverlay.show({
message: event.message,
stack: event.error?.stack,
});
});
window.addEventListener("unhandledrejection", (event) => {
errorOverlay.show({
message: `Unhandled Promise Rejection: ${event.reason}`,
stack: event.reason?.stack,
});
});
}
🛠️ Depurando componentes
DevTools do browser
Um build Debug escreve um source map ao lado de cada módulo, linkado a partir dele e com o C# de onde o módulo veio, então o depurador do browser mostra o próprio C#.
Chrome DevTools:
1.
Abra o DevTools (F12)
2.
Vá para a aba Sources
3.
Encontre o arquivo C# na árvore, nomeado pelo caminho dele dentro do projeto (Pages/Home.cs), nunca pelo da máquina de build
4.
Ponha breakpoints direto no código C#
5.
Inspecione estado, props e variáveis locais
Source maps
O que o mapa de um módulo carrega é a propriedade EQuanticSourceMaps, e o padrão dela segue a configuração:
EQuanticSourceMaps
Padrão em
O que o build escreve
full
Debug
um mapa ao lado de cada módulo, linkado a partir dele, com o C# dentro, para o depurador do browser
external
(opcional)
um mapa sem o C# e sem o link, para um serviço de erros que envia os mapas por conta própria; o debugId do bun é mantido
none
todas as outras configurações
nenhum mapa, e nenhum link
Um mapa com o C# dentro é o código do componente, o corpo de um [ServerAction] incluído, e a pasta de saída é uma raiz web: é por isso que só o Debug escreve um. Diga a propriedade o que disser, um publish não leva mapa nenhum da saída do compilador. Para guardar mapas de um build publicado, escreva-os sem o C#:
1
2
3
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<EQuanticSourceMaps>external</EQuanticSourceMaps>
</PropertyGroup>
O eqc compõe cada mapa ele mesmo, em C#, sem instalar nada: o bun mapeia o JavaScript para o TypeScript que o eqc escreveu, o mapa V3 do próprio eqc leva desse TypeScript ao C#, e os dois viram um mapa só, do JavaScript que o browser roda até o C#. As fontes dele são nomeadas relativas ao mapa e dentro do projeto, então um depurador mostra o caminho do próprio projeto; um arquivo de fora do projeto (as fontes de um pacote no cache do NuGet) é nomeado só pelo nome do arquivo. Um módulo escrito a partir de mais de um arquivo nomeia cada um, com o seu próprio C#: uma classe que adota o padrão que uma interface fornece leva as linhas desse padrão ao arquivo da interface, onde o depurador as mostra (Desde 0.2.0-preview.60). Um mapa é JSON seja o que for que o C# tenha, um form feed ou qualquer outro caractere de controle num comentário inclusive (Desde 0.2.0-preview.60). Um mapa full, resumido:
1
2
3
4
5
6
{
"version": 3,
"sources": ["../../Pages/Home.cs"],
"sourcesContent": ["…o C# de Pages/Home.cs…"],
"mappings": "AAAA;AACA;..."
}
Cada instrução tem um segmento próprio (Desde 0.2.0-preview.58): um frame ou um breakpoint cai na instrução de C# que lançou ou em que foi posto, não na primeira linha do seu método. Uma linha que o compilador escreve por conta própria pertence à instrução que a produziu: o else if de um switch com padrões aponta para o seu case, o dispose de um using para o using, a condição de um do para ela mesma, e as instruções de uma função local e a expressão de um membro com corpo de expressão para as suas próprias linhas. O bloco de uma lambda também mapeia instrução por instrução (Desde 0.2.0-preview.60), e o que vem depois do bloco na linha em que ele fecha volta a apontar para a instrução. Isso vale para uma lambda passada a um método de instância ou estático do próprio app, a um método da própria lista (ForEach, Find, Exists…) ou a Where, Select, Any, All, Aggregate e os outros operadores LINQ escritos como uma forma só, para uma lambda numa expressão de coleção (children: [ … ]), e para uma guardada numa variável local, numa função local ou num delegate; e, desde a mesma versão, para uma dentro de um membro com corpo de expressão, de uma criação de objeto, de um inicializador de objeto ou de coleção ou de um objeto anônimo. Cada instrução de um corpo que tem um parâmetro out ou ref também aponta para a sua própria linha. Esse corpo roda dentro de uma arrow que o compilador acrescenta, então uma pilha lida no browser tem um frame a mais que a do .NET entre o lançamento e quem chamou. Nos outros casos, o bloco de uma lambda aponta, por enquanto, para a instrução que a contém: passada a um método de extensão, a um delegate ou por ?., dada a First, Count, Sum, OrderBy, GroupBy e os outros operadores com tradução própria.
Isso permite:
•
Pôr breakpoints em código C#
•
Percorrer a lógica C# passo a passo
•
Inspecionar os nomes de variáveis do C#
•
Ver os números de linha originais nos stack traces
Inspeção de componentes
Para inspecionar o estado e as props de um componente:
1
2
3
4
5
6
7
8
9
// No console do browser
window.__EQ_DEBUG = true; // Liga o modo de depuração
// Os componentes expõem o estado deles
const component = document.querySelector(
'[data-component-id="abc"]',
).__component;
console.log(component.state);
console.log(component.props);
🧪 Testes e depuração
Testes de integração com o Playwright
Para depurar problemas de renderização entre SSR e CSR:
1
2
3
4
5
6
7
8
9
10
11
12
test("SSR matches CSR", async ({ page }) => {
// Pega o HTML do SSR
const ssrResponse = await page.goto("http://localhost:5000");
const ssrHtml = await ssrResponse.text();
// Espera a hidratação do CSR
await page.waitForLoadState("networkidle");
const csrHtml = await page.content();
// Compara
expect(normalizeHtml(ssrHtml)).toBe(normalizeHtml(csrHtml));
});
Depuração no servidor
Depure o código C# normalmente com o Visual Studio ou o VS Code:
1.
Ponha breakpoints nos arquivos .cs
2.
Rode com o depurador anexado: dotnet run
3.
Os breakpoints são atingidos durante:
•
A renderização no servidor (SSR)
•
As invocações de Server Action
•
A compilação dos componentes
Depuração de rede
Monitore os Server Actions no DevTools do browser:
1.
Abra a aba Network
2.
Filtre por _equantic/actions
3.
Inspecione:
•
A carga da requisição (nome do método, argumentos)
•
Os dados da resposta
•
As informações de tempo
•
Os erros (com stack traces)
Depuração de provedores de asset
Ao usar IRequireAssets, verifique se as dependências estão sendo injetadas corretamente:
1.
Inspecione a fonte: abra o "Ver código-fonte da página" no browser e procure pelas tags de script/estilo.
2.
Aba Network: confira se as URLs externas (por exemplo, CDNs) estão carregando com sucesso (Status 200).
3.
Deduplicação: verifique se vários componentes não injetaram o mesmo script duas vezes.
4.
Ordem: as folhas de estilo devem aparecer antes dos scripts para a renderização correta.
Assets na renderização no servidor (SSR)
Se os assets estiverem faltando no HTML inicial:
1.
Verifique se o componente implementa IRequireAssets.
2.
Garanta que o AddUI() é chamado no Program.cs.
3.
Confira se a AssetCollection está reunindo os assets corretamente durante a passada de render.
📊 Depuração de performance
Performance em tempo de execução
O reconciliador rastreia métricas de performance em modo de desenvolvimento:
1
2
3
4
5
// Liga o rastreamento de performance
window.__EQ_PERF = true;
// Veja as métricas
console.table(window.__EQ_PERF_DATA);
As métricas incluem:
•
Tempo de render: quanto cada componente levou para renderizar
•
Tempo de diff: tempo gasto no reconciliador
•
Operações de DOM: número de mudanças reais no DOM
•
Listeners de evento: contagem de listeners ativos
Performance de build
Monitore os tempos de compilação:
1
dotnet build -v:detailed
Procure por:
•
A duração do target CompileEQuanticUI
•
O número de componentes compilados
•
O tempo de geração do TypeScript
•
O tempo de empacotamento do Bun
🔧 Problemas comuns
O runtime.js não carrega
Sintoma: o boot() nunca executa, GET /_equantic/runtime.js devolve 404
Solução: quem responde o browser é o Server, não o arquivo em wwwroot/_equantic/: o app.MapUI() mapeia /_equantic/runtime.js para o bundle que o assembly eQuantic.UI.Server embarca. Um 404 vem ou desse endpoint, e aí a resposta lista os recursos que o assembly carrega, ou de endpoint nenhum, então confira que o app.MapUI() roda. A cópia do próprio build, wwwroot/_equantic/runtime.js, tem os mesmos bytes, para as ferramentas que carregam os módulos fora de um servidor; o target CopyEQuanticRuntime a tira do pacote Server, e o build falha com "the served runtime was not found" quando não consegue (veja Arquitetura de pacotes)
1
2
3
4
5
6
# A cópia vem do pacote Server (não do SDK)
ls ~/.nuget/packages/equantic.ui.server/<version>/tools/runtime/
# Force a reconstrução
dotnet clean
dotnet build -v:n
Tokens de tema faltando no CSR
Sintoma: o HTML renderizado no servidor está tematizado, mas a renderização no cliente não
Causa raiz: o blob da ponte de tema não foi adotado no boot
Solução:
1.
Verifique se o runtime.js carrega antes dos scripts dos componentes
2.
Procure no console do browser por [eQuantic.UI] Boot process started
3.
Inspecione window.__EQ_THEME__ no console - deve conter o tema serializado
Source maps não funcionam
Sintoma: não dá para depurar o código C# original no DevTools do browser
Solução:
1.
Construa em Debug, ou defina EQuanticSourceMaps como full: um build em qualquer outra configuração não escreve mapa, e um mapa external não é linkado a partir do módulo, então o browser não o encontra sozinho
2.
Confira se os arquivos .map existem em wwwroot/_equantic/
3.
Ligue os source maps nas configurações do DevTools do browser
4.
Limpe o cache do browser e reconstrua
5.
Um frame que para no intermediário TypeScript (obj/eQuantic/ts/…) vem de um mapa que o eqc não conseguiu compor, e o eqc reporta cada um no log do build ("… leads to the TypeScript only")
O overlay de erro não aparece
Sintoma: erros logados no console mas nenhum overlay
Verificações:
1.
O window.__EQ_DEV__ é verdadeiro? (confira no console)
2.
O overlay de erro foi importado? (confira se o runtime.js o inclui)
3.
O CSS do overlay de erro carregou? (procure pelos estilos de #equantic-error-overlay)
Forçar a exibição:
1
2
3
// Dispare o overlay manualmente
import { errorOverlay } from "@equantic/ui-runtime/dev";
errorOverlay.show({ message: "Test error" });
🎯 Boas práticas
Fluxo de desenvolvimento
1.
Use o logger à vontade: adicione logs de depuração durante o desenvolvimento, eles são de graça em produção
2.
Teste os dois modos: sempre teste com o ambiente Development e Production
3.
Monitore a rede: deixe a aba Network do DevTools aberta para pegar Server Actions que falharam
4.
Os source maps vêm com o Debug: um build Debug os escreve (lá o EQuanticSourceMaps é full), então não há nada para ligar
5.
Use o overlay de erro: não suprima os erros, deixe o overlay mostrá-los
Depuração em produção
Para problemas em produção:
1.
Logs do servidor: confira os logs do ASP.NET Core para erros de Server Action
2.
Console do browser: só os logs de warn e error aparecem
3.
Sentry/AppInsights: integre serviços de rastreamento de erros
4.
Source maps: um publish não leva nenhum. Construa com EQuanticSourceMaps=external e envie os mapas que ele escreve em wwwroot/_equantic/ para o seu serviço de rastreamento de erros; nunca sirva um mapa full a partir de uma raiz web, porque ele é o código do componente
Checklist de depuração
Antes de reportar problemas:
•
[ ] Conferir o console do browser por erros
•
[ ] Verificar se window.__EQ_DEV__ é verdadeiro (dev) ou falso (prod)
•
[ ] Confirmar que o runtime.js carrega (aba Network)
•
[ ] Conferir o blob da ponte de tema (window.__EQ_THEME__)
•
[ ] Testar com o cache do browser desligado
•
[ ] Tentar em modo anônimo/privado
•
[ ] Comparar o HTML do SSR com o do CSR
•
[ ] Conferir a saída do MSBuild por avisos
•
[ ] Verificar se os pacotes NuGet estão nas versões corretas
📚 Documentação relacionada
•
Arquitetura do runtime - entendendo o sistema de runtime
•
Fluxo de build - como a compilação e o empacotamento funcionam
•
Performance - técnicas de otimização