@wordpress/eslint-plugin 24.3.1-next.v.202602271551.0 → 24.4.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 CHANGED
@@ -82,11 +82,16 @@ The granular rulesets will not define any environment globals. As such, if they
82
82
  | [i18n-text-domain](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/i18n-text-domain.md) | Enforce passing valid text domains. | ✓ |
83
83
  | [i18n-translator-comments](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/i18n-translator-comments.md) | Enforce adding translator comments. | ✓ |
84
84
  | [no-base-control-with-label-without-id](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-base-control-with-label-without-id.md) | Disallow the usage of BaseControl component with a label prop set but omitting the id property. | ✓ |
85
+ | [no-dom-globals-in-constructor](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-dom-globals-in-constructor.md) | Disallow use of DOM globals in class constructors. | |
86
+ | [no-dom-globals-in-module-scope](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-dom-globals-in-module-scope.md) | Disallow use of DOM globals in module scope. | |
87
+ | [no-dom-globals-in-react-cc-render](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-dom-globals-in-react-cc-render.md) | Disallow use of DOM globals in React class component render methods. | |
88
+ | [no-dom-globals-in-react-fc](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-dom-globals-in-react-fc.md) | Disallow use of DOM globals in the render cycle of a React function component. | |
85
89
  | [components-no-missing-40px-size-prop](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/components-no-missing-40px-size-prop.md) | Disallow missing `__next40pxDefaultSize` prop on `@wordpress/components` components. | ✓ |
86
90
  | [components-no-unsafe-button-disabled](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/components-no-unsafe-button-disabled.md) | Disallow using `disabled` on Button without `accessibleWhenDisabled`. | ✓ |
87
91
  | [no-i18n-in-save](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-i18n-in-save.md) | Disallow translation functions in block save methods. | |
88
92
  | [no-unguarded-get-range-at](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-unguarded-get-range-at.md) | Disallow the usage of unguarded `getRangeAt` calls. | ✓ |
89
93
  | [no-unsafe-wp-apis](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-unsafe-wp-apis.md) | Disallow the usage of unsafe APIs from `@wordpress/*` packages | ✓ |
94
+ | [use-recommended-components](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/use-recommended-components.md) | Encourage the use of recommended UI components in a WordPress environment. | |
90
95
  | [no-unused-vars-before-return](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-unused-vars-before-return.md) | Disallow assigning variable values if unused before a return. | ✓ |
91
96
  | [no-wp-process-env](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/no-wp-process-env.md) | Disallow legacy usage of WordPress variables via `process.env` like `process.env.SCRIPT_DEBUG`. | ✓ |
92
97
  | [react-no-unsafe-timeout](https://github.com/WordPress/gutenberg/tree/HEAD/packages/eslint-plugin/docs/rules/react-no-unsafe-timeout.md) | Disallow unsafe `setTimeout` in component. | |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/eslint-plugin",
3
- "version": "24.3.1-next.v.202602271551.0+464abe399",
3
+ "version": "24.4.0",
4
4
  "description": "ESLint plugin for WordPress development.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -40,9 +40,9 @@
40
40
  "@babel/eslint-parser": "7.25.7",
41
41
  "@typescript-eslint/eslint-plugin": "^6.4.1",
42
42
  "@typescript-eslint/parser": "^6.4.1",
43
- "@wordpress/babel-preset-default": "^8.40.1-next.v.202602271551.0+464abe399",
44
- "@wordpress/prettier-config": "^4.40.1-next.v.202602271551.0+464abe399",
45
- "@wordpress/theme": "^0.8.1-next.v.202602271551.0+464abe399",
43
+ "@wordpress/babel-preset-default": "^8.42.0",
44
+ "@wordpress/prettier-config": "^4.42.0",
45
+ "@wordpress/theme": "^0.9.0",
46
46
  "cosmiconfig": "^7.0.0",
47
47
  "eslint-config-prettier": "^8.3.0",
48
48
  "eslint-import-resolver-typescript": "^4.4.4",
@@ -78,5 +78,5 @@
78
78
  "publishConfig": {
79
79
  "access": "public"
80
80
  },
81
- "gitHead": "95aa7055a5757219e2d96a91efc69f7dd1b2d4c3"
81
+ "gitHead": "c20787b1778ae64c2db65643b1c236309d68e6ba"
82
82
  }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { RuleTester } from 'eslint';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import rule from '../no-dom-globals-in-constructor';
