@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 +9 -7
- package/rules/__tests__/components-no-missing-40px-size-prop.js +16 -131
- package/rules/__tests__/no-unsafe-wp-apis.js +7 -0
- package/rules/components-no-missing-40px-size-prop.js +3 -68
- package/rules/no-unsafe-wp-apis.js +5 -0
- package/rules/use-recommended-components.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/eslint-plugin",
|
|
3
|
-
"version": "25.
|
|
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.
|
|
48
|
-
"@wordpress/prettier-config": "^4.
|
|
49
|
-
"@wordpress/theme": "^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.
|
|
68
|
-
"eslint": "^
|
|
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": "
|
|
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
|
|
25
|
+
// Component with non-default size prop
|
|
26
26
|
{
|
|
27
27
|
code: `
|
|
28
|
-
import {
|
|
29
|
-
<
|
|
28
|
+
import { Button } from '@wordpress/components';
|
|
29
|
+
<Button size="small" />
|
|
30
30
|
`,
|
|
31
31
|
},
|
|
32
|
-
//
|
|
32
|
+
// SelectControl no longer requires __next40pxDefaultSize
|
|
33
33
|
{
|
|
34
34
|
code: `
|
|
35
|
-
import {
|
|
36
|
-
<
|
|
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
|
-
<
|
|
132
|
-
<
|
|
133
|
-
<
|
|
134
|
-
<
|
|
135
|
-
<
|
|
136
|
-
<
|
|
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 {
|
|
182
|
-
<
|
|
133
|
+
import { Button } from '@wordpress/components';
|
|
134
|
+
<Button __next40pxDefaultSize={false} />
|
|
183
135
|
`,
|
|
184
136
|
errors: [
|
|
185
137
|
{
|
|
186
138
|
messageId: 'missingProp',
|
|
187
|
-
data: { component: '
|
|
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
|
|
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 (
|
|
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 (
|
|
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 (
|