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 + } + ] +});