Skip to content

Commit

Permalink
Merge branch 'master' into ross-fix-sort-props-test-cases
Browse files Browse the repository at this point in the history
  • Loading branch information
ROSSROSALES authored Oct 7, 2022
2 parents 2cc1d30 + 5baa3e0 commit bb8c181
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 8 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,19 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
### Fixed
* [`no-unknown-property`]: add `dialog` attributes ([#3436][] @ljharb)
* [`no-arrow-function-lifecycle`]: when converting from an arrow, remove the semi and wrapping parens ([#3337][] @ljharb)
* [`jsx-key`]: Ignore elements inside `React.Children.toArray()` ([#1591][] @silvenon)
* [`jsx-no-constructed-context-values`]: fix false positive for usage in non-components ([#3448][] @golopot)

### Changed
* [Docs] [`no-unknown-property`]: fix typo in link ([#3445][] @denkristoffer)
* [Perf] component detection: improve performance by optimizing getId ([#3451][] @golopot)

[#3451]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3451
[#3448]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3448
[#3445]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3445
[#3436]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3436
[#3337]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3337
[#1591]: https://github.com/jsx-eslint/eslint-plugin-react/pull/1591

## [7.31.8] - 2022.09.08

Expand Down
2 changes: 1 addition & 1 deletion docs/rules/no-unknown-property.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ var AtomPanel = <atom-panel class="foo"></atom-panel>;

If you are using a library that passes something as a prop to JSX elements, it is recommended to add those props to the ignored properties.

For example, if you use [emotion](https://emotion.sh/docs/introduction) and its [`css` prop](https://emotion.sh/docs/css-prop)),
For example, if you use [emotion](https://emotion.sh/docs/introduction) and its [`css` prop](https://emotion.sh/docs/css-prop),
add the following to your `.eslintrc` config file:

```js
Expand Down
33 changes: 32 additions & 1 deletion lib/rules/jsx-key.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,33 @@ module.exports = {
}
}

const childrenToArraySelector = `:matches(
CallExpression
[callee.object.object.name=${reactPragma}]
[callee.object.property.name=Children]
[callee.property.name=toArray],
CallExpression
[callee.object.name=Children]
[callee.property.name=toArray]
)`.replace(/\s/g, '');
let isWithinChildrenToArray = false;

const seen = new WeakSet();

return {
[childrenToArraySelector]() {
isWithinChildrenToArray = true;
},

[`${childrenToArraySelector}:exit`]() {
isWithinChildrenToArray = false;
},

'ArrayExpression, JSXElement > JSXElement'(node) {
if (isWithinChildrenToArray) {
return;
}

const jsx = (node.type === 'ArrayExpression' ? node.elements : node.parent.children).filter((x) => x && x.type === 'JSXElement');
if (jsx.length === 0) {
return;
Expand Down Expand Up @@ -205,7 +228,7 @@ module.exports = {
},

JSXFragment(node) {
if (!checkFragmentShorthand) {
if (!checkFragmentShorthand || isWithinChildrenToArray) {
return;
}

Expand All @@ -226,6 +249,10 @@ module.exports = {
CallExpression[callee.type="OptionalMemberExpression"][callee.property.name="map"],\
OptionalCallExpression[callee.type="MemberExpression"][callee.property.name="map"],\
OptionalCallExpression[callee.type="OptionalMemberExpression"][callee.property.name="map"]'(node) {
if (isWithinChildrenToArray) {
return;
}

const fn = node.arguments.length > 0 && node.arguments[0];
if (!fn || !astUtil.isFunctionLikeExpression(fn)) {
return;
Expand All @@ -238,6 +265,10 @@ module.exports = {

// Array.from
'CallExpression[callee.type="MemberExpression"][callee.property.name="from"]'(node) {
if (isWithinChildrenToArray) {
return;
}

const fn = node.arguments.length > 1 && node.arguments[1];
if (!astUtil.isFunctionLikeExpression(fn)) {
return;
Expand Down
10 changes: 8 additions & 2 deletions lib/rules/jsx-no-constructed-context-values.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

'use strict';

const Components = require('../util/Components');
const docsUrl = require('../util/docsUrl');
const report = require('../util/report');

Expand Down Expand Up @@ -139,7 +140,8 @@ module.exports = {
messages,
},

create(context) {
// eslint-disable-next-line arrow-body-style
create: Components.detect((context, components, utils) => {
return {
JSXOpeningElement(node) {
const openingElementName = node.name;
Expand Down Expand Up @@ -184,6 +186,10 @@ module.exports = {
return;
}

if (!utils.getParentComponent(node)) {
return;
}

// Report found error
const constructType = constructInfo.type;
const constructNode = constructInfo.node;
Expand Down Expand Up @@ -214,5 +220,5 @@ module.exports = {
});
},
};
},
}),
};
2 changes: 1 addition & 1 deletion lib/util/Components.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const isFirstLetterCapitalized = require('./isFirstLetterCapitalized');
const isDestructuredFromPragmaImport = require('./isDestructuredFromPragmaImport');

function getId(node) {
return node && node.range.join(':');
return node ? `${node.range[0]}:${node.range[1]}` : '';
}

function usedPropTypesAreEquivalent(propA, propB) {
Expand Down
27 changes: 27 additions & 0 deletions tests/lib/rules/jsx-key.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,33 @@ ruleTester.run('jsx-key', rule, {
`,
features: ['types', 'no-babel-old'],
},
{ code: 'React.Children.toArray([1, 2 ,3].map(x => <App />));' },
{
code: `
import { Children } from "react";
Children.toArray([1, 2 ,3].map(x => <App />));
`,
},
{
// TODO: uncomment the commented lines below
code: `
import Act from 'react';
import { Children as ReactChildren } from 'react';
const { Children } = Act;
const { toArray } = Children;
Act.Children.toArray([1, 2 ,3].map(x => <App />));
Act.Children.toArray(Array.from([1, 2 ,3], x => <App />));
Children.toArray([1, 2 ,3].map(x => <App />));
Children.toArray(Array.from([1, 2 ,3], x => <App />));
// ReactChildren.toArray([1, 2 ,3].map(x => <App />));
// ReactChildren.toArray(Array.from([1, 2 ,3], x => <App />));
// toArray([1, 2 ,3].map(x => <App />));
// toArray(Array.from([1, 2 ,3], x => <App />));
`,
settings,
},
]),
invalid: parsers.all([
{
Expand Down
16 changes: 13 additions & 3 deletions tests/lib/rules/jsx-no-constructed-context-values.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ const ruleTester = new RuleTester({ parserOptions });
ruleTester.run('react-no-constructed-context-values', rule, {
valid: parsers.all([
{
code: '<Context.Provider value={props}></Context.Provider>',
code: 'const Component = () => <Context.Provider value={props}></Context.Provider>',
},
{
code: '<Context.Provider value={100}></Context.Provider>',
code: 'const Component = () => <Context.Provider value={100}></Context.Provider>',
},
{
code: '<Context.Provider value="Some string"></Context.Provider>',
code: 'const Component = () => <Context.Provider value="Some string"></Context.Provider>',
},
{
code: 'function Component() { const foo = useMemo(() => { return {} }, []); return (<Context.Provider value={foo}></Context.Provider>)}',
Expand Down Expand Up @@ -137,6 +137,16 @@ ruleTester.run('react-no-constructed-context-values', rule, {
}
`,
},
{
code: `
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<AppContext.Provider value={{}}>
<AppView />
</AppContext.Provider>
);
`,
},
]),
invalid: parsers.all([
{
Expand Down

0 comments on commit bb8c181

Please sign in to comment.