10
+
11
+ const ruleTester = new RuleTester( {
12
+ parserOptions: {
13
+ ecmaVersion: 2020,
14
+ sourceType: 'module',
15
+ ecmaFeatures: { jsx: true },
16
+ },
17
+ } );
18
+
19
+ ruleTester.run( 'no-dom-globals-in-constructor', rule, {
20
+ valid: [
21
+ {
22
+ code: `class Foo {
23
+ method() { document.title = "test"; }
24
+ }`,
25
+ },
26
+ {
27
+ code: `class Foo {
28
+ constructor() { this.name = "test"; }
29
+ }`,
30
+ },
31
+ ],
32
+ invalid: [
33
+ {
34
+ code: `class Foo {
35
+ constructor() { document.title = "test"; }
36
+ }`,
37
+ errors: [
38
+ {
39
+ messageId: 'defaultMessage',
40
+ data: { name: 'document' },
41
+ },
42
+ ],
43
+ },
44
+ {
45
+ code: `class Foo {
46
+ constructor() { window.addEventListener("resize", () => {}); }
47
+ }`,
48
+ errors: [
49
+ {
50
+ messageId: 'defaultMessage',
51
+ data: { name: 'window' },
52
+ },
53
+ ],
54
+ },
55
+ ],
56
+ } );
@@ -0,0 +1,117 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { RuleTester } from 'eslint';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import rule from '../no-dom-globals-in-module-scope';
10
+
11
+ const ruleTester = new RuleTester( {
12
+ parserOptions: {
13
+ ecmaVersion: 2020,
14
+ sourceType: 'module',
15
+ ecmaFeatures: { jsx: true },
16
+ },
17
+ } );
18
+
19
+ ruleTester.run( 'no-dom-globals-in-module-scope', rule, {
20
+ valid: [
21
+ {
22
+ code: 'function foo() { window.scrollTo(0, 0); }',
23
+ },
24
+ {
25
+ code: 'if (typeof window !== "undefined") {}',
26
+ },
27
+ {
28
+ code: 'const isClient = typeof document !== "undefined";',
29
+ },
30
+ {
31
+ code: 'function effect() { const el = document.createElement("div"); }',
32
+ },
33
+ {
34
+ // Function scope in a script file should not be flagged.
35
+ code: 'function foo() { window.scrollTo(0, 0); }',
36
+ parserOptions: { ecmaVersion: 2020, sourceType: 'script' },
37
+ },
38
+ // Shared globals (browser + node) should NOT be flagged.
39
+ {
40
+ code: 'console.log("hello");',
41
+ },
42
+ {
43
+ code: 'setTimeout(() => {}, 100);',
44
+ },
45
+ {
46
+ code: 'const u = new URL("https://example.com");',
47
+ },
48
+ {
49
+ code: 'fetch("/api/data");',
50
+ },
51
+ ],
52
+ invalid: [
53
+ {
54
+ code: 'const width = window.innerWidth;',
55
+ errors: [
56
+ {
57
+ messageId: 'defaultMessage',
58
+ data: { name: 'window' },
59
+ },
60
+ ],
61
+ },
62
+ {
63
+ code: 'const el = document.createElement("div");',
64
+ errors: [
65
+ {
66
+ messageId: 'defaultMessage',
67
+ data: { name: 'document' },
68
+ },
69
+ ],
70
+ },
71
+ {
72
+ code: 'navigator.userAgent;',
73
+ errors: [
74
+ {
75
+ messageId: 'defaultMessage',
76
+ data: { name: 'navigator' },
77
+ },
78
+ ],
79
+ },
80
+ {
81
+ code: 'localStorage.getItem("key");',
82
+ errors: [
83
+ {
84
+ messageId: 'defaultMessage',
85
+ data: { name: 'localStorage' },
86
+ },
87
+ ],
88
+ },
89
+ {
90
+ code: 'sessionStorage.setItem("key", "value");',
91
+ errors: [
92
+ {
93
+ messageId: 'defaultMessage',
94
+ data: { name: 'sessionStorage' },
95
+ },
96
+ ],
97
+ },
98
+ {
99
+ code: 'history.pushState({}, "", "/new");',
100
+ errors: [
101
+ {
102
+ messageId: 'defaultMessage',
103
+ data: { name: 'history' },
104
+ },
105
+ ],
106
+ },
107
+ {
108
+ code: 'location.href = "/";',
109
+ errors: [
110
+ {
111
+ messageId: 'defaultMessage',
112
+ data: { name: 'location' },
113
+ },
114
+ ],
115
+ },
116
+ ],
117
+ } );
@@ -0,0 +1,51 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { RuleTester } from 'eslint';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import rule from '../no-dom-globals-in-react-cc-render';
10
+
11
+ const ruleTester = new RuleTester( {
12
+ parserOptions: {
13
+ ecmaVersion: 2020,
14
+ sourceType: 'module',
15
+ ecmaFeatures: { jsx: true },
16
+ },
17
+ } );
18
+
19
+ ruleTester.run( 'no-dom-globals-in-react-cc-render', rule, {
20
+ valid: [
21
+ {
22
+ code: `class Foo {
23
+ render() { const x = 1; return <div>{x}</div>; }
24
+ }`,
25
+ },
26
+ {
27
+ code: `class Foo {
28
+ componentDidMount() { window.scrollTo(0, 0); }
29
+ render() { return <div />; }
30
+ }`,
31
+ },
32
+ {
33
+ code: `class Foo {
34
+ render() { return "not jsx"; }
35
+ }`,
36
+ },
37
+ ],
38
+ invalid: [
39
+ {
40
+ code: `class Foo {
41
+ render() { const w = window.innerWidth; return <div>{w}</div>; }
42
+ }`,
43
+ errors: [
44
+ {
45
+ messageId: 'defaultMessage',
46
+ data: { name: 'window' },
47
+ },
48
+ ],
49
+ },
50
+ ],
51
+ } );
@@ -0,0 +1,77 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { RuleTester } from 'eslint';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import rule from '../no-dom-globals-in-react-fc';
10
+
11
+ const ruleTester = new RuleTester( {
12
+ parserOptions: {
13
+ ecmaVersion: 2020,
14
+ sourceType: 'module',
15
+ ecmaFeatures: { jsx: true },
16
+ },
17
+ } );
18
+
19
+ ruleTester.run( 'no-dom-globals-in-react-fc', rule, {
20
+ valid: [
21
+ {
22
+ code: 'function notAComponent() { window.scrollTo(0, 0); }',
23
+ },
24
+ {
25
+ code: 'function Component() { return <div />; }',
26
+ },
27
+ {
28
+ code: `function Component() {
29
+ useEffect(() => { window.scrollTo(0, 0); });
30
+ return <div />;
31
+ }`,
32
+ },
33
+ {
34
+ // DOM global inside a nested callback (event handler) inside FC
35
+ // is allowed — the handler runs at event time, not render time.
36
+ code: `function Component() {
37
+ const onClick = () => { document.title = "clicked"; };
38
+ return <button onClick={onClick} />;
39
+ }`,
40
+ },
41
+ ],
42
+ invalid: [
43
+ {
44
+ code: `function Component() {
45
+ window.addEventListener("resize", () => {});
46
+ return <div />;
47
+ }`,
48
+ errors: [
49
+ {
50
+ messageId: 'defaultMessage',
51
+ data: { name: 'window' },
52
+ },
53
+ ],
54
+ },
55
+ {
56
+ code: `const Header = () => {
57
+ const w = document.body.clientWidth;
58
+ return <header>{w}</header>;
59
+ }`,
60
+ errors: [
61
+ {
62
+ messageId: 'defaultMessage',
63
+ data: { name: 'document' },
64
+ },
65
+ ],
66
+ },
67
+ {
68
+ code: `const Icon = ( { name } ) => <span className={ window.iconPrefix + name } />;`,
69
+ errors: [
70
+ {
71
+ messageId: 'defaultMessage',
72
+ data: { name: 'window' },
73
+ },
74
+ ],
75
+ },
76
+ ],
77
+ } );
@@ -90,7 +90,6 @@ function render() {
90
90
  errors: [
91
91
  {
92
92
  messageId: 'noI18nInSave',
93
- type: 'CallExpression',
94
93
  },
95
94
  ],
96
95
  },
