Depuração e ferramentas de desenvolvimentoO 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:
window.__EQ_DEV__; // true em desenvolvimento, false em produção
Todas as ferramentas de desenvolvimento são carregadas condicionalmente com base nessa flag.
O logger fornece logs consistentes e com prefixo, que só saem em modo de desenvolvimento.
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);
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]
No DevTools do browser, você pode filtrar pelo prefixo:
O logger está implementado em src/eQuantic.UI.Runtime/src/utils/logger.ts:
const isDev = typeof window !== "undefined" && window.__EQ_DEV__;
if (isDev) console.debug("[eQuantic.UI]", ...args);
if (isDev) console.info("[eQuantic.UI]", ...args);
console.warn("[eQuantic.UI]", ...args);
console.error("[eQuantic.UI]", ...args);
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.
•
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
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
// 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
┌─────────────────────────────────────────────┐
│ ⚠️
Build Error Close (Esc)│ ├─────────────────────────────────────────────┤
│ Mensagem de erro aqui │
│ ┌─────────────────────────────────────────┐ │
│ │ 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:
import { errorOverlay } from "@equantic/ui-runtime/dev";
message: "Custom error message",
componentStack: "Component hierarchy...",
// - Clicar no botão "Close"
O overlay de erro está implementado em src/eQuantic.UI.Runtime/src/dev/error-overlay.ts:
private overlay: HTMLDivElement | null = null;
private errors: ErrorInfo[] = [];
if (!window.__EQ_DEV__) return; // Só em dev
// Cria o overlay de tela cheia com os detalhes do erro
export const errorOverlay = new ErrorOverlay();
// Captura automática de erros
window.addEventListener("error", (event) => {
stack: event.error?.stack,
window.addEventListener("unhandledrejection", (event) => {
message: `Unhandled Promise Rejection: ${event.reason}`,
stack: event.reason?.stack,
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:
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
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#:
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<EQuanticSourceMaps>external</EQuanticSourceMaps>
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:
"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
Para inspecionar o estado e as props de um componente:
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"]',
console.log(component.state);
console.log(component.props);
Testes de integração com o Playwright
Para depurar problemas de renderização entre SSR e CSR:
test("SSR matches CSR", async ({ page }) => {
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();
expect(normalizeHtml(ssrHtml)).toBe(normalizeHtml(csrHtml));
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
Monitore os Server Actions no DevTools do browser:
2.
Filtre por _equantic/actions
•
A carga da requisição (nome do método, argumentos)
•
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:
// Liga o rastreamento de performance
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
Monitore os tempos de compilação:
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
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) # A cópia vem do pacote Server (não do SDK)
ls ~/.nuget/packages/equantic.ui.server/<version>/tools/runtime/
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:
// Dispare o overlay manualmente
import { errorOverlay } from "@equantic/ui-runtime/dev";
errorOverlay.show({ message: "Test error" });
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
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
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