@maz-ui/eslint-config 5.0.0-beta.0 → 5.0.0-beta.11
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 +156 -90
- package/dist/index.d.mts +148 -18
- package/dist/index.d.ts +148 -18
- package/dist/index.mjs +283 -55
- package/package.json +10 -12
package/README.md
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
# @maz-ui/eslint-config
|
|
2
2
|
|
|
3
|
-
Reusable ESLint configuration for JavaScript/TypeScript projects.
|
|
3
|
+
Reusable ESLint configuration for JavaScript/TypeScript projects, built on top of [`@antfu/eslint-config`](https://github.com/antfu/eslint-config).
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- 🚀 **Based on @antfu/eslint-config**
|
|
8
|
-
- 🛡️ **
|
|
9
|
-
- 🎨 **Tailwind CSS** -
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
7
|
+
- 🚀 **Based on @antfu/eslint-config** — modern, performant, flat-config first
|
|
8
|
+
- 🛡️ **TypeScript-first** — strict mode, sensible defaults
|
|
9
|
+
- 🎨 **Tailwind CSS v3 + v4** — via [`eslint-plugin-better-tailwindcss`](https://github.com/schoero/eslint-plugin-better-tailwindcss) (compat ESLint 7→10, both Tailwind versions)
|
|
10
|
+
- 🧩 **Custom `maz/` rules** — ships its own ESLint plugin namespaced `maz/*` (see [Custom rules](#custom-rules))
|
|
11
|
+
- 🔍 **SonarJS** — code quality / cognitive complexity
|
|
12
|
+
- ♿ **Vue accessibility** — opt-in `eslint-plugin-vuejs-accessibility`
|
|
13
|
+
- 📐 **Formatters** — Prettier-style formatting via `eslint-plugin-format`
|
|
14
|
+
- ⚙️ **Configurable** — pick presets, add per-file rules, custom ignore globs
|
|
13
15
|
|
|
14
16
|
## Installation
|
|
15
17
|
|
|
@@ -26,139 +28,203 @@ import { defineConfig } from '@maz-ui/eslint-config'
|
|
|
26
28
|
export default defineConfig()
|
|
27
29
|
```
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
Vue / Nuxt are auto-detected from your `package.json`.
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
30
34
|
|
|
31
35
|
```js
|
|
32
36
|
// eslint.config.js
|
|
33
37
|
import { defineConfig } from '@maz-ui/eslint-config'
|
|
34
38
|
|
|
35
39
|
export default defineConfig({
|
|
36
|
-
//
|
|
37
|
-
env: 'production', // 'development' | 'production'
|
|
40
|
+
env: 'production', // 'development' | 'production' — affects no-console / no-debugger severity
|
|
38
41
|
|
|
39
|
-
//
|
|
42
|
+
// Antfu options (forwarded as-is)
|
|
40
43
|
typescript: true,
|
|
41
|
-
tailwindcss: true,
|
|
42
|
-
sonarjs: true,
|
|
43
44
|
formatters: true,
|
|
45
|
+
unicorn: true,
|
|
46
|
+
|
|
47
|
+
// Maz-UI options
|
|
48
|
+
sonarjs: true,
|
|
49
|
+
vueAccessibility: false,
|
|
50
|
+
tailwindcss: 'recommended', // see "Tailwind support" below
|
|
44
51
|
|
|
45
|
-
//
|
|
52
|
+
// Extra ignore globs (merged with defaults)
|
|
46
53
|
ignores: ['custom-dist/**'],
|
|
47
54
|
|
|
48
55
|
// Custom rules
|
|
49
56
|
rules: {
|
|
50
57
|
'no-console': 'error',
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// 'silent' | 'default' | 'debug' | 'verbose' — see "Logging" below.
|
|
61
|
+
logLevel: 'default',
|
|
53
62
|
})
|
|
54
63
|
```
|
|
55
64
|
|
|
56
|
-
##
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
interface MazESLintOptions {
|
|
60
|
-
typescript?: boolean // TypeScript support (default: true)
|
|
61
|
-
tailwindcss?: boolean // Tailwind CSS rules (default: true)
|
|
62
|
-
sonarjs?: boolean // SonarJS rules (default: true)
|
|
63
|
-
formatters?: boolean // Formatters support (default: true)
|
|
64
|
-
env?: 'development' | 'production' // Environment (default: 'development')
|
|
65
|
-
ignores?: string[] // Files to ignore
|
|
66
|
-
rules?: Record<string, any> // Custom ESLint rules
|
|
67
|
-
}
|
|
68
|
-
```
|
|
65
|
+
## Logging
|
|
69
66
|
|
|
70
|
-
|
|
67
|
+
The preset prints a titled box summarizing what it resolved (typescript, vue, tailwindcss preset, sonarjs, …). Powered by `@maz-ui/node`'s consola-based logger.
|
|
71
68
|
|
|
72
|
-
|
|
69
|
+
| Value | What you see |
|
|
70
|
+
| --------------------------- | -------------------------------------------------- |
|
|
71
|
+
| `'silent'` | Nothing. |
|
|
72
|
+
| `'default'` _(TTY default)_ | Titled box of resolved feature toggles. |
|
|
73
|
+
| `'debug'` | Above + each plugin / preset / overrides addition. |
|
|
74
|
+
| `'verbose'` | Above + final block count and ignore-glob count. |
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
import { baseRules, sonarjsRules, tailwindcssRules } from '@maz-ui/eslint-config'
|
|
76
|
+
The default is **`'default'` in interactive terminals, `'silent'` otherwise** (CI, pipes, JSON / SARIF formatters). This avoids polluting machine-readable ESLint output. Override by passing `logLevel` explicitly.
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
{
|
|
79
|
-
rules: {
|
|
80
|
-
...baseRules,
|
|
81
|
-
...sonarjsRules,
|
|
82
|
-
// Your custom rules
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
]
|
|
86
|
-
```
|
|
78
|
+
## Tailwind support
|
|
87
79
|
|
|
88
|
-
|
|
80
|
+
Powered by [`eslint-plugin-better-tailwindcss`](https://github.com/schoero/eslint-plugin-better-tailwindcss) — compatible with ESLint 7→10 and Tailwind v3 + v4.
|
|
89
81
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
82
|
+
| Value | Behavior |
|
|
83
|
+
| ----------------------- | ----------------------------------------------------------------------- |
|
|
84
|
+
| `false` _(default)_ | Disabled. |
|
|
85
|
+
| `true` | Same as `'recommended'` with default settings. |
|
|
86
|
+
| `'recommended'` | Stylistic + correctness rules. |
|
|
87
|
+
| `'stylistic'` | Formatting rules only (class order, line wrapping, …). |
|
|
88
|
+
| `'correctness'` | Validation rules only (`no-unknown-classes`, `no-conflicting-classes`). |
|
|
89
|
+
| `MazTailwindcssOptions` | Pick a preset and pass plugin settings. |
|
|
93
90
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
91
|
+
```js
|
|
92
|
+
defineConfig({
|
|
93
|
+
tailwindcss: {
|
|
94
|
+
preset: 'recommended',
|
|
95
|
+
// Tailwind v4: path to the CSS that does `@import "tailwindcss" prefix(maz)`
|
|
96
|
+
entryPoint: 'src/main.css',
|
|
97
|
+
// Tailwind v3 instead:
|
|
98
|
+
// tailwindConfig: 'tailwind.config.ts',
|
|
99
|
+
detectComponentClasses: true,
|
|
100
|
+
// Custom `maz/tailwind-no-arbitrary-px` rule — see "Custom rules" below.
|
|
101
|
+
noArbitraryPx: true,
|
|
102
|
+
},
|
|
102
103
|
})
|
|
103
104
|
```
|
|
104
105
|
|
|
105
|
-
|
|
106
|
+
> [!IMPORTANT]
|
|
107
|
+
> The plugin's `no-unknown-classes` rule needs to know about your Tailwind setup (prefix, custom utilities, theme tokens). Without `entryPoint` (v4) or `tailwindConfig` (v3), it falls back to the default Tailwind config and will flood with false positives in projects that customize either. **Pass the path explicitly** if your Tailwind setup is non-standard.
|
|
106
108
|
|
|
107
|
-
|
|
109
|
+
## Custom rules
|
|
108
110
|
|
|
109
|
-
|
|
110
|
-
- Code quality rules
|
|
111
|
-
- Optimized TypeScript support
|
|
111
|
+
This preset ships its own ESLint plugin under the `maz/*` namespace. Rules are organized by category — `maz/tailwind-*` for Tailwind-specific rules, ready for `maz/js-*` and others as they get added.
|
|
112
112
|
|
|
113
|
-
###
|
|
113
|
+
### `maz/tailwind-no-arbitrary-px`
|
|
114
114
|
|
|
115
|
-
-
|
|
116
|
-
- Duplicate code detection
|
|
117
|
-
- Security best practices
|
|
115
|
+
Forbids `px` units inside Tailwind arbitrary value classes (`w-[16px]`, `m-[-16px]`, `[gap:24px]`, …) and **autofixes** them to `rem` (or `em`) using the configured root font-size. Whitespace inside brackets (e.g. `[up to 100px]`) is left alone so plain prose is never rewritten.
|
|
118
116
|
|
|
119
|
-
|
|
117
|
+
> [!NOTE]
|
|
118
|
+
> The rule and the `maz` plugin are only registered when `tailwindcss` is enabled (any value other than `false`). With `tailwindcss: false` _(default)_ neither the plugin nor the rule appear in the final flat-config. To use the rule in a project that doesn't opt into the full Tailwind preset, register it manually (see [Use the plugin directly](#use-the-plugin-directly)).
|
|
120
119
|
|
|
121
|
-
|
|
122
|
-
- Contradictory class detection
|
|
123
|
-
- Valid Tailwind syntax
|
|
120
|
+
When `tailwindcss` is on, the rule is enabled with defaults. You can tune it either way:
|
|
124
121
|
|
|
125
|
-
|
|
122
|
+
**Standard ESLint override** _(idiomatic, max control)_
|
|
126
123
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
124
|
+
```js
|
|
125
|
+
defineConfig({
|
|
126
|
+
tailwindcss: 'recommended',
|
|
127
|
+
rules: {
|
|
128
|
+
'maz/tailwind-no-arbitrary-px': ['error', { baseFontSize: 10, unit: 'em' }],
|
|
129
|
+
},
|
|
130
|
+
})
|
|
131
|
+
```
|
|
130
132
|
|
|
131
|
-
|
|
133
|
+
User `rules` overrides are applied in a _trailing_ block, so they win over the rule's defaults wired in by `tailwindcssConfigs`.
|
|
132
134
|
|
|
133
|
-
|
|
135
|
+
**Ergonomic shortcut** _(set the defaults via `tailwindcss.noArbitraryPx`)_
|
|
134
136
|
|
|
135
137
|
```js
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
138
|
+
defineConfig({
|
|
139
|
+
tailwindcss: {
|
|
140
|
+
preset: 'recommended',
|
|
141
|
+
noArbitraryPx: {
|
|
142
|
+
baseFontSize: 16, // default — convert px / 16 → rem
|
|
143
|
+
unit: 'rem', // default — target unit ('rem' | 'em')
|
|
144
|
+
severity: 'error', // default — 'off' | 'warn' | 'error'
|
|
145
|
+
},
|
|
146
|
+
},
|
|
139
147
|
})
|
|
140
148
|
```
|
|
141
149
|
|
|
142
|
-
|
|
150
|
+
| `noArbitraryPx` value | Behavior |
|
|
151
|
+
| ---------------------------------- | -------------------------------------------------- |
|
|
152
|
+
| `true` _(default)_ | Enabled with defaults (`baseFontSize: 16`, `rem`). |
|
|
153
|
+
| `false` | Disabled. |
|
|
154
|
+
| `{ baseFontSize, unit, severity }` | Override any subset of the defaults. |
|
|
155
|
+
|
|
156
|
+
If both are set, the standard `rules` override wins (last block applied).
|
|
157
|
+
|
|
158
|
+
Examples after autofix (with `baseFontSize: 16`):
|
|
159
|
+
|
|
160
|
+
| Before | After |
|
|
161
|
+
| --------------------- | ----------------------- |
|
|
162
|
+
| `w-[16px]` | `w-[1rem]` |
|
|
163
|
+
| `m-[-16px]` | `m-[-1rem]` |
|
|
164
|
+
| `tracking-[.5px]` | `tracking-[0.03125rem]` |
|
|
165
|
+
| `p-[16px_8px]` | `p-[1rem_0.5rem]` |
|
|
166
|
+
| `[gap:24px]` | `[gap:1.5rem]` |
|
|
167
|
+
| `w-[calc(100%-16px)]` | `w-[calc(100%-1rem)]` |
|
|
168
|
+
|
|
169
|
+
### Use the plugin directly
|
|
170
|
+
|
|
171
|
+
If you don't want the full Tailwind preset (or just prefer wiring rules yourself), import `mazPlugin` and register it in your own flat-config block. This is also the way to use `maz/tailwind-*` rules without enabling `tailwindcss` in `defineConfig`:
|
|
143
172
|
|
|
144
173
|
```js
|
|
145
|
-
|
|
146
|
-
|
|
174
|
+
import { mazPlugin } from '@maz-ui/eslint-config'
|
|
175
|
+
|
|
176
|
+
export default [{
|
|
177
|
+
files: ['**/*.{ts,tsx,vue}'],
|
|
178
|
+
plugins: { maz: mazPlugin },
|
|
147
179
|
rules: {
|
|
148
|
-
'
|
|
149
|
-
}
|
|
150
|
-
}
|
|
180
|
+
'maz/tailwind-no-arbitrary-px': ['error', { baseFontSize: 16 }],
|
|
181
|
+
},
|
|
182
|
+
}]
|
|
151
183
|
```
|
|
152
184
|
|
|
153
|
-
|
|
185
|
+
## Advanced
|
|
186
|
+
|
|
187
|
+
### Compose your own config
|
|
154
188
|
|
|
155
189
|
```js
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
190
|
+
import {
|
|
191
|
+
baseRules,
|
|
192
|
+
mazPlugin,
|
|
193
|
+
sonarjsRules,
|
|
194
|
+
tailwindcssConfigs,
|
|
195
|
+
vueRules,
|
|
196
|
+
} from '@maz-ui/eslint-config'
|
|
197
|
+
|
|
198
|
+
export default [
|
|
199
|
+
{
|
|
200
|
+
plugins: { maz: mazPlugin },
|
|
201
|
+
rules: {
|
|
202
|
+
...baseRules(true), // production = true
|
|
203
|
+
...sonarjsRules,
|
|
204
|
+
...vueRules,
|
|
205
|
+
'maz/tailwind-no-arbitrary-px': 'error',
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
...tailwindcssConfigs('stylistic', {
|
|
209
|
+
entryPoint: 'src/main.css',
|
|
210
|
+
noArbitraryPx: { unit: 'em' },
|
|
211
|
+
}),
|
|
212
|
+
]
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Vue project
|
|
216
|
+
|
|
217
|
+
```js
|
|
218
|
+
defineConfig({
|
|
219
|
+
vue: true, // auto-detected if `vue` / `nuxt` is in deps
|
|
220
|
+
tailwindcss: 'recommended',
|
|
159
221
|
rules: {
|
|
160
|
-
'
|
|
161
|
-
|
|
162
|
-
}
|
|
222
|
+
'vue/custom-event-name-casing': ['error', 'kebab-case'],
|
|
223
|
+
},
|
|
163
224
|
})
|
|
164
225
|
```
|
|
226
|
+
|
|
227
|
+
## Compatibility
|
|
228
|
+
|
|
229
|
+
- **ESLint** `^9.0.0 || ^10.0.0`
|
|
230
|
+
- **Node.js** `>=20.19.0`
|
package/dist/index.d.mts
CHANGED
|
@@ -1,13 +1,102 @@
|
|
|
1
1
|
import { antfu, OptionsConfig, Rules, TypedFlatConfigItem } from '@antfu/eslint-config';
|
|
2
|
-
import { Linter } from 'eslint';
|
|
2
|
+
import { Linter, ESLint, Rule } from 'eslint';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Mirror of `@maz-ui/node`'s LogLevel — kept inline here to avoid making this
|
|
6
|
+
* preset depend on `@maz-ui/node`, which would create a build cycle (the
|
|
7
|
+
* `eslint.config.ts` of `@maz-ui/node` imports `@maz-ui/eslint-config`, which
|
|
8
|
+
* itself would depend on `@maz-ui/node` for the same logger). Direct usage of
|
|
9
|
+
* `consola` keeps the dependency graph acyclic.
|
|
10
|
+
*/
|
|
11
|
+
type LogLevel = 'silent' | 'error' | 'warning' | 'normal' | 'default' | 'debug' | 'trace' | 'verbose';
|
|
3
12
|
|
|
4
13
|
type MazESLintUserConfig = TypedFlatConfigItem | TypedFlatConfigItem[] | Linter.Config | Linter.Config[];
|
|
14
|
+
type TailwindcssPreset = 'recommended' | 'stylistic' | 'correctness';
|
|
15
|
+
/**
|
|
16
|
+
* Settings forwarded to `eslint-plugin-better-tailwindcss` under
|
|
17
|
+
* `settings['better-tailwindcss']`. See the plugin docs for the canonical
|
|
18
|
+
* meaning of each field.
|
|
19
|
+
*/
|
|
20
|
+
interface MazTailwindcssOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Which preset to load. Each one comes in `*-warn` and `*-error` flavors
|
|
23
|
+
* (defaults match the plugin's defaults: stylistic→warn, correctness→error).
|
|
24
|
+
*
|
|
25
|
+
* @default 'recommended'
|
|
26
|
+
*/
|
|
27
|
+
preset?: TailwindcssPreset;
|
|
28
|
+
/**
|
|
29
|
+
* Tailwind v4: path to the CSS entry file (the one that does
|
|
30
|
+
* `@import "tailwindcss"`, possibly with `prefix(...)`). Required for the
|
|
31
|
+
* plugin to know about your prefix and `@theme` tokens.
|
|
32
|
+
*/
|
|
33
|
+
entryPoint?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Tailwind v3: path to `tailwind.config.{js,ts,cjs,mjs}`. Use `entryPoint`
|
|
36
|
+
* for v4 instead.
|
|
37
|
+
*/
|
|
38
|
+
tailwindConfig?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Tailwind v4: enable detection of custom component classes (`@layer
|
|
41
|
+
* components { … }`) so they are not reported as unknown.
|
|
42
|
+
*
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
detectComponentClasses?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Working directory used to resolve `tailwindcss` and the config files
|
|
48
|
+
* above. Useful in monorepos when ESLint runs from the repo root.
|
|
49
|
+
*/
|
|
50
|
+
cwd?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Path to a `tsconfig.json` used to resolve `paths` aliases inside the
|
|
53
|
+
* Tailwind config.
|
|
54
|
+
*/
|
|
55
|
+
tsconfig?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Configure the `maz/tailwind-no-arbitrary-px` rule (forbids `px`
|
|
58
|
+
* values inside Tailwind arbitrary value classes such as `w-[16px]`).
|
|
59
|
+
*
|
|
60
|
+
* - `false`: disable the rule.
|
|
61
|
+
* - `true` *(default)*: enable with defaults (`baseFontSize: 16`,
|
|
62
|
+
* `unit: 'rem'`).
|
|
63
|
+
* - Object: enable with custom options.
|
|
64
|
+
*/
|
|
65
|
+
noArbitraryPx?: boolean | {
|
|
66
|
+
/**
|
|
67
|
+
* Root font size, in pixels, used to convert `Npx` to `rem`/`em`.
|
|
68
|
+
*
|
|
69
|
+
* @default 16
|
|
70
|
+
*/
|
|
71
|
+
baseFontSize?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Output unit for the autofix.
|
|
74
|
+
*
|
|
75
|
+
* @default 'rem'
|
|
76
|
+
*/
|
|
77
|
+
unit?: 'rem' | 'em';
|
|
78
|
+
/**
|
|
79
|
+
* ESLint severity for the rule.
|
|
80
|
+
*
|
|
81
|
+
* @default 'error'
|
|
82
|
+
*/
|
|
83
|
+
severity?: 'off' | 'warn' | 'error';
|
|
84
|
+
};
|
|
85
|
+
}
|
|
5
86
|
interface MazESLintOptions extends OptionsConfig {
|
|
6
87
|
/**
|
|
7
|
-
*
|
|
88
|
+
* Tailwind CSS support — wires `eslint-plugin-better-tailwindcss`.
|
|
89
|
+
*
|
|
90
|
+
* - `false` *(default)*: disabled.
|
|
91
|
+
* - `true`: load the `'recommended'` preset with default settings.
|
|
92
|
+
* - `'recommended' | 'stylistic' | 'correctness'`: pick a preset, default
|
|
93
|
+
* settings.
|
|
94
|
+
* - `MazTailwindcssOptions`: pick a preset and pass settings (e.g.
|
|
95
|
+
* `entryPoint` so the plugin learns your v4 prefix).
|
|
96
|
+
*
|
|
8
97
|
* @default false
|
|
9
98
|
*/
|
|
10
|
-
tailwindcss?: boolean;
|
|
99
|
+
tailwindcss?: boolean | TailwindcssPreset | MazTailwindcssOptions;
|
|
11
100
|
/**
|
|
12
101
|
* Enable SonarJS rules for code quality
|
|
13
102
|
* @default true
|
|
@@ -32,6 +121,19 @@ interface MazESLintOptions extends OptionsConfig {
|
|
|
32
121
|
* Additional rules to merge
|
|
33
122
|
*/
|
|
34
123
|
rules?: Partial<Rules>;
|
|
124
|
+
/**
|
|
125
|
+
* Verbosity of the logs emitted while resolving the config. Defaults to
|
|
126
|
+
* `'default'` so the resolved-configuration box is shown when the preset
|
|
127
|
+
* loads. Set to `'silent'` to hide it, or `'debug'` / `'verbose'` to dig
|
|
128
|
+
* deeper.
|
|
129
|
+
*
|
|
130
|
+
* - `'silent'`: nothing is printed.
|
|
131
|
+
* - `'default'` *(default)*: a titled box summarizing the resolved feature
|
|
132
|
+
* toggles.
|
|
133
|
+
* - `'debug'`: also logs each plugin / preset / overrides addition.
|
|
134
|
+
* - `'verbose'`: also logs the final shape (config block count, etc.).
|
|
135
|
+
*/
|
|
136
|
+
logLevel?: LogLevel;
|
|
35
137
|
}
|
|
36
138
|
type MazESLintConfig = ReturnType<typeof antfu>;
|
|
37
139
|
|
|
@@ -68,24 +170,52 @@ declare const sonarjsTestRules: {
|
|
|
68
170
|
};
|
|
69
171
|
|
|
70
172
|
/**
|
|
71
|
-
* Tailwind
|
|
72
|
-
*
|
|
73
|
-
* These rules only activate when the user opts in via
|
|
74
|
-
* `defineMazEslintConfig({ tailwindcss: true })` AND is on ESLint 9 +
|
|
75
|
-
* Tailwind v3. The plugin itself does not yet support ESLint 10+ or
|
|
76
|
-
* Tailwind v4 (upstream — uses the removed context.getSourceCode API).
|
|
173
|
+
* Files where Tailwind classes commonly live. Exposed for users who want to
|
|
174
|
+
* compose their own config.
|
|
77
175
|
*/
|
|
78
|
-
declare const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
176
|
+
declare const TAILWINDCSS_DEFAULT_FILES: string[];
|
|
177
|
+
/**
|
|
178
|
+
* Build the flat-config block(s) that wire up
|
|
179
|
+
* `eslint-plugin-better-tailwindcss` AND the custom `maz/tailwind-*`
|
|
180
|
+
* rules shipped by this package. The plugin already ships preset
|
|
181
|
+
* objects with `plugins` + `rules`; we spread them and add `files` plus
|
|
182
|
+
* `settings['better-tailwindcss']` so the user's options reach the
|
|
183
|
+
* plugin.
|
|
184
|
+
*/
|
|
185
|
+
declare function tailwindcssConfigs(preset: TailwindcssPreset, settings: MazTailwindcssOptions): Linter.Config[];
|
|
85
186
|
|
|
86
187
|
declare const vueRules: {
|
|
87
188
|
'vue/custom-event-name-casing': ["error", "kebab-case"];
|
|
88
189
|
};
|
|
190
|
+
declare const vueSfcOnlyRules: {
|
|
191
|
+
'no-useless-assignment': "off";
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* The `maz` ESLint plugin bundling all custom rules shipped by
|
|
196
|
+
* `@maz-ui/eslint-config`. Rules are namespaced by category:
|
|
197
|
+
*
|
|
198
|
+
* - `maz/tailwind-*` — Tailwind-specific rules
|
|
199
|
+
* - `maz/js-*` — general JS/TS rules (when added)
|
|
200
|
+
* - …
|
|
201
|
+
*
|
|
202
|
+
* The plugin is auto-wired by the relevant config block in
|
|
203
|
+
* `src/configs/*.ts`, but it is also exported so users can compose
|
|
204
|
+
* their own flat-config setup.
|
|
205
|
+
*/
|
|
206
|
+
declare const mazPlugin: ESLint.Plugin;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Registry of all custom rules shipped by `@maz-ui/eslint-config`.
|
|
210
|
+
*
|
|
211
|
+
* Keys here are the final rule names exposed by the `maz` plugin, so a
|
|
212
|
+
* file in `src/rules/tailwind/no-arbitrary-px.ts` becomes
|
|
213
|
+
* `maz/tailwind-no-arbitrary-px` when wired through the plugin.
|
|
214
|
+
*
|
|
215
|
+
* Add new rules by dropping a file in the matching category folder
|
|
216
|
+
* (`tailwind/`, `js/`, …) and registering it below.
|
|
217
|
+
*/
|
|
218
|
+
declare const rules: Record<string, Rule.RuleModule>;
|
|
89
219
|
|
|
90
220
|
/**
|
|
91
221
|
* Create ESLint configuration for Maz-UI and JavaScript/TypeScript projects
|
|
@@ -107,5 +237,5 @@ declare const vueRules: {
|
|
|
107
237
|
*/
|
|
108
238
|
declare function defineConfig(options?: MazESLintOptions, ...userConfigs: MazESLintUserConfig[]): MazESLintConfig;
|
|
109
239
|
|
|
110
|
-
export { baseRules, defineConfig, sonarjsRules, sonarjsTestRules,
|
|
111
|
-
export type { MazESLintConfig, MazESLintOptions };
|
|
240
|
+
export { TAILWINDCSS_DEFAULT_FILES, baseRules, defineConfig, mazPlugin, rules as mazRules, sonarjsRules, sonarjsTestRules, tailwindcssConfigs, vueRules, vueSfcOnlyRules };
|
|
241
|
+
export type { MazESLintConfig, MazESLintOptions, MazTailwindcssOptions, TailwindcssPreset };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,102 @@
|
|
|
1
1
|
import { antfu, OptionsConfig, Rules, TypedFlatConfigItem } from '@antfu/eslint-config';
|
|
2
|
-
import { Linter } from 'eslint';
|
|
2
|
+
import { Linter, ESLint, Rule } from 'eslint';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Mirror of `@maz-ui/node`'s LogLevel — kept inline here to avoid making this
|
|
6
|
+
* preset depend on `@maz-ui/node`, which would create a build cycle (the
|
|
7
|
+
* `eslint.config.ts` of `@maz-ui/node` imports `@maz-ui/eslint-config`, which
|
|
8
|
+
* itself would depend on `@maz-ui/node` for the same logger). Direct usage of
|
|
9
|
+
* `consola` keeps the dependency graph acyclic.
|
|
10
|
+
*/
|
|
11
|
+
type LogLevel = 'silent' | 'error' | 'warning' | 'normal' | 'default' | 'debug' | 'trace' | 'verbose';
|
|
3
12
|
|
|
4
13
|
type MazESLintUserConfig = TypedFlatConfigItem | TypedFlatConfigItem[] | Linter.Config | Linter.Config[];
|
|
14
|
+
type TailwindcssPreset = 'recommended' | 'stylistic' | 'correctness';
|
|
15
|
+
/**
|
|
16
|
+
* Settings forwarded to `eslint-plugin-better-tailwindcss` under
|
|
17
|
+
* `settings['better-tailwindcss']`. See the plugin docs for the canonical
|
|
18
|
+
* meaning of each field.
|
|
19
|
+
*/
|
|
20
|
+
interface MazTailwindcssOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Which preset to load. Each one comes in `*-warn` and `*-error` flavors
|
|
23
|
+
* (defaults match the plugin's defaults: stylistic→warn, correctness→error).
|
|
24
|
+
*
|
|
25
|
+
* @default 'recommended'
|
|
26
|
+
*/
|
|
27
|
+
preset?: TailwindcssPreset;
|
|
28
|
+
/**
|
|
29
|
+
* Tailwind v4: path to the CSS entry file (the one that does
|
|
30
|
+
* `@import "tailwindcss"`, possibly with `prefix(...)`). Required for the
|
|
31
|
+
* plugin to know about your prefix and `@theme` tokens.
|
|
32
|
+
*/
|
|
33
|
+
entryPoint?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Tailwind v3: path to `tailwind.config.{js,ts,cjs,mjs}`. Use `entryPoint`
|
|
36
|
+
* for v4 instead.
|
|
37
|
+
*/
|
|
38
|
+
tailwindConfig?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Tailwind v4: enable detection of custom component classes (`@layer
|
|
41
|
+
* components { … }`) so they are not reported as unknown.
|
|
42
|
+
*
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
detectComponentClasses?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Working directory used to resolve `tailwindcss` and the config files
|
|
48
|
+
* above. Useful in monorepos when ESLint runs from the repo root.
|
|
49
|
+
*/
|
|
50
|
+
cwd?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Path to a `tsconfig.json` used to resolve `paths` aliases inside the
|
|
53
|
+
* Tailwind config.
|
|
54
|
+
*/
|
|
55
|
+
tsconfig?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Configure the `maz/tailwind-no-arbitrary-px` rule (forbids `px`
|
|
58
|
+
* values inside Tailwind arbitrary value classes such as `w-[16px]`).
|
|
59
|
+
*
|
|
60
|
+
* - `false`: disable the rule.
|
|
61
|
+
* - `true` *(default)*: enable with defaults (`baseFontSize: 16`,
|
|
62
|
+
* `unit: 'rem'`).
|
|
63
|
+
* - Object: enable with custom options.
|
|
64
|
+
*/
|
|
65
|
+
noArbitraryPx?: boolean | {
|
|
66
|
+
/**
|
|
67
|
+
* Root font size, in pixels, used to convert `Npx` to `rem`/`em`.
|
|
68
|
+
*
|
|
69
|
+
* @default 16
|
|
70
|
+
*/
|
|
71
|
+
baseFontSize?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Output unit for the autofix.
|
|
74
|
+
*
|
|
75
|
+
* @default 'rem'
|
|
76
|
+
*/
|
|
77
|
+
unit?: 'rem' | 'em';
|
|
78
|
+
/**
|
|
79
|
+
* ESLint severity for the rule.
|
|
80
|
+
*
|
|
81
|
+
* @default 'error'
|
|
82
|
+
*/
|
|
83
|
+
severity?: 'off' | 'warn' | 'error';
|
|
84
|
+
};
|
|
85
|
+
}
|
|
5
86
|
interface MazESLintOptions extends OptionsConfig {
|
|
6
87
|
/**
|
|
7
|
-
*
|
|
88
|
+
* Tailwind CSS support — wires `eslint-plugin-better-tailwindcss`.
|
|
89
|
+
*
|
|
90
|
+
* - `false` *(default)*: disabled.
|
|
91
|
+
* - `true`: load the `'recommended'` preset with default settings.
|
|
92
|
+
* - `'recommended' | 'stylistic' | 'correctness'`: pick a preset, default
|
|
93
|
+
* settings.
|
|
94
|
+
* - `MazTailwindcssOptions`: pick a preset and pass settings (e.g.
|
|
95
|
+
* `entryPoint` so the plugin learns your v4 prefix).
|
|
96
|
+
*
|
|
8
97
|
* @default false
|
|
9
98
|
*/
|
|
10
|
-
tailwindcss?: boolean;
|
|
99
|
+
tailwindcss?: boolean | TailwindcssPreset | MazTailwindcssOptions;
|
|
11
100
|
/**
|
|
12
101
|
* Enable SonarJS rules for code quality
|
|
13
102
|
* @default true
|
|
@@ -32,6 +121,19 @@ interface MazESLintOptions extends OptionsConfig {
|
|
|
32
121
|
* Additional rules to merge
|
|
33
122
|
*/
|
|
34
123
|
rules?: Partial<Rules>;
|
|
124
|
+
/**
|
|
125
|
+
* Verbosity of the logs emitted while resolving the config. Defaults to
|
|
126
|
+
* `'default'` so the resolved-configuration box is shown when the preset
|
|
127
|
+
* loads. Set to `'silent'` to hide it, or `'debug'` / `'verbose'` to dig
|
|
128
|
+
* deeper.
|
|
129
|
+
*
|
|
130
|
+
* - `'silent'`: nothing is printed.
|
|
131
|
+
* - `'default'` *(default)*: a titled box summarizing the resolved feature
|
|
132
|
+
* toggles.
|
|
133
|
+
* - `'debug'`: also logs each plugin / preset / overrides addition.
|
|
134
|
+
* - `'verbose'`: also logs the final shape (config block count, etc.).
|
|
135
|
+
*/
|
|
136
|
+
logLevel?: LogLevel;
|
|
35
137
|
}
|
|
36
138
|
type MazESLintConfig = ReturnType<typeof antfu>;
|
|
37
139
|
|
|
@@ -68,24 +170,52 @@ declare const sonarjsTestRules: {
|
|
|
68
170
|
};
|
|
69
171
|
|
|
70
172
|
/**
|
|
71
|
-
* Tailwind
|
|
72
|
-
*
|
|
73
|
-
* These rules only activate when the user opts in via
|
|
74
|
-
* `defineMazEslintConfig({ tailwindcss: true })` AND is on ESLint 9 +
|
|
75
|
-
* Tailwind v3. The plugin itself does not yet support ESLint 10+ or
|
|
76
|
-
* Tailwind v4 (upstream — uses the removed context.getSourceCode API).
|
|
173
|
+
* Files where Tailwind classes commonly live. Exposed for users who want to
|
|
174
|
+
* compose their own config.
|
|
77
175
|
*/
|
|
78
|
-
declare const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
176
|
+
declare const TAILWINDCSS_DEFAULT_FILES: string[];
|
|
177
|
+
/**
|
|
178
|
+
* Build the flat-config block(s) that wire up
|
|
179
|
+
* `eslint-plugin-better-tailwindcss` AND the custom `maz/tailwind-*`
|
|
180
|
+
* rules shipped by this package. The plugin already ships preset
|
|
181
|
+
* objects with `plugins` + `rules`; we spread them and add `files` plus
|
|
182
|
+
* `settings['better-tailwindcss']` so the user's options reach the
|
|
183
|
+
* plugin.
|
|
184
|
+
*/
|
|
185
|
+
declare function tailwindcssConfigs(preset: TailwindcssPreset, settings: MazTailwindcssOptions): Linter.Config[];
|
|
85
186
|
|
|
86
187
|
declare const vueRules: {
|
|
87
188
|
'vue/custom-event-name-casing': ["error", "kebab-case"];
|
|
88
189
|
};
|
|
190
|
+
declare const vueSfcOnlyRules: {
|
|
191
|
+
'no-useless-assignment': "off";
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* The `maz` ESLint plugin bundling all custom rules shipped by
|
|
196
|
+
* `@maz-ui/eslint-config`. Rules are namespaced by category:
|
|
197
|
+
*
|
|
198
|
+
* - `maz/tailwind-*` — Tailwind-specific rules
|
|
199
|
+
* - `maz/js-*` — general JS/TS rules (when added)
|
|
200
|
+
* - …
|
|
201
|
+
*
|
|
202
|
+
* The plugin is auto-wired by the relevant config block in
|
|
203
|
+
* `src/configs/*.ts`, but it is also exported so users can compose
|
|
204
|
+
* their own flat-config setup.
|
|
205
|
+
*/
|
|
206
|
+
declare const mazPlugin: ESLint.Plugin;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Registry of all custom rules shipped by `@maz-ui/eslint-config`.
|
|
210
|
+
*
|
|
211
|
+
* Keys here are the final rule names exposed by the `maz` plugin, so a
|
|
212
|
+
* file in `src/rules/tailwind/no-arbitrary-px.ts` becomes
|
|
213
|
+
* `maz/tailwind-no-arbitrary-px` when wired through the plugin.
|
|
214
|
+
*
|
|
215
|
+
* Add new rules by dropping a file in the matching category folder
|
|
216
|
+
* (`tailwind/`, `js/`, …) and registering it below.
|
|
217
|
+
*/
|
|
218
|
+
declare const rules: Record<string, Rule.RuleModule>;
|
|
89
219
|
|
|
90
220
|
/**
|
|
91
221
|
* Create ESLint configuration for Maz-UI and JavaScript/TypeScript projects
|
|
@@ -107,5 +237,5 @@ declare const vueRules: {
|
|
|
107
237
|
*/
|
|
108
238
|
declare function defineConfig(options?: MazESLintOptions, ...userConfigs: MazESLintUserConfig[]): MazESLintConfig;
|
|
109
239
|
|
|
110
|
-
export { baseRules, defineConfig, sonarjsRules, sonarjsTestRules,
|
|
111
|
-
export type { MazESLintConfig, MazESLintOptions };
|
|
240
|
+
export { TAILWINDCSS_DEFAULT_FILES, baseRules, defineConfig, mazPlugin, rules as mazRules, sonarjsRules, sonarjsTestRules, tailwindcssConfigs, vueRules, vueSfcOnlyRules };
|
|
241
|
+
export type { MazESLintConfig, MazESLintOptions, MazTailwindcssOptions, TailwindcssPreset };
|
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { createRequire } from 'node:module';
|
|
3
2
|
import { join } from 'node:path';
|
|
4
3
|
import antfu from '@antfu/eslint-config';
|
|
5
4
|
import { configs } from 'eslint-plugin-sonarjs';
|
|
6
5
|
import vueA11y from 'eslint-plugin-vuejs-accessibility';
|
|
6
|
+
import { createConsola } from 'consola';
|
|
7
|
+
import betterTailwindcss from 'eslint-plugin-better-tailwindcss';
|
|
7
8
|
|
|
8
9
|
function baseRules(isProduction) {
|
|
9
10
|
return {
|
|
@@ -71,6 +72,29 @@ const GLOBAL_IGNORES = [
|
|
|
71
72
|
"**/components.d.ts"
|
|
72
73
|
];
|
|
73
74
|
|
|
75
|
+
const LEVELS = {
|
|
76
|
+
silent: Number.NEGATIVE_INFINITY,
|
|
77
|
+
error: 0,
|
|
78
|
+
warning: 1,
|
|
79
|
+
normal: 2,
|
|
80
|
+
default: 3,
|
|
81
|
+
debug: 4,
|
|
82
|
+
trace: 5,
|
|
83
|
+
verbose: Number.POSITIVE_INFINITY
|
|
84
|
+
};
|
|
85
|
+
function createLogger() {
|
|
86
|
+
const consola = createConsola();
|
|
87
|
+
return {
|
|
88
|
+
setLevel: (level) => {
|
|
89
|
+
consola.level = LEVELS[level];
|
|
90
|
+
},
|
|
91
|
+
info: (msg) => consola.info(msg),
|
|
92
|
+
debug: (msg) => consola.debug(msg),
|
|
93
|
+
verbose: (msg) => consola.verbose(msg),
|
|
94
|
+
box: (opts) => consola.box(opts)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
74
98
|
const markdown = {
|
|
75
99
|
files: ["**/*.md/**"],
|
|
76
100
|
rules: {
|
|
@@ -103,18 +127,167 @@ const sonarjsTestRules = {
|
|
|
103
127
|
"sonarjs/no-duplicate-string": "off"
|
|
104
128
|
};
|
|
105
129
|
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
130
|
+
const DEFAULT_BASE_FONT_SIZE = 16;
|
|
131
|
+
const DEFAULT_UNIT = "rem";
|
|
132
|
+
const ARBITRARY_BRACKET = /\[([^\s\]]+)\]/g;
|
|
133
|
+
const HAS_PX_VALUE = /\dpx/i;
|
|
134
|
+
const PX_VALUE = /(-?(?:\d+(?:\.\d+)?|\.\d+))px/gi;
|
|
135
|
+
function convertPx(pxStr, base, unit) {
|
|
136
|
+
const px = Number.parseFloat(pxStr);
|
|
137
|
+
const value = px / base;
|
|
138
|
+
const formatted = Number.parseFloat(value.toFixed(6)).toString();
|
|
139
|
+
return `${formatted}${unit}`;
|
|
140
|
+
}
|
|
141
|
+
function findReplacements(input, base, unit) {
|
|
142
|
+
const out = [];
|
|
143
|
+
for (const match of input.matchAll(ARBITRARY_BRACKET)) {
|
|
144
|
+
const full = match[0];
|
|
145
|
+
const inner = match[1];
|
|
146
|
+
if (!HAS_PX_VALUE.test(inner))
|
|
147
|
+
continue;
|
|
148
|
+
const fixedInner = inner.replace(PX_VALUE, (_m, num) => convertPx(num, base, unit));
|
|
149
|
+
out.push({
|
|
150
|
+
original: full,
|
|
151
|
+
fixed: `[${fixedInner}]`,
|
|
152
|
+
index: match.index,
|
|
153
|
+
length: full.length
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
function applyReplacements(input, replacements) {
|
|
159
|
+
let output = "";
|
|
160
|
+
let cursor = 0;
|
|
161
|
+
for (const r of replacements) {
|
|
162
|
+
output += input.slice(cursor, r.index) + r.fixed;
|
|
163
|
+
cursor = r.index + r.length;
|
|
164
|
+
}
|
|
165
|
+
output += input.slice(cursor);
|
|
166
|
+
return output;
|
|
167
|
+
}
|
|
168
|
+
const rule = {
|
|
169
|
+
meta: {
|
|
170
|
+
type: "problem",
|
|
171
|
+
docs: {
|
|
172
|
+
description: "Disallow `px` units inside Tailwind arbitrary value classes; prefer `rem` (or `em`).",
|
|
173
|
+
recommended: false
|
|
174
|
+
},
|
|
175
|
+
fixable: "code",
|
|
176
|
+
schema: [
|
|
177
|
+
{
|
|
178
|
+
type: "object",
|
|
179
|
+
additionalProperties: false,
|
|
180
|
+
properties: {
|
|
181
|
+
baseFontSize: { type: "number", minimum: 1 },
|
|
182
|
+
unit: { type: "string", enum: ["rem", "em"] }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
messages: {
|
|
187
|
+
preferRelativeUnit: "Tailwind class uses px; replace `{{original}}` with `{{fixed}}`."
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
create(context) {
|
|
191
|
+
const options = context.options[0] ?? {};
|
|
192
|
+
const base = options.baseFontSize ?? DEFAULT_BASE_FONT_SIZE;
|
|
193
|
+
const unit = options.unit ?? DEFAULT_UNIT;
|
|
194
|
+
const { sourceCode } = context;
|
|
195
|
+
function check(node) {
|
|
196
|
+
const raw = sourceCode.getText(node);
|
|
197
|
+
const replacements = findReplacements(raw, base, unit);
|
|
198
|
+
if (replacements.length === 0)
|
|
199
|
+
return;
|
|
200
|
+
const fixedRaw = applyReplacements(raw, replacements);
|
|
201
|
+
for (const r of replacements) {
|
|
202
|
+
context.report({
|
|
203
|
+
node,
|
|
204
|
+
messageId: "preferRelativeUnit",
|
|
205
|
+
data: { original: r.original, fixed: r.fixed },
|
|
206
|
+
fix: (fixer) => fixer.replaceText(node, fixedRaw)
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const jsVisitor = {
|
|
211
|
+
Literal(node) {
|
|
212
|
+
if (typeof node.value !== "string")
|
|
213
|
+
return;
|
|
214
|
+
check(node);
|
|
215
|
+
},
|
|
216
|
+
TemplateElement(node) {
|
|
217
|
+
check(node);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
const parserServices = context.sourceCode.parserServices;
|
|
221
|
+
if (parserServices.defineTemplateBodyVisitor) {
|
|
222
|
+
return parserServices.defineTemplateBodyVisitor(
|
|
223
|
+
{ VLiteral: (node) => check(node) },
|
|
224
|
+
jsVisitor
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return jsVisitor;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const rules = {
|
|
232
|
+
"tailwind-no-arbitrary-px": rule
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const mazPlugin = {
|
|
236
|
+
meta: {
|
|
237
|
+
name: "maz"
|
|
238
|
+
},
|
|
239
|
+
rules
|
|
116
240
|
};
|
|
117
241
|
|
|
242
|
+
const TAILWINDCSS_FILES = ["**/*.{js,jsx,cjs,mjs,ts,mts,cts,tsx,vue,html,svelte,astro,astrojs,css,scss}"];
|
|
243
|
+
const TAILWINDCSS_DEFAULT_FILES = TAILWINDCSS_FILES;
|
|
244
|
+
function resolveNoArbitraryPx(setting) {
|
|
245
|
+
if (setting === false)
|
|
246
|
+
return { severity: "off", options: { baseFontSize: 16, unit: "rem" } };
|
|
247
|
+
if (setting === void 0 || setting === true) {
|
|
248
|
+
return { severity: "error", options: { baseFontSize: 16, unit: "rem" } };
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
severity: setting.severity ?? "error",
|
|
252
|
+
options: {
|
|
253
|
+
baseFontSize: setting.baseFontSize ?? 16,
|
|
254
|
+
unit: setting.unit ?? "rem"
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function tailwindcssConfigs(preset, settings) {
|
|
259
|
+
const presetConfig = betterTailwindcss.configs[preset];
|
|
260
|
+
const tailwindSettings = {};
|
|
261
|
+
if (settings.entryPoint)
|
|
262
|
+
tailwindSettings.entryPoint = settings.entryPoint;
|
|
263
|
+
if (settings.tailwindConfig)
|
|
264
|
+
tailwindSettings.tailwindConfig = settings.tailwindConfig;
|
|
265
|
+
if (settings.detectComponentClasses !== void 0)
|
|
266
|
+
tailwindSettings.detectComponentClasses = settings.detectComponentClasses;
|
|
267
|
+
if (settings.cwd)
|
|
268
|
+
tailwindSettings.cwd = settings.cwd;
|
|
269
|
+
if (settings.tsconfig)
|
|
270
|
+
tailwindSettings.tsconfig = settings.tsconfig;
|
|
271
|
+
const noArbitraryPx = resolveNoArbitraryPx(settings.noArbitraryPx);
|
|
272
|
+
return [{
|
|
273
|
+
...presetConfig,
|
|
274
|
+
files: TAILWINDCSS_FILES,
|
|
275
|
+
plugins: {
|
|
276
|
+
...presetConfig.plugins,
|
|
277
|
+
maz: mazPlugin
|
|
278
|
+
},
|
|
279
|
+
settings: {
|
|
280
|
+
"better-tailwindcss": tailwindSettings
|
|
281
|
+
},
|
|
282
|
+
rules: {
|
|
283
|
+
...presetConfig.rules,
|
|
284
|
+
"better-tailwindcss/no-unknown-classes": "off",
|
|
285
|
+
"better-tailwindcss/enforce-consistent-line-wrapping": "off",
|
|
286
|
+
"maz/tailwind-no-arbitrary-px": [noArbitraryPx.severity, noArbitraryPx.options]
|
|
287
|
+
}
|
|
288
|
+
}];
|
|
289
|
+
}
|
|
290
|
+
|
|
118
291
|
const testRules = {
|
|
119
292
|
"max-nested-callbacks": "off"
|
|
120
293
|
};
|
|
@@ -122,7 +295,11 @@ const testRules = {
|
|
|
122
295
|
const vueRules = {
|
|
123
296
|
"vue/custom-event-name-casing": ["error", "kebab-case"]
|
|
124
297
|
};
|
|
298
|
+
const vueSfcOnlyRules = {
|
|
299
|
+
"no-useless-assignment": "off"
|
|
300
|
+
};
|
|
125
301
|
|
|
302
|
+
const TAG = "[@maz-ui/eslint-config]";
|
|
126
303
|
const defaultOptions = {
|
|
127
304
|
formatters: true,
|
|
128
305
|
typescript: true,
|
|
@@ -139,39 +316,93 @@ function getPackageJson() {
|
|
|
139
316
|
return void 0;
|
|
140
317
|
}
|
|
141
318
|
}
|
|
142
|
-
function
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
} catch {
|
|
148
|
-
return 0;
|
|
149
|
-
}
|
|
319
|
+
function hasDependency(pkg, ...names) {
|
|
320
|
+
if (!pkg)
|
|
321
|
+
return false;
|
|
322
|
+
const buckets = [pkg.dependencies, pkg.devDependencies, pkg.peerDependencies];
|
|
323
|
+
return names.some((name) => buckets.some((bucket) => bucket && name in bucket));
|
|
150
324
|
}
|
|
151
|
-
function
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
325
|
+
function resolveVue(option) {
|
|
326
|
+
if (option === true)
|
|
327
|
+
return { value: true, source: "explicit" };
|
|
328
|
+
if (option === false)
|
|
329
|
+
return { value: false, source: "explicit" };
|
|
330
|
+
return { value: hasDependency(getPackageJson(), "vue", "nuxt"), source: "auto-detected" };
|
|
331
|
+
}
|
|
332
|
+
function resolveTailwindcss(option) {
|
|
333
|
+
if (option === void 0)
|
|
334
|
+
return { value: false, source: "default" };
|
|
335
|
+
if (option === false)
|
|
336
|
+
return { value: false, source: "explicit" };
|
|
337
|
+
if (option === true)
|
|
338
|
+
return { value: { preset: "recommended", settings: {} }, source: "explicit" };
|
|
339
|
+
if (typeof option === "string")
|
|
340
|
+
return { value: { preset: option, settings: {} }, source: "explicit" };
|
|
341
|
+
return { value: { preset: option.preset ?? "recommended", settings: option }, source: "explicit" };
|
|
159
342
|
}
|
|
160
|
-
function
|
|
161
|
-
const
|
|
162
|
-
|
|
343
|
+
function resolveOptions(opts, env) {
|
|
344
|
+
const vue = resolveVue(opts.vue);
|
|
345
|
+
const tailwind = resolveTailwindcss(opts.tailwindcss);
|
|
346
|
+
return {
|
|
347
|
+
typescript: opts.typescript !== false,
|
|
348
|
+
formatters: opts.formatters !== false,
|
|
349
|
+
unicorn: opts.unicorn !== false,
|
|
350
|
+
sonarjs: opts.sonarjs !== false,
|
|
351
|
+
vue: vue.value,
|
|
352
|
+
vueSource: vue.source,
|
|
353
|
+
vueAccessibility: opts.vueAccessibility === true,
|
|
354
|
+
tailwindcss: tailwind.value,
|
|
355
|
+
tailwindcssSource: tailwind.source,
|
|
356
|
+
env
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function formatSourceTag(source) {
|
|
360
|
+
if (source === "auto-detected")
|
|
361
|
+
return " (auto-detected)";
|
|
362
|
+
if (source === "default")
|
|
363
|
+
return " (default)";
|
|
364
|
+
return "";
|
|
365
|
+
}
|
|
366
|
+
function formatResolutionBox(r) {
|
|
367
|
+
const tailwindLabel = r.tailwindcss === false ? "off" : r.tailwindcss.preset;
|
|
368
|
+
const rows = [
|
|
369
|
+
["typescript", String(r.typescript)],
|
|
370
|
+
["vue", `${r.vue}${formatSourceTag(r.vueSource)}`],
|
|
371
|
+
["vueAccessibility", String(r.vueAccessibility)],
|
|
372
|
+
["sonarjs", String(r.sonarjs)],
|
|
373
|
+
["tailwindcss", `${tailwindLabel}${formatSourceTag(r.tailwindcssSource)}`],
|
|
374
|
+
["formatters", String(r.formatters)],
|
|
375
|
+
["unicorn", String(r.unicorn)],
|
|
376
|
+
["env", r.env]
|
|
377
|
+
];
|
|
378
|
+
const labelWidth = Math.max(...rows.map(([k]) => k.length));
|
|
379
|
+
return rows.map(([k, v]) => `${k.padEnd(labelWidth)} ${v}`).join("\n");
|
|
163
380
|
}
|
|
164
381
|
function defineConfig(options = {}, ...userConfigs) {
|
|
382
|
+
const log = createLogger();
|
|
383
|
+
const defaultLogLevel = process.stdout.isTTY ? "default" : "silent";
|
|
384
|
+
log.setLevel(options.logLevel ?? defaultLogLevel);
|
|
165
385
|
const opts = { ...defaultOptions, ...options, ignores: [...GLOBAL_IGNORES, ...options.ignores || []] };
|
|
166
386
|
const env = opts.env || process.env.NODE_ENV || "production";
|
|
387
|
+
const resolved = resolveOptions(opts, env);
|
|
388
|
+
log.box({
|
|
389
|
+
title: "@maz-ui/eslint-config",
|
|
390
|
+
message: formatResolutionBox(resolved),
|
|
391
|
+
style: { borderColor: "cyan", padding: 1 }
|
|
392
|
+
});
|
|
167
393
|
const additionalConfigs = [];
|
|
168
|
-
if (
|
|
394
|
+
if (resolved.vue) {
|
|
169
395
|
additionalConfigs.push({
|
|
170
396
|
files: ["**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue}"],
|
|
171
397
|
rules: vueRules
|
|
172
398
|
});
|
|
399
|
+
additionalConfigs.push({
|
|
400
|
+
files: ["**/*.vue"],
|
|
401
|
+
rules: vueSfcOnlyRules
|
|
402
|
+
});
|
|
403
|
+
log.debug(`${TAG} Vue: applied vueRules to JS/TS/Vue files + SFC-only overrides to *.vue`);
|
|
173
404
|
}
|
|
174
|
-
if (
|
|
405
|
+
if (resolved.sonarjs) {
|
|
175
406
|
const sonarjsFiles = ["**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue}"];
|
|
176
407
|
additionalConfigs.push({
|
|
177
408
|
...configs.recommended,
|
|
@@ -185,8 +416,9 @@ function defineConfig(options = {}, ...userConfigs) {
|
|
|
185
416
|
files: ["**/*.spec.ts", "**/*.test.ts", "**/*.spec.js", "**/*.test.js"],
|
|
186
417
|
rules: sonarjsTestRules
|
|
187
418
|
});
|
|
419
|
+
log.debug(`${TAG} SonarJS: applied recommended preset + ${Object.keys(sonarjsRules).length} extra rules + test-file relaxations`);
|
|
188
420
|
}
|
|
189
|
-
if (
|
|
421
|
+
if (resolved.vueAccessibility) {
|
|
190
422
|
const vueA11yConfigs = vueA11y.configs["flat/recommended"];
|
|
191
423
|
const configsArray = Array.isArray(vueA11yConfigs) ? vueA11yConfigs : [vueA11yConfigs];
|
|
192
424
|
const fixedConfigs = configsArray.map((config) => {
|
|
@@ -207,39 +439,35 @@ function defineConfig(options = {}, ...userConfigs) {
|
|
|
207
439
|
return config;
|
|
208
440
|
});
|
|
209
441
|
additionalConfigs.push(...fixedConfigs);
|
|
442
|
+
log.debug(`${TAG} Vue a11y: applied ${fixedConfigs.length} flat-config block(s) from eslint-plugin-vuejs-accessibility`);
|
|
210
443
|
}
|
|
211
|
-
if (
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (!tailwindPlugin) {
|
|
218
|
-
console.warn("[maz-eslint-config] opts.tailwindcss is true but eslint-plugin-tailwindcss is not installed. Add it to your devDependencies to enable the Tailwind CSS lint rules.");
|
|
219
|
-
} else {
|
|
220
|
-
const tailwindConfigs = tailwindPlugin.configs?.["flat/recommended"];
|
|
221
|
-
if (Array.isArray(tailwindConfigs)) {
|
|
222
|
-
additionalConfigs.push(...tailwindConfigs);
|
|
223
|
-
}
|
|
224
|
-
additionalConfigs.push({ rules: tailwindcssRules });
|
|
225
|
-
}
|
|
226
|
-
}
|
|
444
|
+
if (resolved.tailwindcss) {
|
|
445
|
+
const blocks = tailwindcssConfigs(resolved.tailwindcss.preset, resolved.tailwindcss.settings);
|
|
446
|
+
additionalConfigs.push(...blocks);
|
|
447
|
+
const settingKeys = Object.keys(resolved.tailwindcss.settings).filter((k) => k !== "preset");
|
|
448
|
+
const settingsHint = settingKeys.length > 0 ? ` with settings (${settingKeys.join(", ")})` : "";
|
|
449
|
+
log.debug(`${TAG} Tailwind: loaded "${resolved.tailwindcss.preset}" preset${settingsHint}`);
|
|
227
450
|
}
|
|
228
451
|
additionalConfigs.push({
|
|
229
452
|
files: ["**/*.spec.ts", "**/*.test.ts", "**/*.spec.js", "**/*.test.js"],
|
|
230
453
|
rules: testRules
|
|
231
454
|
});
|
|
455
|
+
log.debug(`${TAG} Tests: relaxed rules applied to *.spec.{ts,js} / *.test.{ts,js}`);
|
|
456
|
+
if (options.rules && Object.keys(options.rules).length > 0)
|
|
457
|
+
log.debug(`${TAG} User: merged ${Object.keys(options.rules).length} rule override(s)`);
|
|
458
|
+
if (userConfigs.length > 0)
|
|
459
|
+
log.debug(`${TAG} User: appended ${userConfigs.length} flat-config argument(s)`);
|
|
460
|
+
log.verbose(`${TAG} Final config block count: ${additionalConfigs.length + userConfigs.length + 1} (additional: ${additionalConfigs.length}, user: ${userConfigs.length}, +markdown)`);
|
|
461
|
+
log.verbose(`${TAG} Ignore globs: ${opts.ignores.length}`);
|
|
462
|
+
const userRulesBlock = opts.rules && Object.keys(opts.rules).length > 0 ? { rules: opts.rules } : void 0;
|
|
232
463
|
return antfu({
|
|
233
464
|
formatters: opts.formatters,
|
|
234
465
|
...opts,
|
|
235
|
-
rules:
|
|
236
|
-
...baseRules(env === "production"),
|
|
237
|
-
...opts.rules
|
|
238
|
-
},
|
|
466
|
+
rules: baseRules(env === "production"),
|
|
239
467
|
ignores: (() => {
|
|
240
468
|
return opts.ignores;
|
|
241
469
|
})
|
|
242
|
-
}, ...additionalConfigs, ...userConfigs, markdown);
|
|
470
|
+
}, ...additionalConfigs, ...userConfigs, ...userRulesBlock ? [userRulesBlock] : [], markdown);
|
|
243
471
|
}
|
|
244
472
|
|
|
245
|
-
export { baseRules, defineConfig, sonarjsRules, sonarjsTestRules,
|
|
473
|
+
export { TAILWINDCSS_DEFAULT_FILES, baseRules, defineConfig, mazPlugin, rules as mazRules, sonarjsRules, sonarjsTestRules, tailwindcssConfigs, vueRules, vueSfcOnlyRules };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maz-ui/eslint-config",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.0.0-beta.
|
|
4
|
+
"version": "5.0.0-beta.11",
|
|
5
5
|
"description": "ESLint configuration for JavaScript/TypeScript projects",
|
|
6
6
|
"author": "Louis Mazel <me@loicmazuel.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"exports": {
|
|
26
26
|
".": {
|
|
27
27
|
"types": "./dist/index.d.ts",
|
|
28
|
-
"import": "./dist/index.mjs"
|
|
28
|
+
"import": "./dist/index.mjs",
|
|
29
|
+
"default": "./dist/index.mjs"
|
|
29
30
|
}
|
|
30
31
|
},
|
|
31
32
|
"main": "./dist/index.mjs",
|
|
@@ -39,24 +40,18 @@
|
|
|
39
40
|
"node": ">=20.19.0"
|
|
40
41
|
},
|
|
41
42
|
"peerDependencies": {
|
|
42
|
-
"eslint": ">=9.0.0 <11.0.0"
|
|
43
|
-
"eslint-plugin-tailwindcss": "^3.18.0"
|
|
44
|
-
},
|
|
45
|
-
"peerDependenciesMeta": {
|
|
46
|
-
"eslint-plugin-tailwindcss": {
|
|
47
|
-
"optional": true
|
|
48
|
-
}
|
|
43
|
+
"eslint": ">=9.0.0 <11.0.0"
|
|
49
44
|
},
|
|
50
45
|
"dependencies": {
|
|
51
46
|
"@antfu/eslint-config": "^8.2.0",
|
|
47
|
+
"consola": "^3.4.2",
|
|
48
|
+
"eslint-plugin-better-tailwindcss": "^4.5.0",
|
|
52
49
|
"eslint-plugin-format": "^2.0.1",
|
|
53
50
|
"eslint-plugin-sonarjs": "^4.0.3",
|
|
54
51
|
"eslint-plugin-vuejs-accessibility": "^2.5.0"
|
|
55
52
|
},
|
|
56
53
|
"devDependencies": {
|
|
57
|
-
"@types/eslint-plugin-tailwindcss": "^3.17.0",
|
|
58
54
|
"eslint": "^10.3.0",
|
|
59
|
-
"eslint-plugin-tailwindcss": "^3.18.3",
|
|
60
55
|
"unbuild": "^3.6.1"
|
|
61
56
|
},
|
|
62
57
|
"lint-staged": {
|
|
@@ -70,6 +65,9 @@
|
|
|
70
65
|
"dev": "unbuild --stub",
|
|
71
66
|
"typecheck": "tsc --noEmit --skipLibCheck",
|
|
72
67
|
"lint": "cross-env NODE_ENV=production eslint .",
|
|
73
|
-
"lint:fix": "pnpm lint --fix"
|
|
68
|
+
"lint:fix": "pnpm lint --fix",
|
|
69
|
+
"test:unit": "vitest run",
|
|
70
|
+
"test:unit:coverage": "vitest run --coverage",
|
|
71
|
+
"test:unit:watch": "vitest watch"
|
|
74
72
|
}
|
|
75
73
|
}
|