diff --git a/CHANGELOG.md b/CHANGELOG.md
index 79f0bfec7d..5bbcad77f8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2754,3 +2754,4 @@ If you're still not using React 15 you can keep the old behavior by setting the
[`static-property-placement`]: docs/rules/static-property-placement.md
[`jsx-curly-newline`]: docs/rules/jsx-curly-newline.md
[`jsx-no-useless-fragment`]: docs/rules/jsx-no-useless-fragment.md
+[`function-component-definition`]: docs/rules/function-component-definition.md
diff --git a/docs/rules/function-component-definition.md b/docs/rules/function-component-definition.md
new file mode 100644
index 0000000000..ba1efc9c1b
--- /dev/null
+++ b/docs/rules/function-component-definition.md
@@ -0,0 +1,229 @@
+# Enforce a specific function type for function components (react/function-component-definition)
+
+This option enforces a specific function type for function components.
+
+**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line.
+
+## Rule Details
+
+This rule is aimed to enforce consistent function types for function components. By default it prefers function declarations for named components and function expressions for unnamed components.
+
+The following patterns are considered warnings:
+
+```jsx
+// function expression for named component
+var Component = function (props) {
+ return
{props.content}
+}
+
+// arrow function for named component
+var Component = (props) => {
+ return {props.content}
+}
+
+// arrow function for unnamed component
+function getComponent() {
+ return (props) => {
+ return {props.content}
+ }
+}
+```
+
+## Rule Options
+
+This rule takes an options object as a second parameter where the preferred function type for components can be specified. The first property of the options object is `"namedComponents"` which can be `"function-declaration"`, `"function-expression"` or `"arrow-function"` and has `'function-declaration'` as its default. The second property is `"unnamedComponents"` that can be either `"function-expression"` or `"arrow-function"` and has `'function-expression'` as its default.
+
+```js
+...
+"react/function-component-definition": [, {
+ "namedComponents": "function-declaration"|"function-expression"|"arrow-function",
+ "unnamedComponents": "function-expression"|"arrow-function"
+}]
+...
+```
+
+The following patterns are considered warnings:
+
+```jsx
+// only function declarations for named components
+// [2, { "namedComponents": "function-declaration" }]
+var Component = function (props) {
+ return
+}
+
+var Component = (props) => {
+ return
+}
+
+// only function expressions for named components
+// [2, { "namedComponents": "function-expression" }]
+function Component (props) {
+ return
+}
+
+var Component = (props) => {
+ return
+}
+
+// only arrow functions for named components
+// [2, { "namedComponents": "arrow-function" }]
+function Component (props) {
+ return
+}
+
+var Component = function (props) {
+ return
+}
+
+// only function expressions for unnamed components
+// [2, { "unnamedComponents": "function-expression" }]
+function getComponent () {
+ return (props) => {
+ return
+ }
+}
+
+// only arrow functions for unnamed components
+// [2, { "unnamedComponents": "arrow-function" }]
+function getComponent () {
+ return (props) => {
+ return
+ }
+}
+
+```
+
+The following patterns are **not** warnings:
+
+```jsx
+
+// only function declarations for named components
+// [2, { "namedComponents": "function-declaration" }]
+function Component (props) {
+ return
+}
+
+// only function expressions for named components
+// [2, { "namedComponents": "function-expression" }]
+var Component = function (props) {
+ return
+}
+
+// only arrow functions for named components
+// [2, { "namedComponents": "arrow-function" }]
+var Component = (props) => {
+ return
+}
+
+// only function expressions for unnamed components
+// [2, { "unnamedComponents": "function-expression" }]
+function getComponent () {
+ return (props) => {
+ return
+ }
+}
+
+// only arrow functions for unnamed components
+// [2, { "unnamedComponents": "arrow-function" }]
+function getComponent () {
+ return (props) => {
+ return
+ }
+}
+
+```
+
+## Heads up typescript users
+
+Note that the auto fixer is somewhat constrained for typescript users.
+
+The following patterns can **not** be autofixed in typescript:
+
+```tsx
+// function expressions and arrow functions that have type annotations cannot be autofixed to function declarations
+// [2, { "namedComponents": "function-declaration" }]
+var Component: React.FC = function (props) {
+ return
+}
+
+var Component: React.FC = (props) => {
+ return
+}
+
+// function components with one unconstrained type parameter cannot be autofixed to arrow functions because the syntax conflicts with jsx
+// [2, { "namedComponents": "arrow-function" }]
+function Component(props: Props) {
+ return
+}
+
+var Component = function (props: Props) {
+ return
+}
+
+// [2, { "unnamedComponents": "arrow-function" }]
+function getComponent() {
+ return function (props: Props) => {
+ return
+ }
+}
+```
+
+If the single type parameter either have a varraint
+or if there are multiple type parameters the syntax conflicts are resolved and the component can be autofixed again
+
+The following patterns can be autofixed in typescript:
+
+```tsx
+// autofix to function expression with type annotation
+// [2, { "namedComponents": "function-expression" }]
+var Component: React.FC = (props) => {
+ return
+}
+
+// autofix to arrow function with type annotation
+// [2, { "namedComponents": "function-expression" }]
+var Component: React.FC = function (props) {
+ return
+}
+
+// autofix to named arrow function with one constrained type parameter
+// [2, { "namedComponents": "arrow-function" }]
+function Component(props: Props) {
+ return
+}
+
+var Component = function (props: Props) {
+ return
+}
+
+// autofix to named arrow function with multiple type parameters
+// [2, { "namedComponents": "arrow-function" }]
+function Component(props: Props) {
+ return
+}
+
+var Component = function (props: Props) {
+ return
+}
+
+// autofix to unnamed arrow function with one constrained type parameter
+// [2, { "unnamedComponents": "arrow-function" }]
+function getComponent() {
+ return function (props: Props) => {
+ return
+ }
+}
+
+// autofix to unnamed arrow function with multiple type parameters
+// [2, { "unnamedComponents": "arrow-function" }]
+function getComponent() {
+ return function (props: Props) => {
+ return
+ }
+}
+
+```
+
+## When not to use
+
+If you are not interested in consistent types of function components.
diff --git a/index.js b/index.js
index c9511bb1e3..de64c9a8cc 100644
--- a/index.js
+++ b/index.js
@@ -15,6 +15,7 @@ const allRules = {
'forbid-elements': require('./lib/rules/forbid-elements'),
'forbid-foreign-prop-types': require('./lib/rules/forbid-foreign-prop-types'),
'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
+ 'function-component-definition': require('./lib/rules/function-component-definition'),
'jsx-boolean-value': require('./lib/rules/jsx-boolean-value'),
'jsx-child-element-spacing': require('./lib/rules/jsx-child-element-spacing'),
'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),
diff --git a/lib/rules/function-component-definition.js b/lib/rules/function-component-definition.js
new file mode 100644
index 0000000000..4d616ef248
--- /dev/null
+++ b/lib/rules/function-component-definition.js
@@ -0,0 +1,184 @@
+/**
+ * @fileoverview Standardize the way function component get defined
+ * @author Stefan Wullems
+ */
+
+'use strict';
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function buildFunction(template, parts) {
+ return Object.keys(parts)
+ .reduce((acc, key) => acc.replace(`{${key}}`, parts[key] || ''), template);
+}
+
+const NAMED_FUNCTION_TEMPLATES = {
+ 'function-declaration': 'function {name}{typeParams}({params}){returnType} {body}',
+ 'arrow-function': 'var {name}{typeAnnotation} = {typeParams}({params}){returnType} => {body}',
+ 'function-expression': 'var {name}{typeAnnotation} = function{typeParams}({params}){returnType} {body}'
+};
+
+const UNNAMED_FUNCTION_TEMPLATES = {
+ 'function-expression': 'function{typeParams}({params}){returnType} {body}',
+ 'arrow-function': '{typeParams}({params}){returnType} => {body}'
+};
+
+const ERROR_MESSAGES = {
+ 'function-declaration': 'Function component is not a function declaration',
+ 'function-expression': 'Function component is not a function expression',
+ 'arrow-function': 'Function component is not an arrow function'
+};
+
+function hasOneUnconstrainedTypeParam(node) {
+ if (node.typeParameters) {
+ return node.typeParameters.params.length === 1 && !node.typeParameters.params[0].constraint;
+ }
+
+ return false;
+}
+
+function hasName(node) {
+ return node.type === 'FunctionDeclaration' || node.parent.type === 'VariableDeclarator';
+}
+
+function getNodeText(prop, source) {
+ if (!prop) return null;
+ return source.slice(prop.range[0], prop.range[1]);
+}
+
+function getName(node) {
+ if (node.type === 'FunctionDeclaration') {
+ return node.id.name;
+ }
+
+ if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionExpression') {
+ return hasName(node) && node.parent.id.name;
+ }
+}
+
+function getParams(node, source) {
+ if (!node.params.length) return null;
+ return source.slice(node.params[0].range[0], node.params[node.params.length - 1].range[1]);
+}
+
+function getBody(node, source) {
+ const range = node.body.range;
+ if (node.body.type === 'JSXElement') {
+ return [
+ '{',
+ ` return ${source.slice(range[0], range[1])}`,
+ '}'
+ ].join('\n');
+ }
+
+ if (node.body.type === 'BlockStatement') {
+ return source.slice(range[0], range[1]);
+ }
+}
+
+function getTypeAnnotation(node, source) {
+ if (!hasName(node) || node.type === 'FunctionDeclaration') return;
+
+ if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionExpression') {
+ return getNodeText(node.parent.id.typeAnnotation, source);
+ }
+}
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'Standardize the way function component get defined',
+ category: 'Stylistic issues',
+ recommended: false,
+ url: docsUrl('function-component-definition')
+ },
+ fixable: 'code',
+
+ schema: [{
+ type: 'object',
+ properties: {
+ namedComponents: {
+ enum: ['function-declaration', 'arrow-function', 'function-expression']
+ },
+ unnamedComponents: {
+ enum: ['arrow-function', 'function-expression']
+ }
+ }
+ }]
+ },
+
+ create: Components.detect((context, components) => {
+ const configuration = context.options[0] || {};
+
+ const namedConfig = configuration.namedComponents || 'function-declaration';
+ const unnamedConfig = configuration.unnamedComponents || 'function-expression';
+
+ function getFixer(node, options) {
+ const sourceCode = context.getSourceCode();
+ const source = sourceCode.getText();
+
+ const typeAnnotation = getTypeAnnotation(node, source);
+
+ if (options.type === 'function-declaration' && typeAnnotation) return;
+ if (options.type === 'arrow-function' && hasOneUnconstrainedTypeParam(node)) return;
+
+ return fixer => fixer.replaceTextRange(options.range, buildFunction(options.template, {
+ typeAnnotation,
+ typeParams: getNodeText(node.typeParameters, source),
+ params: getParams(node, source),
+ returnType: getNodeText(node.returnType, source),
+ body: getBody(node, source),
+ name: getName(node)
+ }));
+ }
+
+ function report(node, options) {
+ context.report({
+ node,
+ message: options.message,
+ fix: getFixer(node, options.fixerOptions)
+ });
+ }
+
+ function validate(node, functionType) {
+ if (!components.get(node)) return;
+ if (hasName(node) && namedConfig !== functionType) {
+ report(node, {
+ message: ERROR_MESSAGES[namedConfig],
+ fixerOptions: {
+ type: namedConfig,
+ template: NAMED_FUNCTION_TEMPLATES[namedConfig],
+ range: node.type === 'FunctionDeclaration' ?
+ node.range :
+ node.parent.parent.range
+ }
+ });
+ }
+ if (!hasName(node) && unnamedConfig !== functionType) {
+ report(node, {
+ message: ERROR_MESSAGES[unnamedConfig],
+ fixerOptions: {
+ type: unnamedConfig,
+ template: UNNAMED_FUNCTION_TEMPLATES[unnamedConfig],
+ range: node.range
+ }
+ });
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+ FunctionDeclaration(node) { validate(node, 'function-declaration'); },
+ ArrowFunctionExpression(node) { validate(node, 'arrow-function'); },
+ FunctionExpression(node) { validate(node, 'function-expression'); }
+ };
+ })
+};
diff --git a/tests/lib/rules/function-component-definition.js b/tests/lib/rules/function-component-definition.js
new file mode 100644
index 0000000000..40b911b8b3
--- /dev/null
+++ b/tests/lib/rules/function-component-definition.js
@@ -0,0 +1,634 @@
+/**
+ * @fileoverview Standardize the way function component get defined
+ * @author Stefan Wullems
+ */
+
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const RuleTester = require('eslint').RuleTester;
+const rule = require('../../../lib/rules/function-component-definition');
+
+const parserOptions = {
+ ecmaVersion: 2018,
+ sourceType: 'module',
+ ecmaFeatures: {
+ jsx: true
+ }
+};
+
+const parsers = require('../../helpers/parsers');
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({parserOptions});
+ruleTester.run('function-component-definition', rule, {
+
+ valid: [{
+ code: [
+ 'class Hello extends React.Component {',
+ ' render() { return Hello {this.props.name}
}',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'arrow-function'}]
+ }, {
+ code: [
+ 'class Hello extends React.Component {',
+ ' render() { return Hello {this.props.name}
}',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-declaration'}]
+ }, {
+ code: [
+ 'class Hello extends React.Component {',
+ ' render() { return Hello {this.props.name}
}',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-expression'}]
+ }, {
+ code: 'var Hello = (props) => { return }',
+ options: [{namedComponents: 'arrow-function'}]
+ }, {
+ code: 'function Hello(props) { return }',
+ options: [{namedComponents: 'function-declaration'}]
+ }, {
+ code: 'var Hello = function(props) { return }',
+ options: [{namedComponents: 'function-expression'}]
+ }, {
+ code: 'function Hello() { return function() { return } }',
+ options: [{unnamedComponents: 'function-expression'}]
+ }, {
+ code: 'function Hello() { return () => { return }}',
+ options: [{unnamedComponents: 'arrow-function'}]
+ }, {
+ code: 'var Foo = React.memo(function Foo() { return })',
+ options: [{namedComponents: 'function-declaration'}]
+ }, {
+ code: 'function Hello(props: Test) { return }',
+ options: [{namedComponents: 'function-declaration'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'var Hello = function(props: Test) { return }',
+ options: [{namedComponents: 'function-expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'var Hello = (props: Test) => { return }',
+ options: [{namedComponents: 'arrow-function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'var Hello: React.FC = function(props) { return }',
+ options: [{namedComponents: 'function-expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'var Hello: React.FC = (props) => { return }',
+ options: [{namedComponents: 'arrow-function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'function Hello(props: Props) { return }',
+ options: [{namedComponents: 'function-declaration'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'function Hello(props: Props) { return }',
+ options: [{namedComponents: 'function-declaration'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'var Hello = function(props: Props) { return }',
+ options: [{namedComponents: 'function-expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'var Hello = function(props: Props) { return }',
+ options: [{namedComponents: 'function-expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'var Hello = (props: Props) => { return }',
+ options: [{namedComponents: 'arrow-function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'function wrapper() { return function(props: Props) { return } } ',
+ options: [{unnamedComponents: 'function-expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'function wrapper() { return function(props: Props) { return } } ',
+ options: [{unnamedComponents: 'function-expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'function wrapper() { return(props: Props) => { return } } ',
+ options: [{unnamedComponents: 'arrow-function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'function Hello(props): ReactNode { return }',
+ options: [{namedComponents: 'function-declaration'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'var Hello = function(props): ReactNode { return }',
+ options: [{namedComponents: 'function-expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'var Hello = (props): ReactNode => { return }',
+ options: [{namedComponents: 'arrow-function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'function wrapper() { return function(props): ReactNode { return } } ',
+ options: [{unnamedComponents: 'function-expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: 'function wrapper() { return (props): ReactNode => { return } } ',
+ options: [{unnamedComponents: 'arrow-function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }],
+
+ invalid: [{
+ code: [
+ 'function Hello(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello = (props) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'arrow-function'}],
+ errors: [{message: 'Function component is not an arrow function'}],
+ parser: parsers.BABEL_ESLINT
+ }, {
+ code: [
+ 'var Hello = function(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello = (props) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'arrow-function'}],
+ errors: [{message: 'Function component is not an arrow function'}],
+ parser: parsers.BABEL_ESLINT
+ }, {
+ code: [
+ 'var Hello = (props) =>(',
+ ' ',
+ ')'
+ ].join('\n'),
+ output: [
+ 'function Hello(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-declaration'}],
+ errors: [{message: 'Function component is not a function declaration'}],
+ parser: parsers.BABEL_ESLINT
+ }, {
+ code: [
+ 'var Hello = (props) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function Hello(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-declaration'}],
+ errors: [{message: 'Function component is not a function declaration'}],
+ parser: parsers.BABEL_ESLINT
+ }, {
+ code: [
+ 'var Hello = function(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function Hello(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-declaration'}],
+ errors: [{message: 'Function component is not a function declaration'}]
+ }, {
+ code: [
+ 'var Hello = (props) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello = function(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-expression'}],
+ errors: [{message: 'Function component is not a function expression'}],
+ parser: parsers.BABEL_ESLINT
+ }, {
+ code: [
+ 'function Hello(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello = function(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-expression'}],
+ errors: [{message: 'Function component is not a function expression'}]
+ }, {
+ code: [
+ 'function wrap(Component) {',
+ ' return function(props) {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function wrap(Component) {',
+ ' return (props) => {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ errors: [{message: 'Function component is not an arrow function'}],
+ options: [{unnamedComponents: 'arrow-function'}]
+ }, {
+ code: [
+ 'function wrap(Component) {',
+ ' return (props) => {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function wrap(Component) {',
+ ' return function(props) {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ errors: [{message: 'Function component is not a function expression'}],
+ options: [{unnamedComponents: 'function-expression'}],
+ parser: parsers.BABEL_ESLINT
+ }, {
+ code: [
+ 'var Hello = (props: Test) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function Hello(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-declaration'}],
+ errors: [{message: 'Function component is not a function declaration'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'var Hello = function(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function Hello(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-declaration'}],
+ errors: [{message: 'Function component is not a function declaration'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'function Hello(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello = (props: Test) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'arrow-function'}],
+ errors: [{message: 'Function component is not an arrow function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'var Hello = function(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello = (props: Test) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'arrow-function'}],
+ errors: [{message: 'Function component is not an arrow function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'function Hello(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello = function(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-expression'}],
+ errors: [{message: 'Function component is not a function expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'var Hello = (props: Test) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello = function(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-expression'}],
+ errors: [{message: 'Function component is not a function expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'var Hello: React.FC = (props) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello: React.FC = function(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-expression'}],
+ errors: [{message: 'Function component is not a function expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'var Hello: React.FC = function(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello: React.FC = (props) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'arrow-function'}],
+ errors: [{message: 'Function component is not an arrow function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'var Hello: React.FC = function(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello: React.FC = function(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-declaration'}],
+ errors: [{message: 'Function component is not a function declaration'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'var Hello: React.FC = (props) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello: React.FC = (props) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-declaration'}],
+ errors: [{message: 'Function component is not a function declaration'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'var Hello: React.FC = (props) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello: React.FC = (props) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-declaration'}],
+ errors: [{message: 'Function component is not a function declaration'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'function Hello(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello = (props: Test) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'arrow-function'}],
+ errors: [{message: 'Function component is not an arrow function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'function Hello(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function Hello(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'arrow-function'}],
+ errors: [{message: 'Function component is not an arrow function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'function Hello(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello = function(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-expression'}],
+ errors: [{message: 'Function component is not a function expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'var Hello = (props: Test) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function Hello(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-declaration'}],
+ errors: [{message: 'Function component is not a function declaration'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'var Hello = (props: Test) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello = function(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-expression'}],
+ errors: [{message: 'Function component is not a function expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'var Hello = function(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello = (props: Test) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'arrow-function'}],
+ errors: [{message: 'Function component is not an arrow function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'var Hello = function(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'var Hello = function(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'arrow-function'}],
+ errors: [{message: 'Function component is not an arrow function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'var Hello = function(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function Hello(props: Test) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-declaration'}],
+ errors: [{message: 'Function component is not a function declaration'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'function wrap(Component) {',
+ ' return function(props) {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function wrap(Component) {',
+ ' return (props) => {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ errors: [{message: 'Function component is not an arrow function'}],
+ options: [{unnamedComponents: 'arrow-function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'function wrap(Component) {',
+ ' return function(props) {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function wrap(Component) {',
+ ' return function(props) {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ errors: [{message: 'Function component is not an arrow function'}],
+ options: [{unnamedComponents: 'arrow-function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'function wrap(Component) {',
+ ' return (props) => {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function wrap(Component) {',
+ ' return function(props) {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ errors: [{message: 'Function component is not a function expression'}],
+ options: [{unnamedComponents: 'function-expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'function wrap(Component) {',
+ ' return function(props): ReactNode {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function wrap(Component) {',
+ ' return (props): ReactNode => {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ errors: [{message: 'Function component is not an arrow function'}],
+ options: [{unnamedComponents: 'arrow-function'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }, {
+ code: [
+ 'function wrap(Component) {',
+ ' return (props): ReactNode => {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ output: [
+ 'function wrap(Component) {',
+ ' return function(props): ReactNode {',
+ ' return
',
+ ' }',
+ '}'
+ ].join('\n'),
+ errors: [{message: 'Function component is not a function expression'}],
+ options: [{unnamedComponents: 'function-expression'}],
+ parser: parsers.TYPESCRIPT_ESLINT
+ }
+ ]
+});