diff --git a/docs/rules/func-names.md b/docs/rules/func-names.md index b051d9b9bef..27b9cc20fcf 100644 --- a/docs/rules/func-names.md +++ b/docs/rules/func-names.md @@ -29,6 +29,8 @@ This rule has an object option: When a value for `generators` is not provided the behavior for generator functions falls back to the base option. +Please note that `"always"` and `"as-needed"` require function expressions and function declarations in `export default` declarations to have a name. + ### always Examples of **incorrect** code for this rule with the default `"always"` option: @@ -45,6 +47,8 @@ const cat = { (function() { // ... }()) + +export default function() {} ``` Examples of **correct** code for this rule with the default `"always"` option: @@ -61,6 +65,8 @@ const cat = { (function bar() { // ... }()) + +export default function foo() {} ``` ### as-needed @@ -77,6 +83,8 @@ Foo.prototype.bar = function() {}; (function() { // ... }()) + +export default function() {} ``` Examples of **correct** code for this rule with the `"as-needed"` option: @@ -93,6 +101,8 @@ const cat = { (function bar() { // ... }()) + +export default function foo() {} ``` ### never diff --git a/lib/rules/func-names.js b/lib/rules/func-names.js index 1341d036308..4416dd35e6e 100644 --- a/lib/rules/func-names.js +++ b/lib/rules/func-names.js @@ -119,7 +119,6 @@ module.exports = { (parent.type === "VariableDeclarator" && parent.id.type === "Identifier" && parent.init === node) || (parent.type === "Property" && parent.value === node) || (parent.type === "AssignmentExpression" && parent.left.type === "Identifier" && parent.right === node) || - (parent.type === "ExportDefaultDeclaration" && parent.declaration === node) || (parent.type === "AssignmentPattern" && parent.right === node); } @@ -151,33 +150,41 @@ module.exports = { }); } - return { - "FunctionExpression:exit"(node) { + /** + * The listener for function nodes. + * @param {ASTNode} node function node + * @returns {void} + */ + function handleFunction(node) { - // Skip recursive functions. - const nameVar = context.getDeclaredVariables(node)[0]; + // Skip recursive functions. + const nameVar = context.getDeclaredVariables(node)[0]; - if (isFunctionName(nameVar) && nameVar.references.length > 0) { - return; - } + if (isFunctionName(nameVar) && nameVar.references.length > 0) { + return; + } - const hasName = Boolean(node.id && node.id.name); - const config = getConfigForNode(node); - - if (config === "never") { - if (hasName) { - reportUnexpectedNamedFunction(node); - } - } else if (config === "as-needed") { - if (!hasName && !hasInferredName(node)) { - reportUnexpectedUnnamedFunction(node); - } - } else { - if (!hasName && !isObjectOrClassMethod(node)) { - reportUnexpectedUnnamedFunction(node); - } + const hasName = Boolean(node.id && node.id.name); + const config = getConfigForNode(node); + + if (config === "never") { + if (hasName && node.type !== "FunctionDeclaration") { + reportUnexpectedNamedFunction(node); + } + } else if (config === "as-needed") { + if (!hasName && !hasInferredName(node)) { + reportUnexpectedUnnamedFunction(node); + } + } else { + if (!hasName && !isObjectOrClassMethod(node)) { + reportUnexpectedUnnamedFunction(node); } } + } + + return { + "FunctionExpression:exit": handleFunction, + "ExportDefaultDeclaration > FunctionDeclaration": handleFunction }; } }; diff --git a/tests/lib/rules/func-names.js b/tests/lib/rules/func-names.js index 1976d565c17..94a5e335118 100644 --- a/tests/lib/rules/func-names.js +++ b/tests/lib/rules/func-names.js @@ -64,14 +64,6 @@ ruleTester.run("func-names", rule, { code: "(foo = function(){});", options: ["as-needed"] }, - { - code: "export default (function(){});", - options: ["as-needed"], - parserOptions: { - ecmaVersion: 6, - sourceType: "module" - } - }, { code: "({foo = function(){}} = {});", options: ["as-needed"], @@ -127,6 +119,28 @@ ruleTester.run("func-names", rule, { parserOptions: { ecmaVersion: 6 } }, + // export default + { + code: "export default function foo() {}", + options: ["always"], + parserOptions: { sourceType: "module", ecmaVersion: 6 } + }, + { + code: "export default function foo() {}", + options: ["as-needed"], + parserOptions: { sourceType: "module", ecmaVersion: 6 } + }, + { + code: "export default function foo() {}", + options: ["never"], + parserOptions: { sourceType: "module", ecmaVersion: 6 } + }, + { + code: "export default function() {}", + options: ["never"], + parserOptions: { sourceType: "module", ecmaVersion: 6 } + }, + // generators { code: "var foo = bar(function *baz() {});", @@ -415,6 +429,41 @@ ruleTester.run("func-names", rule, { }] }, + // export default + { + code: "export default function() {}", + options: ["always"], + parserOptions: { sourceType: "module", ecmaVersion: 6 }, + errors: [{ + messageId: "unnamed", + type: "FunctionDeclaration", + column: 16, + endColumn: 24 + }] + }, + { + code: "export default function() {}", + options: ["as-needed"], + parserOptions: { sourceType: "module", ecmaVersion: 6 }, + errors: [{ + messageId: "unnamed", + type: "FunctionDeclaration", + column: 16, + endColumn: 24 + }] + }, + { + code: "export default (function(){});", + options: ["as-needed"], + parserOptions: { sourceType: "module", ecmaVersion: 6 }, + errors: [{ + messageId: "unnamed", + type: "FunctionExpression", + column: 17, + endColumn: 25 + }] + }, + // generators { code: "var foo = bar(function *() {});",