@@ -103,7 +102,6 @@ function save() {
103
102
  errors: [
104
103
  {
105
104
  messageId: 'noI18nInSave',
106
- type: 'CallExpression',
107
105
  },
108
106
  ],
109
107
  },
@@ -116,7 +114,6 @@ const save = () => {
116
114
  errors: [
117
115
  {
118
116
  messageId: 'noI18nInSave',
119
- type: 'CallExpression',
120
117
  },
121
118
  ],
122
119
  },
@@ -129,7 +126,6 @@ const save = function() {
129
126
  errors: [
130
127
  {
131
128
  messageId: 'noI18nInSave',
132
- type: 'CallExpression',
133
129
  },
134
130
  ],
135
131
  },
@@ -142,7 +138,6 @@ export default function save() {
142
138
  errors: [
143
139
  {
144
140
  messageId: 'noI18nInSave',
145
- type: 'CallExpression',
146
141
  },
147
142
  ],
148
143
  },
@@ -157,7 +152,6 @@ const settings = {
157
152
  errors: [
158
153
  {
159
154
  messageId: 'noI18nInSave',
160
- type: 'CallExpression',
161
155
  },
162
156
  ],
163
157
  },
@@ -170,7 +164,6 @@ const settings = {
170
164
  errors: [
171
165
  {
172
166
  messageId: 'noI18nInSave',
173
- type: 'CallExpression',
174
167
  },
175
168
  ],
176
169
  },
