diff --git a/CHANGELOG.md b/CHANGELOG.md
index f77f1748af..62b70cb309 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
* [`jsx-props-no-spreading`][]: add `explicitSpread` option to allow explicit spread of props ([#2449][] @pawelnvk)
* [`jsx-no-target-blank`][]: warn on `target={'_blank'}` expressions ([#2451][] @timkraut)
* [`require-default-props`]: add option to ignore functional components ([#2532][] @RedTn)
+ * [`function-component-definition`]: Enforce a specific function type for function components ([#2414][] @Stefanwullems)
### Fixed
* [`sort-prop-types`][], [`jsx-sort-default-props`][]: disable broken autofix ([#2505][] @webOS101)
@@ -56,6 +57,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
[#2443]: https://github.com/yannickcr/eslint-plugin-react/pull/2443
[#2438]: https://github.com/yannickcr/eslint-plugin-react/pull/2438
[#2436]: https://github.com/yannickcr/eslint-plugin-react/pull/2436
+[#2414]: https://github.com/yannickcr/eslint-plugin-react/pull/2414
[#2273]: https://github.com/yannickcr/eslint-plugin-react/pull/2273
## [7.16.0] - 2019-10-04
@@ -2811,3 +2813,4 @@ If you're still not using React 15 you can keep the old behavior by setting the
[`jsx-no-useless-fragment`]: docs/rules/jsx-no-useless-fragment.md
[`jsx-no-script-url`]: docs/rules/jsx-no-script-url.md
[`no-adjacent-inline-elements`]: docs/rules/no-adjacent-inline-elements.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..7d4226503e
--- /dev/null
+++ b/docs/rules/function-component-definition.md
@@ -0,0 +1,254 @@
+# 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 ;
+ };
+}
+
+```
+
+## Unfixable patterns
+
+There is one unfixable pattern in JavaScript.
+
+It has to do with the fact that this is valid syntax:
+
+```js
+export default function getComponent () {
+ return ;
+}
+```
+
+While these are not:
+
+```js
+export default var getComponent = () => {
+ return ;
+}
+
+export default var getComponent = function () {
+ return ;
+}
+```
+
+These patterns have to be manually fixed.
+
+## Heads up, TypeScript users
+
+Note that the autofixer 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 ;
+ }
+}
+```
+
+Type parameters do not produce syntax conflicts if either there are multiple type parameters or, if there is only one constrained type parameter.
+
+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.
\ No newline at end of file
diff --git a/index.js b/index.js
index 2396353682..53b50230f4 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..e582dc48f6
--- /dev/null
+++ b/lib/rules/function-component-definition.js
@@ -0,0 +1,193 @@
+/**
+ * @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 === 0) 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 !== 'BlockStatement') {
+ return [
+ '{',
+ ` return ${source.slice(range[0], range[1])}`,
+ '}'
+ ].join('\n');
+ }
+
+ 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);
+ }
+}
+
+function isUnfixableBecauseOfExport(node) {
+ return node.type === 'FunctionDeclaration' && node.parent && node.parent.type === 'ExportDefaultDeclaration';
+}
+
+function isFunctionExpressionWithName(node) {
+ return node.type === 'FunctionExpression' && node.id && node.id.name;
+}
+
+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;
+ if (isUnfixableBecauseOfExport(node)) return;
+ if (isFunctionExpressionWithName(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..dbf61d24e6
--- /dev/null
+++ b/tests/lib/rules/function-component-definition.js
@@ -0,0 +1,707 @@
+/**
+ * @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: '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
+ }, {
+ 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: [
+ '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
+ },
+ {
+ code: [
+ 'export function Hello(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'export var Hello = (props) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'arrow-function'}],
+ errors: [{message: 'Function component is not an arrow function'}],
+ parser: parsers.BABEL_ESLINT
+ }, {
+ code: [
+ 'export var Hello = function(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'export var Hello = (props) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'arrow-function'}],
+ errors: [{message: 'Function component is not an arrow function'}],
+ parser: parsers.BABEL_ESLINT
+ }, {
+ code: [
+ 'export var Hello = (props) => {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'export function Hello(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'function-declaration'}],
+ errors: [{message: 'Function component is not a function declaration'}],
+ parser: parsers.BABEL_ESLINT
+ },
+ {
+ code: [
+ 'export default function Hello(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'export default function Hello(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{namedComponents: 'arrow-function'}],
+ errors: [{message: 'Function component is not an arrow function'}],
+ parser: parsers.BABEL_ESLINT
+ }, {
+ code: [
+ 'module.exports = function Hello(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ output: [
+ 'module.exports = function Hello(props) {',
+ ' return ',
+ '}'
+ ].join('\n'),
+ options: [{unnamedComponents: 'arrow-function'}],
+ errors: [{message: 'Function component is not an arrow function'}],
+ parser: parsers.BABEL_ESLINT
+ }]
+});