@wordpress/eslint-plugin 25.6.0 → 25.7.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/eslint-plugin",
3
- "version": "25.6.0",
3
+ "version": "25.7.0",
4
4
  "description": "ESLint plugin for WordPress development.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -44,9 +44,9 @@
44
44
  "@babel/eslint-parser": "^7.28.6",
45
45
  "@eslint-community/eslint-plugin-eslint-comments": "^4.7.0",
46
46
  "@eslint/compat": "^2.0.0",
47
- "@wordpress/babel-preset-default": "^8.50.0",
48
- "@wordpress/prettier-config": "^4.50.0",
49
- "@wordpress/theme": "^0.17.0",
47
+ "@wordpress/babel-preset-default": "^8.51.0",
48
+ "@wordpress/prettier-config": "^4.51.0",
49
+ "@wordpress/theme": "^1.0.0",
50
50
  "cosmiconfig": "^7.0.0",
51
51
  "eslint-config-prettier": "^10.0.0",
52
52
  "eslint-import-resolver-typescript": "^4.4.4",
@@ -64,8 +64,10 @@
64
64
  },
65
65
  "devDependencies": {
66
66
  "@types/eslint": "^9",
67
- "@types/estree": "^1.0.5",
68
- "eslint": "^10.0.0"
67
+ "@types/estree": "^1.0.8",
68
+ "@typescript-eslint/parser": "^8.57.1",
69
+ "eslint": "^10.0.0",
70
+ "typescript": "npm:@typescript/typescript6@^6.0.2"
69
71
  },
70
72
  "peerDependencies": {
71
73
  "@babel/core": ">=7",
@@ -84,5 +86,5 @@
84
86
  "publishConfig": {
85
87
  "access": "public"
86
88
  },
87
- "gitHead": "fee6e24e8e63d36f6bbcf487c193461e9bd79753"
89
+ "gitHead": "e9a74f9c14095a34398ecd4d1f7a908e55051205"
88
90
  }
