1 |
|
2 |
|
3 |
|
4 |
|
5 | "use strict";
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | const astUtils = require("../ast-utils");
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | module.exports = {
|
18 | meta: {
|
19 | docs: {
|
20 | description: "disallow confusing multiline expressions",
|
21 | category: "Possible Errors",
|
22 | recommended: true,
|
23 | url: "https://eslint.org/docs/rules/no-unexpected-multiline"
|
24 | },
|
25 |
|
26 | schema: []
|
27 | },
|
28 |
|
29 | create(context) {
|
30 |
|
31 | const FUNCTION_MESSAGE = "Unexpected newline between function and ( of function call.";
|
32 | const PROPERTY_MESSAGE = "Unexpected newline between object and [ of property access.";
|
33 | const TAGGED_TEMPLATE_MESSAGE = "Unexpected newline between template tag and template literal.";
|
34 | const DIVISION_MESSAGE = "Unexpected newline between numerator and division operator.";
|
35 |
|
36 | const REGEX_FLAG_MATCHER = /^[gimsuy]+$/;
|
37 |
|
38 | const sourceCode = context.getSourceCode();
|
39 |
|
40 | |
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | function checkForBreakAfter(node, msg) {
|
49 | const openParen = sourceCode.getTokenAfter(node, astUtils.isNotClosingParenToken);
|
50 | const nodeExpressionEnd = sourceCode.getTokenBefore(openParen);
|
51 |
|
52 | if (openParen.loc.start.line !== nodeExpressionEnd.loc.end.line) {
|
53 | context.report({ node, loc: openParen.loc.start, message: msg, data: { char: openParen.value } });
|
54 | }
|
55 | }
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 | return {
|
62 |
|
63 | MemberExpression(node) {
|
64 | if (!node.computed) {
|
65 | return;
|
66 | }
|
67 | checkForBreakAfter(node.object, PROPERTY_MESSAGE);
|
68 | },
|
69 |
|
70 | TaggedTemplateExpression(node) {
|
71 | if (node.tag.loc.end.line === node.quasi.loc.start.line) {
|
72 | return;
|
73 | }
|
74 | context.report({ node, loc: node.loc.start, message: TAGGED_TEMPLATE_MESSAGE });
|
75 | },
|
76 |
|
77 | CallExpression(node) {
|
78 | if (node.arguments.length === 0) {
|
79 | return;
|
80 | }
|
81 | checkForBreakAfter(node.callee, FUNCTION_MESSAGE);
|
82 | },
|
83 |
|
84 | "BinaryExpression[operator='/'] > BinaryExpression[operator='/'].left"(node) {
|
85 | const secondSlash = sourceCode.getTokenAfter(node, token => token.value === "/");
|
86 | const tokenAfterOperator = sourceCode.getTokenAfter(secondSlash);
|
87 |
|
88 | if (
|
89 | tokenAfterOperator.type === "Identifier" &&
|
90 | REGEX_FLAG_MATCHER.test(tokenAfterOperator.value) &&
|
91 | secondSlash.range[1] === tokenAfterOperator.range[0]
|
92 | ) {
|
93 | checkForBreakAfter(node.left, DIVISION_MESSAGE);
|
94 | }
|
95 | }
|
96 | };
|
97 |
|
98 | }
|
99 | };
|