Skip to content

Commit 0ad8409

Browse files
authored
Merge branch 'WordPress:trunk' into trunk
2 parents eb4c754 + 0466f7d commit 0ad8409

File tree

5,649 files changed

+313448
-254083
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

5,649 files changed

+313448
-254083
lines changed

.eslintrc.js

Lines changed: 233 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,6 @@ const restrictedImports = [
4646
name: 'lodash',
4747
message: 'Please use native functionality instead.',
4848
},
49-
{
50-
name: 'reakit',
51-
message:
52-
'Please use Reakit API through `@wordpress/components` instead.',
53-
},
5449
{
5550
name: '@ariakit/react',
5651
message:
@@ -61,10 +56,6 @@ const restrictedImports = [
6156
importNames: [ 'combineReducers' ],
6257
message: 'Please use `combineReducers` from `@wordpress/data` instead.',
6358
},
64-
{
65-
name: 'puppeteer-testing-library',
66-
message: '`puppeteer-testing-library` is still experimental.',
67-
},
6859
{
6960
name: '@emotion/css',
7061
message:
@@ -85,6 +76,77 @@ const restrictedImports = [
8576
message:
8677
"edit-widgets is a WordPress top level package that shouldn't be imported into other packages",
8778
},
79+
{
80+
name: 'classnames',
81+
message:
82+
"Please use `clsx` instead. It's a lighter and faster drop-in replacement for `classnames`.",
83+
},
84+
];
85+
86+
const restrictedSyntax = [
87+
// NOTE: We can't include the forward slash in our regex or
88+
// we'll get a `SyntaxError` (Invalid regular expression: \ at end of pattern)
89+
// here. That's why we use \\u002F in the regexes below.
90+
{
91+
selector:
92+
'ImportDeclaration[source.value=/^@wordpress\\u002F.+\\u002F/]',
93+
message: 'Path access on WordPress dependencies is not allowed.',
94+
},
95+
{
96+
selector:
97+
'CallExpression[callee.name="deprecated"] Property[key.name="version"][value.value=/' +
98+
majorMinorRegExp +
99+
'/]',
100+
message:
101+
'Deprecated functions must be removed before releasing this version.',
102+
},
103+
{
104+
selector:
105+
'CallExpression[callee.object.name="page"][callee.property.name="waitFor"]',
106+
message:
107+
'This method is deprecated. You should use the more explicit API methods available.',
108+
},
109+
{
110+
selector:
111+
'CallExpression[callee.object.name="page"][callee.property.name="waitForTimeout"]',
112+
message: 'Prefer page.waitForSelector instead.',
113+
},
114+
{
115+
selector: 'JSXAttribute[name.name="id"][value.type="Literal"]',
116+
message:
117+
'Do not use string literals for IDs; use withInstanceId instead.',
118+
},
119+
{
120+
// Discourage the usage of `Math.random()` as it's a code smell
121+
// for UUID generation, for which we already have a higher-order
122+
// component: `withInstanceId`.
123+
selector:
124+
'CallExpression[callee.object.name="Math"][callee.property.name="random"]',
125+
message:
126+
'Do not use Math.random() to generate unique IDs; use withInstanceId instead. (If you’re not generating unique IDs: ignore this message.)',
127+
},
128+
{
129+
selector:
130+
'CallExpression[callee.name="withDispatch"] > :function > BlockStatement > :not(VariableDeclaration,ReturnStatement)',
131+
message:
132+
'withDispatch must return an object with consistent keys. Avoid performing logic in `mapDispatchToProps`.',
133+
},
134+
{
135+
selector:
136+
'LogicalExpression[operator="&&"][left.property.name="length"][right.type="JSXElement"]',
137+
message:
138+
'Avoid truthy checks on length property rendering, as zero length is rendered verbatim.',
139+
},
140+
];
141+
142+
/** `no-restricted-syntax` rules for components. */
143+
const restrictedSyntaxComponents = [
144+
{
145+
selector:
146+
'JSXOpeningElement[name.name="Button"]:not(:has(JSXAttribute[name.name="accessibleWhenDisabled"])) JSXAttribute[name.name="disabled"]',
147+
message:
148+
'`disabled` used without the `accessibleWhenDisabled` prop. Disabling a control without maintaining focusability can cause accessibility issues, by hiding their presence from screen reader users, or preventing focus from returning to a trigger element. (Ignore this error if you truly mean to disable.)',
149+
},
88150
];
89151

90152
module.exports = {
@@ -96,6 +158,7 @@ module.exports = {
96158
],
97159
globals: {
98160
wp: 'off',
161+
globalThis: 'readonly',
99162
},
100163
settings: {
101164
jsdoc: {
@@ -106,9 +169,16 @@ module.exports = {
106169
},
107170
rules: {
108171
'jest/expect-expect': 'off',
172+
'react/jsx-boolean-value': 'error',
173+
'react/jsx-curly-brace-presence': [
174+
'error',
175+
{ props: 'never', children: 'never' },
176+
],
109177
'@wordpress/dependency-group': 'error',
110-
'@wordpress/is-gutenberg-plugin': 'error',
178+
'@wordpress/wp-global-usage': 'error',
111179
'@wordpress/react-no-unsafe-timeout': 'error',
180+
'@wordpress/i18n-hyphenated-range': 'error',
181+
'@wordpress/i18n-no-flanking-whitespace': 'error',
112182
'@wordpress/i18n-text-domain': [
113183
'error',
114184
{
@@ -145,61 +215,11 @@ module.exports = {
145215
disallowTypeAnnotations: false,
146216
},
147217
],
148-
'no-restricted-syntax': [
218+
'no-restricted-syntax': [ 'error', ...restrictedSyntax ],
219+
'jsdoc/check-tag-names': [
149220
'error',
150-
// NOTE: We can't include the forward slash in our regex or
151-
// we'll get a `SyntaxError` (Invalid regular expression: \ at end of pattern)
152-
// here. That's why we use \\u002F in the regexes below.
153-
{
154-
selector:
155-
'ImportDeclaration[source.value=/^@wordpress\\u002F.+\\u002F/]',
156-
message:
157-
'Path access on WordPress dependencies is not allowed.',
158-
},
159-
{
160-
selector:
161-
'CallExpression[callee.name="deprecated"] Property[key.name="version"][value.value=/' +
162-
majorMinorRegExp +
163-
'/]',
164-
message:
165-
'Deprecated functions must be removed before releasing this version.',
166-
},
167-
{
168-
selector:
169-
'CallExpression[callee.object.name="page"][callee.property.name="waitFor"]',
170-
message:
171-
'This method is deprecated. You should use the more explicit API methods available.',
172-
},
173221
{
174-
selector:
175-
'CallExpression[callee.object.name="page"][callee.property.name="waitForTimeout"]',
176-
message: 'Prefer page.waitForSelector instead.',
177-
},
178-
{
179-
selector: 'JSXAttribute[name.name="id"][value.type="Literal"]',
180-
message:
181-
'Do not use string literals for IDs; use withInstanceId instead.',
182-
},
183-
{
184-
// Discourage the usage of `Math.random()` as it's a code smell
185-
// for UUID generation, for which we already have a higher-order
186-
// component: `withInstanceId`.
187-
selector:
188-
'CallExpression[callee.object.name="Math"][callee.property.name="random"]',
189-
message:
190-
'Do not use Math.random() to generate unique IDs; use withInstanceId instead. (If you’re not generating unique IDs: ignore this message.)',
191-
},
192-
{
193-
selector:
194-
'CallExpression[callee.name="withDispatch"] > :function > BlockStatement > :not(VariableDeclaration,ReturnStatement)',
195-
message:
196-
'withDispatch must return an object with consistent keys. Avoid performing logic in `mapDispatchToProps`.',
197-
},
198-
{
199-
selector:
200-
'LogicalExpression[operator="&&"][left.property.name="length"][right.type="JSXElement"]',
201-
message:
202-
'Avoid truthy checks on length property rendering, as zero length is rendered verbatim.',
222+
definedTags: [ 'jest-environment' ],
203223
},
204224
],
205225
},
@@ -253,14 +273,87 @@ module.exports = {
253273
},
254274
{
255275
files: [
256-
// Components package.
257-
'packages/components/src/**/*.[tj]s?(x)',
258-
// Navigation block.
259-
'packages/block-library/src/navigation/**/*.[tj]s?(x)',
276+
'packages/*/src/**/*.[tj]s?(x)',
277+
'storybook/stories/**/*.[tj]s?(x)',
278+
],
279+
excludedFiles: [ '**/*.native.js' ],
280+
rules: {
281+
'no-restricted-syntax': [
282+
'error',
283+
...restrictedSyntax,
284+
...restrictedSyntaxComponents,
285+
],
286+
},
287+
},
288+
{
289+
files: [ 'packages/*/src/**/*.[tj]s?(x)' ],
290+
excludedFiles: [
291+
'packages/*/src/**/@(test|stories)/**',
292+
'**/*.@(native|ios|android).js',
260293
],
261-
excludedFiles: [ ...developmentFiles ],
262294
rules: {
263-
'react-hooks/exhaustive-deps': 'error',
295+
'no-restricted-syntax': [
296+
'error',
297+
...restrictedSyntax,
298+
...restrictedSyntaxComponents,
299+
// Temporary rules until we're ready to officially deprecate the bottom margins.
300+
...[
301+
'BaseControl',
302+
'CheckboxControl',
303+
'ComboboxControl',
304+
'DimensionControl',
305+
'FocalPointPicker',
306+
'RangeControl',
307+
'SearchControl',
308+
'SelectControl',
309+
'TextControl',
310+
'TextareaControl',
311+
'ToggleControl',
312+
'ToggleGroupControl',
313+
'TreeSelect',
314+
].map( ( componentName ) => ( {
315+
selector: `JSXOpeningElement[name.name="${ componentName }"]:not(:has(JSXAttribute[name.name="__nextHasNoMarginBottom"]))`,
316+
message:
317+
componentName +
318+
' should have the `__nextHasNoMarginBottom` prop to opt-in to the new margin-free styles.',
319+
} ) ),
320+
// Temporary rules until we're ready to officially default to the new size.
321+
...[
322+
'BorderBoxControl',
323+
'BorderControl',
324+
'BoxControl',
325+
'Button',
326+
'ComboboxControl',
327+
'CustomSelectControl',
328+
'DimensionControl',
329+
'FontAppearanceControl',
330+
'FontFamilyControl',
331+
'FontSizePicker',
332+
'FormTokenField',
333+
'InputControl',
334+
'LetterSpacingControl',
335+
'LineHeightControl',
336+
'NumberControl',
337+
'RangeControl',
338+
'SelectControl',
339+
'TextControl',
340+
'ToggleGroupControl',
341+
'UnitControl',
342+
].map( ( componentName ) => ( {
343+
// Falsy `__next40pxDefaultSize` without a non-default `size` prop.
344+
selector: `JSXOpeningElement[name.name="${ componentName }"]:not(:has(JSXAttribute[name.name="__next40pxDefaultSize"][value.expression.value!=false])):not(:has(JSXAttribute[name.name="size"][value.value!="default"]))`,
345+
message:
346+
componentName +
347+
' should have the `__next40pxDefaultSize` prop when using the default size.',
348+
} ) ),
349+
{
350+
// Falsy `__next40pxDefaultSize` without a `render` prop.
351+
selector:
352+
'JSXOpeningElement[name.name="FormFileUpload"]:not(:has(JSXAttribute[name.name="__next40pxDefaultSize"][value.expression.value!=false])):not(:has(JSXAttribute[name.name="render"]))',
353+
message:
354+
'FormFileUpload should have the `__next40pxDefaultSize` prop to opt-in to the new default size.',
355+
},
356+
],
264357
},
265358
},
266359
{
@@ -361,12 +454,59 @@ module.exports = {
361454
'jsdoc/require-param': 'off',
362455
},
363456
},
457+
{
458+
files: [ 'packages/components/src/**' ],
459+
excludedFiles: [
460+
'packages/components/src/utils/colors-values.js',
461+
'packages/components/src/theme/**',
462+
],
463+
rules: {
464+
'no-restricted-syntax': [
465+
'error',
466+
...restrictedSyntax,
467+
...restrictedSyntaxComponents,
468+
{
469+
selector:
470+
':matches(Literal[value=/--wp-admin-theme-/],TemplateElement[value.cooked=/--wp-admin-theme-/])',
471+
message:
472+
'--wp-admin-theme-* variables do not support component theming. Use variables from the COLORS object in packages/components/src/utils/colors-values.js instead.',
473+
},
474+
{
475+
selector:
476+
// Allow overriding definitions, but not access with var()
477+
':matches(Literal[value=/var\\(\\s*--wp-components-color-/],TemplateElement[value.cooked=/var\\(\\s*--wp-components-color-/])',
478+
message:
479+
'To ensure proper fallbacks, --wp-components-color-* variables should not be used directly. Use variables from the COLORS object in packages/components/src/utils/colors-values.js instead.',
480+
},
481+
],
482+
},
483+
},
364484
{
365485
files: [ 'packages/components/src/**' ],
366486
excludedFiles: [ 'packages/components/src/**/@(test|stories)/**' ],
367487
plugins: [ 'ssr-friendly' ],
368488
extends: [ 'plugin:ssr-friendly/recommended' ],
369489
},
490+
{
491+
files: [ 'packages/components/src/**' ],
492+
rules: {
493+
'no-restricted-imports': [
494+
'error',
495+
// The `ariakit` and `framer-motion` APIs are meant to be consumed via
496+
// the `@wordpress/components` package, hence why importing those
497+
// dependencies should be allowed in the components package.
498+
{
499+
paths: restrictedImports.filter(
500+
( { name } ) =>
501+
! [
502+
'@ariakit/react',
503+
'framer-motion',
504+
].includes( name )
505+
),
506+
},
507+
],
508+
},
509+
},
370510
{
371511
files: [ 'packages/block-editor/**' ],
372512
rules: {
@@ -390,5 +530,29 @@ module.exports = {
390530
],
391531
},
392532
},
533+
{
534+
files: [ 'packages/edit-post/**', 'packages/edit-site/**' ],
535+
rules: {
536+
'no-restricted-imports': [
537+
'error',
538+
{
539+
paths: [
540+
...restrictedImports,
541+
{
542+
name: '@wordpress/interface',
543+
message:
544+
'The edit-post and edit-site package should not directly import the interface package. They should import them from the private APIs of the editor package instead.',
545+
},
546+
],
547+
},
548+
],
549+
},
550+
},
551+
{
552+
files: [ 'packages/interactivity*/src/**' ],
553+
rules: {
554+
'react/react-in-jsx-scope': 'error',
555+
},
556+
},
393557
],
394558
};

.git-blame-ignore-revs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,12 @@ c56e8a1910ed74f405b74bbb12fe81dea974e5c3
1212

1313
# Prettier upgrade to 3.0.3.
1414
0bee15148fe4330c20cf372cb46a33693e45cb5f
15+
16+
# ESLint: Enable react/jsx-boolean-value
17+
9a34927870df80ac3b2da14d71f81d20ec23e2b6
18+
19+
# Autofix eslint curly rule.
20+
0221522f253e094b277a1485b7a2d186cb172632
21+
22+
# ESLint: Enable react/jsx-curly-brace-presence
23+
5d4baa9ab5f57d207cc3a048003216a8574574d9

0 commit comments

Comments
 (0)