@@ -22,18 +22,18 @@ ruleTester.run( 'components-no-missing-40px-size-prop', rule, {
22
22
  <Button __next40pxDefaultSize />
23
23
  `,
24
24
  },
25
- // Component with __next40pxDefaultSize={true}
25
+ // Component with non-default size prop
26
26
  {
27
27
  code: `
28
- import { InputControl } from '@wordpress/components';
29
- <InputControl __next40pxDefaultSize={true} />
28
+ import { Button } from '@wordpress/components';
29
+ <Button size="small" />
30
30
  `,
31
31
  },
32
- // Component with non-default size prop
32
+ // SelectControl no longer requires __next40pxDefaultSize
33
33
  {
34
34
  code: `
35
- import { Button } from '@wordpress/components';
36
- <Button size="small" />
35
+ import { SelectControl } from '@wordpress/components';
36
+ <SelectControl />
37
37
  `,
38
38
  },
39
39
  // Component with size="compact"
@@ -71,20 +71,6 @@ ruleTester.run( 'components-no-missing-40px-size-prop', rule, {
71
71
  <WPButton __next40pxDefaultSize />
72
72
  `,
73
73
  },
74
- // FormFileUpload with render prop (special case)
75
- {
76
- code: `
77
- import { FormFileUpload } from '@wordpress/components';
78
- <FormFileUpload render={({ open }) => <button onClick={open}>Upload</button>} />
79
- `,
80
- },
81
- // FormFileUpload with __next40pxDefaultSize
82
- {
83
- code: `
84
- import { FormFileUpload } from '@wordpress/components';
85
- <FormFileUpload __next40pxDefaultSize />
86
- `,
87
- },
88
74
  // Component with dynamic size prop (assumes it could be non-default)
89
75
  {
90
76
  code: `
@@ -110,40 +96,19 @@ ruleTester.run( 'components-no-missing-40px-size-prop', rule, {
110
96
  {
111
97
  code: `
112
98
  import {
113
- BorderBoxControl,
114
- BorderControl,
115
- ComboboxControl,
116
- CustomSelectControl,
117
- FontAppearanceControl,
118
- FontFamilyControl,
119
- FontSizePicker,
120
- FormTokenField,
121
99
  InputControl,
122
- LetterSpacingControl,
123
- LineHeightControl,
124
100
  NumberControl,
125
101
  RangeControl,
126
102
  SelectControl,
127
103
  ToggleGroupControl,
128
- UnitControl,
129
104
  } from '@wordpress/components';
130
105
  <>
131
- <BorderBoxControl __next40pxDefaultSize />
132
- <BorderControl __next40pxDefaultSize />
133
- <ComboboxControl __next40pxDefaultSize />
134
- <CustomSelectControl __next40pxDefaultSize />
135
- <FontAppearanceControl __next40pxDefaultSize />
136
- <FontFamilyControl __next40pxDefaultSize />
137
- <FontSizePicker __next40pxDefaultSize />
138
- <FormTokenField __next40pxDefaultSize />
139
- <InputControl __next40pxDefaultSize />
140
- <LetterSpacingControl __next40pxDefaultSize />
141
- <LineHeightControl __next40pxDefaultSize />
142
- <NumberControl __next40pxDefaultSize />
143
- <RangeControl __next40pxDefaultSize />
144
- <SelectControl __next40pxDefaultSize />
145
- <ToggleGroupControl __next40pxDefaultSize />
146
- <UnitControl __next40pxDefaultSize />
106
+ <FormTokenField />
107
+ <InputControl />
108
+ <NumberControl />
109
+ <RangeControl />
110
+ <SelectControl />
111
+ <ToggleGroupControl />
147
112
  </>
148
113
  `,
149
114
  },
@@ -162,29 +127,16 @@ ruleTester.run( 'components-no-missing-40px-size-prop', rule, {
162
127
  },
163
128
  ],
164
129
  },
165
- // InputControl without __next40pxDefaultSize
166
- {
167
- code: `
168
- import { InputControl } from '@wordpress/components';
169
- <InputControl value={value} onChange={onChange} />
170
- `,
171
- errors: [
172
- {
173
- messageId: 'missingProp',
174
- data: { component: 'InputControl' },
175
- },
176
- ],
177
- },
178
130
  // Component with __next40pxDefaultSize={false}
179
131
  {
180
132
  code: `
181
- import { SelectControl } from '@wordpress/components';
182
- <SelectControl __next40pxDefaultSize={false} />
133
+ import { Button } from '@wordpress/components';
134
+ <Button __next40pxDefaultSize={false} />
183
135
  `,
184
136
  errors: [
185
137
  {
186
138
  messageId: 'missingProp',
187
- data: { component: 'SelectControl' },
139
+ data: { component: 'Button' },
188
140
  },
189
141
  ],
190
142
  },
@@ -201,47 +153,6 @@ ruleTester.run( 'components-no-missing-40px-size-prop', rule, {
201
153
  },
202
154
  ],
203
155
  },
204
- // Aliased import without __next40pxDefaultSize
205
- {
206
- code: `
207
- import { InputControl as MyInputControl } from '@wordpress/components';
208
- <MyInputControl />
209
- `,
210
- errors: [
211
- {
212
- messageId: 'missingProp',
213
- data: { component: 'InputControl' },
214
- },
215
- ],
216
- },
217
- // FormFileUpload without __next40pxDefaultSize or render
218
- {
219
- code: `
220
- import { FormFileUpload } from '@wordpress/components';
221
- <FormFileUpload onChange={handleChange} />
222
- `,
223
- errors: [
224
- {
225
- messageId: 'missingPropFormFileUpload',
226
- },
227
- ],
228
- },
229
- // Multiple components, some invalid
230
- {
231
- code: `
232
- import { Button, InputControl } from '@wordpress/components';
233
- <>
234
- <Button __next40pxDefaultSize />
235
- <InputControl />
236
- </>
237
- `,
238
- errors: [
239
- {
240
- messageId: 'missingProp',
241
- data: { component: 'InputControl' },
242
- },
243
- ],
244
- },
245
156
  // Multiple invalid components
246
157
  {
247
158
  code: `
@@ -256,10 +167,6 @@ ruleTester.run( 'components-no-missing-40px-size-prop', rule, {
256
167
  messageId: 'missingProp',
257
168
  data: { component: 'Button' },
258
169
  },
259
- {
260
- messageId: 'missingProp',
261
- data: { component: 'SelectControl' },
262
- },
263
170
  ],
264
171
  },
265
172
  // Relative import with checkLocalImports enabled
@@ -276,20 +183,6 @@ ruleTester.run( 'components-no-missing-40px-size-prop', rule, {
276
183
  },
277
184
  ],
278
185
  },