@@ -183,7 +176,6 @@ function save() {
183
176
  errors: [
184
177
  {
185
178
  messageId: 'noI18nInSave',
186
- type: 'CallExpression',
187
179
  },
188
180
  ],
189
181
  },
@@ -197,7 +189,6 @@ function save() {
197
189
  errors: [
198
190
  {
199
191
  messageId: 'noI18nInSave',
200
- type: 'CallExpression',
201
192
  },
202
193
  ],
203
194
  },
@@ -211,7 +202,6 @@ function save() {
211
202
  errors: [
212
203
  {
213
204
  messageId: 'noI18nInSave',
214
- type: 'CallExpression',
215
205
  },
216
206
  ],
217
207
  },
@@ -228,7 +218,6 @@ function save() {
228
218
  errors: [
229
219
  {
230
220
  messageId: 'noI18nInSave',
231
- type: 'CallExpression',
232
221
  },
233
222
  ],
234
223
  },
@@ -243,11 +232,9 @@ function save() {
243
232
  errors: [
244
233
  {
245
234
  messageId: 'noI18nInSave',
246
- type: 'CallExpression',
247
235
  },
248
236
  {
249
237
  messageId: 'noI18nInSave',
250
- type: 'CallExpression',
251
238
  },
252
239
  ],
253
240
  },
@@ -262,7 +249,6 @@ function save() {
262
249
  errors: [
263
250
  {
264
251
  messageId: 'noI18nInSave',
265
- type: 'CallExpression',
266
252
  },
267
253
  ],
268
254
  },
