-
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
10 changed files
with
271 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
packages/eslint-plugin-react/src/rules/no-children-prop.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# react/no-children-prop | ||
|
||
<!-- end auto-generated rule header --> | ||
|
||
## Rule category | ||
|
||
Suspicious. | ||
|
||
## What it does | ||
|
||
Disallows passing of children as props. | ||
|
||
## Why is this bad? | ||
|
||
Most of the time, `children` should be actual `children`, not passed in as a `prop`. | ||
|
||
When using JSX, the `children` should be nested between the opening and closing tags. When not using JSX, the `children` should be passed as additional arguments to `React.createElement`. | ||
|
||
## Examples | ||
|
||
### ❌ Incorrect | ||
|
||
```tsx | ||
<div children='Children' /> | ||
|
||
<Component children={<AnotherComponent />} /> | ||
<Component children={['Child 1', 'Child 2']} /> | ||
|
||
React.createElement("div", { children: 'Children' }) | ||
``` | ||
|
||
### ✅ Correct | ||
|
||
```tsx | ||
<div>Children</div> | ||
|
||
<Component>Children</Component> | ||
|
||
<Component> | ||
<span>Child 1</span> | ||
<span>Child 2</span> | ||
</Component> | ||
|
||
React.createElement("div", {}, 'Children') | ||
React.createElement("div", 'Child 1', 'Child 2') | ||
``` |
125 changes: 125 additions & 0 deletions
125
packages/eslint-plugin-react/src/rules/no-children-prop.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import { allValid } from "@eslint-react/shared"; | ||
import dedent from "dedent"; | ||
|
||
import RuleTester, { getFixturesRootDir } from "../../../../test/rule-tester"; | ||
import rule, { RULE_NAME } from "./no-children-prop"; | ||
|
||
const rootDir = getFixturesRootDir(); | ||
|
||
const ruleTester = new RuleTester({ | ||
parser: "@typescript-eslint/parser", | ||
parserOptions: { | ||
ecmaFeatures: { | ||
jsx: true, | ||
}, | ||
ecmaVersion: 2021, | ||
sourceType: "module", | ||
project: "./tsconfig.json", | ||
tsconfigRootDir: rootDir, | ||
}, | ||
}); | ||
|
||
ruleTester.run(RULE_NAME, rule, { | ||
valid: [ | ||
...allValid, | ||
"<div />;", | ||
"<div></div>;", | ||
'React.createElement("div", {});', | ||
'React.createElement("div", undefined);', | ||
'<div className="class-name"></div>;', | ||
'React.createElement("div", {className: "class-name"});', | ||
"<div>Children</div>;", | ||
'React.createElement("div", "Children");', | ||
'React.createElement("div", {}, "Children");', | ||
'React.createElement("div", undefined, "Children");', | ||
'<div className="class-name">Children</div>;', | ||
'React.createElement("div", {className: "class-name"}, "Children");', | ||
"<div><div /></div>;", | ||
'React.createElement("div", React.createElement("div"));', | ||
'React.createElement("div", {}, React.createElement("div"));', | ||
'React.createElement("div", undefined, React.createElement("div"));', | ||
"<div><div /><div /></div>;", | ||
'React.createElement("div", React.createElement("div"), React.createElement("div"));', | ||
'React.createElement("div", {}, React.createElement("div"), React.createElement("div"));', | ||
'React.createElement("div", undefined, React.createElement("div"), React.createElement("div"));', | ||
'React.createElement("div", [React.createElement("div"), React.createElement("div")]);', | ||
'React.createElement("div", {}, [React.createElement("div"), React.createElement("div")]);', | ||
'React.createElement("div", undefined, [React.createElement("div"), React.createElement("div")]);', | ||
"<MyComponent />", | ||
"React.createElement(MyComponent);", | ||
"React.createElement(MyComponent, {});", | ||
"React.createElement(MyComponent, undefined);", | ||
"<MyComponent>Children</MyComponent>;", | ||
'React.createElement(MyComponent, "Children");', | ||
'React.createElement(MyComponent, {}, "Children");', | ||
'React.createElement(MyComponent, undefined, "Children");', | ||
'<MyComponent className="class-name"></MyComponent>;', | ||
'React.createElement(MyComponent, {className: "class-name"});', | ||
'<MyComponent className="class-name">Children</MyComponent>;', | ||
'React.createElement(MyComponent, {className: "class-name"}, "Children");', | ||
'<MyComponent className="class-name" {...props} />;', | ||
'React.createElement(MyComponent, {className: "class-name", ...props});', | ||
], | ||
invalid: [ | ||
{ | ||
code: "<div children />;", // not a valid use case but make sure we don't crash | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: '<div children="Children" />;', | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: "<div children={<div />} />;", | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: "<div children={[<div />, <div />]} />;", | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: '<div children="Children">Children</div>;', | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: 'React.createElement("div", {children: "Children"});', | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: 'React.createElement("div", {children: "Children"}, "Children");', | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: 'React.createElement("div", {children: React.createElement("div")});', | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: 'React.createElement("div", {children: [React.createElement("div"), React.createElement("div")]});', | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: '<MyComponent children="Children" />', | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: 'React.createElement(MyComponent, {children: "Children"});', | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: '<MyComponent className="class-name" children="Children" />;', | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: 'React.createElement(MyComponent, {children: "Children", className: "class-name"});', | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: '<MyComponent {...props} children="Children" />;', | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
{ | ||
code: 'React.createElement(MyComponent, {...props, children: "Children"})', | ||
errors: [{ messageId: "NO_CHILDREN_PROP" }], | ||
}, | ||
], | ||
}); |
91 changes: 91 additions & 0 deletions
91
packages/eslint-plugin-react/src/rules/no-children-prop.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { NodeType } from "@eslint-react/ast"; | ||
import { findPropInProperties, getProp, isCreateElementCall } from "@eslint-react/jsx"; | ||
import { O } from "@eslint-react/tools"; | ||
import type { ESLintUtils } from "@typescript-eslint/utils"; | ||
import type { ConstantCase } from "string-ts"; | ||
|
||
import { createRule } from "../utils"; | ||
|
||
export const RULE_NAME = "no-children-prop"; | ||
|
||
export type MessageID = ConstantCase<typeof RULE_NAME>; | ||
|
||
// No need to check because TypeScript don't allow this | ||
// function isValidAttribute( | ||
// prop: TSESTree.JSXAttribute | TSESTree.JSXSpreadAttribute, | ||
// ) { | ||
// return ( | ||
// "value" in prop | ||
// && prop.value | ||
// && "expression" in prop.value | ||
// && isFunction(prop.value.expression) | ||
// ); | ||
// } | ||
|
||
// No need to check because TypeScript don't allow this | ||
// function isValidProperty( | ||
// prop: | ||
// | TSESTree.PropertyComputedName | ||
// | TSESTree.PropertyNonComputedName | ||
// | TSESTree.RestElement | ||
// | TSESTree.SpreadElement, | ||
// ) { | ||
// return ( | ||
// "value" in prop | ||
// && prop.value | ||
// && "type" in prop.value | ||
// && isFunction(prop.value) | ||
// ); | ||
// } | ||
|
||
export default createRule<[], MessageID>({ | ||
name: RULE_NAME, | ||
meta: { | ||
type: "suggestion", | ||
docs: { | ||
description: "disallow passing of children as props", | ||
recommended: "recommended", | ||
requiresTypeChecking: false, | ||
}, | ||
schema: [], | ||
messages: { | ||
NO_CHILDREN_PROP: "Children should always be actual children, not passed in as a prop.", | ||
}, | ||
}, | ||
defaultOptions: [], | ||
create(context) { | ||
return { | ||
JSXElement(node) { | ||
O.map(getProp(node.openingElement.attributes, "children", context), prop => { | ||
context.report({ | ||
messageId: "NO_CHILDREN_PROP", | ||
node: prop, | ||
}); | ||
}); | ||
}, | ||
// eslint-disable-next-line perfectionist/sort-objects | ||
CallExpression(node) { | ||
if (node.arguments.length === 0) { | ||
return; | ||
} | ||
|
||
if (!isCreateElementCall(node, context)) { | ||
return; | ||
} | ||
|
||
const [_, props] = node.arguments; | ||
|
||
if (!props || props.type !== NodeType.ObjectExpression) { | ||
return; | ||
} | ||
|
||
O.map(findPropInProperties(props.properties, context)("children"), prop => { | ||
context.report({ | ||
messageId: "NO_CHILDREN_PROP", | ||
node: prop, | ||
}); | ||
}); | ||
}, | ||
}; | ||
}, | ||
}); |
3 changes: 1 addition & 2 deletions
3
packages/eslint-plugin-react/src/rules/no-unsafe-target-blank.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters