UNPKG

3.67 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("../ast-utils");
12
13//------------------------------------------------------------------------------
14// Rule Definition
15//------------------------------------------------------------------------------
16
17module.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 = /^[gimuy]+$/;
37
38 const sourceCode = context.getSourceCode();
39
40 /**
41 * Check to see if there is a newline between the node and the following open bracket
42 * line's expression
43 * @param {ASTNode} node The node to check.
44 * @param {string} msg The error message to use.
45 * @returns {void}
46 * @private
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 // Public API
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};