From 50bce6598da783a29aac4bc4ff41a88961bc3ae8 Mon Sep 17 00:00:00 2001 From: Oscar Barrett Date: Thu, 26 Jul 2018 17:21:43 +0800 Subject: [PATCH] Update: Added generators option to func-names (fixes #9511) --- docs/rules/func-names.md | 56 ++++++++++++++++++++++++ lib/rules/func-names.js | 81 +++++++++++++++++++++++++++------- tests/lib/rules/func-names.js | 82 +++++++++++++++++++++++++++++++++++ 3 files changed, 204 insertions(+), 15 deletions(-) diff --git a/docs/rules/func-names.md b/docs/rules/func-names.md index 758abc13f862..ea61ce24ca1b 100644 --- a/docs/rules/func-names.md +++ b/docs/rules/func-names.md @@ -20,6 +20,12 @@ This rule has a string option: * `"as-needed"` requires function expressions to have a name, if the name cannot be assigned automatically in an ES6 environment * `"never"` disallows named function expressions, except in recursive functions, where a name is needed +This rule has an object option: + +* `"generators": true (default) | false` + * `true`: require generators to follow the func-names rule. `"always"` or `"as-needed"` will require named generator functions, `"never"` will allow unnamed generator functions. + * `false`: require generators to not follow the func-names rule. `"always"` or `"as-needed"` will allow unnamed generator functions, `"never"` will require named generator functions. + ### always Examples of **incorrect** code for this rule with the default `"always"` option: @@ -100,6 +106,56 @@ Foo.prototype.bar = function() {}; }()) ``` +### generators + +Examples of **incorrect** code for this rule with the `"always", { "generators": false }` options: + +```js +/*eslint func-names: ["error", "always", { "generators": false }]*/ + +var foo = bar(function *baz(){}); +``` + +Examples of **correct** code for this rule with the `"always", { "generators": false }` options: + +```js +/*eslint func-names: ["error", "always", { "generators": false }]*/ + +var foo = bar(function *(){}); +``` + +Examples of **incorrect** code for this rule with the `"as-needed", { "generators": false }` options: + +```js +/*eslint func-names: ["error", "as-needed", { "generators": false }]*/ + +var foo = bar(function *baz(){}); +``` + +Examples of **correct** code for this rule with the `"as-needed", { "generators": false }` options: + +```js +/*eslint func-names: ["error", "as-needed", { "generators": false }]*/ + +var foo = bar(function *(){}); +``` + +Examples of **incorrect** code for this rule with the `"never", { "generators": false }` options: + +```js +/*eslint func-names: ["error", "never", { "generators": false }]*/ + +var foo = bar(function *(){}); +``` + +Examples of **correct** code for this rule with the `"never", { "generators": false }` options: + +```js +/*eslint func-names: ["error", "never", { "generators": false }]*/ + +var foo = bar(function *baz(){}); +``` + ## Further Reading * [Functions Explained](http://markdaggett.com/blog/2013/02/15/functions-explained/) diff --git a/lib/rules/func-names.js b/lib/rules/func-names.js index 500b95a1c6fd..227a70cadeef 100644 --- a/lib/rules/func-names.js +++ b/lib/rules/func-names.js @@ -36,6 +36,15 @@ module.exports = { schema: [ { enum: ["always", "as-needed", "never"] + }, + { + type: "object", + properties: { + generators: { + type: "boolean" + } + }, + additionalProperties: false } ] }, @@ -44,6 +53,23 @@ module.exports = { const never = context.options[0] === "never"; const asNeeded = context.options[0] === "as-needed"; + // defaults to true (func-name required) for "always" and "as-needed", false (func-name not required) for "never". + let requireNamedGenerators = !never; + + if (context.options[1] && context.options[1].hasOwnProperty("generators")) { + switch (context.options[0]) { + case "always": + case "as-needed": + requireNamedGenerators = context.options[1].generators; + break; + case "never": + requireNamedGenerators = !context.options[1].generators; + + // no default + } + + } + /** * Determines whether the current FunctionExpression node is a get, set, or * shorthand method in an object literal or a class. @@ -79,6 +105,32 @@ module.exports = { (parent.type === "AssignmentPattern" && parent.right === node); } + /** + * Reports that an unnamed function should be named + * @param {ASTNode} node - The node to report in the event of an error. + * @returns {void} + */ + function reportUnexpectedUnnamedFunction(node) { + context.report({ + node, + message: "Unexpected unnamed {{name}}.", + data: { name: astUtils.getFunctionNameWithKind(node) } + }); + } + + /** + * Reports that a named function should be unnamed + * @param {ASTNode} node - The node to report in the event of an error. + * @returns {void} + */ + function reportUnexpectedNamedFunction(node) { + context.report({ + node, + message: "Unexpected named {{name}}.", + data: { name: astUtils.getFunctionNameWithKind(node) } + }); + } + return { "FunctionExpression:exit"(node) { @@ -90,23 +142,22 @@ module.exports = { } const hasName = Boolean(node.id && node.id.name); - const name = astUtils.getFunctionNameWithKind(node); - - if (never) { - if (hasName) { - context.report({ - node, - message: "Unexpected named {{name}}.", - data: { name } - }); + + if (node.generator) { + if (requireNamedGenerators && !hasName) { + reportUnexpectedUnnamedFunction(node); + } else if (!requireNamedGenerators && hasName) { + reportUnexpectedNamedFunction(node); } } else { - if (!hasName && (asNeeded ? !hasInferredName(node) : !isObjectOrClassMethod(node))) { - context.report({ - node, - message: "Unexpected unnamed {{name}}.", - data: { name } - }); + if (never) { + if (hasName) { + reportUnexpectedNamedFunction(node); + } + } else { + if (!hasName && (asNeeded ? !hasInferredName(node) : !isObjectOrClassMethod(node))) { + reportUnexpectedUnnamedFunction(node); + } } } } diff --git a/tests/lib/rules/func-names.js b/tests/lib/rules/func-names.js index fba51917385e..c6a5a2c63208 100644 --- a/tests/lib/rules/func-names.js +++ b/tests/lib/rules/func-names.js @@ -125,6 +125,38 @@ ruleTester.run("func-names", rule, { code: "({ foo() {} });", options: ["never"], parserOptions: { ecmaVersion: 6 } + }, + + // generators + { + code: "var foo = bar(function *baz() {});", + options: ["always", { generators: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var foo = bar(function *() {});", + options: ["always", { generators: false }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var foo = bar(function *baz() {});", + options: ["as-needed", { generators: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var foo = bar(function *() {});", + options: ["as-needed", { generators: false }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var foo = bar(function *() {});", + options: ["never", { generators: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var foo = bar(function *baz() {});", + options: ["never", { generators: false }], + parserOptions: { ecmaVersion: 6 } } ], invalid: [ @@ -197,6 +229,56 @@ ruleTester.run("func-names", rule, { code: "({foo: function foo() {}})", options: ["never"], errors: [{ message: "Unexpected named method 'foo'.", type: "FunctionExpression" }] + }, + + // generators + { + code: "var foo = bar(function *() {});", + options: ["always", { generators: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ + message: "Unexpected unnamed generator function." + }] + }, + { + code: "var foo = bar(function *baz() {});", + options: ["always", { generators: false }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ + message: "Unexpected named generator function 'baz'." + }] + }, + { + code: "var foo = bar(function *() {});", + options: ["as-needed", { generators: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ + message: "Unexpected unnamed generator function." + }] + }, + { + code: "var foo = bar(function *baz() {});", + options: ["as-needed", { generators: false }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ + message: "Unexpected named generator function 'baz'." + }] + }, + { + code: "var foo = bar(function *baz() {});", + options: ["never", { generators: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ + message: "Unexpected named generator function 'baz'." + }] + }, + { + code: "var foo = bar(function *() {});", + options: ["never", { generators: false }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ + message: "Unexpected unnamed generator function." + }] } ] });