@@ -24,6 +24,15 @@ ruleTester.run( 'no-setting-ds-tokens', rule, {
24
24
  {
25
25
  code: `<div style={ { margin: '10px' } } />`,
26
26
  },
27
+ {
28
+ code: `const styles = { '--my-custom-prop': 'value' };`,
29
+ },
30
+ {
31
+ code: `const styles = { color: 'var(--wpds-color-fg-content-neutral)' };`,
32
+ },
33
+ {
34
+ code: `const { '--wpds-color-fg-content-neutral': neutralColor } = styles;`,
35
+ },
27
36
  ],
28
37
  invalid: [
29
38
  {
@@ -42,5 +51,29 @@ ruleTester.run( 'no-setting-ds-tokens', rule, {
42
51
  },
43
52
  ],
44
53
  },
54
+ {
55
+ code: `const styles = { '--wpds-color-fg-content-neutral': 'red' };`,
56
+ errors: [
57
+ {
58
+ messageId: 'disallowedSet',
59
+ },
60
+ ],
61
+ },
62
+ {
63
+ code: `function getStyles() { return { '--wpds-font-size-md': '10px' }; }`,
64
+ errors: [
65
+ {
66
+ messageId: 'disallowedSet',
67
+ },
68
+ ],
69
+ },
70
+ {
71
+ code: `const config = { inner: { '--wpds-color-fg-content-neutral': 'red' } };`,
72
+ errors: [
73
+ {
74
+ messageId: 'disallowedSet',
75
+ },
76
+ ],
77
+ },
45
78
  ],
46
79
  } );
@@ -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
  } );
@@ -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
  },
@@ -0,0 +1,91 @@
1
+ /* eslint-env jest */
2
+
3
+ /**
4
+ * External dependencies
5
+ */
6
+ import { RuleTester } from 'eslint';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import rule, { ALLOWLIST, DENYLIST } from '../use-recommended-components';
12
+
13
+ const ruleTester = new RuleTester( {
14
+ parserOptions: {
15
+ sourceType: 'module',
16
+ ecmaVersion: 6,
17
+ },
18
+ } );
19
+
20
+ ruleTester.run( 'use-recommended-components', rule, {
21
+ valid: [
22
+ // Unrelated packages are not affected.
23
+ "import { Anything } from 'other-package';",
24
+ "import { Button } from '@wordpress/components';",
25
+
26
+ // Default and namespace imports are not affected.
27
+ "import UI from '@wordpress/ui';",
28
+ "import * as UI from '@wordpress/ui';",
29
+
30
+ // Allowed @wordpress/ui components.
31
+ "import { Badge } from '@wordpress/ui';",
32
+ "import { Stack } from '@wordpress/ui';",
33
+ "import { Badge, Stack } from '@wordpress/ui';",
34
+ ],
35
+
36
+ invalid: [
37
+ // Allowlist: non-allowed @wordpress/ui imports are flagged.
38
+ {
39
+ code: "import { SomeComponent } from '@wordpress/ui';",
40
+ errors: [
41
+ {
42
+ message:
43
+ '`SomeComponent` from `@wordpress/ui` is not yet recommended for use in a WordPress environment.',
44
+ },
45
+ ],
46
+ },
47
+ {
48
+ code: "import { Foo, Bar } from '@wordpress/ui';",
49
+ errors: [
50
+ {
51
+ message:
52
+ '`Foo` from `@wordpress/ui` is not yet recommended for use in a WordPress environment.',
53
+ },
54
+ {
55
+ message:
56
+ '`Bar` from `@wordpress/ui` is not yet recommended for use in a WordPress environment.',
57
+ },
58
+ ],
59
+ },
60
+ // Denylist: denied components are flagged with their message.
61
+ {
62
+ code: "import { __experimentalZStack } from '@wordpress/components';",
63
+ errors: [
64
+ {
65
+ message:
66
+ '__experimentalZStack is planned for deprecation. Write your own CSS instead.',
67
+ },
68
+ ],
69
+ },
70
+ {
71
+ code: "import { __experimentalZStack as ZStack } from '@wordpress/components';",
72
+ errors: [
73
+ {
74
+ message:
75
+ '__experimentalZStack is planned for deprecation. Write your own CSS instead.',
76
+ },
77
+ ],
78
+ },
79
+ ],
80
+ } );
81
+
82
+ describe( 'ALLOWLIST and DENYLIST', () => {
83
+ it( 'should not have overlapping package keys', () => {
84
+ const allowlistPackages = Object.keys( ALLOWLIST );
85
+ const denylistPackages = Object.keys( DENYLIST );
86
+ const overlap = allowlistPackages.filter( ( pkg ) =>
87
+ denylistPackages.includes( pkg )
88
+ );
89
+ expect( overlap ).toEqual( [] );
90
+ } );
91
+ } );