diff --git a/docs/rules/new-parens.md b/docs/rules/new-parens.md index 85038a62dd8..1640d37a2e4 100644 --- a/docs/rules/new-parens.md +++ b/docs/rules/new-parens.md @@ -1,4 +1,4 @@ -# require parentheses when invoking a constructor with no arguments (new-parens) +# Require parentheses when invoking a constructor with no arguments (new-parens) JavaScript allows the omission of parentheses when invoking a function via the `new` keyword and the constructor has no arguments. However, some coders believe that omitting the parentheses is inconsistent with the rest of the language and thus makes code less clear. @@ -8,9 +8,18 @@ var person = new Person; ## Rule Details -This rule requires parentheses when invoking a constructor with no arguments using the `new` keyword in order to increase code clarity. +This rule can enforce or disallow parentheses when invoking a constructor with no arguments using the `new` keyword. -Examples of **incorrect** code for this rule: +## Options + +This rule takes one option. + +- `"always"` enforces parenthesis after a new constructor with no arguments (default) +- `"never"` enforces no parenthesis after a new constructor with no arguments + +### always + +Examples of **incorrect** code for this rule with the `"always"` option: ```js /*eslint new-parens: "error"*/ @@ -19,7 +28,7 @@ var person = new Person; var person = new (Person); ``` -Examples of **correct** code for this rule: +Examples of **correct** code for this rule with the `"always"` option: ```js /*eslint new-parens: "error"*/ @@ -27,3 +36,24 @@ Examples of **correct** code for this rule: var person = new Person(); var person = new (Person)(); ``` + +### never + +Examples of **incorrect** code for this rule with the `"never"` option: + +```js +/*eslint new-parens: ["error", "never"]*/ + +var person = new Person(); +var person = new (Person)(); +``` + +Examples of **correct** code for this rule with the `"never"` option: + +```js +/*eslint new-parens: ["error", "never"]*/ + +var person = new Person; +var person = (new Person); +var person = new Person("Name"); +``` diff --git a/lib/rules/new-parens.js b/lib/rules/new-parens.js index edd3c1e1c2e..6955284f1eb 100644 --- a/lib/rules/new-parens.js +++ b/lib/rules/new-parens.js @@ -24,38 +24,70 @@ module.exports = { type: "layout", docs: { - description: "require parentheses when invoking a constructor with no arguments", + description: "enforce or disallow parentheses when invoking a constructor with no arguments", category: "Stylistic Issues", recommended: false, url: "https://eslint.org/docs/rules/new-parens" }, fixable: "code", - schema: [], + schema: { + anyOf: [ + { + type: "array", + items: [ + { + enum: ["always", "never"] + } + ], + minItems: 0, + maxItems: 1 + } + ] + }, messages: { - missing: "Missing '()' invoking a constructor." + missing: "Missing '()' invoking a constructor.", + unnecessary: "Unnecessary '()' invoking a constructor with no arguments." } }, create(context) { + const options = context.options; + const always = options[0] !== "never"; // Default is always + const sourceCode = context.getSourceCode(); return { NewExpression(node) { if (node.arguments.length !== 0) { - return; // shortcut: if there are arguments, there have to be parens + return; // if there are arguments, there have to be parens } const lastToken = sourceCode.getLastToken(node); const hasLastParen = lastToken && astUtils.isClosingParenToken(lastToken); const hasParens = hasLastParen && astUtils.isOpeningParenToken(sourceCode.getTokenBefore(lastToken)); - if (!hasParens) { - context.report({ - node, - messageId: "missing", - fix: fixer => fixer.insertTextAfter(node, "()") - }); + if (always) { + if (!hasParens) { + context.report({ + node, + messageId: "missing", + fix: fixer => fixer.insertTextAfter(node, "()") + }); + } + } else { + if (hasParens) { + context.report({ + node, + messageId: "unnecessary", + fix: fixer => [ + fixer.remove(sourceCode.getTokenBefore(lastToken)), + fixer.remove(lastToken), + fixer.insertTextBefore(node, "("), + fixer.insertTextAfter(node, ")") + ] + }); + } } } }; diff --git a/tests/lib/rules/new-parens.js b/tests/lib/rules/new-parens.js index d56cd079d44..b8bbba55c52 100644 --- a/tests/lib/rules/new-parens.js +++ b/tests/lib/rules/new-parens.js @@ -17,11 +17,14 @@ const parser = require("../../fixtures/fixture-parser"), // Tests //------------------------------------------------------------------------------ const error = { messageId: "missing", type: "NewExpression" }; +const neverError = { messageId: "unnecessary", type: "NewExpression" }; const ruleTester = new RuleTester(); ruleTester.run("new-parens", rule, { valid: [ + + // Default (Always) "var a = new Date();", "var a = new Date(function() {});", "var a = new (Date)();", @@ -29,9 +32,31 @@ ruleTester.run("new-parens", rule, { "var a = (new Date());", "var a = new foo.Bar();", "var a = (new Foo()).bar;", - { code: "new Storage('state');", parser: parser("typescript-parsers/new-parens") } + { + code: "new Storage('state');", + parser: parser("typescript-parsers/new-parens") + }, + + // Explicit Always + { code: "var a = new Date();", options: ["always"] }, + { code: "var a = new foo.Bar();", options: ["always"] }, + { code: "var a = (new Foo()).bar;", options: ["always"] }, + + // Never + { code: "var a = new Date;", options: ["never"] }, + { code: "var a = new Date(function() {});", options: ["never"] }, + { code: "var a = new (Date);", options: ["never"] }, + { code: "var a = new ((Date));", options: ["never"] }, + { code: "var a = (new Date);", options: ["never"] }, + { code: "var a = new foo.Bar;", options: ["never"] }, + { code: "var a = (new Foo).bar;", options: ["never"] }, + { code: "var a = new Person('Name')", options: ["never"] }, + { code: "var a = new Person('Name', 12)", options: ["never"] }, + { code: "var a = new ((Person))('Name');", options: ["never"] } ], invalid: [ + + // Default (Always) { code: "var a = new Date;", output: "var a = new Date();", @@ -73,6 +98,76 @@ ruleTester.run("new-parens", rule, { code: "var a = (new Foo).bar;", output: "var a = (new Foo()).bar;", errors: [error] + }, + + // Explicit always + { + code: "var a = new Date;", + output: "var a = new Date();", + options: ["always"], + errors: [error] + }, + { + code: "var a = new foo.Bar;", + output: "var a = new foo.Bar();", + options: ["always"], + errors: [error] + }, + { + code: "var a = (new Foo).bar;", + output: "var a = (new Foo()).bar;", + options: ["always"], + errors: [error] + }, + + // Never + { + code: "var a = new Date();", + output: "var a = (new Date);", + options: ["never"], + errors: [neverError] + }, + { + code: "var a = new Date()", + output: "var a = (new Date)", + options: ["never"], + errors: [neverError] + }, + { + code: "var a = new (Date)();", + output: "var a = (new (Date));", + options: ["never"], + errors: [neverError] + }, + { + code: "var a = new (Date)()", + output: "var a = (new (Date))", + options: ["never"], + errors: [neverError] + }, + { + code: "var a = (new Date())", + output: "var a = ((new Date))", + options: ["never"], + errors: [neverError] + }, + { + code: "var a = (new Date())()", + output: "var a = ((new Date))()", + options: ["never"], + errors: [neverError] + }, + { + code: "var a = new foo.Bar();", + output: "var a = (new foo.Bar);", + options: ["never"], + errors: [neverError] + }, + { + code: "var a = (new Foo()).bar;", + output: "var a = ((new Foo)).bar;", + options: ["never"], + errors: [neverError] } ] });