UNPKG

3.51 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to ensure newline per method call when chaining calls
3 * @author Rajendra Patil
4 * @author Burak Yigit Kaya
5 */
6
7"use strict";
8
9const astUtils = require("../ast-utils");
10
11//------------------------------------------------------------------------------
12// Rule Definition
13//------------------------------------------------------------------------------
14
15module.exports = {
16 meta: {
17 docs: {
18 description: "require a newline after each call in a method chain",
19 category: "Stylistic Issues",
20 recommended: false,
21 url: "https://eslint.org/docs/rules/newline-per-chained-call"
22 },
23 fixable: "whitespace",
24 schema: [{
25 type: "object",
26 properties: {
27 ignoreChainWithDepth: {
28 type: "integer",
29 minimum: 1,
30 maximum: 10
31 }
32 },
33 additionalProperties: false
34 }]
35 },
36
37 create(context) {
38
39 const options = context.options[0] || {},
40 ignoreChainWithDepth = options.ignoreChainWithDepth || 2;
41
42 const sourceCode = context.getSourceCode();
43
44 /**
45 * Get the prefix of a given MemberExpression node.
46 * If the MemberExpression node is a computed value it returns a
47 * left bracket. If not it returns a period.
48 *
49 * @param {ASTNode} node - A MemberExpression node to get
50 * @returns {string} The prefix of the node.
51 */
52 function getPrefix(node) {
53 return node.computed ? "[" : ".";
54 }
55
56 /**
57 * Gets the property text of a given MemberExpression node.
58 * If the text is multiline, this returns only the first line.
59 *
60 * @param {ASTNode} node - A MemberExpression node to get.
61 * @returns {string} The property text of the node.
62 */
63 function getPropertyText(node) {
64 const prefix = getPrefix(node);
65 const lines = sourceCode.getText(node.property).split(astUtils.LINEBREAK_MATCHER);
66 const suffix = node.computed && lines.length === 1 ? "]" : "";
67
68 return prefix + lines[0] + suffix;
69 }
70
71 return {
72 "CallExpression:exit"(node) {
73 if (!node.callee || node.callee.type !== "MemberExpression") {
74 return;
75 }
76
77 const callee = node.callee;
78 let parent = callee.object;
79 let depth = 1;
80
81 while (parent && parent.callee) {
82 depth += 1;
83 parent = parent.callee.object;
84 }
85
86 if (depth > ignoreChainWithDepth && astUtils.isTokenOnSameLine(callee.object, callee.property)) {
87 context.report({
88 node: callee.property,
89 loc: callee.property.loc.start,
90 message: "Expected line break before `{{callee}}`.",
91 data: {
92 callee: getPropertyText(callee)
93 },
94 fix(fixer) {
95 const firstTokenAfterObject = sourceCode.getTokenAfter(callee.object, astUtils.isNotClosingParenToken);
96
97 return fixer.insertTextBefore(firstTokenAfterObject, "\n");
98 }
99 });
100 }
101 }
102 };
103 }
104};