]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-unexpected-multiline.js
4447959ed9a17636d08668fb40df6a107fcfe79c
[pve-eslint.git] / eslint / lib / rules / no-unexpected-multiline.js
1 /**
2 * @fileoverview Rule to spot scenarios where a newline looks like it is ending a statement, but is not.
3 * @author Glen Mailer
4 */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Requirements
9 //------------------------------------------------------------------------------
10
11 const astUtils = require("./utils/ast-utils");
12
13 //------------------------------------------------------------------------------
14 // Rule Definition
15 //------------------------------------------------------------------------------
16
17 module.exports = {
18 meta: {
19 type: "problem",
20
21 docs: {
22 description: "disallow confusing multiline expressions",
23 recommended: true,
24 url: "https://eslint.org/docs/rules/no-unexpected-multiline"
25 },
26
27 schema: [],
28 messages: {
29 function: "Unexpected newline between function and ( of function call.",
30 property: "Unexpected newline between object and [ of property access.",
31 taggedTemplate: "Unexpected newline between template tag and template literal.",
32 division: "Unexpected newline between numerator and division operator."
33 }
34 },
35
36 create(context) {
37
38 const REGEX_FLAG_MATCHER = /^[gimsuy]+$/u;
39
40 const sourceCode = context.getSourceCode();
41
42 /**
43 * Check to see if there is a newline between the node and the following open bracket
44 * line's expression
45 * @param {ASTNode} node The node to check.
46 * @param {string} messageId The error messageId to use.
47 * @returns {void}
48 * @private
49 */
50 function checkForBreakAfter(node, messageId) {
51 const openParen = sourceCode.getTokenAfter(node, astUtils.isNotClosingParenToken);
52 const nodeExpressionEnd = sourceCode.getTokenBefore(openParen);
53
54 if (openParen.loc.start.line !== nodeExpressionEnd.loc.end.line) {
55 context.report({
56 node,
57 loc: openParen.loc,
58 messageId
59 });
60 }
61 }
62
63 //--------------------------------------------------------------------------
64 // Public API
65 //--------------------------------------------------------------------------
66
67 return {
68
69 MemberExpression(node) {
70 if (!node.computed || node.optional) {
71 return;
72 }
73 checkForBreakAfter(node.object, "property");
74 },
75
76 TaggedTemplateExpression(node) {
77 const { quasi } = node;
78
79 // handles common tags, parenthesized tags, and typescript's generic type arguments
80 const tokenBefore = sourceCode.getTokenBefore(quasi);
81
82 if (tokenBefore.loc.end.line !== quasi.loc.start.line) {
83 context.report({
84 node,
85 loc: {
86 start: quasi.loc.start,
87 end: {
88 line: quasi.loc.start.line,
89 column: quasi.loc.start.column + 1
90 }
91 },
92 messageId: "taggedTemplate"
93 });
94 }
95 },
96
97 CallExpression(node) {
98 if (node.arguments.length === 0 || node.optional) {
99 return;
100 }
101 checkForBreakAfter(node.callee, "function");
102 },
103
104 "BinaryExpression[operator='/'] > BinaryExpression[operator='/'].left"(node) {
105 const secondSlash = sourceCode.getTokenAfter(node, token => token.value === "/");
106 const tokenAfterOperator = sourceCode.getTokenAfter(secondSlash);
107
108 if (
109 tokenAfterOperator.type === "Identifier" &&
110 REGEX_FLAG_MATCHER.test(tokenAfterOperator.value) &&
111 secondSlash.range[1] === tokenAfterOperator.range[0]
112 ) {
113 checkForBreakAfter(node.left, "division");
114 }
115 }
116 };
117
118 }
119 };