279
- // Default import from input-control path with checkLocalImports enabled
280
- {
281
- code: `
282
- import InputControl from '../input-control';
283
- <InputControl />
284
- `,
285
- options: [ { checkLocalImports: true } ],
286
- errors: [
287
- {
288
- messageId: 'missingProp',
289
- data: { component: 'InputControl' },
290
- },
291
- ],
292
- },
293
186
  ],
294
187
  } );
295
188
 
@@ -311,7 +204,7 @@ ruleTester.run(
311
204
  {
312
205
  code: `
313
206
  import InputControl from './input-control';
314
- <InputControl __next40pxDefaultSize />
207
+ <InputControl />
315
208
  `,
316
209
  options: [ { checkLocalImports: true } ],
317
210
  },
@@ -337,14 +230,6 @@ ruleTester.run(
337
230
  <InputControl />
338
231
  `,
339
232
  },
340
- // FormFileUpload relative import with render prop
341
- {
342
- code: `
343
- import { FormFileUpload } from '../form-file-upload';
344
- <FormFileUpload render={({ open }) => <button onClick={open}>Upload</button>} />
345
- `,
346
- options: [ { checkLocalImports: true } ],
347
- },
348
233
  ],
349
234
  invalid: [],
350
235
  }
@@ -54,6 +54,13 @@ ruleTester.run( 'no-unsafe-wp-apis', rule, {
54
54
  options,
55
55
  },
56
56
  { code: "import * as s from '@wordpress/package';", options },
57
+ {
58
+ // String-literal imports (e.g. `import { 'a' as b }`) have a
59
+ // non-identifier `imported` node and are ignored by the rule.
60
+ code: "import { '__experimentalUnsafe' as s } from '@wordpress/package';",
61
+ options,
62
+ languageOptions: { sourceType: 'module', ecmaVersion: 2022 },
63
+ },
57
64
  ],
58
65
 
59
66
  invalid: [
@@ -12,40 +12,9 @@ const { hasTruthyJsxAttribute } = require( '../utils' );
12
12
  * These can be exempted if they have a non-default `size` prop.
13
13
  */
14
14
  const COMPONENTS_REQUIRING_40PX = new Set( [
15
- 'BorderBoxControl',
16
- 'BorderControl',
17
15
  'Button',
18
16
  'ClipboardButton',
19
- 'ComboboxControl',
20
- 'CustomSelectControl',
21
- 'FontAppearanceControl',
22
- 'FontFamilyControl',
23
- 'FontSizePicker',
24
- 'FormTokenField',
25
17
  'IconButton',
26
- 'InputControl',
27
- 'LetterSpacingControl',
28
- 'LineHeightControl',
29
- 'NumberControl',
30
- 'Radio',
31
- 'RangeControl',
32
- 'SelectControl',
33
- 'TreeSelect',
34
- 'ToggleGroupControl',
35
- 'UnitControl',
36
- ] );
37
-
38
- /**
39
- * Components that can use the `render` prop as an alternative to __next40pxDefaultSize.
40
- */
41
- const COMPONENTS_WITH_RENDER_EXEMPTION = new Set( [ 'FormFileUpload' ] );
42
-
43
- /**
44
- * All tracked component names for path-based detection.
45
- */
46
- const ALL_TRACKED_COMPONENTS = new Set( [
47
- ...COMPONENTS_REQUIRING_40PX,
48
- ...COMPONENTS_WITH_RENDER_EXEMPTION,
49
18
  ] );
50
19
 
51
20
  module.exports = {
@@ -67,8 +36,6 @@ module.exports = {
67
36
  messages: {
68
37
  missingProp:
69
38
  '{{ component }} should have the `__next40pxDefaultSize` prop when using the default size.',
70
- missingPropFormFileUpload:
71
- 'FormFileUpload should have the `__next40pxDefaultSize` prop to opt-in to the new default size.',
72
39
  },
73
40
  },
74
41
  create( context ) {
@@ -121,7 +88,7 @@ module.exports = {
121
88
  .join( '' );
122
89
 
123
90
  // Check if it's one of our tracked components
124
- if ( ALL_TRACKED_COMPONENTS.has( pascalCase ) ) {
91
+ if ( COMPONENTS_REQUIRING_40PX.has( pascalCase ) ) {
125
92
  return pascalCase;
126
93
  }
127
94
 
@@ -166,21 +133,6 @@ module.exports = {
166
133
  return false;
167
134
  }
168
135
 
169
- /**
170
- * Check if the `render` prop exists.
171
- *
172
- * @param {Array} attributes - JSX attributes array
173
- * @return {boolean} Whether render prop exists
174
- */
175
- function hasRenderProp( attributes ) {
176
- return attributes.some(
177
- ( a ) =>
178
- a.type === 'JSXAttribute' &&
179
- a.name &&
180
- a.name.name === 'render'
181
- );
182
- }
183
-
184
136
  /**
185
137
  * Check if the `variant` prop has the value "link".
186
138
  * Button with variant="link" doesn't need __next40pxDefaultSize.
@@ -230,10 +182,7 @@ module.exports = {
230
182
  const localName = specifier.local.name;
231
183
 
232
184
  // Track components that require the prop
233
- if (
234
- COMPONENTS_REQUIRING_40PX.has( importedName ) ||
235
- COMPONENTS_WITH_RENDER_EXEMPTION.has( importedName )
236
- ) {
185
+ if ( COMPONENTS_REQUIRING_40PX.has( importedName ) ) {
237
186
  trackedImports.set( localName, importedName );
238
187
  }
239
188
  } );
@@ -254,7 +203,7 @@ module.exports = {
254
203
  // Support patterns like `import ClipboardButton from '.';`
255
204
  // (common in component folder examples/tests).
256
205
  // If the local name matches a tracked component, treat it as such.
257
- if ( ALL_TRACKED_COMPONENTS.has( localName ) ) {
206
+ if ( COMPONENTS_REQUIRING_40PX.has( localName ) ) {
258
207
  trackedImports.set( localName, localName );
259
208
  }
260
209
  }
@@ -285,20 +234,6 @@ module.exports = {
285
234
  return;
286
235
  }
287
236
 
288
- // Handle FormFileUpload special case
289
- if ( COMPONENTS_WITH_RENDER_EXEMPTION.has( importedName ) ) {
290
- // FormFileUpload is valid if it has a `render` prop
291
- if ( hasRenderProp( attributes ) ) {
292
- return;
293
- }
294
-
295
- context.report( {
296
- node,
297
- messageId: 'missingPropFormFileUpload',
298
- } );
299
- return;
300
- }
301
-
302
237
  // For other components, check if size prop has a non-default value
303
238
  if ( hasNonDefaultSize( attributes ) ) {
304
239
  return;
@@ -63,6 +63,11 @@ function makeListener( { allowedImports, context } ) {
63
63
  return;
64
64
  }
65
65
 
66
+ /* `imported` may be a string literal (e.g. `import { 'a' as b }`); only identifiers are relevant here. */
67
+ if ( specifierNode.imported.type !== 'Identifier' ) {
68
+ return;
69
+ }
70
+
66
71
  const importedName = specifierNode.imported.name;
67
72
 
68
73
  if (
@@ -23,6 +23,7 @@ const ALLOWLIST = {
23
23
  'EmptyState',
24
24
  'Icon',
25
25
  'Link',
26
+ 'Skeleton',
26
27
  'Stack',
27
28
  'Tabs',
28
29
  'Text',