-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
/
statement.js
151 lines (129 loc) · 3.79 KB
/
statement.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
"use strict";
const { isNextLineEmpty } = require("../../common/util");
const {
builders: { concat, join, hardline },
} = require("../../document");
const pathNeedsParens = require("../needs-parens");
const {
classChildNeedsASIProtection,
classPropMayCauseASIProblems,
getLeftSidePathName,
hasNakedLeftSide,
isJSXNode,
isLastStatement,
isTheOnlyJSXElementInMarkdown,
} = require("../utils");
const { locEnd } = require("../loc");
const { shouldPrintParamsWithoutParens } = require("./function");
/** @typedef {import("../../document").Doc} Doc */
function printStatement({ path, index, bodyNode, isClass }, options, print) {
const node = path.getValue();
// Just in case the AST has been modified to contain falsy
// "statements," it's safer simply to skip them.
/* istanbul ignore if */
if (!node) {
return;
}
// Skip printing EmptyStatement nodes to avoid leaving stray
// semicolons lying around.
if (node.type === "EmptyStatement") {
return;
}
const printed = print(path);
const text = options.originalText;
const parts = [];
// in no-semi mode, prepend statement with semicolon if it might break ASI
// don't prepend the only JSX element in a program with semicolon
if (
!options.semi &&
!isClass &&
!isTheOnlyJSXElementInMarkdown(options, path) &&
statementNeedsASIProtection(path, options)
) {
if (node.comments && node.comments.some((comment) => comment.leading)) {
parts.push(print(path, { needsSemi: true }));
} else {
parts.push(";", printed);
}
} else {
parts.push(printed);
}
if (!options.semi && isClass) {
if (classPropMayCauseASIProblems(path)) {
parts.push(";");
} else if (
node.type === "ClassProperty" ||
node.type === "FieldDefinition"
) {
const nextChild = bodyNode.body[index + 1];
if (classChildNeedsASIProtection(nextChild)) {
parts.push(";");
}
}
}
if (isNextLineEmpty(text, node, locEnd) && !isLastStatement(path)) {
parts.push(hardline);
}
return concat(parts);
}
function printStatementSequence(path, options, print) {
const bodyNode = path.getNode();
const isClass = bodyNode.type === "ClassBody";
const printed = path
.map((statementPath, index) =>
printStatement(
{
path,
index,
bodyNode,
isClass,
},
options,
print
)
)
.filter(Boolean);
return join(hardline, printed);
}
function statementNeedsASIProtection(path, options) {
const node = path.getNode();
if (node.type !== "ExpressionStatement") {
return false;
}
return path.call(
(childPath) => expressionNeedsASIProtection(childPath, options),
"expression"
);
}
function expressionNeedsASIProtection(path, options) {
const node = path.getValue();
const maybeASIProblem =
pathNeedsParens(path, options) ||
node.type === "ParenthesizedExpression" ||
node.type === "TypeCastExpression" ||
(node.type === "ArrowFunctionExpression" &&
!shouldPrintParamsWithoutParens(path, options)) ||
node.type === "ArrayExpression" ||
node.type === "ArrayPattern" ||
(node.type === "UnaryExpression" &&
node.prefix &&
(node.operator === "+" || node.operator === "-")) ||
node.type === "TemplateLiteral" ||
node.type === "TemplateElement" ||
isJSXNode(node) ||
(node.type === "BindExpression" && !node.object) ||
node.type === "RegExpLiteral" ||
(node.type === "Literal" && node.pattern) ||
(node.type === "Literal" && node.regex);
if (maybeASIProblem) {
return true;
}
if (!hasNakedLeftSide(node)) {
return false;
}
return path.call(
(childPath) => expressionNeedsASIProtection(childPath, options),
...getLeftSidePathName(path, node)
);
}
module.exports = { printStatementSequence };