summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/lib/rules/function-call-argument-newline.js
blob: 31ebc097c46f5164567d43e7dc2c38fe8edb2471 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
 * @fileoverview Rule to enforce line breaks between arguments of a function call
 * @author Alexey Gonchar <https://github.com/finico>
 */

"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
    meta: {
        type: "layout",

        docs: {
            description: "enforce line breaks between arguments of a function call",
            category: "Stylistic Issues",
            recommended: false,
            url: "https://eslint.org/docs/rules/function-call-argument-newline"
        },

        fixable: "whitespace",

        schema: [
            {
                enum: ["always", "never", "consistent"]
            }
        ],

        messages: {
            unexpectedLineBreak: "There should be no line break here.",
            missingLineBreak: "There should be a line break after this argument."
        }
    },

    create(context) {
        const sourceCode = context.getSourceCode();

        const checkers = {
            unexpected: {
                messageId: "unexpectedLineBreak",
                check: (prevToken, currentToken) => prevToken.loc.end.line !== currentToken.loc.start.line,
                createFix: (token, tokenBefore) => fixer =>
                    fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], " ")
            },
            missing: {
                messageId: "missingLineBreak",
                check: (prevToken, currentToken) => prevToken.loc.end.line === currentToken.loc.start.line,
                createFix: (token, tokenBefore) => fixer =>
                    fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], "\n")
            }
        };

        /**
         * Check all arguments for line breaks in the CallExpression
         * @param {CallExpression} node node to evaluate
         * @param {{ messageId: string, check: Function }} checker selected checker
         * @returns {void}
         * @private
         */
        function checkArguments(node, checker) {
            for (let i = 1; i < node.arguments.length; i++) {
                const prevArgToken = sourceCode.getLastToken(node.arguments[i - 1]);
                const currentArgToken = sourceCode.getFirstToken(node.arguments[i]);

                if (checker.check(prevArgToken, currentArgToken)) {
                    const tokenBefore = sourceCode.getTokenBefore(
                        currentArgToken,
                        { includeComments: true }
                    );

                    context.report({
                        node,
                        loc: {
                            start: tokenBefore.loc.end,
                            end: currentArgToken.loc.start
                        },
                        messageId: checker.messageId,
                        fix: checker.createFix(currentArgToken, tokenBefore)
                    });
                }
            }
        }

        /**
         * Check if open space is present in a function name
         * @param {CallExpression} node node to evaluate
         * @returns {void}
         * @private
         */
        function check(node) {
            if (node.arguments.length < 2) {
                return;
            }

            const option = context.options[0] || "always";

            if (option === "never") {
                checkArguments(node, checkers.unexpected);
            } else if (option === "always") {
                checkArguments(node, checkers.missing);
            } else if (option === "consistent") {
                const firstArgToken = sourceCode.getLastToken(node.arguments[0]);
                const secondArgToken = sourceCode.getFirstToken(node.arguments[1]);

                if (firstArgToken.loc.end.line === secondArgToken.loc.start.line) {
                    checkArguments(node, checkers.unexpected);
                } else {
                    checkArguments(node, checkers.missing);
                }
            }
        }

        return {
            CallExpression: check,
            NewExpression: check
        };
    }
};