Skip to content

Commit 2690c13

Browse files
committed
[new] support namedComponents option being an array
This adds support to the `function-component-definition` rule to have the `namedComponents` rule be an array.
1 parent 21e01b6 commit 2690c13

File tree

3 files changed

+137
-26
lines changed

3 files changed

+137
-26
lines changed

docs/rules/function-component-definition.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ function getComponent() {
3131

3232
## Rule Options
3333

34-
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.
34+
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
35+
containing any of those, and has `'function-declaration'` as its default. The second property is `"unnamedComponents"` that can be either `"function-expression"`, `"arrow-function"`, or an array containing any of those, and has `'function-expression'` as its default.
3536

3637
```js
3738
...
3839
"react/function-component-definition": [<enabled>, {
39-
"namedComponents": "function-declaration" | "function-expression" | "arrow-function",
40-
"unnamedComponents": "function-expression" | "arrow-function"
40+
"namedComponents": "function-declaration" | "function-expression" | "arrow-function" | Array<"function-declaration" | "function-expression" | "arrow-function">,
41+
"unnamedComponents": "function-expression" | "arrow-function" | Array<"function-expression" | "arrow-function">
4142
}]
4243
...
4344
```

lib/rules/function-component-definition.js

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -109,24 +109,44 @@ module.exports = {
109109

110110
messages,
111111

112-
schema: [{
113-
type: 'object',
114-
properties: {
115-
namedComponents: {
116-
enum: ['function-declaration', 'arrow-function', 'function-expression'],
117-
},
118-
unnamedComponents: {
119-
enum: ['arrow-function', 'function-expression'],
112+
schema: [
113+
{
114+
type: 'object',
115+
properties: {
116+
namedComponents: {
117+
oneOf: [
118+
{ enum: ['function-declaration', 'arrow-function', 'function-expression'] },
119+
{
120+
type: 'array',
121+
items: {
122+
type: 'string',
123+
enum: ['function-declaration', 'arrow-function', 'function-expression'],
124+
},
125+
},
126+
],
127+
},
128+
unnamedComponents: {
129+
oneOf: [
130+
{ enum: ['arrow-function', 'function-expression'] },
131+
{
132+
type: 'array',
133+
items: {
134+
type: 'string',
135+
enum: ['arrow-function', 'function-expression'],
136+
},
137+
},
138+
],
139+
},
120140
},
121141
},
122-
}],
142+
],
123143
},
124144

125145
create: Components.detect((context, components) => {
126146
const configuration = context.options[0] || {};
127147

128-
const namedConfig = configuration.namedComponents || 'function-declaration';
129-
const unnamedConfig = configuration.unnamedComponents || 'function-expression';
148+
const namedConfig = [].concat(configuration.namedComponents || 'function-declaration');
149+
const unnamedConfig = [].concat(configuration.unnamedComponents || 'function-expression');
130150

131151
function getFixer(node, options) {
132152
const sourceCode = context.getSourceCode();
@@ -161,24 +181,24 @@ module.exports = {
161181

162182
if (node.parent && node.parent.type === 'Property') return;
163183

164-
if (hasName(node) && namedConfig !== functionType) {
184+
if (hasName(node) && !namedConfig.includes(functionType)) {
165185
report(node, {
166-
messageId: namedConfig,
186+
messageId: namedConfig[0],
167187
fixerOptions: {
168-
type: namedConfig,
169-
template: NAMED_FUNCTION_TEMPLATES[namedConfig],
188+
type: namedConfig[0],
189+
template: NAMED_FUNCTION_TEMPLATES[namedConfig[0]],
170190
range: node.type === 'FunctionDeclaration'
171191
? node.range
172192
: node.parent.parent.range,
173193
},
174194
});
175195
}
176-
if (!hasName(node) && unnamedConfig !== functionType) {
196+
if (!hasName(node) && !unnamedConfig.includes(functionType)) {
177197
report(node, {
178-
messageId: unnamedConfig,
198+
messageId: unnamedConfig[0],
179199
fixerOptions: {
180-
type: unnamedConfig,
181-
template: UNNAMED_FUNCTION_TEMPLATES[unnamedConfig],
200+
type: unnamedConfig[0],
201+
template: UNNAMED_FUNCTION_TEMPLATES[unnamedConfig[0]],
182202
range: node.range,
183203
},
184204
});

tests/lib/rules/function-component-definition.js

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ ruleTester.run('function-component-definition', rule, {
7878
options: [{ namedComponents: 'function-declaration' }],
7979
},
8080
{
81-
// shouldn't trigger this rule since functions stating with a lowercase
82-
// letter are not considered components
81+
// shouldn't trigger this rule since functions stating with a lowercase
82+
// letter are not considered components
8383
code: `
8484
const selectAvatarByUserId = (state, id) => {
8585
const user = selectUserById(state, id)
@@ -89,8 +89,8 @@ ruleTester.run('function-component-definition', rule, {
8989
options: [{ namedComponents: 'function-declaration' }],
9090
},
9191
{
92-
// shouldn't trigger this rule since functions stating with a lowercase
93-
// letter are not considered components
92+
// shouldn't trigger this rule since functions stating with a lowercase
93+
// letter are not considered components
9494
code: `
9595
function ensureValidSourceType(sourceType: string) {
9696
switch (sourceType) {
@@ -346,6 +346,54 @@ ruleTester.run('function-component-definition', rule, {
346346
`,
347347
options: [{ unnamedComponents: 'function-expression' }],
348348
},
349+
350+
{
351+
code: 'function Hello(props) { return <div/> }',
352+
options: [{ namedComponents: ['function-declaration', 'function-expression'] }],
353+
},
354+
{
355+
code: 'var Hello = function(props) { return <div/> }',
356+
options: [{ namedComponents: ['function-declaration', 'function-expression'] }],
357+
},
358+
{
359+
code: 'var Foo = React.memo(function Foo() { return <p/> })',
360+
options: [{ namedComponents: ['function-declaration', 'function-expression'] }],
361+
},
362+
{
363+
code: 'function Hello(props: Test) { return <p/> }',
364+
options: [{ namedComponents: ['function-declaration', 'function-expression'] }],
365+
features: ['types'],
366+
},
367+
{
368+
code: 'var Hello = function(props: Test) { return <p/> }',
369+
options: [{ namedComponents: ['function-expression', 'function-expression'] }],
370+
features: ['types'],
371+
},
372+
{
373+
code: 'var Hello = (props: Test) => { return <p/> }',
374+
options: [{ namedComponents: ['arrow-function', 'function-expression'] }],
375+
features: ['types'],
376+
},
377+
{
378+
code: `
379+
function wrap(Component) {
380+
return function(props) {
381+
return <div><Component {...props}/></div>;
382+
};
383+
}
384+
`,
385+
options: [{ unnamedComponents: ['arrow-function', 'function-expression'] }],
386+
},
387+
{
388+
code: `
389+
function wrap(Component) {
390+
return (props) => {
391+
return <div><Component {...props}/></div>;
392+
};
393+
}
394+
`,
395+
options: [{ unnamedComponents: ['arrow-function', 'function-expression'] }],
396+
},
349397
]),
350398

351399
invalid: parsers.all([
@@ -879,5 +927,47 @@ ruleTester.run('function-component-definition', rule, {
879927
options: [{ unnamedComponents: 'arrow-function' }],
880928
errors: [{ messageId: 'arrow-function' }],
881929
},
930+
{
931+
code: `
932+
function Hello(props) {
933+
return <div/>;
934+
}
935+
`,
936+
output: `
937+
var Hello = (props) => {
938+
return <div/>;
939+
}
940+
`,
941+
options: [{ namedComponents: ['arrow-function', 'function-expression'] }],
942+
errors: [{ messageId: 'arrow-function' }],
943+
},
944+
{
945+
code: `
946+
var Hello = (props) => {
947+
return <div/>;
948+
};
949+
`,
950+
output: `
951+
function Hello(props) {
952+
return <div/>;
953+
}
954+
`,
955+
options: [{ namedComponents: ['function-declaration', 'function-expression'] }],
956+
errors: [{ messageId: 'function-declaration' }],
957+
},
958+
{
959+
code: `
960+
var Hello = (props) => {
961+
return <div/>;
962+
};
963+
`,
964+
output: `
965+
var Hello = function(props) {
966+
return <div/>;
967+
}
968+
`,
969+
options: [{ namedComponents: ['function-expression', 'function-declaration'] }],
970+
errors: [{ messageId: 'function-expression' }],
971+
},
882972
]),
883973
});

0 commit comments

Comments
 (0)