Escrevendo plugins JS
INFO
Plugins JS estão em alfa.
A API deve ser idêntica à do ESLint. Comportamento diferente é bug — reporte.
API compatível com ESLint
Documentação ESLint sobre criar plugin e regras customizadas.
Plugin mínimo que reclama quando há mais de 5 declarações de classe:
// plugin.js
const rule = {
create(context) {
let classCount = 0;
return {
ClassDeclaration(node) {
classCount++;
if (classCount === 6) {
context.report({ message: "Too many classes", node });
}
},
};
},
};
const plugin = {
meta: {
name: "best-plugin-ever",
},
rules: {
"max-classes": rule,
},
};
export default plugin;{
"jsPlugins": ["./plugin.js"],
"rules": {
"best-plugin-ever/max-classes": "error"
}
}import { defineConfig } from "oxlint";
export default defineConfig({
jsPlugins: ["./plugin.js"],
rules: {
"best-plugin-ever/max-classes": "error",
},
});API alternativa
O Oxlint oferece variante pensada para desempenho; regras continuam compatíveis com ESLint (explicação abaixo).
Mesma regra com a API alternativa:
import { eslintCompatPlugin } from "@oxlint/plugins";
const rule = {
createOnce(context) {
let classCount;
return {
before() {
classCount = 0;
},
ClassDeclaration(node) {
classCount++;
if (classCount === 6) {
context.report({ message: "Too many classes", node });
}
},
};
},
};
const plugin = eslintCompatPlugin({
meta: {
name: "best-plugin-ever",
},
rules: {
"max-classes": rule,
},
});
export default plugin;Diferenças:
- Envolva o objeto do plugin em
eslintCompatPlugin(...).
- const plugin = {
+ const plugin = eslintCompatPlugin({- Use
createOnceem vez decreate.
- create(context) {
+ createOnce(context) {- No ESLint,
createroda por arquivo; aquicreateOnceroda uma vez. Setup por arquivo vai no ganchobefore.
- let classCount = 0;
+ let classCount;
return {
+ before() {
+ classCount = 0;
+ },
ClassDeclaration(node) {O que eslintCompatPlugin faz?
Acrescenta create delegando para createOnce.
Plugin serve para Oxlint e ESLint.
- No Oxlint, ganho com
createOnce. - No ESLint, igual a usar só
create.
Publicando no npm: @oxlint/plugins como dependência de runtime (não só dev).
Pular travessia do AST
Retornar false em before faz a regra pular aquele arquivo.
// Regra não roda em arquivos que começam com // @skip-me
const rule = {
createOnce(context) {
return {
before() {
if (context.sourceCode.text.startsWith("// @skip-me")) {
return false;
}
},
FunctionDeclaration(node) {
// ...
},
};
},
};Equivalente no ESLint:
const rule = {
create(context) {
if (context.sourceCode.text.startsWith("// @skip-me")) {
return {};
}
return {
FunctionDeclaration(node) {
// ...
},
};
},
};Gancho before
Roda antes de visitar o AST.
Importante: hoje não está garantido que before rode em todo arquivo. No futuro, o lado Rust poderá omitir chamadas quando a regra não “interessa” aos nós presentes — before também seria omitido em alguns casos.
Se não houver FunctionDeclaration, a regra (e inclusive before) pode ser inteiramente pulada.
Para código que precisa rodar sempre por arquivo, use visitante Program:
const rule = {
createOnce(context) {
return {
Program(node) {
// Sempre executa uma vez por arquivo
},
FunctionDeclaration(node) {
/* ... */
},
};
},
};Gancho after
Uma vez por arquivo, depois de percorrer o AST inteiro (Program:exit).
Para liberar recursos caros da regra.
Se before retornar false, after também não roda.
Como before, after pode não rodar em todo arquivo (veja acima).
Por que a API alternativa é mais rápida?
Hoje na prática ainda não é — mas será.
Há espaço grande de otimização Rust↔JS; a API alternativa existe para quando createOnce permitir não percorrer o AST em JS (só nós pré-filtrados) e até pular a chamada para JS quando o arquivo não tiver nós relevantes.
Sem isso, create obriga chamar sempre para saber qual visitante usar.
Detalhes técnicos (regra das 5 classes): com create por arquivo não dá inferir estaticamente quais nós interessam. Com createOnce, dá planejar essas otimizações — sem culpar o ESLint; é limitação aparece com interop Rust.
Próximo passo
Seção Suporte à API: quais APIs ESLint já funcionam no Oxlint.