eslint-plugin-mui-v7 1.0.0 → 1.2.0
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 +124 -95
- package/index.cjs +104 -121
- package/index.js +104 -121
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
# eslint-plugin-mui-v7
|
|
2
2
|
|
|
3
|
-
> ESLint plugin
|
|
3
|
+
> ESLint plugin focused on Material-UI V6 to V7 **breaking changes** with educational error messages
|
|
4
4
|
|
|
5
|
-
Automatically detect
|
|
5
|
+
Automatically detect code that **BREAKS** when migrating from MUI V6 to V7 and teach developers the correct way through helpful messages with emojis and examples!
|
|
6
|
+
|
|
7
|
+
## 🎯 Philosophy
|
|
8
|
+
|
|
9
|
+
This plugin focuses on **breaking changes only** - code that will actually break when upgrading to V7. We don't warn about best practices or style preferences, just things that will cause errors.
|
|
6
10
|
|
|
7
11
|
## ✨ Features
|
|
8
12
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
13
|
+
- 🚀 **Detect Unstable_Grid2 usage** - Now promoted to stable Grid
|
|
14
|
+
- ⚠️ **Catch Grid2 usage** - Grid2 was renamed to Grid in V7
|
|
15
|
+
- 🎯 **Grid item prop detection** - Grid doesn't use `item` prop anymore, use `size` instead
|
|
16
|
+
- ✨ **Find moved @mui/lab components** - Alert, Skeleton, Rating, etc. are now in @mui/material
|
|
17
|
+
- 🔄 **Detect deprecated props** - onBackdropClick, size="normal", Hidden component
|
|
18
|
+
- 💡 **Theme variables suggestion** - Use `theme.vars.*` for automatic dark mode support (optional)
|
|
15
19
|
- 🔧 **Auto-fix available** for most rules!
|
|
16
20
|
|
|
17
21
|
## 📦 Installation
|
|
@@ -20,9 +24,20 @@ Automatically detect incorrect usage of Material-UI V7 and teach developers the
|
|
|
20
24
|
npm install --save-dev eslint-plugin-mui-v7
|
|
21
25
|
```
|
|
22
26
|
|
|
23
|
-
## 🚀
|
|
27
|
+
## 🚀 Quick Start
|
|
28
|
+
|
|
29
|
+
### ESLint 9+ (Flat Config) - Recommended
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
// eslint.config.js
|
|
33
|
+
import muiV7Plugin from 'eslint-plugin-mui-v7'
|
|
34
|
+
|
|
35
|
+
export default [
|
|
36
|
+
muiV7Plugin.configs.recommended, // ✅ Apply all recommended rules
|
|
37
|
+
]
|
|
38
|
+
```
|
|
24
39
|
|
|
25
|
-
###
|
|
40
|
+
### Manual Configuration
|
|
26
41
|
|
|
27
42
|
```javascript
|
|
28
43
|
// eslint.config.js
|
|
@@ -34,15 +49,14 @@ export default [
|
|
|
34
49
|
'mui-v7': muiV7Plugin,
|
|
35
50
|
},
|
|
36
51
|
rules: {
|
|
37
|
-
//
|
|
38
|
-
'mui-v7/no-
|
|
52
|
+
// Breaking changes - ERRORS (código quebra)
|
|
53
|
+
'mui-v7/no-unstable-grid': 'error',
|
|
39
54
|
'mui-v7/no-grid2-import': 'error',
|
|
40
|
-
'mui-v7/no-lab-imports': 'error',
|
|
41
55
|
'mui-v7/no-grid-item-prop': 'error',
|
|
56
|
+
'mui-v7/no-lab-imports': 'error',
|
|
42
57
|
'mui-v7/no-deprecated-props': 'error',
|
|
43
58
|
|
|
44
|
-
//
|
|
45
|
-
'mui-v7/no-old-grid-import': 'warn',
|
|
59
|
+
// Best practices - WARNINGS (sugestões)
|
|
46
60
|
'mui-v7/prefer-theme-vars': 'warn',
|
|
47
61
|
},
|
|
48
62
|
},
|
|
@@ -56,42 +70,33 @@ export default [
|
|
|
56
70
|
module.exports = {
|
|
57
71
|
plugins: ['mui-v7'],
|
|
58
72
|
rules: {
|
|
59
|
-
'mui-v7/no-
|
|
73
|
+
'mui-v7/no-unstable-grid': 'error',
|
|
60
74
|
'mui-v7/no-grid2-import': 'error',
|
|
61
|
-
'mui-v7/no-lab-imports': 'error',
|
|
62
75
|
'mui-v7/no-grid-item-prop': 'error',
|
|
76
|
+
'mui-v7/no-lab-imports': 'error',
|
|
63
77
|
'mui-v7/no-deprecated-props': 'error',
|
|
64
|
-
'mui-v7/no-old-grid-import': 'warn',
|
|
65
78
|
'mui-v7/prefer-theme-vars': 'warn',
|
|
66
79
|
},
|
|
67
80
|
}
|
|
68
81
|
```
|
|
69
82
|
|
|
70
|
-
### Using Recommended Config
|
|
71
|
-
|
|
72
|
-
```javascript
|
|
73
|
-
// eslint.config.js
|
|
74
|
-
import muiV7Plugin from 'eslint-plugin-mui-v7'
|
|
75
|
-
|
|
76
|
-
export default [
|
|
77
|
-
muiV7Plugin.configs.recommended, // Applies all recommended rules
|
|
78
|
-
]
|
|
79
|
-
```
|
|
80
|
-
|
|
81
83
|
## 📋 Rules
|
|
82
84
|
|
|
83
|
-
### 🚨
|
|
85
|
+
### 🚨 Breaking Changes (Errors)
|
|
86
|
+
|
|
87
|
+
These rules detect code that **WILL BREAK** in MUI V7.
|
|
84
88
|
|
|
85
|
-
#### `mui-v7/no-
|
|
89
|
+
#### `mui-v7/no-unstable-grid` ✨ NEW in v1.1.0
|
|
86
90
|
|
|
87
|
-
|
|
91
|
+
Unstable_Grid2 was promoted to stable Grid in V7.
|
|
88
92
|
|
|
89
93
|
```typescript
|
|
90
|
-
// ❌
|
|
91
|
-
import
|
|
94
|
+
// ❌ Breaks in V7
|
|
95
|
+
import Grid from '@mui/material/Unstable_Grid2'
|
|
96
|
+
import Grid2 from '@mui/material/Unstable_Grid2'
|
|
92
97
|
|
|
93
|
-
// ✅
|
|
94
|
-
import {
|
|
98
|
+
// ✅ Recommended
|
|
99
|
+
import { Grid } from '@mui/material'
|
|
95
100
|
```
|
|
96
101
|
|
|
97
102
|
#### `mui-v7/no-grid2-import`
|
|
@@ -99,102 +104,94 @@ import { createTheme } from '@mui/material/styles'
|
|
|
99
104
|
Grid2 was renamed to Grid in V7.
|
|
100
105
|
|
|
101
106
|
```typescript
|
|
102
|
-
// ❌
|
|
107
|
+
// ❌ Breaks in V7
|
|
103
108
|
import Grid2 from '@mui/material/Grid2'
|
|
109
|
+
import { grid2Classes } from '@mui/material/Grid2'
|
|
104
110
|
|
|
105
|
-
// ✅
|
|
106
|
-
import Grid from '@mui/material
|
|
111
|
+
// ✅ Recommended
|
|
112
|
+
import { Grid } from '@mui/material'
|
|
113
|
+
import { gridClasses } from '@mui/material'
|
|
107
114
|
```
|
|
108
115
|
|
|
109
|
-
#### `mui-v7/no-lab-imports`
|
|
110
|
-
|
|
111
|
-
Components moved from @mui/lab to @mui/material.
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
// ❌ Bad
|
|
115
|
-
import Alert from '@mui/lab/Alert'
|
|
116
|
-
import Skeleton from '@mui/lab/Skeleton'
|
|
117
|
-
|
|
118
|
-
// ✅ Good
|
|
119
|
-
import Alert from '@mui/material/Alert'
|
|
120
|
-
import Skeleton from '@mui/material/Skeleton'
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
**Moved components:** Alert, AlertTitle, Autocomplete, AvatarGroup, Pagination, PaginationItem, Rating, Skeleton, SpeedDial, SpeedDialAction, SpeedDialIcon, TabContext, TabList, TabPanel, Timeline*, ToggleButton, ToggleButtonGroup, TreeView, TreeItem
|
|
124
|
-
|
|
125
116
|
#### `mui-v7/no-grid-item-prop`
|
|
126
117
|
|
|
127
118
|
Grid doesn't use `item` prop anymore, use `size` instead.
|
|
128
119
|
|
|
129
120
|
```typescript
|
|
130
|
-
// ❌
|
|
131
|
-
<Grid item xs={12}
|
|
121
|
+
// ❌ Breaks in V7
|
|
122
|
+
<Grid item xs={12} sm={6} md={4}>
|
|
132
123
|
Content
|
|
133
124
|
</Grid>
|
|
134
125
|
|
|
135
|
-
// ✅
|
|
136
|
-
<Grid size={{ xs: 12,
|
|
126
|
+
// ✅ Works in V7
|
|
127
|
+
<Grid size={{ xs: 12, sm: 6, md: 4 }}>
|
|
137
128
|
Content
|
|
138
129
|
</Grid>
|
|
139
130
|
```
|
|
140
131
|
|
|
132
|
+
#### `mui-v7/no-lab-imports`
|
|
133
|
+
|
|
134
|
+
Components moved from @mui/lab to @mui/material.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// ❌ Breaks in V7
|
|
138
|
+
import { Alert } from '@mui/lab'
|
|
139
|
+
import { Skeleton } from '@mui/lab'
|
|
140
|
+
|
|
141
|
+
// ✅ Recommended
|
|
142
|
+
import { Alert } from '@mui/material'
|
|
143
|
+
import { Skeleton } from '@mui/material'
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Moved components:** Alert, AlertTitle, Autocomplete, AvatarGroup, Pagination, PaginationItem, Rating, Skeleton, SpeedDial, SpeedDialAction, SpeedDialIcon, TabContext, TabList, TabPanel, Timeline*, ToggleButton, ToggleButtonGroup, TreeView, TreeItem
|
|
147
|
+
|
|
141
148
|
#### `mui-v7/no-deprecated-props`
|
|
142
149
|
|
|
143
|
-
Detects
|
|
150
|
+
Detects props removed in V7.
|
|
144
151
|
|
|
145
152
|
```typescript
|
|
146
|
-
// ❌
|
|
153
|
+
// ❌ Dialog.onBackdropClick - REMOVED
|
|
147
154
|
<Dialog onBackdropClick={handleClick}>
|
|
148
155
|
|
|
149
|
-
// ✅
|
|
156
|
+
// ✅ Use onClose with reason check
|
|
150
157
|
<Dialog onClose={(event, reason) => {
|
|
151
158
|
if (reason === 'backdropClick') {
|
|
152
159
|
// Your logic here
|
|
153
160
|
}
|
|
154
161
|
}}>
|
|
155
162
|
|
|
156
|
-
// ❌
|
|
163
|
+
// ❌ InputLabel size="normal" - RENAMED
|
|
157
164
|
<InputLabel size="normal">
|
|
158
165
|
|
|
159
|
-
// ✅
|
|
166
|
+
// ✅ Use size="medium"
|
|
160
167
|
<InputLabel size="medium">
|
|
161
168
|
|
|
162
|
-
// ❌
|
|
169
|
+
// ❌ Hidden component - REMOVED
|
|
163
170
|
<Hidden xlUp><Paper /></Hidden>
|
|
164
171
|
|
|
165
|
-
// ✅
|
|
172
|
+
// ✅ Use sx prop
|
|
166
173
|
<Paper sx={{ display: { xl: 'none' } }} />
|
|
167
174
|
|
|
168
|
-
// ✅
|
|
175
|
+
// ✅ Or use useMediaQuery
|
|
169
176
|
const hidden = useMediaQuery(theme => theme.breakpoints.up('xl'))
|
|
170
177
|
return hidden ? null : <Paper />
|
|
171
178
|
```
|
|
172
179
|
|
|
173
|
-
###
|
|
174
|
-
|
|
175
|
-
#### `mui-v7/no-old-grid-import`
|
|
180
|
+
### 💡 Best Practices (Warnings)
|
|
176
181
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
// ⚠️ If you want to keep old Grid
|
|
181
|
-
import Grid from '@mui/material/GridLegacy'
|
|
182
|
-
|
|
183
|
-
// ✅ Recommended: Migrate to new Grid
|
|
184
|
-
import Grid from '@mui/material/Grid'
|
|
185
|
-
```
|
|
182
|
+
These are suggestions, not breaking changes.
|
|
186
183
|
|
|
187
184
|
#### `mui-v7/prefer-theme-vars`
|
|
188
185
|
|
|
189
|
-
When using `cssVariables: true`, use `theme.vars.*` for automatic dark mode
|
|
186
|
+
When using `cssVariables: true`, use `theme.vars.*` for better performance and automatic dark mode.
|
|
190
187
|
|
|
191
188
|
```typescript
|
|
192
|
-
// ⚠️
|
|
189
|
+
// ⚠️ Works but doesn't change with dark mode automatically
|
|
193
190
|
const Custom = styled('div')(({ theme }) => ({
|
|
194
191
|
color: theme.palette.text.primary,
|
|
195
192
|
}))
|
|
196
193
|
|
|
197
|
-
// ✅
|
|
194
|
+
// ✅ Better: Changes automatically with dark mode
|
|
198
195
|
const Custom = styled('div')(({ theme }) => ({
|
|
199
196
|
color: theme.vars.palette.text.primary,
|
|
200
197
|
}))
|
|
@@ -214,41 +211,73 @@ The plugin provides educational messages with emojis and examples:
|
|
|
214
211
|
<Grid size={{ xs: 12, sm: 6, md: 4 }}>
|
|
215
212
|
|
|
216
213
|
💡 A nova sintaxe é mais limpa e poderosa!
|
|
217
|
-
Você pode usar
|
|
214
|
+
Você pode usar: size, offset, spacing responsivo e mais.
|
|
218
215
|
```
|
|
219
216
|
|
|
220
|
-
## 🔧 Configuration
|
|
217
|
+
## 🔧 Configuration Presets
|
|
218
|
+
|
|
219
|
+
### `recommended` - Balanced (Default)
|
|
221
220
|
|
|
222
|
-
|
|
221
|
+
Breaking changes as **errors**, best practices as **warnings**.
|
|
223
222
|
|
|
224
223
|
```javascript
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
224
|
+
import muiV7Plugin from 'eslint-plugin-mui-v7'
|
|
225
|
+
|
|
226
|
+
export default [
|
|
227
|
+
muiV7Plugin.configs.recommended,
|
|
228
|
+
]
|
|
230
229
|
```
|
|
231
230
|
|
|
232
|
-
###
|
|
231
|
+
### `strict` - Strict Mode
|
|
232
|
+
|
|
233
|
+
Everything as **errors** (including best practices).
|
|
233
234
|
|
|
234
235
|
```javascript
|
|
235
236
|
import muiV7Plugin from 'eslint-plugin-mui-v7'
|
|
236
237
|
|
|
237
238
|
export default [
|
|
238
|
-
muiV7Plugin.configs.
|
|
239
|
+
muiV7Plugin.configs.strict,
|
|
239
240
|
]
|
|
240
241
|
```
|
|
241
242
|
|
|
242
|
-
|
|
243
|
+
## 🆕 What's New in v1.1.0
|
|
244
|
+
|
|
245
|
+
### Added
|
|
246
|
+
- ✨ New rule `no-unstable-grid` - Detects Unstable_Grid2 usage
|
|
243
247
|
|
|
248
|
+
### Changed
|
|
249
|
+
- 📝 All import examples now show recommended style: `import { Grid } from '@mui/material'`
|
|
250
|
+
- 🎯 Refocused on breaking changes only (removed non-breaking rules)
|
|
251
|
+
- 📦 Updated plugin description and categories
|
|
252
|
+
|
|
253
|
+
### Removed
|
|
254
|
+
- ❌ `no-deep-imports` - Not a breaking change in V7
|
|
255
|
+
- ❌ `no-old-grid-import` - Confusing and not a breaking change
|
|
256
|
+
|
|
257
|
+
## 📚 Migration Guide
|
|
258
|
+
|
|
259
|
+
1. Install the plugin:
|
|
260
|
+
```bash
|
|
261
|
+
npm install --save-dev eslint-plugin-mui-v7
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
2. Add to your ESLint config:
|
|
244
265
|
```javascript
|
|
266
|
+
// eslint.config.js
|
|
245
267
|
import muiV7Plugin from 'eslint-plugin-mui-v7'
|
|
246
268
|
|
|
247
269
|
export default [
|
|
248
|
-
muiV7Plugin.configs.
|
|
270
|
+
muiV7Plugin.configs.recommended,
|
|
249
271
|
]
|
|
250
272
|
```
|
|
251
273
|
|
|
274
|
+
3. Run ESLint:
|
|
275
|
+
```bash
|
|
276
|
+
npx eslint . --fix
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
4. Fix remaining issues manually (the plugin will guide you!)
|
|
280
|
+
|
|
252
281
|
## 🤝 Contributing
|
|
253
282
|
|
|
254
283
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -269,4 +298,4 @@ Created by **Matheus Pimenta** (Koda AI Studio) + **Claude Code**
|
|
|
269
298
|
|
|
270
299
|
---
|
|
271
300
|
|
|
272
|
-
**Keywords:** eslint, mui, material-ui, mui-v7, react, typescript, linter, code-quality
|
|
301
|
+
**Keywords:** eslint, mui, material-ui, mui-v7, react, typescript, linter, code-quality, migration, breaking-changes
|
package/index.cjs
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ESLint Plugin
|
|
2
|
+
* ESLint Plugin para MUI V7 - Foca em Breaking Changes (CommonJS version)
|
|
3
3
|
*
|
|
4
|
-
* Detecta automaticamente
|
|
5
|
-
* mensagens educativas para
|
|
4
|
+
* Detecta automaticamente código que QUEBRA na migração V6 → V7
|
|
5
|
+
* e fornece mensagens educativas para corrigir.
|
|
6
6
|
*
|
|
7
|
+
* @version 1.2.0
|
|
7
8
|
* @created 2025-01-26
|
|
9
|
+
* @updated 2025-01-29
|
|
8
10
|
* @author Matheus (Koda AI Studio) + Claude Code
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
const muiV7Rules = {
|
|
12
|
-
'no-
|
|
14
|
+
'no-unstable-grid': {
|
|
13
15
|
meta: {
|
|
14
16
|
type: 'problem',
|
|
15
17
|
docs: {
|
|
16
|
-
description: '
|
|
17
|
-
category: '
|
|
18
|
+
description: 'Unstable_Grid2 foi promovido para Grid no MUI V7',
|
|
19
|
+
category: 'Breaking Changes',
|
|
18
20
|
recommended: true,
|
|
19
21
|
},
|
|
20
22
|
messages: {
|
|
21
|
-
|
|
22
|
-
'🔧 Forma
|
|
23
|
-
' import
|
|
24
|
-
'
|
|
25
|
-
'
|
|
23
|
+
unstableGrid: '🚀 Unstable_Grid2 foi promovido para Grid estável no MUI V7!\n\n' +
|
|
24
|
+
'🔧 Forma antiga (V6):\n' +
|
|
25
|
+
' import Grid from "@mui/material/Unstable_Grid2"\n' +
|
|
26
|
+
' import Grid2 from "@mui/material/Unstable_Grid2"\n\n' +
|
|
27
|
+
'✅ Forma nova (V7):\n' +
|
|
28
|
+
' import { Grid } from "@mui/material"\n\n' +
|
|
29
|
+
'💡 O Grid agora é estável e usa a prop `size`!',
|
|
26
30
|
},
|
|
27
31
|
schema: [],
|
|
28
32
|
fixable: 'code',
|
|
@@ -32,30 +36,14 @@ const muiV7Rules = {
|
|
|
32
36
|
ImportDeclaration(node) {
|
|
33
37
|
const source = node.source.value;
|
|
34
38
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
messageId: 'deepImport',
|
|
44
|
-
fix(fixer) {
|
|
45
|
-
// Tenta converter para named import
|
|
46
|
-
const specifier = node.specifiers[0];
|
|
47
|
-
if (specifier && specifier.type === 'ImportDefaultSpecifier') {
|
|
48
|
-
const importName = specifier.local.name;
|
|
49
|
-
const newPath = parts.slice(0, 3).join('/'); // Remove último nível
|
|
50
|
-
return fixer.replaceText(
|
|
51
|
-
node,
|
|
52
|
-
`import { ${importName} } from "${newPath}"`
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
}
|
|
39
|
+
if (source === '@mui/material/Unstable_Grid2') {
|
|
40
|
+
context.report({
|
|
41
|
+
node,
|
|
42
|
+
messageId: 'unstableGrid',
|
|
43
|
+
fix(fixer) {
|
|
44
|
+
return fixer.replaceText(node.source, '"@mui/material"');
|
|
45
|
+
},
|
|
46
|
+
});
|
|
59
47
|
}
|
|
60
48
|
},
|
|
61
49
|
};
|
|
@@ -67,7 +55,7 @@ const muiV7Rules = {
|
|
|
67
55
|
type: 'problem',
|
|
68
56
|
docs: {
|
|
69
57
|
description: 'Grid2 foi renomeado para Grid no MUI V7',
|
|
70
|
-
category: '
|
|
58
|
+
category: 'Breaking Changes',
|
|
71
59
|
recommended: true,
|
|
72
60
|
},
|
|
73
61
|
messages: {
|
|
@@ -75,10 +63,10 @@ const muiV7Rules = {
|
|
|
75
63
|
'🔧 Forma antiga (V6):\n' +
|
|
76
64
|
' import Grid2 from "@mui/material/Grid2"\n' +
|
|
77
65
|
' import { grid2Classes } from "@mui/material/Grid2"\n\n' +
|
|
78
|
-
'✅
|
|
79
|
-
' import Grid from "@mui/material
|
|
80
|
-
' import { gridClasses } from "@mui/material
|
|
81
|
-
'💡
|
|
66
|
+
'✅ Recomendado:\n' +
|
|
67
|
+
' import { Grid } from "@mui/material"\n' +
|
|
68
|
+
' import { gridClasses } from "@mui/material"\n\n' +
|
|
69
|
+
'💡 O novo Grid é mais poderoso e usa a prop `size`!',
|
|
82
70
|
},
|
|
83
71
|
schema: [],
|
|
84
72
|
fixable: 'code',
|
|
@@ -93,23 +81,7 @@ const muiV7Rules = {
|
|
|
93
81
|
node,
|
|
94
82
|
messageId: 'grid2Import',
|
|
95
83
|
fix(fixer) {
|
|
96
|
-
|
|
97
|
-
fixer.replaceText(node.source, '"@mui/material/Grid"')
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
// Renomeia Grid2 -> Grid e grid2Classes -> gridClasses
|
|
101
|
-
node.specifiers.forEach(spec => {
|
|
102
|
-
if (spec.imported) {
|
|
103
|
-
const name = spec.imported.name;
|
|
104
|
-
if (name.includes('grid2')) {
|
|
105
|
-
const newName = name.replace('grid2', 'grid');
|
|
106
|
-
// Nota: Isso só funciona bem para casos simples
|
|
107
|
-
// Para casos complexos, o usuário precisa fazer manual
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
return fixes;
|
|
84
|
+
return fixer.replaceText(node.source, '"@mui/material"');
|
|
113
85
|
},
|
|
114
86
|
});
|
|
115
87
|
}
|
|
@@ -118,67 +90,20 @@ const muiV7Rules = {
|
|
|
118
90
|
},
|
|
119
91
|
},
|
|
120
92
|
|
|
121
|
-
'no-old-grid-import': {
|
|
122
|
-
meta: {
|
|
123
|
-
type: 'suggestion',
|
|
124
|
-
docs: {
|
|
125
|
-
description: 'Sugere migração do Grid antigo para o novo',
|
|
126
|
-
category: 'Best Practices',
|
|
127
|
-
recommended: false,
|
|
128
|
-
},
|
|
129
|
-
messages: {
|
|
130
|
-
oldGrid: '💡 O Grid antigo agora é GridLegacy. Considere migrar para o novo Grid!\n\n' +
|
|
131
|
-
'🔧 Se quiser manter o Grid antigo:\n' +
|
|
132
|
-
' import Grid from "@mui/material/GridLegacy"\n' +
|
|
133
|
-
' import { gridLegacyClasses } from "@mui/material/GridLegacy"\n\n' +
|
|
134
|
-
'✅ Recomendado: Migrar para o novo Grid:\n' +
|
|
135
|
-
' import Grid from "@mui/material/Grid"\n\n' +
|
|
136
|
-
'📚 O novo Grid usa `size` em vez de `xs/sm/md`:\n' +
|
|
137
|
-
' <Grid size={{ xs: 12, md: 6 }}>Conteúdo</Grid>',
|
|
138
|
-
},
|
|
139
|
-
schema: [],
|
|
140
|
-
},
|
|
141
|
-
create(context) {
|
|
142
|
-
const sourceCode = context.getSourceCode();
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
ImportDeclaration(node) {
|
|
146
|
-
const source = node.source.value;
|
|
147
|
-
|
|
148
|
-
// Detecta import Grid from '@mui/material/Grid'
|
|
149
|
-
if (source === '@mui/material/Grid') {
|
|
150
|
-
const defaultImport = node.specifiers.find(
|
|
151
|
-
spec => spec.type === 'ImportDefaultSpecifier'
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
if (defaultImport) {
|
|
155
|
-
// Verifica se está usando props antigas (xs, sm, md) no código
|
|
156
|
-
// Isso é apenas um aviso suave, não um erro
|
|
157
|
-
context.report({
|
|
158
|
-
node,
|
|
159
|
-
messageId: 'oldGrid',
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
|
-
};
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
|
|
168
93
|
'no-lab-imports': {
|
|
169
94
|
meta: {
|
|
170
95
|
type: 'problem',
|
|
171
96
|
docs: {
|
|
172
97
|
description: 'Componentes movidos de @mui/lab para @mui/material',
|
|
173
|
-
category: '
|
|
98
|
+
category: 'Breaking Changes',
|
|
174
99
|
recommended: true,
|
|
175
100
|
},
|
|
176
101
|
messages: {
|
|
177
102
|
labImport: '✨ Este componente foi movido para @mui/material no V7!\n\n' +
|
|
178
103
|
'🔧 Forma antiga (V6):\n' +
|
|
179
|
-
' import {{ component }} from "@mui/lab
|
|
180
|
-
'✅
|
|
181
|
-
' import {{ component }} from "@mui/material
|
|
104
|
+
' import {{ component }} from "@mui/lab"\n\n' +
|
|
105
|
+
'✅ Recomendado:\n' +
|
|
106
|
+
' import { {{ component }} } from "@mui/material"\n\n' +
|
|
182
107
|
'📦 Componentes movidos: Alert, Autocomplete, Pagination, Rating,\n' +
|
|
183
108
|
' Skeleton, SpeedDial, ToggleButton, AvatarGroup, e mais!',
|
|
184
109
|
},
|
|
@@ -216,10 +141,7 @@ const muiV7Rules = {
|
|
|
216
141
|
messageId: 'labImport',
|
|
217
142
|
data: { component: componentName },
|
|
218
143
|
fix(fixer) {
|
|
219
|
-
return fixer.replaceText(
|
|
220
|
-
node.source,
|
|
221
|
-
`"@mui/material/${componentName}"`
|
|
222
|
-
);
|
|
144
|
+
return fixer.replaceText(node.source, '"@mui/material"');
|
|
223
145
|
},
|
|
224
146
|
});
|
|
225
147
|
}
|
|
@@ -235,7 +157,7 @@ const muiV7Rules = {
|
|
|
235
157
|
type: 'problem',
|
|
236
158
|
docs: {
|
|
237
159
|
description: 'Grid não usa mais a prop `item`, agora usa `size`',
|
|
238
|
-
category: '
|
|
160
|
+
category: 'Breaking Changes',
|
|
239
161
|
recommended: true,
|
|
240
162
|
},
|
|
241
163
|
messages: {
|
|
@@ -245,7 +167,7 @@ const muiV7Rules = {
|
|
|
245
167
|
'✅ Forma nova (V7):\n' +
|
|
246
168
|
' <Grid size={{ xs: 12, sm: 6, md: 4 }}>\n\n' +
|
|
247
169
|
'💡 A nova sintaxe é mais limpa e poderosa!\n' +
|
|
248
|
-
' Você pode usar
|
|
170
|
+
' Você pode usar: size, offset, spacing responsivo e mais.',
|
|
249
171
|
},
|
|
250
172
|
schema: [],
|
|
251
173
|
},
|
|
@@ -279,7 +201,7 @@ const muiV7Rules = {
|
|
|
279
201
|
type: 'problem',
|
|
280
202
|
docs: {
|
|
281
203
|
description: 'Detecta props depreciadas no MUI V7',
|
|
282
|
-
category: '
|
|
204
|
+
category: 'Breaking Changes',
|
|
283
205
|
recommended: true,
|
|
284
206
|
},
|
|
285
207
|
messages: {
|
|
@@ -379,6 +301,55 @@ const muiV7Rules = {
|
|
|
379
301
|
create(context) {
|
|
380
302
|
const sourceCode = context.getSourceCode();
|
|
381
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Verifica se o node está dentro de um ternário que checa theme.vars
|
|
306
|
+
* Exemplo: theme.vars ? `${theme.vars.palette.primary.main}` : `${theme.palette.primary.main}`
|
|
307
|
+
*/
|
|
308
|
+
function isInsideThemeVarsConditional(node) {
|
|
309
|
+
let current = node;
|
|
310
|
+
|
|
311
|
+
// Sobe até 10 níveis na árvore AST procurando por ConditionalExpression
|
|
312
|
+
for (let i = 0; i < 10; i++) {
|
|
313
|
+
if (!current.parent) break;
|
|
314
|
+
current = current.parent;
|
|
315
|
+
|
|
316
|
+
// Se encontrar um ternário (ConditionalExpression)
|
|
317
|
+
if (current.type === 'ConditionalExpression') {
|
|
318
|
+
// Verifica se o teste é "theme.vars"
|
|
319
|
+
const test = current.test;
|
|
320
|
+
if (
|
|
321
|
+
test &&
|
|
322
|
+
test.type === 'MemberExpression' &&
|
|
323
|
+
test.object &&
|
|
324
|
+
test.object.name === 'theme' &&
|
|
325
|
+
test.property &&
|
|
326
|
+
test.property.name === 'vars'
|
|
327
|
+
) {
|
|
328
|
+
// Se estamos no consequent (parte do `?`), não reportar
|
|
329
|
+
// Apenas reportar se estamos no alternate (parte do `:`)
|
|
330
|
+
return true; // Ignora warnings quando dentro de ternário com theme.vars
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Verifica se está dentro de uma função sx que usa theme.vars!
|
|
340
|
+
* Exemplo: sx={(theme) => ({ background: `${theme.vars!.palette...}` })}
|
|
341
|
+
*/
|
|
342
|
+
function isUsingNonNullAssertion(node) {
|
|
343
|
+
const sourceText = sourceCode.getText(node.parent);
|
|
344
|
+
|
|
345
|
+
// Procura por theme.vars! (non-null assertion)
|
|
346
|
+
if (sourceText.includes('theme.vars!')) {
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
|
|
382
353
|
return {
|
|
383
354
|
MemberExpression(node) {
|
|
384
355
|
// Detecta theme.palette.* (sem .vars)
|
|
@@ -393,6 +364,16 @@ const muiV7Rules = {
|
|
|
393
364
|
// Verifica se não é theme.vars.palette
|
|
394
365
|
const parent = node.object.object;
|
|
395
366
|
if (parent.type === 'Identifier' && parent.name === 'theme') {
|
|
367
|
+
// Ignora se estiver dentro de um ternário que checa theme.vars
|
|
368
|
+
if (isInsideThemeVarsConditional(node)) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Ignora se já está usando theme.vars! (non-null assertion)
|
|
373
|
+
if (isUsingNonNullAssertion(node)) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
396
377
|
context.report({
|
|
397
378
|
node,
|
|
398
379
|
messageId: 'useThemeVars',
|
|
@@ -412,24 +393,26 @@ const plugin = {
|
|
|
412
393
|
recommended: {
|
|
413
394
|
plugins: ['mui-v7'],
|
|
414
395
|
rules: {
|
|
415
|
-
|
|
396
|
+
// Breaking changes - ERRORS (código quebra)
|
|
397
|
+
'mui-v7/no-unstable-grid': 'error',
|
|
416
398
|
'mui-v7/no-grid2-import': 'error',
|
|
417
|
-
'mui-v7/no-lab-imports': 'error',
|
|
418
399
|
'mui-v7/no-grid-item-prop': 'error',
|
|
400
|
+
'mui-v7/no-lab-imports': 'error',
|
|
419
401
|
'mui-v7/no-deprecated-props': 'error',
|
|
420
|
-
|
|
402
|
+
// Best practices - WARNINGS (sugestões)
|
|
421
403
|
'mui-v7/prefer-theme-vars': 'warn',
|
|
422
404
|
},
|
|
423
405
|
},
|
|
424
406
|
strict: {
|
|
425
407
|
plugins: ['mui-v7'],
|
|
426
408
|
rules: {
|
|
427
|
-
|
|
409
|
+
// Breaking changes - ERRORS
|
|
410
|
+
'mui-v7/no-unstable-grid': 'error',
|
|
428
411
|
'mui-v7/no-grid2-import': 'error',
|
|
429
|
-
'mui-v7/no-lab-imports': 'error',
|
|
430
412
|
'mui-v7/no-grid-item-prop': 'error',
|
|
413
|
+
'mui-v7/no-lab-imports': 'error',
|
|
431
414
|
'mui-v7/no-deprecated-props': 'error',
|
|
432
|
-
|
|
415
|
+
// Best practices - ERRORS também no strict
|
|
433
416
|
'mui-v7/prefer-theme-vars': 'error',
|
|
434
417
|
},
|
|
435
418
|
},
|
package/index.js
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ESLint Plugin
|
|
2
|
+
* ESLint Plugin para MUI V7 - Foca em Breaking Changes
|
|
3
3
|
*
|
|
4
|
-
* Detecta automaticamente
|
|
5
|
-
* mensagens educativas para
|
|
4
|
+
* Detecta automaticamente código que QUEBRA na migração V6 → V7
|
|
5
|
+
* e fornece mensagens educativas para corrigir.
|
|
6
6
|
*
|
|
7
|
+
* @version 1.2.0
|
|
7
8
|
* @created 2025-01-26
|
|
9
|
+
* @updated 2025-01-29
|
|
8
10
|
* @author Matheus (Koda AI Studio) + Claude Code
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
const muiV7Rules = {
|
|
12
|
-
'no-
|
|
14
|
+
'no-unstable-grid': {
|
|
13
15
|
meta: {
|
|
14
16
|
type: 'problem',
|
|
15
17
|
docs: {
|
|
16
|
-
description: '
|
|
17
|
-
category: '
|
|
18
|
+
description: 'Unstable_Grid2 foi promovido para Grid no MUI V7',
|
|
19
|
+
category: 'Breaking Changes',
|
|
18
20
|
recommended: true,
|
|
19
21
|
},
|
|
20
22
|
messages: {
|
|
21
|
-
|
|
22
|
-
'🔧 Forma
|
|
23
|
-
' import
|
|
24
|
-
'
|
|
25
|
-
'
|
|
23
|
+
unstableGrid: '🚀 Unstable_Grid2 foi promovido para Grid estável no MUI V7!\n\n' +
|
|
24
|
+
'🔧 Forma antiga (V6):\n' +
|
|
25
|
+
' import Grid from "@mui/material/Unstable_Grid2"\n' +
|
|
26
|
+
' import Grid2 from "@mui/material/Unstable_Grid2"\n\n' +
|
|
27
|
+
'✅ Forma nova (V7):\n' +
|
|
28
|
+
' import { Grid } from "@mui/material"\n\n' +
|
|
29
|
+
'💡 O Grid agora é estável e usa a prop `size`!',
|
|
26
30
|
},
|
|
27
31
|
schema: [],
|
|
28
32
|
fixable: 'code',
|
|
@@ -32,30 +36,14 @@ const muiV7Rules = {
|
|
|
32
36
|
ImportDeclaration(node) {
|
|
33
37
|
const source = node.source.value;
|
|
34
38
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
messageId: 'deepImport',
|
|
44
|
-
fix(fixer) {
|
|
45
|
-
// Tenta converter para named import
|
|
46
|
-
const specifier = node.specifiers[0];
|
|
47
|
-
if (specifier && specifier.type === 'ImportDefaultSpecifier') {
|
|
48
|
-
const importName = specifier.local.name;
|
|
49
|
-
const newPath = parts.slice(0, 3).join('/'); // Remove último nível
|
|
50
|
-
return fixer.replaceText(
|
|
51
|
-
node,
|
|
52
|
-
`import { ${importName} } from "${newPath}"`
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
}
|
|
39
|
+
if (source === '@mui/material/Unstable_Grid2') {
|
|
40
|
+
context.report({
|
|
41
|
+
node,
|
|
42
|
+
messageId: 'unstableGrid',
|
|
43
|
+
fix(fixer) {
|
|
44
|
+
return fixer.replaceText(node.source, '"@mui/material"');
|
|
45
|
+
},
|
|
46
|
+
});
|
|
59
47
|
}
|
|
60
48
|
},
|
|
61
49
|
};
|
|
@@ -67,7 +55,7 @@ const muiV7Rules = {
|
|
|
67
55
|
type: 'problem',
|
|
68
56
|
docs: {
|
|
69
57
|
description: 'Grid2 foi renomeado para Grid no MUI V7',
|
|
70
|
-
category: '
|
|
58
|
+
category: 'Breaking Changes',
|
|
71
59
|
recommended: true,
|
|
72
60
|
},
|
|
73
61
|
messages: {
|
|
@@ -75,10 +63,10 @@ const muiV7Rules = {
|
|
|
75
63
|
'🔧 Forma antiga (V6):\n' +
|
|
76
64
|
' import Grid2 from "@mui/material/Grid2"\n' +
|
|
77
65
|
' import { grid2Classes } from "@mui/material/Grid2"\n\n' +
|
|
78
|
-
'✅
|
|
79
|
-
' import Grid from "@mui/material
|
|
80
|
-
' import { gridClasses } from "@mui/material
|
|
81
|
-
'💡
|
|
66
|
+
'✅ Recomendado:\n' +
|
|
67
|
+
' import { Grid } from "@mui/material"\n' +
|
|
68
|
+
' import { gridClasses } from "@mui/material"\n\n' +
|
|
69
|
+
'💡 O novo Grid é mais poderoso e usa a prop `size`!',
|
|
82
70
|
},
|
|
83
71
|
schema: [],
|
|
84
72
|
fixable: 'code',
|
|
@@ -93,23 +81,7 @@ const muiV7Rules = {
|
|
|
93
81
|
node,
|
|
94
82
|
messageId: 'grid2Import',
|
|
95
83
|
fix(fixer) {
|
|
96
|
-
|
|
97
|
-
fixer.replaceText(node.source, '"@mui/material/Grid"')
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
// Renomeia Grid2 -> Grid e grid2Classes -> gridClasses
|
|
101
|
-
node.specifiers.forEach(spec => {
|
|
102
|
-
if (spec.imported) {
|
|
103
|
-
const name = spec.imported.name;
|
|
104
|
-
if (name.includes('grid2')) {
|
|
105
|
-
const newName = name.replace('grid2', 'grid');
|
|
106
|
-
// Nota: Isso só funciona bem para casos simples
|
|
107
|
-
// Para casos complexos, o usuário precisa fazer manual
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
return fixes;
|
|
84
|
+
return fixer.replaceText(node.source, '"@mui/material"');
|
|
113
85
|
},
|
|
114
86
|
});
|
|
115
87
|
}
|
|
@@ -118,67 +90,20 @@ const muiV7Rules = {
|
|
|
118
90
|
},
|
|
119
91
|
},
|
|
120
92
|
|
|
121
|
-
'no-old-grid-import': {
|
|
122
|
-
meta: {
|
|
123
|
-
type: 'suggestion',
|
|
124
|
-
docs: {
|
|
125
|
-
description: 'Sugere migração do Grid antigo para o novo',
|
|
126
|
-
category: 'Best Practices',
|
|
127
|
-
recommended: false,
|
|
128
|
-
},
|
|
129
|
-
messages: {
|
|
130
|
-
oldGrid: '💡 O Grid antigo agora é GridLegacy. Considere migrar para o novo Grid!\n\n' +
|
|
131
|
-
'🔧 Se quiser manter o Grid antigo:\n' +
|
|
132
|
-
' import Grid from "@mui/material/GridLegacy"\n' +
|
|
133
|
-
' import { gridLegacyClasses } from "@mui/material/GridLegacy"\n\n' +
|
|
134
|
-
'✅ Recomendado: Migrar para o novo Grid:\n' +
|
|
135
|
-
' import Grid from "@mui/material/Grid"\n\n' +
|
|
136
|
-
'📚 O novo Grid usa `size` em vez de `xs/sm/md`:\n' +
|
|
137
|
-
' <Grid size={{ xs: 12, md: 6 }}>Conteúdo</Grid>',
|
|
138
|
-
},
|
|
139
|
-
schema: [],
|
|
140
|
-
},
|
|
141
|
-
create(context) {
|
|
142
|
-
const sourceCode = context.getSourceCode();
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
ImportDeclaration(node) {
|
|
146
|
-
const source = node.source.value;
|
|
147
|
-
|
|
148
|
-
// Detecta import Grid from '@mui/material/Grid'
|
|
149
|
-
if (source === '@mui/material/Grid') {
|
|
150
|
-
const defaultImport = node.specifiers.find(
|
|
151
|
-
spec => spec.type === 'ImportDefaultSpecifier'
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
if (defaultImport) {
|
|
155
|
-
// Verifica se está usando props antigas (xs, sm, md) no código
|
|
156
|
-
// Isso é apenas um aviso suave, não um erro
|
|
157
|
-
context.report({
|
|
158
|
-
node,
|
|
159
|
-
messageId: 'oldGrid',
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
|
-
};
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
|
|
168
93
|
'no-lab-imports': {
|
|
169
94
|
meta: {
|
|
170
95
|
type: 'problem',
|
|
171
96
|
docs: {
|
|
172
97
|
description: 'Componentes movidos de @mui/lab para @mui/material',
|
|
173
|
-
category: '
|
|
98
|
+
category: 'Breaking Changes',
|
|
174
99
|
recommended: true,
|
|
175
100
|
},
|
|
176
101
|
messages: {
|
|
177
102
|
labImport: '✨ Este componente foi movido para @mui/material no V7!\n\n' +
|
|
178
103
|
'🔧 Forma antiga (V6):\n' +
|
|
179
|
-
' import {{ component }} from "@mui/lab
|
|
180
|
-
'✅
|
|
181
|
-
' import {{ component }} from "@mui/material
|
|
104
|
+
' import {{ component }} from "@mui/lab"\n\n' +
|
|
105
|
+
'✅ Recomendado:\n' +
|
|
106
|
+
' import { {{ component }} } from "@mui/material"\n\n' +
|
|
182
107
|
'📦 Componentes movidos: Alert, Autocomplete, Pagination, Rating,\n' +
|
|
183
108
|
' Skeleton, SpeedDial, ToggleButton, AvatarGroup, e mais!',
|
|
184
109
|
},
|
|
@@ -216,10 +141,7 @@ const muiV7Rules = {
|
|
|
216
141
|
messageId: 'labImport',
|
|
217
142
|
data: { component: componentName },
|
|
218
143
|
fix(fixer) {
|
|
219
|
-
return fixer.replaceText(
|
|
220
|
-
node.source,
|
|
221
|
-
`"@mui/material/${componentName}"`
|
|
222
|
-
);
|
|
144
|
+
return fixer.replaceText(node.source, '"@mui/material"');
|
|
223
145
|
},
|
|
224
146
|
});
|
|
225
147
|
}
|
|
@@ -235,7 +157,7 @@ const muiV7Rules = {
|
|
|
235
157
|
type: 'problem',
|
|
236
158
|
docs: {
|
|
237
159
|
description: 'Grid não usa mais a prop `item`, agora usa `size`',
|
|
238
|
-
category: '
|
|
160
|
+
category: 'Breaking Changes',
|
|
239
161
|
recommended: true,
|
|
240
162
|
},
|
|
241
163
|
messages: {
|
|
@@ -245,7 +167,7 @@ const muiV7Rules = {
|
|
|
245
167
|
'✅ Forma nova (V7):\n' +
|
|
246
168
|
' <Grid size={{ xs: 12, sm: 6, md: 4 }}>\n\n' +
|
|
247
169
|
'💡 A nova sintaxe é mais limpa e poderosa!\n' +
|
|
248
|
-
' Você pode usar
|
|
170
|
+
' Você pode usar: size, offset, spacing responsivo e mais.',
|
|
249
171
|
},
|
|
250
172
|
schema: [],
|
|
251
173
|
},
|
|
@@ -279,7 +201,7 @@ const muiV7Rules = {
|
|
|
279
201
|
type: 'problem',
|
|
280
202
|
docs: {
|
|
281
203
|
description: 'Detecta props depreciadas no MUI V7',
|
|
282
|
-
category: '
|
|
204
|
+
category: 'Breaking Changes',
|
|
283
205
|
recommended: true,
|
|
284
206
|
},
|
|
285
207
|
messages: {
|
|
@@ -379,6 +301,55 @@ const muiV7Rules = {
|
|
|
379
301
|
create(context) {
|
|
380
302
|
const sourceCode = context.getSourceCode();
|
|
381
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Verifica se o node está dentro de um ternário que checa theme.vars
|
|
306
|
+
* Exemplo: theme.vars ? `${theme.vars.palette.primary.main}` : `${theme.palette.primary.main}`
|
|
307
|
+
*/
|
|
308
|
+
function isInsideThemeVarsConditional(node) {
|
|
309
|
+
let current = node;
|
|
310
|
+
|
|
311
|
+
// Sobe até 10 níveis na árvore AST procurando por ConditionalExpression
|
|
312
|
+
for (let i = 0; i < 10; i++) {
|
|
313
|
+
if (!current.parent) break;
|
|
314
|
+
current = current.parent;
|
|
315
|
+
|
|
316
|
+
// Se encontrar um ternário (ConditionalExpression)
|
|
317
|
+
if (current.type === 'ConditionalExpression') {
|
|
318
|
+
// Verifica se o teste é "theme.vars"
|
|
319
|
+
const test = current.test;
|
|
320
|
+
if (
|
|
321
|
+
test &&
|
|
322
|
+
test.type === 'MemberExpression' &&
|
|
323
|
+
test.object &&
|
|
324
|
+
test.object.name === 'theme' &&
|
|
325
|
+
test.property &&
|
|
326
|
+
test.property.name === 'vars'
|
|
327
|
+
) {
|
|
328
|
+
// Se estamos no consequent (parte do `?`), não reportar
|
|
329
|
+
// Apenas reportar se estamos no alternate (parte do `:`)
|
|
330
|
+
return true; // Ignora warnings quando dentro de ternário com theme.vars
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Verifica se está dentro de uma função sx que usa theme.vars!
|
|
340
|
+
* Exemplo: sx={(theme) => ({ background: `${theme.vars!.palette...}` })}
|
|
341
|
+
*/
|
|
342
|
+
function isUsingNonNullAssertion(node) {
|
|
343
|
+
const sourceText = sourceCode.getText(node.parent);
|
|
344
|
+
|
|
345
|
+
// Procura por theme.vars! (non-null assertion)
|
|
346
|
+
if (sourceText.includes('theme.vars!')) {
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
|
|
382
353
|
return {
|
|
383
354
|
MemberExpression(node) {
|
|
384
355
|
// Detecta theme.palette.* (sem .vars)
|
|
@@ -393,6 +364,16 @@ const muiV7Rules = {
|
|
|
393
364
|
// Verifica se não é theme.vars.palette
|
|
394
365
|
const parent = node.object.object;
|
|
395
366
|
if (parent.type === 'Identifier' && parent.name === 'theme') {
|
|
367
|
+
// Ignora se estiver dentro de um ternário que checa theme.vars
|
|
368
|
+
if (isInsideThemeVarsConditional(node)) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Ignora se já está usando theme.vars! (non-null assertion)
|
|
373
|
+
if (isUsingNonNullAssertion(node)) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
396
377
|
context.report({
|
|
397
378
|
node,
|
|
398
379
|
messageId: 'useThemeVars',
|
|
@@ -412,24 +393,26 @@ const plugin = {
|
|
|
412
393
|
recommended: {
|
|
413
394
|
plugins: ['mui-v7'],
|
|
414
395
|
rules: {
|
|
415
|
-
|
|
396
|
+
// Breaking changes - ERRORS (código quebra)
|
|
397
|
+
'mui-v7/no-unstable-grid': 'error',
|
|
416
398
|
'mui-v7/no-grid2-import': 'error',
|
|
417
|
-
'mui-v7/no-lab-imports': 'error',
|
|
418
399
|
'mui-v7/no-grid-item-prop': 'error',
|
|
400
|
+
'mui-v7/no-lab-imports': 'error',
|
|
419
401
|
'mui-v7/no-deprecated-props': 'error',
|
|
420
|
-
|
|
402
|
+
// Best practices - WARNINGS (sugestões)
|
|
421
403
|
'mui-v7/prefer-theme-vars': 'warn',
|
|
422
404
|
},
|
|
423
405
|
},
|
|
424
406
|
strict: {
|
|
425
407
|
plugins: ['mui-v7'],
|
|
426
408
|
rules: {
|
|
427
|
-
|
|
409
|
+
// Breaking changes - ERRORS
|
|
410
|
+
'mui-v7/no-unstable-grid': 'error',
|
|
428
411
|
'mui-v7/no-grid2-import': 'error',
|
|
429
|
-
'mui-v7/no-lab-imports': 'error',
|
|
430
412
|
'mui-v7/no-grid-item-prop': 'error',
|
|
413
|
+
'mui-v7/no-lab-imports': 'error',
|
|
431
414
|
'mui-v7/no-deprecated-props': 'error',
|
|
432
|
-
|
|
415
|
+
// Best practices - ERRORS também no strict
|
|
433
416
|
'mui-v7/prefer-theme-vars': 'error',
|
|
434
417
|
},
|
|
435
418
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-mui-v7",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "ESLint plugin
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "ESLint plugin focused on Material-UI V6 to V7 breaking changes with educational error messages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
7
7
|
"eslintplugin",
|