@wordpress/eslint-plugin 24.4.1-next.v.202603161435.0 → 24.5.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 +5 -0
- package/package.json +5 -5
- package/rules/__tests__/no-dom-globals-in-constructor.js +103 -0
- package/rules/__tests__/no-dom-globals-in-module-scope.js +160 -0
- package/rules/__tests__/no-dom-globals-in-react-cc-render.js +98 -0
- package/rules/__tests__/no-dom-globals-in-react-fc.js +125 -0
- package/rules/__tests__/no-i18n-in-save.js +0 -14
- package/rules/__tests__/no-setting-ds-tokens.js +33 -0
- package/rules/__tests__/no-unknown-ds-tokens.js +58 -0
- package/rules/__tests__/no-unmerged-classname.js +174 -0
- package/rules/__tests__/no-unsafe-wp-apis.js +0 -5
- package/rules/__tests__/use-recommended-components.js +0 -5
- package/rules/data-no-store-string-literals.js +8 -5
- package/rules/dependency-group.js +3 -4
- package/rules/i18n-translator-comments.js +6 -2
- package/rules/no-dom-globals-in-constructor.js +17 -0
- package/rules/no-dom-globals-in-module-scope.js +12 -0
- package/rules/no-dom-globals-in-react-cc-render.js +26 -0
- package/rules/no-dom-globals-in-react-fc.js +17 -0
- package/rules/no-i18n-in-save.js +1 -1
- package/rules/no-setting-ds-tokens.js +2 -4
- package/rules/no-unknown-ds-tokens.js +68 -19
- package/rules/no-unmerged-classname.js +107 -0
- package/rules/no-unused-vars-before-return.js +7 -6
- package/rules/react-no-unsafe-timeout.js +1 -1
- package/utils/dom-globals.js +157 -0
|
@@ -39,6 +39,9 @@ ruleTester.run( 'no-unknown-ds-tokens', rule, {
|
|
|
39
39
|
{
|
|
40
40
|
code: '`var(--wpds-color-fg-content-neutral) ${ suffix }`',
|
|
41
41
|
},
|
|
42
|
+
{
|
|
43
|
+
code: `const style = { '--wpds-color-fg-content-neutral': 'red' };`,
|
|
44
|
+
},
|
|
42
45
|
],
|
|
43
46
|
invalid: [
|
|
44
47
|
{
|
|
@@ -142,5 +145,60 @@ ruleTester.run( 'no-unknown-ds-tokens', rule, {
|
|
|
142
145
|
},
|
|
143
146
|
],
|
|
144
147
|
},
|
|
148
|
+
{
|
|
149
|
+
code: `const token = '--wpds-color-fg-content-neutral';`,
|
|
150
|
+
errors: [
|
|
151
|
+
{
|
|
152
|
+
messageId: 'bareToken',
|
|
153
|
+
data: {
|
|
154
|
+
tokenNames: "'--wpds-color-fg-content-neutral'",
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
code: 'const token = `--wpds-color-fg-content-neutral`;',
|
|
161
|
+
errors: [
|
|
162
|
+
{
|
|
163
|
+
messageId: 'bareToken',
|
|
164
|
+
data: {
|
|
165
|
+
tokenNames: "'--wpds-color-fg-content-neutral'",
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
code: '<div style={ { gap: `--wpds-color-fg-content-neutral` } } />',
|
|
172
|
+
errors: [
|
|
173
|
+
{
|
|
174
|
+
messageId: 'bareToken',
|
|
175
|
+
data: {
|
|
176
|
+
tokenNames: "'--wpds-color-fg-content-neutral'",
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
code: '`${ prefix }: --wpds-color-fg-content-neutral`',
|
|
183
|
+
errors: [
|
|
184
|
+
{
|
|
185
|
+
messageId: 'bareToken',
|
|
186
|
+
data: {
|
|
187
|
+
tokenNames: "'--wpds-color-fg-content-neutral'",
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
code: '`var(--wpds-color-fg-content-neutral) --wpds-color-fg-content-neutral ${ x }`',
|
|
194
|
+
errors: [
|
|
195
|
+
{
|
|
196
|
+
messageId: 'bareToken',
|
|
197
|
+
data: {
|
|
198
|
+
tokenNames: "'--wpds-color-fg-content-neutral'",
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
},
|
|
145
203
|
],
|
|
146
204
|
} );
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { RuleTester } from 'eslint';
|
|
2
|
+
import rule from '../no-unmerged-classname';
|
|
3
|
+
|
|
4
|
+
const ruleTester = new RuleTester( {
|
|
5
|
+
parserOptions: {
|
|
6
|
+
sourceType: 'module',
|
|
7
|
+
ecmaVersion: 2018,
|
|
8
|
+
ecmaFeatures: {
|
|
9
|
+
jsx: true,
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
} );
|
|
13
|
+
|
|
14
|
+
ruleTester.run( 'no-unmerged-classname', rule, {
|
|
15
|
+
valid: [
|
|
16
|
+
{
|
|
17
|
+
// className destructured and merged via clsx
|
|
18
|
+
code: `
|
|
19
|
+
function Foo( { className, ...restProps } ) {
|
|
20
|
+
return <div className={ clsx( styles.foo, className ) } { ...restProps } />;
|
|
21
|
+
}
|
|
22
|
+
`,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
// className destructured and used directly
|
|
26
|
+
code: `
|
|
27
|
+
function Foo( { className, ...restProps } ) {
|
|
28
|
+
return <div className={ className } { ...restProps } />;
|
|
29
|
+
}
|
|
30
|
+
`,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
// className merged via template literal
|
|
34
|
+
code: `
|
|
35
|
+
function Foo( { className, ...restProps } ) {
|
|
36
|
+
return <div className={ \`\${styles.foo} \${className}\` } { ...restProps } />;
|
|
37
|
+
}
|
|
38
|
+
`,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
// className merged via string concatenation
|
|
42
|
+
code: `
|
|
43
|
+
function Foo( { className, ...restProps } ) {
|
|
44
|
+
return <div className={ styles.foo + ' ' + className } { ...restProps } />;
|
|
45
|
+
}
|
|
46
|
+
`,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
// No spread — internal elements are fine
|
|
50
|
+
code: `
|
|
51
|
+
function Foo( { ...restProps } ) {
|
|
52
|
+
return <div className={ styles.foo } />;
|
|
53
|
+
}
|
|
54
|
+
`,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
// No className attribute at all
|
|
58
|
+
code: `
|
|
59
|
+
function Foo( { ...restProps } ) {
|
|
60
|
+
return <div { ...restProps } />;
|
|
61
|
+
}
|
|
62
|
+
`,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
// No rest element in destructuring
|
|
66
|
+
code: `
|
|
67
|
+
function Foo( { padding } ) {
|
|
68
|
+
return <div className={ styles.foo } { ...someOtherSpread } />;
|
|
69
|
+
}
|
|
70
|
+
`,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
// Arrow function component
|
|
74
|
+
code: `
|
|
75
|
+
const Foo = ( { className, ...restProps } ) => (
|
|
76
|
+
<div className={ clsx( styles.foo, className ) } { ...restProps } />
|
|
77
|
+
);
|
|
78
|
+
`,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
// className in conditional expression
|
|
82
|
+
code: `
|
|
83
|
+
function Foo( { className, isActive, ...restProps } ) {
|
|
84
|
+
return <div className={ isActive ? className : styles.inactive } { ...restProps } />;
|
|
85
|
+
}
|
|
86
|
+
`,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
// className in logical expression
|
|
90
|
+
code: `
|
|
91
|
+
function Foo( { className, ...restProps } ) {
|
|
92
|
+
return <div className={ className || styles.default } { ...restProps } />;
|
|
93
|
+
}
|
|
94
|
+
`,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
// className forwarded on a different element than the one with spread
|
|
98
|
+
code: `
|
|
99
|
+
function Foo( { className, style, ...props } ) {
|
|
100
|
+
return (
|
|
101
|
+
<Outer className={ clsx( styles.outer, className ) } style={ style }>
|
|
102
|
+
<Inner className={ styles.inner } { ...props } />
|
|
103
|
+
</Outer>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
`,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
// className destructured but not referenced — not this rule's concern
|
|
110
|
+
// (would be caught by no-unused-vars)
|
|
111
|
+
code: `
|
|
112
|
+
function Foo( { className, ...restProps } ) {
|
|
113
|
+
return <div className={ clsx( styles.foo ) } { ...restProps } />;
|
|
114
|
+
}
|
|
115
|
+
`,
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
invalid: [
|
|
119
|
+
{
|
|
120
|
+
// className not destructured, sits in rest props
|
|
121
|
+
code: `
|
|
122
|
+
function Foo( { padding, ...rest } ) {
|
|
123
|
+
return <div className={ clsx( styles.foo ) } { ...rest } />;
|
|
124
|
+
}
|
|
125
|
+
`,
|
|
126
|
+
errors: [ { messageId: 'noUnmergedClassname' } ],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
// className not destructured, no other named props
|
|
130
|
+
code: `
|
|
131
|
+
function Foo( { ...others } ) {
|
|
132
|
+
return <div className={ styles.foo } { ...others } />;
|
|
133
|
+
}
|
|
134
|
+
`,
|
|
135
|
+
errors: [ { messageId: 'noUnmergedClassname' } ],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
// Arrow function, className not destructured
|
|
139
|
+
code: `
|
|
140
|
+
const Foo = ( { padding, ...props } ) => (
|
|
141
|
+
<div className={ styles.foo } { ...props } />
|
|
142
|
+
);
|
|
143
|
+
`,
|
|
144
|
+
errors: [ { messageId: 'noUnmergedClassname' } ],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
// Named function expression
|
|
148
|
+
code: `
|
|
149
|
+
const Foo = function Foo( { padding, ...rest } ) {
|
|
150
|
+
return <div className={ styles.foo } { ...rest } />;
|
|
151
|
+
};
|
|
152
|
+
`,
|
|
153
|
+
errors: [ { messageId: 'noUnmergedClassname' } ],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
// forwardRef pattern — className not destructured
|
|
157
|
+
code: `
|
|
158
|
+
const Foo = forwardRef( function Foo( { padding, ...restProps }, ref ) {
|
|
159
|
+
return <div ref={ ref } className={ clsx( styles.foo ) } { ...restProps } />;
|
|
160
|
+
} );
|
|
161
|
+
`,
|
|
162
|
+
errors: [ { messageId: 'noUnmergedClassname' } ],
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
// Props not destructured — spread of entire props object
|
|
166
|
+
code: `
|
|
167
|
+
function Foo( props ) {
|
|
168
|
+
return <div className={ styles.foo } { ...props } />;
|
|
169
|
+
}
|
|
170
|
+
`,
|
|
171
|
+
errors: [ { messageId: 'noUnmergedClassname' } ],
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
} );
|
|
@@ -64,7 +64,6 @@ ruleTester.run( 'no-unsafe-wp-apis', rule, {
|
|
|
64
64
|
{
|
|
65
65
|
message: `Usage of \`__experimentalUnsafe\` from \`@wordpress/package\` is not allowed.
|
|
66
66
|
See https://developer.wordpress.org/block-editor/contributors/develop/coding-guidelines/#experimental-and-unstable-apis for details.`,
|
|
67
|
-
type: 'ImportSpecifier',
|
|
68
67
|
},
|
|
69
68
|
],
|
|
70
69
|
},
|
|
@@ -75,7 +74,6 @@ See https://developer.wordpress.org/block-editor/contributors/develop/coding-gui
|
|
|
75
74
|
{
|
|
76
75
|
message: `Usage of \`__experimentalSafe\` from \`@wordpress/unsafe\` is not allowed.
|
|
77
76
|
See https://developer.wordpress.org/block-editor/contributors/develop/coding-guidelines/#experimental-and-unstable-apis for details.`,
|
|
78
|
-
type: 'ImportSpecifier',
|
|
79
77
|
},
|
|
80
78
|
],
|
|
81
79
|
},
|
|
@@ -86,7 +84,6 @@ See https://developer.wordpress.org/block-editor/contributors/develop/coding-gui
|
|
|
86
84
|
{
|
|
87
85
|
message: `Usage of \`__experimentalSafe\` from \`@wordpress/unsafe\` is not allowed.
|
|
88
86
|
See https://developer.wordpress.org/block-editor/contributors/develop/coding-guidelines/#experimental-and-unstable-apis for details.`,
|
|
89
|
-
type: 'ImportSpecifier',
|
|
90
87
|
},
|
|
91
88
|
],
|
|
92
89
|
},
|
|
@@ -97,7 +94,6 @@ See https://developer.wordpress.org/block-editor/contributors/develop/coding-gui
|
|
|
97
94
|
{
|
|
98
95
|
message: `Usage of \`__experimentalUnsafe\` from \`@wordpress/package\` is not allowed.
|
|
99
96
|
See https://developer.wordpress.org/block-editor/contributors/develop/coding-guidelines/#experimental-and-unstable-apis for details.`,
|
|
100
|
-
type: 'ImportSpecifier',
|
|
101
97
|
},
|
|
102
98
|
],
|
|
103
99
|
},
|
|
@@ -108,7 +104,6 @@ See https://developer.wordpress.org/block-editor/contributors/develop/coding-gui
|
|
|
108
104
|
{
|
|
109
105
|
message: `Usage of \`__unstableFeature\` from \`@wordpress/package\` is not allowed.
|
|
110
106
|
See https://developer.wordpress.org/block-editor/contributors/develop/coding-guidelines/#experimental-and-unstable-apis for details.`,
|
|
111
|
-
type: 'ImportSpecifier',
|
|
112
107
|
},
|
|
113
108
|
],
|
|
114
109
|
},
|
|
@@ -41,7 +41,6 @@ ruleTester.run( 'use-recommended-components', rule, {
|
|
|
41
41
|
{
|
|
42
42
|
message:
|
|
43
43
|
'`SomeComponent` from `@wordpress/ui` is not yet recommended for use in a WordPress environment.',
|
|
44
|
-
type: 'ImportSpecifier',
|
|
45
44
|
},
|
|
46
45
|
],
|
|
47
46
|
},
|
|
@@ -51,12 +50,10 @@ ruleTester.run( 'use-recommended-components', rule, {
|
|
|
51
50
|
{
|
|
52
51
|
message:
|
|
53
52
|
'`Foo` from `@wordpress/ui` is not yet recommended for use in a WordPress environment.',
|
|
54
|
-
type: 'ImportSpecifier',
|
|
55
53
|
},
|
|
56
54
|
{
|
|
57
55
|
message:
|
|
58
56
|
'`Bar` from `@wordpress/ui` is not yet recommended for use in a WordPress environment.',
|
|
59
|
-
type: 'ImportSpecifier',
|
|
60
57
|
},
|
|
61
58
|
],
|
|
62
59
|
},
|
|
@@ -67,7 +64,6 @@ ruleTester.run( 'use-recommended-components', rule, {
|
|
|
67
64
|
{
|
|
68
65
|
message:
|
|
69
66
|
'__experimentalZStack is planned for deprecation. Write your own CSS instead.',
|
|
70
|
-
type: 'ImportSpecifier',
|
|
71
67
|
},
|
|
72
68
|
],
|
|
73
69
|
},
|
|
@@ -77,7 +73,6 @@ ruleTester.run( 'use-recommended-components', rule, {
|
|
|
77
73
|
{
|
|
78
74
|
message:
|
|
79
75
|
'__experimentalZStack is planned for deprecation. Write your own CSS instead.',
|
|
80
|
-
type: 'ImportSpecifier',
|
|
81
76
|
},
|
|
82
77
|
],
|
|
83
78
|
},
|
|
@@ -31,7 +31,7 @@ function arrayLast( array ) {
|
|
|
31
31
|
function getReferences( context, specifiers ) {
|
|
32
32
|
const variables = specifiers.reduce(
|
|
33
33
|
( acc, specifier ) =>
|
|
34
|
-
acc.concat( context.getDeclaredVariables( specifier ) ),
|
|
34
|
+
acc.concat( context.sourceCode.getDeclaredVariables( specifier ) ),
|
|
35
35
|
[]
|
|
36
36
|
);
|
|
37
37
|
const references = variables.reduce(
|
|
@@ -58,7 +58,9 @@ function collectAllNodesFromCallbackFunctions( context, node ) {
|
|
|
58
58
|
( acc, { identifier: { parent } } ) =>
|
|
59
59
|
parent && parent.arguments && parent.arguments.length > 0
|
|
60
60
|
? acc.concat(
|
|
61
|
-
context.getDeclaredVariables(
|
|
61
|
+
context.sourceCode.getDeclaredVariables(
|
|
62
|
+
parent.arguments[ 0 ]
|
|
63
|
+
)
|
|
62
64
|
)
|
|
63
65
|
: acc,
|
|
64
66
|
[]
|
|
@@ -153,9 +155,10 @@ function getFixes( fixer, context, callNode ) {
|
|
|
153
155
|
fixer.replaceText( callNode.arguments[ 0 ], variableName ),
|
|
154
156
|
];
|
|
155
157
|
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
const ancestors = context.sourceCode.getAncestors( callNode );
|
|
159
|
+
const imports = ancestors[ 0 ].body.filter(
|
|
160
|
+
( node ) => node.type === 'ImportDeclaration'
|
|
161
|
+
);
|
|
159
162
|
const packageImports = imports.filter(
|
|
160
163
|
( node ) => node.source.value === importName
|
|
161
164
|
);
|
|
@@ -22,7 +22,8 @@ module.exports = {
|
|
|
22
22
|
},
|
|
23
23
|
create( context ) {
|
|
24
24
|
const mode = context.options[ 0 ] || 'always';
|
|
25
|
-
const
|
|
25
|
+
const sourceCode = context.sourceCode;
|
|
26
|
+
const comments = sourceCode.getAllComments();
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* Locality classification of an import, one of "External",
|
|
@@ -194,9 +195,7 @@ module.exports = {
|
|
|
194
195
|
return null;
|
|
195
196
|
}
|
|
196
197
|
|
|
197
|
-
const text =
|
|
198
|
-
.getSourceCode()
|
|
199
|
-
.getText();
|
|
198
|
+
const text = sourceCode.getText();
|
|
200
199
|
|
|
201
200
|
// Trim preceding and trailing newlines.
|
|
202
201
|
let [ start, end ] = comment.range;
|
|
@@ -61,6 +61,7 @@ function extractTranslatorKeys( commentText ) {
|
|
|
61
61
|
module.exports = {
|
|
62
62
|
meta: {
|
|
63
63
|
type: 'problem',
|
|
64
|
+
schema: [],
|
|
64
65
|
messages: {
|
|
65
66
|
missing:
|
|
66
67
|
'Translation function with placeholders is missing preceding translator comment',
|
|
@@ -71,6 +72,7 @@ module.exports = {
|
|
|
71
72
|
},
|
|
72
73
|
},
|
|
73
74
|
create( context ) {
|
|
75
|
+
const sourceCode = context.sourceCode;
|
|
74
76
|
return {
|
|
75
77
|
CallExpression( node ) {
|
|
76
78
|
const {
|
|
@@ -107,7 +109,7 @@ module.exports = {
|
|
|
107
109
|
return;
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
const comments =
|
|
112
|
+
const comments = sourceCode.getCommentsBefore( node ).slice();
|
|
111
113
|
|
|
112
114
|
let parentNode = parent;
|
|
113
115
|
|
|
@@ -123,7 +125,9 @@ module.exports = {
|
|
|
123
125
|
parentNode.type !== 'Program' &&
|
|
124
126
|
Math.abs( parentNode.loc.start.line - currentLine ) <= 1
|
|
125
127
|
) {
|
|
126
|
-
comments.push(
|
|
128
|
+
comments.push(
|
|
129
|
+
...sourceCode.getCommentsBefore( parentNode )
|
|
130
|
+
);
|
|
127
131
|
parentNode = parentNode.parent;
|
|
128
132
|
}
|
|
129
133
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
const { createDOMGlobalRule } = require( '../utils/dom-globals' );
|
|
5
|
+
|
|
6
|
+
module.exports = createDOMGlobalRule( {
|
|
7
|
+
description: 'Disallow use of DOM globals in class constructors',
|
|
8
|
+
message:
|
|
9
|
+
"Use of DOM global '{{name}}' is forbidden in class constructors, consider moving this to componentDidMount() or equivalent for non React components",
|
|
10
|
+
test( scope ) {
|
|
11
|
+
if ( scope.block?.parent ) {
|
|
12
|
+
const { type, kind } = scope.block.parent;
|
|
13
|
+
return type === 'MethodDefinition' && kind === 'constructor';
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
},
|
|
17
|
+
} );
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
const { createDOMGlobalRule } = require( '../utils/dom-globals' );
|
|
5
|
+
|
|
6
|
+
module.exports = createDOMGlobalRule( {
|
|
7
|
+
description: 'Disallow use of DOM globals in module scope',
|
|
8
|
+
message: "Use of DOM global '{{name}}' is forbidden in module scope",
|
|
9
|
+
test( scope ) {
|
|
10
|
+
return scope.type === 'module' || scope.type === 'global';
|
|
11
|
+
},
|
|
12
|
+
} );
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
const {
|
|
5
|
+
createDOMGlobalRule,
|
|
6
|
+
isReturnValueJSX,
|
|
7
|
+
} = require( '../utils/dom-globals' );
|
|
8
|
+
|
|
9
|
+
module.exports = createDOMGlobalRule( {
|
|
10
|
+
description:
|
|
11
|
+
'Disallow use of DOM globals in render() method of a React class component',
|
|
12
|
+
message:
|
|
13
|
+
"Use of DOM global '{{name}}' is forbidden in render(), consider moving this to componentDidMount()",
|
|
14
|
+
test( scope ) {
|
|
15
|
+
if ( scope.block?.parent ) {
|
|
16
|
+
const { type, kind, key } = scope.block.parent;
|
|
17
|
+
return (
|
|
18
|
+
type === 'MethodDefinition' &&
|
|
19
|
+
kind === 'method' &&
|
|
20
|
+
key.name === 'render' &&
|
|
21
|
+
isReturnValueJSX( scope )
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
},
|
|
26
|
+
} );
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
const {
|
|
5
|
+
createDOMGlobalRule,
|
|
6
|
+
isReturnValueJSX,
|
|
7
|
+
} = require( '../utils/dom-globals' );
|
|
8
|
+
|
|
9
|
+
module.exports = createDOMGlobalRule( {
|
|
10
|
+
description:
|
|
11
|
+
'Disallow use of DOM globals in the render cycle of a React function component',
|
|
12
|
+
message:
|
|
13
|
+
"Use of DOM global '{{name}}' is forbidden in the render-cycle of a React FC, consider moving this inside useEffect()",
|
|
14
|
+
test( scope ) {
|
|
15
|
+
return isReturnValueJSX( scope );
|
|
16
|
+
},
|
|
17
|
+
} );
|
package/rules/no-i18n-in-save.js
CHANGED
|
@@ -22,7 +22,7 @@ module.exports = {
|
|
|
22
22
|
},
|
|
23
23
|
create( context ) {
|
|
24
24
|
let saveFunctionDepth = 0;
|
|
25
|
-
const filename = context.
|
|
25
|
+
const filename = context.filename;
|
|
26
26
|
|
|
27
27
|
// Skip deprecated files as they preserve old behavior including translation functions
|
|
28
28
|
const normalizedFilename = filename.replace( /\\/g, '/' );
|
|
@@ -3,7 +3,7 @@ module.exports = /** @type {import('eslint').Rule.RuleModule} */ ( {
|
|
|
3
3
|
type: 'problem',
|
|
4
4
|
docs: {
|
|
5
5
|
description:
|
|
6
|
-
'Disallow setting any CSS custom property beginning with --wpds-
|
|
6
|
+
'Disallow setting any CSS custom property beginning with --wpds-',
|
|
7
7
|
},
|
|
8
8
|
schema: [],
|
|
9
9
|
messages: {
|
|
@@ -14,9 +14,7 @@ module.exports = /** @type {import('eslint').Rule.RuleModule} */ ( {
|
|
|
14
14
|
create( context ) {
|
|
15
15
|
return {
|
|
16
16
|
/** @param {import('estree').Property} node */
|
|
17
|
-
'
|
|
18
|
-
node
|
|
19
|
-
) {
|
|
17
|
+
'ObjectExpression > Property[key.value=/^--wpds-/]'( node ) {
|
|
20
18
|
context.report( {
|
|
21
19
|
node: node.key,
|
|
22
20
|
messageId: 'disallowedSet',
|
|
@@ -4,35 +4,41 @@ const tokenList = tokenListModule.default || tokenListModule;
|
|
|
4
4
|
const DS_TOKEN_PREFIX = 'wpds-';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Single-pass extraction that finds all `--prefix-*` tokens in a CSS value
|
|
8
|
+
* string and classifies each occurrence as `var()`-wrapped or bare.
|
|
9
9
|
*
|
|
10
|
-
* @param {string} value - The CSS value string to search
|
|
10
|
+
* @param {string} value - The CSS value string to search.
|
|
11
11
|
* @param {string} [prefix=''] - Optional prefix to filter variables (e.g., 'wpds-').
|
|
12
|
-
* @return {Set<string
|
|
12
|
+
* @return {{ tokens: Set<string>, bare: Set<string> }}
|
|
13
|
+
* `tokens` — every unique matched token;
|
|
14
|
+
* `bare` — the subset that appeared at least once without a `var()` wrapper.
|
|
13
15
|
*
|
|
14
16
|
* @example
|
|
15
|
-
*
|
|
16
|
-
* '
|
|
17
|
-
* '
|
|
18
|
-
* 'background: var(--unrelated-bg);',
|
|
19
|
-
* 'wpds'
|
|
17
|
+
* classifyTokens(
|
|
18
|
+
* 'var(--wpds-color-fg) --wpds-color-bg',
|
|
19
|
+
* 'wpds-'
|
|
20
20
|
* );
|
|
21
|
-
* // → Set {
|
|
21
|
+
* // → { tokens: Set {'--wpds-color-fg','--wpds-color-bg'},
|
|
22
|
+
* // bare: Set {'--wpds-color-bg'} }
|
|
22
23
|
*/
|
|
23
|
-
function
|
|
24
|
-
const regex =
|
|
25
|
-
|
|
24
|
+
function classifyTokens( value, prefix = '' ) {
|
|
25
|
+
const regex = new RegExp(
|
|
26
|
+
`(?:^|[^\\w])(var\\(\\s*)?(--${ prefix }[\\w-]+)`,
|
|
27
|
+
'g'
|
|
28
|
+
);
|
|
29
|
+
const tokens = new Set();
|
|
30
|
+
const bare = new Set();
|
|
26
31
|
|
|
27
32
|
let match;
|
|
28
33
|
while ( ( match = regex.exec( value ) ) !== null ) {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
const token = match[ 2 ];
|
|
35
|
+
tokens.add( token );
|
|
36
|
+
if ( ! match[ 1 ] ) {
|
|
37
|
+
bare.add( token );
|
|
32
38
|
}
|
|
33
39
|
}
|
|
34
40
|
|
|
35
|
-
return
|
|
41
|
+
return { tokens, bare };
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
const knownTokens = new Set( tokenList );
|
|
@@ -50,6 +56,8 @@ module.exports = /** @type {import('eslint').Rule.RuleModule} */ ( {
|
|
|
50
56
|
'The following CSS variables are not valid Design System tokens: {{ tokenNames }}',
|
|
51
57
|
dynamicToken:
|
|
52
58
|
'Design System tokens must not be dynamically constructed, as they cannot be statically verified for correctness or processed automatically to inject fallbacks.',
|
|
59
|
+
bareToken:
|
|
60
|
+
'Design System tokens must be wrapped in `var()` for build-time fallback injection to work: {{ tokenNames }}',
|
|
53
61
|
},
|
|
54
62
|
},
|
|
55
63
|
create( context ) {
|
|
@@ -71,6 +79,7 @@ module.exports = /** @type {import('eslint').Rule.RuleModule} */ ( {
|
|
|
71
79
|
[ dynamicTemplateLiteralAST ]( node ) {
|
|
72
80
|
let hasDynamic = false;
|
|
73
81
|
const unknownTokens = [];
|
|
82
|
+
const bareTokens = [];
|
|
74
83
|
|
|
75
84
|
for ( const quasi of node.quasis ) {
|
|
76
85
|
const raw = quasi.value.raw;
|
|
@@ -84,7 +93,7 @@ module.exports = /** @type {import('eslint').Rule.RuleModule} */ ( {
|
|
|
84
93
|
hasDynamic = true;
|
|
85
94
|
}
|
|
86
95
|
|
|
87
|
-
const tokens =
|
|
96
|
+
const { tokens, bare } = classifyTokens(
|
|
88
97
|
value,
|
|
89
98
|
DS_TOKEN_PREFIX
|
|
90
99
|
);
|
|
@@ -95,12 +104,15 @@ module.exports = /** @type {import('eslint').Rule.RuleModule} */ ( {
|
|
|
95
104
|
const endMatch = value.match( /(--([\w-]+))$/ );
|
|
96
105
|
if ( endMatch ) {
|
|
97
106
|
tokens.delete( endMatch[ 1 ] );
|
|
107
|
+
bare.delete( endMatch[ 1 ] );
|
|
98
108
|
}
|
|
99
109
|
}
|
|
100
110
|
|
|
101
111
|
for ( const token of tokens ) {
|
|
102
112
|
if ( ! knownTokens.has( token ) ) {
|
|
103
113
|
unknownTokens.push( token );
|
|
114
|
+
} else if ( bare.has( token ) ) {
|
|
115
|
+
bareTokens.push( token );
|
|
104
116
|
}
|
|
105
117
|
}
|
|
106
118
|
}
|
|
@@ -123,6 +135,18 @@ module.exports = /** @type {import('eslint').Rule.RuleModule} */ ( {
|
|
|
123
135
|
},
|
|
124
136
|
} );
|
|
125
137
|
}
|
|
138
|
+
|
|
139
|
+
if ( bareTokens.length > 0 ) {
|
|
140
|
+
context.report( {
|
|
141
|
+
node,
|
|
142
|
+
messageId: 'bareToken',
|
|
143
|
+
data: {
|
|
144
|
+
tokenNames: bareTokens
|
|
145
|
+
.map( ( token ) => `'${ token }'` )
|
|
146
|
+
.join( ', ' ),
|
|
147
|
+
},
|
|
148
|
+
} );
|
|
149
|
+
}
|
|
126
150
|
},
|
|
127
151
|
/** @param {import('estree').Literal | import('estree').TemplateElement} node */
|
|
128
152
|
[ staticTokensAST ]( node ) {
|
|
@@ -145,7 +169,7 @@ module.exports = /** @type {import('eslint').Rule.RuleModule} */ ( {
|
|
|
145
169
|
return;
|
|
146
170
|
}
|
|
147
171
|
|
|
148
|
-
const usedTokens =
|
|
172
|
+
const { tokens: usedTokens, bare } = classifyTokens(
|
|
149
173
|
computedValue,
|
|
150
174
|
DS_TOKEN_PREFIX
|
|
151
175
|
);
|
|
@@ -164,6 +188,31 @@ module.exports = /** @type {import('eslint').Rule.RuleModule} */ ( {
|
|
|
164
188
|
},
|
|
165
189
|
} );
|
|
166
190
|
}
|
|
191
|
+
|
|
192
|
+
// Skip bare-token check for property keys
|
|
193
|
+
// (e.g. `{ '--wpds-token': value }` declaring a custom property).
|
|
194
|
+
const isPropertyKey =
|
|
195
|
+
node.parent?.type === 'Property' &&
|
|
196
|
+
node.parent.key === node;
|
|
197
|
+
|
|
198
|
+
if ( ! isPropertyKey ) {
|
|
199
|
+
const bareTokens = [ ...usedTokens ].filter(
|
|
200
|
+
( token ) =>
|
|
201
|
+
knownTokens.has( token ) && bare.has( token )
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
if ( bareTokens.length > 0 ) {
|
|
205
|
+
context.report( {
|
|
206
|
+
node,
|
|
207
|
+
messageId: 'bareToken',
|
|
208
|
+
data: {
|
|
209
|
+
tokenNames: bareTokens
|
|
210
|
+
.map( ( token ) => `'${ token }'` )
|
|
211
|
+
.join( ', ' ),
|
|
212
|
+
},
|
|
213
|
+
} );
|
|
214
|
+
}
|
|
215
|
+
}
|
|
167
216
|
},
|
|
168
217
|
};
|
|
169
218
|
},
|