Redocly/redocly-lint-rules

Implemente uma regra de lint do Redocly CLI no redocly.yaml do usuário ou em um plugin personalizado para descrições OpenAPI, AsyncAPI ou Arazzo. Use quando o usuário desejar uma convenção forçada, solicitar uma regra configurável ou uma regra de plugin, depurar uma regra que nunca reporta ou dá falsos positivos, ou migrar regras de outro linter como Spectral.

O que é redocly-lint-rules?

Esta skill orienta desenvolvedores e agentes de IA na criação de regras de lint para o Redocly CLI, uma ferramenta de linting para especificações OpenAPI, AsyncAPI e Arazzo. Ela promove uma abordagem em camadas (built-in, regra configurável, plugin) para implementar verificações, priorizando soluções mais simples e de baixa manutenção. A skill cobre desde o diagnóstico inicial de verificações até a configuração no `redocly.yaml`, passando pela verificação rigorosa com fixtures de teste. Suporta migração de outros linters como Spectral, garantindo que todas as regras efetivas sejam migradas e testadas corretamente. Plataformas suportadas: Redocly CLI.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Redocly/redocly-cli/tree/HEAD/.claude/skills/redocly-lint-rules

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Implementando uma regra de lint do Redocly CLI

Transforme uma verificação escrita em linguagem natural ("todo schema de array deve declarar items") em uma regra de lint do Redocly que funcione e seja testada.

