]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/newline-per-chained-call.js
import 8.3.0 source
[pve-eslint.git] / eslint / lib / rules / newline-per-chained-call.js
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
9 const astUtils = require("./utils/ast-utils");
10
11 //------------------------------------------------------------------------------
12 // Rule Definition
13 //------------------------------------------------------------------------------
14
15 module.exports = {
16 meta: {
17 type: "layout",
18
19 docs: {
20 description: "require a newline after each call in a method chain",
21 recommended: false,
22 url: "https://eslint.org/docs/rules/newline-per-chained-call"
23 },
24
25 fixable: "whitespace",
26
27 schema: [{
28 type: "object",
29 properties: {
30 ignoreChainWithDepth: {
31 type: "integer",
32 minimum: 1,
33 maximum: 10,
34 default: 2
35 }
36 },
37 additionalProperties: false
38 }],
39 messages: {
40 expected: "Expected line break before `{{callee}}`."
41 }
42 },
43
44 create(context) {
45
46 const options = context.options[0] || {},
47 ignoreChainWithDepth = options.ignoreChainWithDepth || 2;
48
49 const sourceCode = context.getSourceCode();
50
51 /**
52 * Get the prefix of a given MemberExpression node.
53 * If the MemberExpression node is a computed value it returns a
54 * left bracket. If not it returns a period.
55 * @param {ASTNode} node A MemberExpression node to get
56 * @returns {string} The prefix of the node.
57 */
58 function getPrefix(node) {
59 if (node.computed) {
60 if (node.optional) {
61 return "?.[";
62 }
63 return "[";
64 }
65 if (node.optional) {
66 return "?.";
67 }
68 return ".";
69 }
70
71 /**
72 * Gets the property text of a given MemberExpression node.
73 * If the text is multiline, this returns only the first line.
74 * @param {ASTNode} node A MemberExpression node to get.
75 * @returns {string} The property text of the node.
76 */
77 function getPropertyText(node) {
78 const prefix = getPrefix(node);
79 const lines = sourceCode.getText(node.property).split(astUtils.LINEBREAK_MATCHER);
80 const suffix = node.computed && lines.length === 1 ? "]" : "";
81
82 return prefix + lines[0] + suffix;
83 }
84
85 return {
86 "CallExpression:exit"(node) {
87 const callee = astUtils.skipChainExpression(node.callee);
88
89 if (callee.type !== "MemberExpression") {
90 return;
91 }
92
93 let parent = astUtils.skipChainExpression(callee.object);
94 let depth = 1;
95
96 while (parent && parent.callee) {
97 depth += 1;
98 parent = astUtils.skipChainExpression(astUtils.skipChainExpression(parent.callee).object);
99 }
100
101 if (depth > ignoreChainWithDepth && astUtils.isTokenOnSameLine(callee.object, callee.property)) {
102 const firstTokenAfterObject = sourceCode.getTokenAfter(callee.object, astUtils.isNotClosingParenToken);
103
104 context.report({
105 node: callee.property,
106 loc: {
107 start: firstTokenAfterObject.loc.start,
108 end: callee.loc.end
109 },
110 messageId: "expected",
111 data: {
112 callee: getPropertyText(callee)
113 },
114 fix(fixer) {
115 return fixer.insertTextBefore(firstTokenAfterObject, "\n");
116 }
117 });
118 }
119 }
120 };
121 }
122 };