@polariens/kitsune-lint 1.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +268 -0
- package/bin/copy-prettierignore.mjs +30 -0
- package/eslint/configs/base.d.mts +10 -0
- package/eslint/configs/base.mjs +28 -0
- package/eslint/configs/clean-code.d.mts +20 -0
- package/eslint/configs/clean-code.mjs +102 -0
- package/eslint/configs/pinia.d.mts +10 -0
- package/eslint/configs/pinia.mjs +36 -0
- package/eslint/configs/security.d.mts +12 -0
- package/eslint/configs/security.mjs +46 -0
- package/eslint/configs/tests.d.mts +10 -0
- package/eslint/configs/tests.mjs +40 -0
- package/eslint/configs/typescript.d.mts +14 -0
- package/eslint/configs/typescript.mjs +216 -0
- package/eslint/configs/vitest.d.mts +20 -0
- package/eslint/configs/vitest.mjs +76 -0
- package/eslint/configs/vue.d.mts +25 -0
- package/eslint/configs/vue.mjs +73 -0
- package/eslint/index.d.mts +51 -0
- package/eslint/index.mjs +85 -0
- package/eslint/rules/no-null-in-types.mjs +117 -0
- package/eslint/utils.d.mts +7 -0
- package/eslint/utils.mjs +33 -0
- package/package.json +119 -0
- package/prettier/.prettierignore +14 -0
- package/prettier/index.d.mts +50 -0
- package/prettier/index.mjs +77 -0
package/README.md
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# @polariens/kitsune-lint 🦊
|
|
2
|
+
|
|
3
|
+
Opinionated ESLint & Prettier configs for high-quality Vue, TypeScript & Vitest projects.
|
|
4
|
+
|
|
5
|
+
> **Kitsune** (狐) é a raposa mística do folclore japonês — astuta, adaptável e capaz de se transformar conforme o contexto. Assim como a kitsune, este pacote se molda ao seu projeto: você escolhe os módulos e as opções, e ele compõe as regras certas para cada cenário. Código limpo com a precisão de uma raposa.
|
|
6
|
+
|
|
7
|
+
## O que é
|
|
8
|
+
|
|
9
|
+
Um pacote instalável que padroniza regras de linting e formatação entre múltiplos projetos. As configurações são **modulares** — cada conjunto de regras (TypeScript, Vue, Pinia, segurança, etc.) é independente e configurável via opções.
|
|
10
|
+
|
|
11
|
+
## Como usar
|
|
12
|
+
|
|
13
|
+
### Instalação
|
|
14
|
+
|
|
15
|
+
Primeiro, instale o pacote principal em seu projeto como dependência de desenvolvimento:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install --save-dev @polariens/kitsune-lint
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
As _peer dependencies_ correspondentes devem ser instaladas no projeto consumidor. Veja abaixo as opções de instalação dependendo do seu cenário.
|
|
22
|
+
|
|
23
|
+
#### Instalação Completa (Todos os módulos)
|
|
24
|
+
|
|
25
|
+
Se você planeja utilizar a configuração máxima com todos os módulos ativados no seu ecossistema (`security`, `cleanCode`, `pinia`, `vitest`, `typescript` e `vue`), execute este comando para instalar todas as ferramentas de uma vez:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install --save-dev eslint @eslint/js typescript-eslint globals eslint-plugin-security eslint-plugin-vue vue-eslint-parser eslint-plugin-pinia @vitest/eslint-plugin
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
#### Instalação Específica (Por módulo)
|
|
32
|
+
|
|
33
|
+
Se preferir incluir apenas as peças que for usar, você deve sempre iniciar instalando as **Dependências Base**, que são essenciais para os módulos principais (que já vêm ativos por padrão, como `base`, `cleanCode` e `typescript`):
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install --save-dev eslint @eslint/js typescript-eslint globals
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Em seguida, instale os complementos exclusivos para cada módulo extra que for habilitar em seu `createKitsuneConfig()`:
|
|
40
|
+
|
|
41
|
+
**Módulo `security` (Habilitado por padrão)**
|
|
42
|
+
Necessário para a suíte de regras focadas em prevenir vulnerabilidades de segurança e injeções de código:
|
|
43
|
+
```bash
|
|
44
|
+
npm install --save-dev eslint-plugin-security
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Módulo `vue`**
|
|
48
|
+
Pacotes necessários para realizar o lint na sintaxe `<template>`, `script setup` e validar as regras do Vue 3:
|
|
49
|
+
```bash
|
|
50
|
+
npm install --save-dev eslint-plugin-vue vue-eslint-parser
|
|
51
|
+
```
|
|
52
|
+
> *(Exemplo prático de setup em um projeto para uso apenas dos essenciais e módulo Vue:)*
|
|
53
|
+
> ```bash
|
|
54
|
+
> npm install --save-dev eslint @eslint/js typescript-eslint globals eslint-plugin-vue vue-eslint-parser
|
|
55
|
+
> ```
|
|
56
|
+
|
|
57
|
+
**Módulo `pinia`**
|
|
58
|
+
O verificador de store e regras recomendadas para o gerenciamento de estado via Pinia:
|
|
59
|
+
```bash
|
|
60
|
+
npm install --save-dev eslint-plugin-pinia
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Módulo `vitest`**
|
|
64
|
+
Para linting focado em arquivos de teste especificados nativamente pelas boas práticas do Vitest:
|
|
65
|
+
```bash
|
|
66
|
+
npm install --save-dev @vitest/eslint-plugin
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### ESLint — Factory function (recomendado)
|
|
70
|
+
|
|
71
|
+
A forma mais simples. O `createKitsuneConfig` compõe os módulos selecionados:
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
// eslint.config.js
|
|
75
|
+
import { createKitsuneConfig } from '@polariens/kitsune-lint/eslint';
|
|
76
|
+
|
|
77
|
+
export default await createKitsuneConfig({
|
|
78
|
+
vue: true,
|
|
79
|
+
pinia: true,
|
|
80
|
+
tests: true,
|
|
81
|
+
vitest: true,
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Por padrão, `base`, `typescript`, `security` e `cleanCode` já vêm habilitados. Passe `false` para desativar:
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
export default await createKitsuneConfig({
|
|
89
|
+
security: false,
|
|
90
|
+
vue: true,
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### ESLint — Customizando módulos
|
|
95
|
+
|
|
96
|
+
Cada módulo aceita um objeto de opções no lugar de `true`:
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
export default await createKitsuneConfig({
|
|
100
|
+
base: { environment: 'node' },
|
|
101
|
+
cleanCode: { maxDepth: 3, maxParams: 3, complexity: 10, maxLines: 300 },
|
|
102
|
+
vue: { apiStyle: 'composition' },
|
|
103
|
+
pinia: true,
|
|
104
|
+
vitest: {
|
|
105
|
+
titlePattern: '^should .+',
|
|
106
|
+
titleMessage: 'Test title must start with "should"',
|
|
107
|
+
fn: 'it',
|
|
108
|
+
maxNestedDescribe: 2,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### ESLint — Imports granulares
|
|
114
|
+
|
|
115
|
+
Para controle total, importe cada módulo diretamente:
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
// eslint.config.js
|
|
119
|
+
import { base } from '@polariens/kitsune-lint/eslint/base';
|
|
120
|
+
import { typescript } from '@polariens/kitsune-lint/eslint/typescript';
|
|
121
|
+
import { vue } from '@polariens/kitsune-lint/eslint/vue';
|
|
122
|
+
import { pinia } from '@polariens/kitsune-lint/eslint/pinia';
|
|
123
|
+
|
|
124
|
+
export default [
|
|
125
|
+
...base(),
|
|
126
|
+
...typescript(),
|
|
127
|
+
...vue(),
|
|
128
|
+
...(await pinia()),
|
|
129
|
+
];
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### ESLint — Estendendo com configs extras
|
|
133
|
+
|
|
134
|
+
Use `extend` para adicionar configs de plugins externos (ex: `eslint-config-prettier`):
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
import { createKitsuneConfig } from '@polariens/kitsune-lint/eslint';
|
|
138
|
+
import eslintConfigPrettier from 'eslint-config-prettier';
|
|
139
|
+
import pluginVue from 'eslint-plugin-vue';
|
|
140
|
+
|
|
141
|
+
export default await createKitsuneConfig({
|
|
142
|
+
vue: true,
|
|
143
|
+
pinia: true,
|
|
144
|
+
tests: true,
|
|
145
|
+
vitest: true,
|
|
146
|
+
extend: [
|
|
147
|
+
...pluginVue.configs['flat/recommended'],
|
|
148
|
+
eslintConfigPrettier,
|
|
149
|
+
],
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Prettier
|
|
154
|
+
|
|
155
|
+
Uso direto da config padrão:
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
// prettier.config.mjs
|
|
159
|
+
import { prettierConfig } from '@polariens/kitsune-lint/prettier';
|
|
160
|
+
export default prettierConfig;
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Com overrides:
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
import { createPrettierConfig } from '@polariens/kitsune-lint/prettier';
|
|
167
|
+
export default createPrettierConfig({ printWidth: 120 });
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Módulos
|
|
171
|
+
|
|
172
|
+
| Módulo | Default | Descrição |
|
|
173
|
+
| ------------ | ------- | ------------------------------------------------------------ |
|
|
174
|
+
| `base` | ✅ on | Globals do ambiente (browser/node) |
|
|
175
|
+
| `typescript` | ✅ on | Regras TS recomendadas + naming conventions |
|
|
176
|
+
| `security` | ✅ on | Prevenção de eval, XSS, injection + eslint-plugin-security |
|
|
177
|
+
| `cleanCode` | ✅ on | SRP, legibilidade, imutabilidade, organização |
|
|
178
|
+
| `vue` | ❌ off | Vue 3 + script setup + type-based props/emits |
|
|
179
|
+
| `pinia` | ❌ off | Stores Pinia — naming, organização, boas práticas |
|
|
180
|
+
| `tests` | ❌ off | Relaxamentos para arquivos de teste (desliga regras rígidas) |
|
|
181
|
+
| `vitest` | ❌ off | Regras do plugin Vitest (naming, hooks, describe) |
|
|
182
|
+
|
|
183
|
+
## Opções dos módulos
|
|
184
|
+
|
|
185
|
+
### base
|
|
186
|
+
|
|
187
|
+
| Opção | Padrão | Descrição |
|
|
188
|
+
| ------------- | ----------- | ------------------------------------------------ |
|
|
189
|
+
| `environment` | `'browser'` | Globals: `browser`, `node`, `shared-node-browser`|
|
|
190
|
+
|
|
191
|
+
### typescript
|
|
192
|
+
|
|
193
|
+
| Opção | Padrão | Descrição |
|
|
194
|
+
| -------- | -------------- | ------------------ |
|
|
195
|
+
| `ignores`| configs + dist | Patterns a ignorar |
|
|
196
|
+
| `rules` | `{}` | Regras extras |
|
|
197
|
+
|
|
198
|
+
### security
|
|
199
|
+
|
|
200
|
+
| Opção | Padrão | Descrição |
|
|
201
|
+
| --------------- | ------ | ----------------------------- |
|
|
202
|
+
| `pluginEnabled` | `true` | Usar `eslint-plugin-security` |
|
|
203
|
+
| `rules` | `{}` | Regras extras |
|
|
204
|
+
|
|
205
|
+
### cleanCode
|
|
206
|
+
|
|
207
|
+
| Opção | Padrão | Descrição |
|
|
208
|
+
| --------------------- | ------ | ---------------------------------- |
|
|
209
|
+
| `maxDepth` | `4` | Profundidade máxima de aninhamento |
|
|
210
|
+
| `maxParams` | `4` | Parâmetros máximos por função |
|
|
211
|
+
| `complexity` | `14` | Complexidade ciclomática máxima |
|
|
212
|
+
| `maxLines` | `400` | Linhas máximas por arquivo |
|
|
213
|
+
| `maxLinesPerFunction` | `80` | Linhas máximas por função |
|
|
214
|
+
| `rules` | `{}` | Regras extras |
|
|
215
|
+
|
|
216
|
+
### vue
|
|
217
|
+
|
|
218
|
+
| Opção | Padrão | Descrição |
|
|
219
|
+
| ---------- | ---------------- | ------------------- |
|
|
220
|
+
| `apiStyle` | `'script-setup'` | Estilo de API Vue |
|
|
221
|
+
| `rules` | `{}` | Regras extras |
|
|
222
|
+
|
|
223
|
+
### pinia
|
|
224
|
+
|
|
225
|
+
| Opção | Padrão | Descrição |
|
|
226
|
+
| ------- | ------------------------- | ------------------------------- |
|
|
227
|
+
| `files` | `['src/state/**/*.ts']` | Override dos file patterns |
|
|
228
|
+
| `rules` | `{}` | Regras extras |
|
|
229
|
+
|
|
230
|
+
### tests
|
|
231
|
+
|
|
232
|
+
Relaxamentos para arquivos de teste — desliga regras rígidas de produção como `complexity`, `max-lines`, `max-lines-per-function`, `no-explicit-any`, `naming-convention`, etc.
|
|
233
|
+
|
|
234
|
+
| Opção | Padrão | Descrição |
|
|
235
|
+
| ------- | ------------------------------- | -------------------------- |
|
|
236
|
+
| `files` | `['tests/**/*.{js,mjs,cjs,ts}']`| Override dos file patterns |
|
|
237
|
+
| `rules` | `{}` | Regras extras |
|
|
238
|
+
|
|
239
|
+
### vitest
|
|
240
|
+
|
|
241
|
+
| Opção | Padrão | Descrição |
|
|
242
|
+
| ------------------ | -------------- | -------------------------------- |
|
|
243
|
+
| `fn` | `'test'` | `test()` ou `it()` |
|
|
244
|
+
| `titlePattern` | Gherkin PT-BR | Regex do título dos testes |
|
|
245
|
+
| `titleMessage` | mensagem PT-BR | Mensagem de erro |
|
|
246
|
+
| `maxNestedDescribe`| `3` | Máximo de describe aninhados |
|
|
247
|
+
| `rules` | `{}` | Regras extras |
|
|
248
|
+
|
|
249
|
+
## Estrutura
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
@polariens/kitsune-lint/
|
|
253
|
+
├── package.json
|
|
254
|
+
├── eslint/
|
|
255
|
+
│ ├── index.mjs # Factory createKitsuneConfig + re-exports
|
|
256
|
+
│ ├── utils.mjs # Patterns de arquivos compartilhados
|
|
257
|
+
│ └── configs/
|
|
258
|
+
│ ├── base.mjs # Globals do ambiente (browser/node)
|
|
259
|
+
│ ├── typescript.mjs # Regras TypeScript + naming conventions
|
|
260
|
+
│ ├── security.mjs # Prevenção de eval, XSS, injection
|
|
261
|
+
│ ├── clean-code.mjs # SRP, legibilidade, imutabilidade
|
|
262
|
+
│ ├── vue.mjs # Vue 3 + script setup
|
|
263
|
+
│ ├── pinia.mjs # Stores Pinia
|
|
264
|
+
│ ├── tests.mjs # Relaxamentos para testes
|
|
265
|
+
│ └── vitest.mjs # Regras plugin Vitest
|
|
266
|
+
└── prettier/
|
|
267
|
+
└── index.mjs # Config Prettier exportável
|
|
268
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { cpSync, existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const source = resolve(__dirname, '..', 'prettier', '.prettierignore');
|
|
9
|
+
const dest = resolve(process.cwd(), '.prettierignore');
|
|
10
|
+
|
|
11
|
+
const force = process.argv.includes('--force');
|
|
12
|
+
|
|
13
|
+
if (existsSync(dest) && !force) {
|
|
14
|
+
const existing = readFileSync(dest, 'utf-8');
|
|
15
|
+
const template = readFileSync(source, 'utf-8');
|
|
16
|
+
|
|
17
|
+
if (existing === template) {
|
|
18
|
+
console.info('[lint-config] .prettierignore já está atualizado.');
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.error(
|
|
23
|
+
'[lint-config] .prettierignore já existe e difere do template.\n' +
|
|
24
|
+
' Use --force para sobrescrever.'
|
|
25
|
+
);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
cpSync(source, dest);
|
|
30
|
+
console.info(`[lint-config] .prettierignore copiado para ${dest}`);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Linter } from 'eslint';
|
|
2
|
+
|
|
3
|
+
export interface BaseOptions {
|
|
4
|
+
/** File patterns override */
|
|
5
|
+
files?: string[];
|
|
6
|
+
/** Ambiente de globals a aplicar @default 'browser' */
|
|
7
|
+
environment?: 'browser' | 'node' | 'shared-node-browser' | 'worker' | 'serviceworker';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export declare function base(options?: BaseOptions): Linter.Config[];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import globals from 'globals';
|
|
2
|
+
|
|
3
|
+
import { resolveFiles } from '../utils.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} BaseOptions
|
|
7
|
+
* @property {string[]} [files] - File patterns override
|
|
8
|
+
* @property {'browser' | 'node' | 'shared-node-browser'} [environment='browser'] - Ambiente global
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Configuração base com globals do ambiente.
|
|
13
|
+
* @param {BaseOptions} [options={}]
|
|
14
|
+
* @returns {import('eslint').Linter.Config[]}
|
|
15
|
+
*/
|
|
16
|
+
export function base(options = {}) {
|
|
17
|
+
const { files, environment = 'browser' } = options;
|
|
18
|
+
|
|
19
|
+
return [
|
|
20
|
+
{
|
|
21
|
+
files: resolveFiles('all', files),
|
|
22
|
+
name: '@kitsune/base/globals',
|
|
23
|
+
languageOptions: {
|
|
24
|
+
globals: globals[environment],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Linter } from 'eslint';
|
|
2
|
+
|
|
3
|
+
export interface CleanCodeOptions {
|
|
4
|
+
/** File patterns override */
|
|
5
|
+
files?: string[];
|
|
6
|
+
/** Profundidade máxima de aninhamento @default 4 */
|
|
7
|
+
maxDepth?: number;
|
|
8
|
+
/** Número máximo de parâmetros por função @default 4 */
|
|
9
|
+
maxParams?: number;
|
|
10
|
+
/** Complexidade ciclomática máxima @default 14 */
|
|
11
|
+
complexity?: number;
|
|
12
|
+
/** Linhas máximas por arquivo @default 400 */
|
|
13
|
+
maxLines?: number;
|
|
14
|
+
/** Linhas máximas por função @default 80 */
|
|
15
|
+
maxLinesPerFunction?: number;
|
|
16
|
+
/** Regras adicionais ou overrides */
|
|
17
|
+
rules?: Partial<Linter.RulesRecord>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export declare function cleanCode(options?: CleanCodeOptions): Linter.Config[];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { resolveFiles } from '../utils.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} CleanCodeOptions
|
|
5
|
+
* @property {string[]} [files] - File patterns override
|
|
6
|
+
* @property {number} [maxDepth=4] - Profundidade máxima de aninhamento
|
|
7
|
+
* @property {number} [maxParams=4] - Número máximo de parâmetros
|
|
8
|
+
* @property {number} [complexity=14] - Complexidade ciclomática máxima
|
|
9
|
+
* @property {number} [maxLines=400] - Linhas máximas por arquivo
|
|
10
|
+
* @property {number} [maxLinesPerFunction=80] - Linhas máximas por função
|
|
11
|
+
* @property {Record<string, unknown>} [rules] - Regras adicionais ou overrides
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Regras de clean code para legibilidade e manutenibilidade.
|
|
16
|
+
* @param {CleanCodeOptions} [options={}]
|
|
17
|
+
* @returns {import('eslint').Linter.Config[]}
|
|
18
|
+
*/
|
|
19
|
+
export function cleanCode(options = {}) {
|
|
20
|
+
const {
|
|
21
|
+
files,
|
|
22
|
+
maxDepth = 4,
|
|
23
|
+
maxParams = 4,
|
|
24
|
+
complexity: maxComplexity = 14,
|
|
25
|
+
maxLines = 400,
|
|
26
|
+
maxLinesPerFunction = 80,
|
|
27
|
+
rules: extraRules = {},
|
|
28
|
+
} = options;
|
|
29
|
+
|
|
30
|
+
const resolvedFiles = resolveFiles('all', files);
|
|
31
|
+
|
|
32
|
+
return [
|
|
33
|
+
{
|
|
34
|
+
files: resolvedFiles,
|
|
35
|
+
name: '@kitsune/clean-code/rules',
|
|
36
|
+
rules: {
|
|
37
|
+
// --- Single Responsibility (SRP) ---
|
|
38
|
+
complexity: ['warn', maxComplexity],
|
|
39
|
+
'max-lines': ['warn', { max: maxLines, skipBlankLines: true, skipComments: true }],
|
|
40
|
+
'max-lines-per-function': ['warn', { max: maxLinesPerFunction, skipBlankLines: true, skipComments: true }],
|
|
41
|
+
'max-depth': ['error', maxDepth],
|
|
42
|
+
'max-params': ['error', maxParams],
|
|
43
|
+
|
|
44
|
+
// --- Readability & Expressiveness ---
|
|
45
|
+
'no-negated-condition': 'error',
|
|
46
|
+
'no-nested-ternary': 'error',
|
|
47
|
+
'no-unneeded-ternary': 'error',
|
|
48
|
+
'no-else-return': 'error',
|
|
49
|
+
'no-lonely-if': 'error',
|
|
50
|
+
'prefer-template': 'error',
|
|
51
|
+
'object-shorthand': 'error',
|
|
52
|
+
curly: ['error', 'all'],
|
|
53
|
+
'arrow-body-style': ['error', 'as-needed'],
|
|
54
|
+
'no-multi-assign': 'error',
|
|
55
|
+
'no-unreachable-loop': 'error',
|
|
56
|
+
'no-unused-labels': 'error',
|
|
57
|
+
|
|
58
|
+
// --- Immutability & Safety ---
|
|
59
|
+
'no-param-reassign': ['error', { props: false }],
|
|
60
|
+
'no-return-assign': ['error', 'always'],
|
|
61
|
+
'no-sequences': 'error',
|
|
62
|
+
'no-constructor-return': 'error',
|
|
63
|
+
'no-promise-executor-return': 'error',
|
|
64
|
+
// Evita modificação de built-ins
|
|
65
|
+
'no-extend-native': 'error',
|
|
66
|
+
|
|
67
|
+
// --- Code Organization ---
|
|
68
|
+
'default-case-last': 'error',
|
|
69
|
+
'grouped-accessor-pairs': ['error', 'getBeforeSet'],
|
|
70
|
+
'max-statements': ['error', 20],
|
|
71
|
+
|
|
72
|
+
// --- Bug Prevention ---
|
|
73
|
+
'no-template-curly-in-string': 'warn',
|
|
74
|
+
'no-useless-return': 'error',
|
|
75
|
+
|
|
76
|
+
...extraRules,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
/**
|
|
81
|
+
* Regra personalizada para composables e stores pinia que por padrão podem ter mais linhas
|
|
82
|
+
* devido aos exports e variáveis de estado ref/computed.
|
|
83
|
+
* Padrão de arquivos: iniciando com `use`
|
|
84
|
+
*/
|
|
85
|
+
files: resolvedFiles.map((pattern) => {
|
|
86
|
+
const parts = pattern.split('/');
|
|
87
|
+
const lastPart = parts[parts.length - 1];
|
|
88
|
+
if (lastPart.includes('*')) {
|
|
89
|
+
parts[parts.length - 1] = lastPart.replace('*', 'use*');
|
|
90
|
+
}
|
|
91
|
+
return parts.join('/');
|
|
92
|
+
}),
|
|
93
|
+
name: '@kitsune/clean-code/use-rules',
|
|
94
|
+
rules: {
|
|
95
|
+
'max-lines-per-function': [
|
|
96
|
+
'warn',
|
|
97
|
+
{ max: Math.max(200, maxLinesPerFunction), skipBlankLines: true, skipComments: true },
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Linter } from 'eslint';
|
|
2
|
+
|
|
3
|
+
export interface PiniaOptions {
|
|
4
|
+
/** File patterns override @default ['src/state/**\/*.ts'] */
|
|
5
|
+
files?: string[];
|
|
6
|
+
/** Regras adicionais ou overrides */
|
|
7
|
+
rules?: Partial<Linter.RulesRecord>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export declare function pinia(options?: PiniaOptions): Promise<Linter.Config[]>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { resolveFiles } from '../utils.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} PiniaOptions
|
|
5
|
+
* @property {string[]} [files] - File patterns override (default: src/state/**\/*.ts)
|
|
6
|
+
* @property {Record<string, unknown>} [rules] - Regras adicionais ou overrides
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Regras para stores Pinia — organização, naming e boas práticas.
|
|
11
|
+
* @param {PiniaOptions} [options={}]
|
|
12
|
+
* @returns {Promise<import('eslint').Linter.Config[]>}
|
|
13
|
+
*/
|
|
14
|
+
export async function pinia(options = {}) {
|
|
15
|
+
const { files, rules: extraRules = {} } = options;
|
|
16
|
+
|
|
17
|
+
const pluginPinia = await import('eslint-plugin-pinia').then((m) => m.default ?? m);
|
|
18
|
+
|
|
19
|
+
return [
|
|
20
|
+
{
|
|
21
|
+
files: resolveFiles('pinia', files),
|
|
22
|
+
name: '@kitsune/pinia/rules',
|
|
23
|
+
plugins: { pinia: pluginPinia },
|
|
24
|
+
rules: {
|
|
25
|
+
'pinia/never-export-initialized-store': 'error',
|
|
26
|
+
'pinia/no-duplicate-store-ids': 'error',
|
|
27
|
+
'pinia/no-return-global-properties': 'error',
|
|
28
|
+
'pinia/no-store-to-refs-in-store': 'error',
|
|
29
|
+
'pinia/prefer-single-store-per-file': 'warn',
|
|
30
|
+
'pinia/prefer-use-store-naming-convention': 'warn',
|
|
31
|
+
'pinia/require-setup-store-properties-export': 'warn',
|
|
32
|
+
...extraRules,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Linter } from 'eslint';
|
|
2
|
+
|
|
3
|
+
export interface SecurityOptions {
|
|
4
|
+
/** File patterns override */
|
|
5
|
+
files?: string[];
|
|
6
|
+
/** Habilitar eslint-plugin-security @default true */
|
|
7
|
+
pluginEnabled?: boolean;
|
|
8
|
+
/** Regras adicionais ou overrides */
|
|
9
|
+
rules?: Partial<Linter.RulesRecord>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export declare function security(options?: SecurityOptions): Linter.Config[];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import pluginSecurity from 'eslint-plugin-security';
|
|
2
|
+
|
|
3
|
+
import { resolveFiles } from '../utils.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} SecurityOptions
|
|
7
|
+
* @property {string[]} [files] - File patterns override
|
|
8
|
+
* @property {boolean} [pluginEnabled=true] - Habilitar eslint-plugin-security
|
|
9
|
+
* @property {Record<string, unknown>} [rules] - Regras adicionais ou overrides
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Regras de segurança para prevenir vulnerabilidades comuns.
|
|
14
|
+
* @param {SecurityOptions} [options={}]
|
|
15
|
+
* @returns {import('eslint').Linter.Config[]}
|
|
16
|
+
*/
|
|
17
|
+
export function security(options = {}) {
|
|
18
|
+
const { files, pluginEnabled = true, rules: extraRules = {} } = options;
|
|
19
|
+
const resolvedFiles = resolveFiles('all', files);
|
|
20
|
+
|
|
21
|
+
const configs = []
|
|
22
|
+
|
|
23
|
+
if (pluginEnabled) {
|
|
24
|
+
configs.push({
|
|
25
|
+
...pluginSecurity.configs.recommended,
|
|
26
|
+
files: resolvedFiles,
|
|
27
|
+
name: '@kitsune/security/plugin',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
configs.push({
|
|
32
|
+
files: resolvedFiles,
|
|
33
|
+
name: '@kitsune/security/rules',
|
|
34
|
+
rules: {
|
|
35
|
+
'no-alert': 'error',
|
|
36
|
+
'no-eval': 'error',
|
|
37
|
+
'no-debugger': 'error',
|
|
38
|
+
'no-new-func': 'error',
|
|
39
|
+
'no-script-url': 'error',
|
|
40
|
+
'no-implied-eval': 'error',
|
|
41
|
+
...extraRules,
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
return configs;
|
|
46
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Linter } from 'eslint';
|
|
2
|
+
|
|
3
|
+
export interface TestsOptions {
|
|
4
|
+
/** File patterns override */
|
|
5
|
+
files?: string[];
|
|
6
|
+
/** Regras adicionais ou overrides */
|
|
7
|
+
rules?: Partial<Linter.RulesRecord>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export declare function tests(options?: TestsOptions): Linter.Config[];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { resolveFiles } from '../utils.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} TestsOptions
|
|
5
|
+
* @property {string[]} [files] - File patterns override
|
|
6
|
+
* @property {Record<string, unknown>} [rules] - Regras adicionais ou overrides
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Relaxamentos para arquivos de teste — desliga regras rígidas de produção
|
|
11
|
+
* que atrapalham a escrita de testes (complexity, max-lines, naming, etc).
|
|
12
|
+
* @param {TestsOptions} [options={}]
|
|
13
|
+
* @returns {import('eslint').Linter.Config[]}
|
|
14
|
+
*/
|
|
15
|
+
export function tests(options = {}) {
|
|
16
|
+
const { files, rules: extraRules = {} } = options;
|
|
17
|
+
|
|
18
|
+
return [
|
|
19
|
+
{
|
|
20
|
+
files: resolveFiles('tests', files),
|
|
21
|
+
name: '@kitsune/tests/relaxations',
|
|
22
|
+
rules: {
|
|
23
|
+
'max-lines-per-function': 'off',
|
|
24
|
+
'max-lines': 'off',
|
|
25
|
+
complexity: 'off',
|
|
26
|
+
'@typescript-eslint/no-shadow': 'off',
|
|
27
|
+
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
28
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
29
|
+
'@typescript-eslint/naming-convention': 'off',
|
|
30
|
+
'no-promise-executor-return': 'off',
|
|
31
|
+
'no-param-reassign': 'off',
|
|
32
|
+
'no-constructor-return': 'off',
|
|
33
|
+
'no-script-url': 'off',
|
|
34
|
+
'security/detect-object-injection': 'off',
|
|
35
|
+
'security/detect-unsafe-regex': 'off',
|
|
36
|
+
...extraRules,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Linter } from 'eslint';
|
|
2
|
+
|
|
3
|
+
export interface TypescriptOptions {
|
|
4
|
+
/** File patterns override */
|
|
5
|
+
files?: string[];
|
|
6
|
+
/** Patterns a ignorar */
|
|
7
|
+
ignores?: string[];
|
|
8
|
+
/** Patterns a ignorar substituindo qualquer padrão do pacote */
|
|
9
|
+
replaceIgnores?: string[];
|
|
10
|
+
/** Regras adicionais ou overrides */
|
|
11
|
+
rules?: Partial<Linter.RulesRecord>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export declare function typescript(options?: TypescriptOptions): Linter.Config[];
|