A escada. O Redocly oferece três maneiras de realizar uma verificação. A primeira é a mais barata de manter.

  1. Regra nativa — uma das regras nativas realiza a verificação.
  2. Regra configurável — uma ou mais entradas rule/* no redocly.yaml realizam a verificação com asserções.
  3. Plugin personalizado — código JavaScript que implementa sua própria regra com o padrão de visitante. Use este degrau por último.

Faça as verificações uma de cada vez. Comece cada verificação no degrau 1. Desça um degrau somente quando o degrau atual não puder realizar a verificação. Use toda a sintaxe da documentação oficial ou do livro de receitas. Confie na documentação, não na sua memória. Use sintaxe moderna, como ESM em vez de CommonJS. Se o projeto tiver seu próprio estilo, siga o projeto.

1. Triagem

Para cada verificação, anote três coisas: o nó que ela examina, o que deve ser verdadeiro e a aparência de uma violação. Faça a triagem de cada verificação individualmente. Verificações diferentes podem cair em degraus diferentes.

Obtenha os nomes dos nós do comando inspect-node-types, não da memória. A árvore de tipos é diferente em cada especificação e cada versão. Os nomes que ele imprime são o subject.type de uma regra configurável e as chaves do visitante de um plugin.

para aprenderexecute
o tipo em um local--pointer='#/paths/~1orders'
cada nó, ou cada nó de um tiposem sinalizador, ou --type=Schema
quais tipos a descrição usa--summary
onde um tipo pode ficar--type=<Type> --parents
os ancestrais de um nó--pointer=<pointer> --parents

As duas últimas linhas dão à regra seu escopo. Quando um tipo fica abaixo de mais de um pai, a regra precisa de uma cláusula where. Sem essa cláusula, a regra também reporta os ramos que você não deseja. O comando mostra a descrição diante de você. Ele não mostra tudo que a especificação permite. Quando o comando não imprime um ramo, esse ramo é não comprovado. Não é ausente. O comando não encurta o passo 3. Ele fornece nomes de tipos. Ele não diz se suas asserções expressam a verificação.

Percorra a escada para cada verificação:

  • Pesquise a lista de regras nativas pelo conceito. Uma barra final em um caminho dá no-path-trailing-slash. Quando a configuração já estende um conjunto de regras que ativa a regra nativa, informe o usuário. Não adicione a regra novamente. Use a regra nativa quando sua descrição corresponder à verificação, mesmo que ela reporte mais do que a regra que você substitui. Uma regra nativa geralmente é uma versão melhor da mesma ideia, portanto suas descobertas extras são a verificação feita corretamente. Quando você escreve o comportamento mais restrito manualmente, troca uma regra mantida por código que você precisa manter. Desça um degrau somente quando a regra nativa verificar algo diferente. Informe qual regra nativa você usou e o que ela adiciona.
  • Certifique-se de que a regra nativa suporta as versões da especificação que o projeto usa para lint. Algumas regras nativas suportam apenas uma versão. no-invalid-media-type-examples suporta OpenAPI 3.x, portanto deixa uma verificação para Swagger 2.0 sem ser aplicada. O Redocly imprime Unused rules found in redocly.yaml quando uma regra ativada não pode ser aplicada a um documento. Corrija essa linha. Não é ruído.
  • Tente asserções e uma cláusula where. Veja "Regras configuráveis" abaixo. Uma verificação pode precisar de várias entradas rule/*, uma para cada caso ou local. Isso ainda é melhor que um plugin.
  • Escreva um plugin somente quando as regras configuráveis não puderem expressar a verificação, mesmo em combinação. Primeiro trabalhe com os três dialetos abaixo. Um plugin pode fazer qualquer verificação, mas é código. Adiciona complexidade, manutenção e seus próprios bugs.
  • Diga ao usuário para abrir uma issue somente quando nenhuma regra configurável puder preencher uma lacuna real.

A triagem está completa quando cada verificação tem um degrau. Forneça uma solução para cada verificação: o degrau mais alto que funciona. Informe apenas o que o usuário deve resolver: uma incompatibilidade entre uma descrição e uma implementação, uma regra não comprovada ou uma decisão que poderia ser diferente. Informe no chat. Nunca escreva um arquivo de resumo. A configuração registra o resto, portanto não narre o degrau de cada verificação.

Migrar de outro linter

  • Migre o conjunto de regras efetivo, não apenas as regras escritas no arquivo. Um extends, ou uma base herdada, ativa mais regras com suas severidades padrão. Cada uma dessas regras também é uma verificação. Resolva essa base e conte as regras antes de começar. Assim você saberá o tamanho real do trabalho.
  • Considere cada verificação de dois lugares: a descrição da regra e sua implementação. Nunca considere a partir de sua memória do nome da regra. Quando os dois discordarem, use a descrição. A descrição diz o que o autor queria, e a implementação pode conter um bug. Informe o usuário sobre a diferença.
  • Pule uma regra com severidade off. Não a migre, nem mesmo como uma entrada desabilitada. Quando as regras ativadas estiverem prontas, liste cada regra pulada em uma linha. Então o usuário pode decidir.
  • Para Spectral, leia primeiro a tabela de mapeamento no guia de migração. Muitas regras principais do Spectral mapeiam para uma regra nativa. Uma célula vazia significa que você desce na escada.
  • Quando você copia uma função da origem, também copia seus bugs para um hospedeiro mais restrito. O Redocly interrompe o documento inteiro quando uma regra lança uma exceção, mas o linter de origem pode interromper apenas uma regra. Uma correspondência sem proteção, como v.match(/…/)[0] em um valor que pode falhar na correspondência, remove todas as descobertas nesse arquivo. Adicione uma proteção ao código que você copia. Em seguida, faça lint de um documento que contenha a forma problemática.

2. Configurar

Escreva no redocly.yaml do usuário. Use exatamente esse nome, sem ponto inicial. Quando o arquivo existir, leia-o primeiro e mantenha o extends e as regras existentes. Defina cada regra como error, a menos que o usuário solicite outro nível. Ao migrar, mantenha a severidade da origem e escolha o nível do Redocly mais próximo. Existem apenas error, que falha a execução, warn e off, portanto info e hint tornam-se warn. Referencie todos os arquivos que você criar a partir da configuração e os teste na execução de lint. Não deixe arquivos mortos.

Uma configuração sem extends não herda nada — nem recommended, nem mesmo struct. A base padrão se aplica somente quando nenhum arquivo de configuração existe. Portanto, um redocly.yaml executa exatamente as regras que nomeia. O degrau de regra nativa existe somente quando extends nomeia um conjunto de regras, ou quando a configuração lista cada regra nativa por conta própria. Com nenhum dos dois, um documento que viola a especificação três vezes lê limpo.

Conecte todas as regras. Um plugin em plugins: não faz nada quando suas regras estão ausentes de rules:, e o Redocly não dá aviso. Depois de escrever a configuração, liste os ids de regra que devem estar ativos. Confirme que cada id está em rules: ou em um bloco com escopo de especificação.

Quando todas as regras estiverem prontas, compare sua lista com os conjuntos de regras predefinidos: minimal, recommended, recommended-strict e spec.

  • Quando suas regras estiverem próximas de um conjunto de regras, estenda-o. Mantenha apenas as diferenças como substituições: outras severidades, regras para desativar e verificações que o conjunto de regras não contém.
  • Um conjunto de regras também ativa regras que o usuário não solicitou. Leia o que ele adiciona e substitua cada regra que contradiz ou repete uma regra que você escreveu. Um conjunto de regras que ativa uma verificação que a origem define como off é um erro a ser corrigido, não um benefício.
  • Quando as substituições são tantas quanto as regras, ou mais, mantenha a lista simples e sem extends.

3. Verificar

Uma regra é não comprovada até que uma execução ao vivo a faça reportar. Comprove cada regra com npx redocly lint <arquivo-api> em duas direções:

  • um fixture violador: a regra reporta, no local correto;
  • um fixture conforme: a execução permanece silenciosa.

Um falso positivo é a falha comum, portanto uma regra que passou apenas no fixture violador é não comprovada. Escreva o fixture violador para separar esta regra da regra ao lado: a regra que você substitui, ou sua vizinha na configuração. Dois erros precisam dessa separação:

  • Coloque um valor falso no fixture violador. Uma regra que deve rejeitar uma description vazia deve reportar em description: ''. Quando você coloca '' apenas no fixture conforme, você não comprova nada, porque uma asserção de presença o aceita. A regra então parece correta e a lacuna permanece.
  • Uma regra nativa que substitui duas verificações de origem precisa de um fixture que viole apenas a segunda verificação. no-unsafe-markdown reporta <script>, mas nunca eval(. Um fixture que contém ambas dá à regra nativa crédito por uma verificação que ela não faz.

Para manter a saída limpa, passe uma pequena configuração temporária com --config. Exclua essa configuração e os fixtures quando terminar. Todo trecho que você entregar deve estar correto como escrito: ele analisa como YAML e se comporta como você afirma.

Você termina quando cada verificação nomeia um fixture violador que reportou e um fixture conforme que permaneceu silencioso. Nomeie esses fixtures em seu relatório. Um número não é evidência. Uma execução limpa nos próprios arquivos do projeto também não é evidência: mostra apenas que nada reporta lá, e isso é verdade para uma regra correta e para uma regra morta. Quando você verifica as regras arriscadas e assume as outras, a entrega é não comprovada. Diga isso, e nomeie cada regra não comprovada. Um fixture conforme comprova apenas as formas dentro dele. Uma versão da especificação que você não tentou, ou um local que --parents mostrou e você não testou, permanece não testado. Informe isso. Quando uma execução ao vivo é impossível, marque a regra como não verificada. Não afirme que ela funciona.

Você também termina quando cada verificação solicitada está na configuração ou na lista pulada, e o projeto contém apenas os arquivos de configuração e plugin que você criou.

Regras configuráveis

Documentação: regras configuráveis. Exemplos práticos: cookbook/configurable-rules.

Uma regra é uma entrada sob rules: com um nome que começa com rule/:

rules:
  rule/minha-verificacao:
    subject:
      type: Schema # um tipo de nó da lista de tipos da documentação
      property: title # opcional
    assertions:
      defined: true
    where: [] # cláusula opcional
    message: Texto de problema personalizado.
    severity: error

rules simples se aplica a todo documento que o projeto usa para lint. Mantenha regras lá por padrão. Mova uma regra para uma chave com escopo de especificação somente quando a verificação precisar de algo que existe em uma versão da especificação, como nullable no OAS 3.0, ou quando o usuário solicitar o escopo. As chaves são oas2Rules, oas3_0Rules, oas3_1Rules, oas3_2Rules, async2Rules, async3Rules, arazzo1Rules e overlay1Rules. Uma regra dentro de uma chave não é executada para as outras versões, portanto uma verificação que o projeto precisa em duas versões precisa de uma entrada que ambas as versões alcancem. Escopar uma regra de plugin dentro do próprio plugin, em seu mapa rules.

O que uma asserção testa

Dois fatos causam a maioria dos falsos positivos e a maioria das ausências silenciosas.

Um valor ausente passa. Quase toda asserção é válida quando o valor que ela verifica está ausente. const: array em property: type corresponde a um schema tipado array, e também a todo schema sem type. Portanto, quando uma entrada where testa um valor de propriedade, adicione defined: true ao lado da asserção de valor se a propriedade puder estar ausente.

Presença não é valor. defined, required, requireAny, disallowed e mutuallyExclusive testam se uma chave existe. Eles não dizem nada sobre o conteúdo, portanto defined: true em description passa para description: ''. Afirme o valor em vez disso: nonEmpty: true rejeita uma string vazia, e const, enum e pattern testam o conteúdo. Este é o defeito mais frequente na prática. Parece correto em revisão, e apenas um fixture violador que contenha '' o mostra.

Dois resultados desses fatos:

  • const: <valor> é também a maneira de dizer "este campo deve ser falso", porque a asserção é válida para esse valor e para um campo ausente.
  • requireAny conta apenas chaves. "Pelo menos um destes campos, e não vazio" não tem forma de asserção. Essa verificação é um caso real de plugin.

O que a asserção inspeciona

  • property: nome — o valor desse campo no nó sujeito.
  • property: [nome, url, email] — cada campo listado individualmente. Todo campo que falha se torna seu próprio problema com seu próprio ponteiro JSON, como #/info/contact/name.
  • property ausente — as chaves do nó sujeito. Por exemplo, subject: { type: Paths } com assertions: { notPattern: /.+\/$/ } é executado nas próprias strings de caminho.

Aponte o sujeito, depois use a cláusula

Três dialetos apontam uma regra. Resolva-os nesta ordem.

1. Sujeito: o nó que possui o que você afirma.

  • Uma verificação sobre as chaves que um mapa contém tem como alvo o próprio tipo do mapa. Os mapas de respostas, cabeçalhos, propriedades de schema e caminhos são cada um seu próprio tipo. Portanto "este mapa deve conter essa chave" é um required em um sujeito. Não procure o pai.
  • Uma verificação sobre um valor tem como alvo o nó que contém o campo, mais property.
  • type: any com um property verifica essa propriedade onde quer que ela apareça.
  • Confirme o nome do tipo com inspect-node-types, em um documento da versão da especificação que a regra tem como alvo. Um tipo nomeado pode existir em uma versão e não em outra. HeadersMap é um tipo no OpenAPI 3. No OpenAPI 2 o mapa de headers da resposta é anônimo e não tem nome de tipo, portanto um sujeito HeadersMap sob oas2Rules nunca reporta. O Redocly avisa sobre um id de regra não resolvido, mas não sobre um subject.type não resolvido, portanto um tipo errado dá a você uma regra que nunca reporta.

2. Escopo: onde esse tipo aparece.

  • Use where para limitar o sujeito a locais específicos. "Um campo type deve ser string quando o pai é um parâmetro de consulta" precisa de subject: { type: Schema, property: type } e where: [ { subject: { type: Parameter, property: in }, assertions: { const: query } } ]. Isto é: "para cada local onde um Schema aparece sob um Parameter com in: query, a asserção é executada no campo type".
  • Sem where, a asserção é executada em toda aparição do sujeito.

3. Assertividade (opcional): o que fazer com o resultado.

  • Omitir assertions transforma a regra em uma regra de rastreamento: ela gera uma descoberta para cada aparição do sujeito, sem verificar nada. Útil para auditoria.
  • assertions vazio ({}) não faz nada. Verifique a digitação.

Plugins

Documentação: plugins. Exemplos: cookbook/plugins.

Um plugin é um arquivo JavaScript que exporta um objeto id e um objeto rules.

import { oas2, oas3_0 } from '@redocly/openapi-core';

const myPlugin = {
  id: 'meu-plugin',
  rules: {
    oas2: {
      'caminho-sem-barra': () => ({
        PathItem: {
          enter(pathItem, { report, location }) {
            if (pathItem.path.endsWith('/')) {
              report({
                message: 'Os caminhos não devem ter barra final.',
                location,
              });
            }
          },
        },
      }),
    },
    oas3_0: {
      'caminho-sem-barra': () => ({
        PathItem: {
          enter(pathItem, { report, location }) {
            if (pathItem.path.endsWith('/')) {
              report({
                message: 'Os caminhos não devem ter barra final.',
                location,
              });
            }
          },
        },
      }),
    },
  },
};

export default myPlugin;

Use visitantes enter ou leave. Cada um recebe o objeto e um contexto com report, location e type.

Para configurar um plugin:

plugins:
  - './meu-plugin.js'
rules:
  meu-plugin/caminho-sem-barra: error

Não se esqueça de mapear a regra. O plugin por si só não adiciona nada a rules:.

Casos que pedem um plugin

  • Uma verificação que precisa da forma de um valor que não é um campo, como o padrão de uma chave de mapa. Uma regra de padrão em subject: { type: Paths } verifica as strings de caminho, não os nomes de parâmetro. Para verificar nomes de parâmetro, você precisa do contexto: o pai Parameter e seu campo name.
  • Uma verificação que deve rejeitar uma string vazia entre várias opções, como requireAny com conteúdo não vazio.
  • Uma verificação que usa matches ou outras APIs de nó de Redocly que as asserções não expõem.
  • Uma verificação que precisa de uma condição dependente de estado, como "se o pai tiver format: url, então isso deve ser uma URL".

Use as mesmas regras para escrever um plugin que você usa para escrever uma regra configurável:

  • Afirme o valor, não a presença.
  • Teste '' no fixture violador.
  • Teste ambas as direções (conforme e violador).
  • Confirme os nomes dos tipos com inspect-node-types.
  • Coloque a regra no bloco de versão da especificação correto.

Regras de plugin não se beneficiam da proteção de tipo. Um sujeito de plugin recebe cada nó desse tipo, mesmo em versões da especificação onde esse tipo não está definido. Sua primeira linha deve verificar se o objeto tem a forma que você espera:

PathItem: {
  enter(pathItem, { report, location }) {
    if (typeof pathItem !== 'object' || pathItem === null) return;
    // ...
  },
},

Listas e exemplos práticos

Toda regra inicia com severidade error a menos que a origem ou o usuário peça outro nível.

Use oas2Rules, oas3_0Rules, etc., para regras específicas da versão. Coloque regras que funcionam em todas as versões em rules:.

Pre-fixo de plugin: a regra deve ser id-do-plugin/nome-da-regra.

Teste sempre: uma regra configurável é mais rápida de escrever e mais barata de manter do que um plugin.

Lembrete: --parents mostra onde um tipo fica. Para uma regra que deve ser executada apenas em schemas que estão em parâmetros de consulta, use:

subject:
  type: Schema
  property: type
where:
  - subject:
      type: Parameter
      property: in
    assertions:
      const: query

Lembrete: um valor ausente passa em quase todas as asserções. Use defined: true para verificar presença.

Migração passo a passo

  1. Liste todas as regras efetivas (incluindo de extends herdados).
  2. Para cada regra:
    • Comece no degrau 1 (regra nativa). Encontre uma correspondência? Se sim, use-a.
    • Degrau 2 (regra configurável). Ela pode expressar a verificação? Se sim, escreva-a.
    • Degrau 3 (plugin). Escreva um plugin.
  3. Para cada regra migrada, escreva um fixture violador e um fixture conforme.
  4. Execute o lint em ambos. Ajuste a regra até que o violador reporte e o conforme fique em silêncio.
  5. Quando todas as regras estiverem prontas, verifique se você tem arquivos mortos (config ou plugin não referenciados).
  6. Informe: quais regras você usou, quais regras você pulou (com severidade off), quais regras permanecem não comprovadas.
  7. Não escreva um resumo em arquivo. Use o chat.

Fim.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Habilidades Relacionadas