eslint-plugin-mui-v7 1.1.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.
Files changed (4) hide show
  1. package/README.md +124 -95
  2. package/index.cjs +61 -2
  3. package/index.js +61 -2
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -1,17 +1,21 @@
1
1
  # eslint-plugin-mui-v7
2
2
 
3
- > ESLint plugin for Material-UI v7 with educational and friendly error messages
3
+ > ESLint plugin focused on Material-UI V6 to V7 **breaking changes** with educational error messages
4
4
 
5
- Automatically detect incorrect usage of Material-UI V7 and teach developers the right way through helpful messages with emojis and examples!
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
- - **Detect deprecated deep imports** - No more `import createTheme from '@mui/material/styles/createTheme'`
10
- - **Catch Grid2 usage** - Grid2 was renamed to Grid in V7
11
- - **Find moved @mui/lab components** - Alert, Skeleton, Rating, etc. are now in @mui/material
12
- - **Detect deprecated props** - onBackdropClick, size="normal", Hidden component
13
- - **Grid item prop detection** - Grid doesn't use `item` prop anymore, use `size` instead
14
- - ⚠️ **Theme variables suggestion** - Use `theme.vars.*` for automatic dark mode support
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
- ## 🚀 Usage
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
- ### ESLint 9+ (Flat Config)
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
- // Errors (block code)
38
- 'mui-v7/no-deep-imports': 'error',
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
- // Warnings (suggest improvements)
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-deep-imports': 'error',
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
- ### 🚨 Error Rules (Must Fix)
85
+ ### 🚨 Breaking Changes (Errors)
86
+
87
+ These rules detect code that **WILL BREAK** in MUI V7.
84
88
 
85
- #### `mui-v7/no-deep-imports`
89
+ #### `mui-v7/no-unstable-grid` ✨ NEW in v1.1.0
86
90
 
87
- Deep imports with more than one level are not supported in MUI V7.
91
+ Unstable_Grid2 was promoted to stable Grid in V7.
88
92
 
89
93
  ```typescript
90
- // ❌ Bad
91
- import createTheme from '@mui/material/styles/createTheme'
94
+ // ❌ Breaks in V7
95
+ import Grid from '@mui/material/Unstable_Grid2'
96
+ import Grid2 from '@mui/material/Unstable_Grid2'
92
97
 
93
- // ✅ Good
94
- import { createTheme } from '@mui/material/styles'
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
- // ❌ Bad
107
+ // ❌ Breaks in V7
103
108
  import Grid2 from '@mui/material/Grid2'
109
+ import { grid2Classes } from '@mui/material/Grid2'
104
110
 
105
- // ✅ Good
106
- import Grid from '@mui/material/Grid'
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
- // ❌ Bad
131
- <Grid item xs={12} md={6}>
121
+ // ❌ Breaks in V7
122
+ <Grid item xs={12} sm={6} md={4}>
132
123
  Content
133
124
  </Grid>
134
125
 
135
- // ✅ Good
136
- <Grid size={{ xs: 12, md: 6 }}>
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 deprecated props in various components.
150
+ Detects props removed in V7.
144
151
 
145
152
  ```typescript
146
- // ❌ Bad: Dialog.onBackdropClick
153
+ // ❌ Dialog.onBackdropClick - REMOVED
147
154
  <Dialog onBackdropClick={handleClick}>
148
155
 
149
- // ✅ Good
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
- // ❌ Bad: InputLabel size="normal"
163
+ // ❌ InputLabel size="normal" - RENAMED
157
164
  <InputLabel size="normal">
158
165
 
159
- // ✅ Good
166
+ // ✅ Use size="medium"
160
167
  <InputLabel size="medium">
161
168
 
162
- // ❌ Bad: Hidden component
169
+ // ❌ Hidden component - REMOVED
163
170
  <Hidden xlUp><Paper /></Hidden>
164
171
 
165
- // ✅ Good: Use sx prop
172
+ // ✅ Use sx prop
166
173
  <Paper sx={{ display: { xl: 'none' } }} />
167
174
 
