diff --git a/docs/rules/function-component-definition.md b/docs/rules/function-component-definition.md index 5851d3baeb..f1883733cc 100644 --- a/docs/rules/function-component-definition.md +++ b/docs/rules/function-component-definition.md @@ -31,12 +31,13 @@ function getComponent() { ## 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. +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"`, `"arrow-function"`, or an array +containing any of those 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", + "namedComponents": "function-declaration" | "function-expression" | "arrow-function" | Array, "unnamedComponents": "function-expression" | "arrow-function" }] ... diff --git a/lib/rules/function-component-definition.js b/lib/rules/function-component-definition.js index 6842b25bf2..2804188881 100644 --- a/lib/rules/function-component-definition.js +++ b/lib/rules/function-component-definition.js @@ -109,23 +109,44 @@ module.exports = { messages, - schema: [{ - type: 'object', - properties: { - namedComponents: { - enum: ['function-declaration', 'arrow-function', 'function-expression'], - }, - unnamedComponents: { - enum: ['arrow-function', 'function-expression'], + schema: [ + { + type: 'object', + properties: { + namedComponents: { + oneOf: [ + { + enum: [ + 'function-declaration', + 'arrow-function', + 'function-expression', + ], + }, + { + type: 'array', + items: { + type: 'string', + 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 namedConfig = Array.isArray(configuration.namedComponents) ? configuration.namedComponents : [configuration.namedComponents] || ['function-declaration']; const unnamedConfig = configuration.unnamedComponents || 'function-expression'; function getFixer(node, options) { @@ -161,12 +182,12 @@ module.exports = { if (node.parent && node.parent.type === 'Property') return; - if (hasName(node) && namedConfig !== functionType) { + if (hasName(node) && !namedConfig.includes(functionType)) { report(node, { - messageId: namedConfig, + messageId: namedConfig[0], fixerOptions: { - type: namedConfig, - template: NAMED_FUNCTION_TEMPLATES[namedConfig], + type: namedConfig[0], + template: NAMED_FUNCTION_TEMPLATES[namedConfig[0]], range: node.type === 'FunctionDeclaration' ? node.range : node.parent.parent.range, diff --git a/tests/lib/rules/function-component-definition.js b/tests/lib/rules/function-component-definition.js index 47f94e0b72..3a84bcad77 100644 --- a/tests/lib/rules/function-component-definition.js +++ b/tests/lib/rules/function-component-definition.js @@ -78,8 +78,8 @@ ruleTester.run('function-component-definition', rule, { options: [{ namedComponents: 'function-declaration' }], }, { - // shouldn't trigger this rule since functions stating with a lowercase - // letter are not considered components + // shouldn't trigger this rule since functions stating with a lowercase + // letter are not considered components code: ` const selectAvatarByUserId = (state, id) => { const user = selectUserById(state, id) @@ -89,8 +89,8 @@ ruleTester.run('function-component-definition', rule, { options: [{ namedComponents: 'function-declaration' }], }, { - // shouldn't trigger this rule since functions stating with a lowercase - // letter are not considered components + // shouldn't trigger this rule since functions stating with a lowercase + // letter are not considered components code: ` function ensureValidSourceType(sourceType: string) { switch (sourceType) { @@ -346,6 +346,34 @@ ruleTester.run('function-component-definition', rule, { `, options: [{ unnamedComponents: 'function-expression' }], }, + + { + code: 'function Hello(props) { return
}', + options: [{ namedComponents: ['function-declaration', 'function-expression'] }], + }, + { + code: 'var Hello = function(props) { return
}', + options: [{ namedComponents: ['function-declaration', 'function-expression'] }], + }, + { + code: 'var Foo = React.memo(function Foo() { return

})', + options: [{ namedComponents: ['function-declaration', 'function-expression'] }], + }, + { + code: 'function Hello(props: Test) { return

}', + options: [{ namedComponents: ['function-declaration', 'function-expression'] }], + features: ['types'], + }, + { + code: 'var Hello = function(props: Test) { return

}', + options: [{ namedComponents: ['function-expression', 'function-expression'] }], + features: ['types'], + }, + { + code: 'var Hello = (props: Test) => { return

}', + options: [{ namedComponents: ['arrow-function', 'function-expression'] }], + features: ['types'], + }, ]), invalid: parsers.all([ @@ -879,5 +907,47 @@ ruleTester.run('function-component-definition', rule, { options: [{ unnamedComponents: 'arrow-function' }], errors: [{ messageId: 'arrow-function' }], }, + { + code: ` + function Hello(props) { + return

; + } + `, + output: ` + var Hello = (props) => { + return
; + } + `, + options: [{ namedComponents: ['arrow-function', 'function-expression'] }], + errors: [{ messageId: 'arrow-function' }], + }, + { + code: ` + var Hello = (props) => { + return
; + }; + `, + output: ` + function Hello(props) { + return
; + } + `, + options: [{ namedComponents: ['function-declaration', 'function-expression'] }], + errors: [{ messageId: 'function-declaration' }], + }, + { + code: ` + var Hello = (props) => { + return
; + }; + `, + output: ` + var Hello = function(props) { + return
; + } + `, + options: [{ namedComponents: ['function-expression', 'function-declaration'] }], + errors: [{ messageId: 'function-expression' }], + }, ]), });