• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * @fileoverview Rule to flag when using constructor without parentheses
3 * @author Ilya Volodin
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13
14//------------------------------------------------------------------------------
15// Helpers
16//------------------------------------------------------------------------------
17
18//------------------------------------------------------------------------------
19// Rule Definition
20//------------------------------------------------------------------------------
21
22module.exports = {
23    meta: {
24        type: "layout",
25
26        docs: {
27            description: "enforce or disallow parentheses when invoking a constructor with no arguments",
28            category: "Stylistic Issues",
29            recommended: false,
30            url: "https://eslint.org/docs/rules/new-parens"
31        },
32
33        fixable: "code",
34        schema: {
35            anyOf: [
36                {
37                    type: "array",
38                    items: [
39                        {
40                            enum: ["always", "never"]
41                        }
42                    ],
43                    minItems: 0,
44                    maxItems: 1
45                }
46            ]
47        },
48        messages: {
49            missing: "Missing '()' invoking a constructor.",
50            unnecessary: "Unnecessary '()' invoking a constructor with no arguments."
51        }
52    },
53
54    create(context) {
55        const options = context.options;
56        const always = options[0] !== "never"; // Default is always
57
58        const sourceCode = context.getSourceCode();
59
60        return {
61            NewExpression(node) {
62                if (node.arguments.length !== 0) {
63                    return; // if there are arguments, there have to be parens
64                }
65
66                const lastToken = sourceCode.getLastToken(node);
67                const hasLastParen = lastToken && astUtils.isClosingParenToken(lastToken);
68
69                // `hasParens` is true only if the new expression ends with its own parens, e.g., new new foo() does not end with its own parens
70                const hasParens = hasLastParen &&
71                    astUtils.isOpeningParenToken(sourceCode.getTokenBefore(lastToken)) &&
72                    node.callee.range[1] < node.range[1];
73
74                if (always) {
75                    if (!hasParens) {
76                        context.report({
77                            node,
78                            messageId: "missing",
79                            fix: fixer => fixer.insertTextAfter(node, "()")
80                        });
81                    }
82                } else {
83                    if (hasParens) {
84                        context.report({
85                            node,
86                            messageId: "unnecessary",
87                            fix: fixer => [
88                                fixer.remove(sourceCode.getTokenBefore(lastToken)),
89                                fixer.remove(lastToken),
90                                fixer.insertTextBefore(node, "("),
91                                fixer.insertTextAfter(node, ")")
92                            ]
93                        });
94                    }
95                }
96            }
97        };
98    }
99};
100