99// Requirements
1010//------------------------------------------------------------------------------
1111
12- const { getVariableByName, isArrowToken, isClosingBraceToken, isClosingParenToken } = require ( "./utils/ast-utils" ) ;
13-
14- //------------------------------------------------------------------------------
15- // Helpers
16- //------------------------------------------------------------------------------
17-
18- const BREAK_OR_CONTINUE = new Set ( [ "BreakStatement" , "ContinueStatement" ] ) ;
19-
20- // Declaration types that must contain a string Literal node at the end.
21- const DECLARATIONS = new Set ( [ "ExportAllDeclaration" , "ExportNamedDeclaration" , "ImportDeclaration" ] ) ;
22-
23- const IDENTIFIER_OR_KEYWORD = new Set ( [ "Identifier" , "Keyword" ] ) ;
24-
25- // Keywords that can immediately precede an ExpressionStatement node, mapped to the their node types.
26- const NODE_TYPES_BY_KEYWORD = {
27- __proto__ : null ,
28- break : "BreakStatement" ,
29- continue : "ContinueStatement" ,
30- debugger : "DebuggerStatement" ,
31- do : "DoWhileStatement" ,
32- else : "IfStatement" ,
33- return : "ReturnStatement" ,
34- yield : "YieldExpression"
35- } ;
36-
37- /*
38- * Before an opening parenthesis, postfix `++` and `--` always trigger ASI;
39- * the tokens `:`, `;`, `{` and `=>` don't expect a semicolon, as that would count as an empty statement.
40- */
41- const PUNCTUATORS = new Set ( [ ":" , ";" , "{" , "=>" , "++" , "--" ] ) ;
42-
43- /*
44- * Statements that can contain an `ExpressionStatement` after a closing parenthesis.
45- * DoWhileStatement is an exception in that it always triggers ASI after the closing parenthesis.
46- */
47- const STATEMENTS = new Set ( [
48- "DoWhileStatement" ,
49- "ForInStatement" ,
50- "ForOfStatement" ,
51- "ForStatement" ,
52- "IfStatement" ,
53- "WhileStatement" ,
54- "WithStatement"
55- ] ) ;
56-
57- /**
58- * Tests if a node appears at the beginning of an ancestor ExpressionStatement node.
59- * @param {ASTNode } node The node to check.
60- * @returns {boolean } Whether the node appears at the beginning of an ancestor ExpressionStatement node.
61- */
62- function isStartOfExpressionStatement ( node ) {
63- const start = node . range [ 0 ] ;
64- let ancestor = node ;
65-
66- while ( ( ancestor = ancestor . parent ) && ancestor . range [ 0 ] === start ) {
67- if ( ancestor . type === "ExpressionStatement" ) {
68- return true ;
69- }
70- }
71- return false ;
72- }
12+ const {
13+ getVariableByName,
14+ isArrowToken,
15+ isStartOfExpressionStatement,
16+ needsPrecedingSemicolon
17+ } = require ( "./utils/ast-utils" ) ;
7318
7419//------------------------------------------------------------------------------
7520// Rule Definition
@@ -120,50 +65,6 @@ module.exports = {
12065 return false ;
12166 }
12267
123- /**
124- * Determines whether a parenthesized object literal that replaces a specified node needs to be preceded by a semicolon.
125- * @param {ASTNode } node The node to be replaced. This node should be at the start of an `ExpressionStatement` or at the start of the body of an `ArrowFunctionExpression`.
126- * @returns {boolean } Whether a semicolon is required before the parenthesized object literal.
127- */
128- function needsSemicolon ( node ) {
129- const prevToken = sourceCode . getTokenBefore ( node ) ;
130-
131- if ( ! prevToken || prevToken . type === "Punctuator" && PUNCTUATORS . has ( prevToken . value ) ) {
132- return false ;
133- }
134-
135- const prevNode = sourceCode . getNodeByRangeIndex ( prevToken . range [ 0 ] ) ;
136-
137- if ( isClosingParenToken ( prevToken ) ) {
138- return ! STATEMENTS . has ( prevNode . type ) ;
139- }
140-
141- if ( isClosingBraceToken ( prevToken ) ) {
142- return (
143- prevNode . type === "BlockStatement" && prevNode . parent . type === "FunctionExpression" ||
144- prevNode . type === "ClassBody" && prevNode . parent . type === "ClassExpression" ||
145- prevNode . type === "ObjectExpression"
146- ) ;
147- }
148-
149- if ( IDENTIFIER_OR_KEYWORD . has ( prevToken . type ) ) {
150- if ( BREAK_OR_CONTINUE . has ( prevNode . parent . type ) ) {
151- return false ;
152- }
153-
154- const keyword = prevToken . value ;
155- const nodeType = NODE_TYPES_BY_KEYWORD [ keyword ] ;
156-
157- return prevNode . type !== nodeType ;
158- }
159-
160- if ( prevToken . type === "String" ) {
161- return ! DECLARATIONS . has ( prevNode . parent . type ) ;
162- }
163-
164- return true ;
165- }
166-
16768 /**
16869 * Reports on nodes where the `Object` constructor is called without arguments.
16970 * @param {ASTNode } node The node to evaluate.
@@ -183,7 +84,7 @@ module.exports = {
18384
18485 if ( needsParentheses ( node ) ) {
18586 replacement = "({})" ;
186- if ( needsSemicolon ( node ) ) {
87+ if ( needsPrecedingSemicolon ( sourceCode , node ) ) {
18788 fixText = ";({})" ;
18889 messageId = "useLiteralAfterSemicolon" ;
18990 } else {
0 commit comments