Skip to content

Debug pt BR

Edgar Mesquita edited this page Oct 4, 2026 · 10 revisions

Depuração e ferramentas de desenvolvimento

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

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:

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

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:

[eQuantic.UI]

Implementação

O logger está implementado em src/eQuantic.UI.Runtime/src/utils/logger.ts:

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
// 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                       │
│                                             │
│ ┌─────────────────────────────────────────┐ │
│ │ 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:

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

// 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:

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#:

<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:

{
  "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:

// 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:

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:

// 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:

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)

# 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:

// 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

Clone this wiki locally