UNPKG

3.68 kBJavaScriptView Raw
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
11const astUtils = require("./utils/ast-utils");
12
13//------------------------------------------------------------------------------
14// Rule Definition
15//------------------------------------------------------------------------------
16
17module.exports = {
18 meta: {
19 type: "problem",
20
21 docs: {
22 description: "disallow confusing multiline expressions",
23 category: "Possible Errors",
24 recommended: true,
25 url: "https://eslint.org/docs/rules/no-unexpected-multiline"
26 },
27
28 schema: [],
29 messages: {
30 function: "Unexpected newline between function and ( of function call.",
31 property: "Unexpected newline between object and [ of property access.",
32 taggedTemplate: "Unexpected newline between template tag and template literal.",
33 division: "Unexpected newline between numerator and division operator."
34 }
35 },
36
37 create(context) {
38
39 const REGEX_FLAG_MATCHER = /^[gimsuy]+$/u;
40
41 const sourceCode = context.getSourceCode();
42
43 /**
44 * Check to see if there is a newline between the node and the following open bracket
45 * line's expression
46 * @param {ASTNode} node The node to check.
47 * @param {string} messageId The error messageId to use.
48 * @returns {void}
49 * @private
50 */
51 function checkForBreakAfter(node, messageId) {
52 const openParen = sourceCode.getTokenAfter(node, astUtils.isNotClosingParenToken);
53 const nodeExpressionEnd = sourceCode.getTokenBefore(openParen);
54
55 if (openParen.loc.start.line !== nodeExpressionEnd.loc.end.line) {
56 context.report({ node, loc: openParen.loc.start, messageId, data: { char: openParen.value } });
57 }
58 }
59
60 //--------------------------------------------------------------------------
61 // Public API
62 //--------------------------------------------------------------------------
63
64 return {
65
66 MemberExpression(node) {
67 if (!node.computed) {
68 return;
69 }
70 checkForBreakAfter(node.object, "property");
71 },
72
73 TaggedTemplateExpression(node) {
74 if (node.tag.loc.end.line === node.quasi.loc.start.line) {
75 return;
76 }
77 context.report({ node, loc: node.loc.start, messageId: "taggedTemplate" });
78 },
79
80 CallExpression(node) {
81 if (node.arguments.length === 0) {
82 return;
83 }
84 checkForBreakAfter(node.callee, "function");
85 },
86
87 "BinaryExpression[operator='/'] > BinaryExpression[operator='/'].left"(node) {
88 const secondSlash = sourceCode.getTokenAfter(node, token => token.value === "/");
89 const tokenAfterOperator = sourceCode.getTokenAfter(secondSlash);
90
91 if (
92 tokenAfterOperator.type === "Identifier" &&
93 REGEX_FLAG_MATCHER.test(tokenAfterOperator.value) &&
94 secondSlash.range[1] === tokenAfterOperator.range[0]
95 ) {
96 checkForBreakAfter(node.left, "division");
97 }
98 }
99 };
100
101 }
102};