1/** 2 * @fileoverview Rule to enforce line breaks between arguments of a function call 3 * @author Alexey Gonchar <https://github.com/finico> 4 */ 5 6"use strict"; 7 8//------------------------------------------------------------------------------ 9// Rule Definition 10//------------------------------------------------------------------------------ 11 12module.exports = { 13 meta: { 14 type: "layout", 15 16 docs: { 17 description: "enforce line breaks between arguments of a function call", 18 category: "Stylistic Issues", 19 recommended: false, 20 url: "https://eslint.org/docs/rules/function-call-argument-newline" 21 }, 22 23 fixable: "whitespace", 24 25 schema: [ 26 { 27 enum: ["always", "never", "consistent"] 28 } 29 ], 30 31 messages: { 32 unexpectedLineBreak: "There should be no line break here.", 33 missingLineBreak: "There should be a line break after this argument." 34 } 35 }, 36 37 create(context) { 38 const sourceCode = context.getSourceCode(); 39 40 const checkers = { 41 unexpected: { 42 messageId: "unexpectedLineBreak", 43 check: (prevToken, currentToken) => prevToken.loc.end.line !== currentToken.loc.start.line, 44 createFix: (token, tokenBefore) => fixer => 45 fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], " ") 46 }, 47 missing: { 48 messageId: "missingLineBreak", 49 check: (prevToken, currentToken) => prevToken.loc.end.line === currentToken.loc.start.line, 50 createFix: (token, tokenBefore) => fixer => 51 fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], "\n") 52 } 53 }; 54 55 /** 56 * Check all arguments for line breaks in the CallExpression 57 * @param {CallExpression} node node to evaluate 58 * @param {{ messageId: string, check: Function }} checker selected checker 59 * @returns {void} 60 * @private 61 */ 62 function checkArguments(node, checker) { 63 for (let i = 1; i < node.arguments.length; i++) { 64 const prevArgToken = sourceCode.getLastToken(node.arguments[i - 1]); 65 const currentArgToken = sourceCode.getFirstToken(node.arguments[i]); 66 67 if (checker.check(prevArgToken, currentArgToken)) { 68 const tokenBefore = sourceCode.getTokenBefore( 69 currentArgToken, 70 { includeComments: true } 71 ); 72 73 const hasLineCommentBefore = tokenBefore.type === "Line"; 74 75 context.report({ 76 node, 77 loc: { 78 start: tokenBefore.loc.end, 79 end: currentArgToken.loc.start 80 }, 81 messageId: checker.messageId, 82 fix: hasLineCommentBefore ? null : checker.createFix(currentArgToken, tokenBefore) 83 }); 84 } 85 } 86 } 87 88 /** 89 * Check if open space is present in a function name 90 * @param {CallExpression} node node to evaluate 91 * @returns {void} 92 * @private 93 */ 94 function check(node) { 95 if (node.arguments.length < 2) { 96 return; 97 } 98 99 const option = context.options[0] || "always"; 100 101 if (option === "never") { 102 checkArguments(node, checkers.unexpected); 103 } else if (option === "always") { 104 checkArguments(node, checkers.missing); 105 } else if (option === "consistent") { 106 const firstArgToken = sourceCode.getLastToken(node.arguments[0]); 107 const secondArgToken = sourceCode.getFirstToken(node.arguments[1]); 108 109 if (firstArgToken.loc.end.line === secondArgToken.loc.start.line) { 110 checkArguments(node, checkers.unexpected); 111 } else { 112 checkArguments(node, checkers.missing); 113 } 114 } 115 } 116 117 return { 118 CallExpression: check, 119 NewExpression: check 120 }; 121 } 122}; 123