168
- // ✅ Good: Use useMediaQuery
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
- ### ⚠️ Warning Rules (Suggestions)
174
-
175
- #### `mui-v7/no-old-grid-import`
180
+ ### 💡 Best Practices (Warnings)
176
181
 
177
- Suggests migrating from old Grid to the new one.
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 support.
186
+ When using `cssVariables: true`, use `theme.vars.*` for better performance and automatic dark mode.
190
187
 
191
188
  ```typescript
192
- // ⚠️ Warning (doesn't change with dark mode)
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
- // ✅ Good (changes automatically with dark mode)
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 offset, push, pull e mais.
214
+ Você pode usar: size, offset, spacing responsivo e mais.
218
215
  ```
219
216
 
220
- ## 🔧 Configuration Options
217
+ ## 🔧 Configuration Presets
218
+
219
+ ### `recommended` - Balanced (Default)
221
220
 
222
- ### Severity Levels
221
+ Breaking changes as **errors**, best practices as **warnings**.
223
222
 
224
223
  ```javascript
225
- rules: {
226
- 'mui-v7/no-deep-imports': 'error', // Blocks code
227
- 'mui-v7/no-deep-imports': 'warn', // Shows warning
228
- 'mui-v7/no-deep-imports': 'off', // Disables rule
229
- }
224
+ import muiV7Plugin from 'eslint-plugin-mui-v7'
225
+
226
+ export default [
227
+ muiV7Plugin.configs.recommended,
228
+ ]
230
229
  ```
231
230
 
232
- ### Recommended Config
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.recommended, // All errors + warnings
239
+ muiV7Plugin.configs.strict,
239
240
  ]
240
241
  ```
241
242
 
242
- ### Strict Config
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.strict, // Everything as error
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
@@ -4,9 +4,9 @@
4
4
  * Detecta automaticamente código que QUEBRA na migração V6 → V7
5
5
  * e fornece mensagens educativas para corrigir.
6
6
  *
7
- * @version 1.1.0
7
+ * @version 1.2.0
8
8
  * @created 2025-01-26
9
- * @updated 2025-01-27
9
+ * @updated 2025-01-29
10
10
  * @author Matheus (Koda AI Studio) + Claude Code
11
11
  */
12
12
 
@@ -301,6 +301,55 @@ const muiV7Rules = {
301
301
  create(context) {
302
302
  const sourceCode = context.getSourceCode();
303
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
+
304
353
  return {
305
354
  MemberExpression(node) {
306
355
  // Detecta theme.palette.* (sem .vars)
@@ -315,6 +364,16 @@ const muiV7Rules = {
315
364
  // Verifica se não é theme.vars.palette
316
365
  const parent = node.object.object;
317
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
+
318
377
  context.report({
319
378
  node,
320
379
  messageId: 'useThemeVars',
package/index.js CHANGED
@@ -4,9 +4,9 @@
4
4
  * Detecta automaticamente código que QUEBRA na migração V6 → V7
5
5
  * e fornece mensagens educativas para corrigir.
6
6
  *
7
- * @version 1.1.0
7
+ * @version 1.2.0
8
8
  * @created 2025-01-26
9
- * @updated 2025-01-27
9
+ * @updated 2025-01-29
10
10
  * @author Matheus (Koda AI Studio) + Claude Code
11
11
  */
12
12
 
@@ -301,6 +301,55 @@ const muiV7Rules = {
301
301
  create(context) {
302
302
  const sourceCode = context.getSourceCode();
303
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
+
304
353
  return {
305
354
  MemberExpression(node) {
306
355
  // Detecta theme.palette.* (sem .vars)
@@ -315,6 +364,16 @@ const muiV7Rules = {
315
364
  // Verifica se não é theme.vars.palette
316
365
  const parent = node.object.object;
317
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
+
318
377
  context.report({
319
378
  node,
320
379
  messageId: 'useThemeVars',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-mui-v7",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "ESLint plugin focused on Material-UI V6 to V7 breaking changes with educational error messages",
5
5
  "keywords": [
6
6
  "eslint",