add noThisInSFC rule and tests
add support for arrow func and forwardref and memo in noThisInSFC
MODIFY
packages/@romejs/compiler/lint/rules/index.ts
packages/@romejs/compiler/lint/rules/index.ts
@@ -94,6 +94,7 @@ import noDidUpdateSetState from "./react/noDidUpdateSetState";
import noFindDOMNode from "./react/noFindDOMNode";
import noRedundantShouldComponentUpdate from "./react/noRedundantShouldComponentUpdate";
import noStringRefs from "./react/noStringRefs";
+import noThisInSFC from "./react/noThisInSFC";
import noUnsafe from "./react/noUnsafe";
import noUselessFragment from "./react/noUselessFragment";
import noWillUpdateSetState from "./react/noWillUpdateSetState";
@@ -192,6 +193,7 @@ export const lintTransforms = [
noFindDOMNode,
noRedundantShouldComponentUpdate,
noStringRefs,
+ noThisInSFC,
noUnsafe,
noUselessFragment,
noWillUpdateSetState,

ADD
packages/@romejs/compiler/lint/rules/react/noThisInSFC.test.ts
+import {test} from "rome";
+import {testLint} from "../testHelpers";
+
+test(
+ "react no this in stateless functional component",
+ async (t) => {
+ await testLint(
+ t,
+ {
+ invalid: [
+ "const Foo = () => <div>{this.props.bar</div>",
+ `
+ const Foo = () => {
+ return (
+ <div>{this.props.bar}</div>
+ );
+ }
+ `,
+ `
+ const Foo = () => {
+ const { bar } = this.props;
+ return (
+ <div>{ bar }</div>
+ );
+ }
+ `,
+ `
+ function Foo(props) {
+ return (
+ <div>{this.props.bar}</div>
+ );
+ }
+ `,
+ `
+ function Foo(props) {
+ const { bar } = this.props;
+ return (
+ <div>
+ { bar }
+ </div>
+ );
+ }
+ `,
+ `
+ function Foo(props, context) {
+ return (
+ <div>
+ {this.context.foo ? this.props.bar : ''}
+ </div>
+ );
+ }
+ `,
+ `
+ function Foo(props, context) {
+ const { foo } = this.context;
+ const { bar } = this.props;
+ return (
+ <div>
+ {foo ? bar : ''}
+ </div>
+ );
+ }
+ `,
+ `
+ function Foo(props) {
+ if (this.state.loading) {
+ return <Loader />;
+ }
+ return (
+ <div>
+ {this.props.bar}
+ </div>
+ );
+ }
+ `,
+ `
+ function Foo(props) {
+ const { loading } = this.state;
+ const { bar } = this.props;
+ if (loading) {
+ return <Loader />;
+ }
+ return (
+ <div>
+ {bar}
+ </div>
+ );
+ }
+ `,
+ `
+ React.memo(
+ function Foo(props) {
+ return (
+ <div>{this.props.bar}</div>
+ );
+ }
+ )
+ `,
+ `
+ React.forwardRef((props, ref) => (
+ <div>
+ {this.props.bar}
+ </div>
+ ));
+ `,
+ `
+ const Foo = React.forwardRef((props, ref) => (
+ <div>
+ {this.props.bar}
+ </div>
+ ));
+ `,
+ `
+ const Foo = React.memo((props, ref) => (
+ <div>
+ {this.props.bar}
+ </div>
+ )
+ )
+ `,
+ ],
+ valid: [
+ "const Foo = (bar) => <div>{bar}</div>",
+ `
+ function Foo(props) {
+ return (
+ <div>{props.bar}</div>
+ );
+ }
+ `,
+ `
+ function Foo(props) {
+ const { bar } = props;
+ return (
+ <div>{bar}</div>
+ );
+ }
+ `,
+ `
+ function Foo({ bar }) {
+ return (
+ <div>{bar}</div>
+ );
+ }
+ `,
+ `
+ function Foo(props, context) {
+ return (
+ <div>
+ {context.foo ? props.bar : ''}
+ </div>
+ );
+ }
+ `,
+ `
+ function Foo(props, context) {
+ const { foo } = context;
+ const { bar } = props;
+ return (
+ <div>
+ {foo ? bar : ''}
+ </div>
+ );
+ }
+ `,
+ `
+ function Foo({ bar }, { foo }) {
+ return (
+ <div>
+ {foo ? bar : ''}
+ </div>
+ );
+ }
+ `,
+ `
+ class MyComponent extends React.Component {
+ foo() {
+ return <div>some jsx</div>
+ }
+
+ render() {
+ return "content"
+ }
+ }
+ `,
+ `
+ React.memo(
+ function Foo(props) {
+ return (
+ <div>{props.bar}</div>
+ );
+ }
+ )
+ `,
+ `
+ React.forwardRef((props, ref) => (
+ <div>
+ {props.bar}
+ </div>
+ ));
+ `,
+ `
+ const Foo = React.forwardRef((props, ref) => (
+ <div>
+ {props.bar}
+ </div>
+ ));
+ `,
+ `
+ const Foo = React.memo((props, ref) => (
+ <div>
+ {props.bar}
+ </div>
+ )
+ )
+ `,
+ ],
+ },
+ {category: "lint/react/noThisInSFC"},
+ );
+ },
+);

ADD
packages/@romejs/compiler/lint/rules/react/noThisInSFC.ts
+import {Path, TransformExitResult} from "@romejs/compiler";
+import {descriptions} from "@romejs/diagnostics";
+import {isFunctionNode, isJSXElement} from "@romejs/js-ast-utils";
+
+export default {
+ name: "reactNoThisInSFC",
+ enter(path: Path): TransformExitResult {
+ const {node} = path;
+
+ if (node.type === "JSThisExpression") {
+ const hasJSX = path.findAncestry((path) => {
+ if (isJSXElement(path.node)) {
+ return true;
+ }
+
+ if (path.node.type === "JSBlockStatement") {
+ if (
+ path.node.body.some((statement) =>
+ statement.type === "JSReturnStatement" &&
+ statement.argument &&
+ isJSXElement(statement.argument)
+ )
+ ) {
+ return true;
+ }
+ }
+
+ if (
+ path.node.type === "JSVariableDeclarator" &&
+ path.node.init &&
+ path.node.init.type === "JSArrowFunctionExpression" &&
+ isJSXElement(path.node.init.body)
+ ) {
+ return true;
+ }
+ return false;
+ });
+
+ if (hasJSX) {
+ const isFunction = hasJSX.findAncestry((path) => {
+ return isFunctionNode(path.node);
+ });
+
+ const declaration = hasJSX.node.type === "JSVariableDeclarator";
+
+ if (isFunction || declaration) {
+ path.context.addNodeDiagnostic(
+ node,
+ descriptions.LINT.REACT_NO_THIS_IN_SFC,
+ );
+ }
+ }
+ }
+
+ return node;
+ },
+};

MODIFY
packages/@romejs/diagnostics/categories.ts
packages/@romejs/diagnostics/categories.ts
@@ -181,6 +181,7 @@ type LintDiagnosticCategory =
| "lint/react/noFindDOMNode"
| "lint/react/noRedundantShouldComponentUpdate"
| "lint/react/noStringRefs"
+ | "lint/react/noThisInSFC"
| "lint/react/noUnsafe"
| "lint/react/noUselessFragment"
| "lint/react/noWillUpdateSetState"

MODIFY
packages/@romejs/diagnostics/descriptions/lint.ts
packages/@romejs/diagnostics/descriptions/lint.ts
@@ -16,6 +16,17 @@ import {buildSuggestionAdvice} from "../helpers";
import {createDiagnosticsCategory, orJoin} from "./index";
export const lint = createDiagnosticsCategory({
+ REACT_NO_THIS_IN_SFC: {
+ category: "lint/react/noThisInSFC",
+ message: "Avoid using <emphasis>this</emphasis> in stateless functional components.",
+ advice: [
+ {
+ type: "log",
+ category: "info",
+ text: "The <emphasis>this</emphasis> keyword has no binding in functional components. Use hooks instead.",
+ },
+ ],
+ },
JSX_A11Y_NO_NONINTERACTIVE_ELEMENT_TO_INTERACTIVE_ROLE: (element: string) => ({
category: "lint/jsx-a11y/noNoninteractiveElementToInteractiveRole",
message: `The HTML element <emphasis>${element}</emphasis> is non-interactive and should not have an interactive role.`,

add noThisInSFC rule and tests (#575)

add support for arrow func and forwardref and memo in noThisInSFC