Refactor leading semicolon print (#14313)

This commit is contained in:
fisker Cheung authored and GitHub committed 2023-02-13 10:31:52 +08:00
1 parent 222d84f9ed
commit de00f5bec0
3 files changed
+100 -96

No files matched your search

+89
View File
@@ -0,0 +1,89 @@
import pathNeedsParens from "../needs-parens.js";
import {
getLeftSidePathName,
hasNakedLeftSide,
isJsxElement,
isTheOnlyJsxElementInMarkdown,
} from "../utils/index.js";
import { shouldPrintParamsWithoutParens } from "./function.js";
function shouldPrintLeadingSemicolon(path, options) {
if (options.semi || isTheOnlyJsxElementInMarkdown(options, path)) {
return false;
}
const { node, key, parent } = path;
if (
node.type === "ExpressionStatement" &&
// `Program.directives` don't need leading semicolon
((key === "body" &&
(parent.type === "Program" ||
parent.type === "BlockStatement" ||
parent.type === "StaticBlock" ||
parent.type === "TSModuleBlock")) ||
(key === "consequent" && parent.type === "SwitchCase")) &&
path.call(() => expressionNeedsASIProtection(path, options), "expression")
) {
return true;
}
return false;
}
function expressionNeedsASIProtection(path, options) {
const { node } = path;
switch (node.type) {
case "ParenthesizedExpression":
case "TypeCastExpression":
case "ArrayExpression":
case "ArrayPattern":
case "TemplateLiteral":
case "TemplateElement":
case "RegExpLiteral":
return true;
case "ArrowFunctionExpression":
if (!shouldPrintParamsWithoutParens(path, options)) {
return true;
}
break;
case "UnaryExpression": {
const { prefix, operator } = node;
if (prefix && (operator === "+" || operator === "-")) {
return true;
}
break;
}
case "BindExpression":
if (!node.object) {
return true;
}
break;
case "Literal":
if (node.regex) {
return true;
}
break;
default:
if (isJsxElement(node)) {
return true;
}
}
if (pathNeedsParens(path, options)) {
return true;
}
if (!hasNakedLeftSide(node)) {
return false;
}
return path.call(
() => expressionNeedsASIProtection(path, options),
...getLeftSidePathName(node)
);
}
export { shouldPrintLeadingSemicolon };
+9 -95
View File
@@ -1,21 +1,18 @@
import { hardline } from "../../document/builders.js";
import pathNeedsParens from "../needs-parens.js";
import {
getLeftSidePathName,
hasNakedLeftSide,
isJsxElement,
isTheOnlyJsxElementInMarkdown,
hasComment,
CommentCheckFlags,
isNextLineEmpty,
} from "../utils/index.js";
import { shouldPrintParamsWithoutParens } from "./function.js";
import { isNextLineEmpty } from "../utils/index.js";
/**
* @typedef {import("../../document/builders.js").Doc} Doc
* @typedef {import("../../common/ast-path.js")} AstPath
*/
/*
- `Program` ("directives" and "body")
- `BlockStatement`
- `StaticBlock`
- `SwitchCase` ("consequent")
- `TSModuleBlock` (TypeScript)
*/
function printStatementSequence(path, options, print, property) {
const { node } = path;
const parts = [];
@@ -28,21 +25,7 @@ function printStatementSequence(path, options, print, property) {
return;
}
const needsSemi =
!options.semi &&
!isTheOnlyJsxElementInMarkdown(options, path) &&
statementNeedsASIProtection(path, options);
// 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 (needsSemi && hasComment(node, CommentCheckFlags.Leading)) {
parts.push(print([], { needsSemi: true }));
} else {
if (needsSemi) {
parts.push(";");
}
parts.push(print());
}
parts.push(print());
if (node !== lastStatement) {
parts.push(hardline);
@@ -65,73 +48,4 @@ function getLastStatement(statements) {
}
}
function statementNeedsASIProtection(path, options) {
const { node } = path;
if (node.type !== "ExpressionStatement") {
return false;
}
return path.call(
(childPath) => expressionNeedsASIProtection(childPath, options),
"expression"
);
}
function expressionNeedsASIProtection(path, options) {
const { node } = path;
switch (node.type) {
case "ParenthesizedExpression":
case "TypeCastExpression":
case "ArrayExpression":
case "ArrayPattern":
case "TemplateLiteral":
case "TemplateElement":
case "RegExpLiteral":
return true;
case "ArrowFunctionExpression":
if (!shouldPrintParamsWithoutParens(path, options)) {
return true;
}
break;
case "UnaryExpression": {
const { prefix, operator } = node;
if (prefix && (operator === "+" || operator === "-")) {
return true;
}
break;
}
case "BindExpression":
if (!node.object) {
return true;
}
break;
case "Literal":
if (node.regex) {
return true;
}
break;
default:
if (isJsxElement(node)) {
return true;
}
}
if (pathNeedsParens(path, options)) {
return true;
}
if (!hasNakedLeftSide(node)) {
return false;
}
return path.call(
() => expressionNeedsASIProtection(path, options),
...getLeftSidePathName(node)
);
}
export { printStatementSequence };
+2 -1
View File
@@ -88,6 +88,7 @@ import { printBlock, printBlockBody } from "./print/block.js";
import { printLiteral } from "./print/literal.js";
import { printDecorators } from "./print/decorators.js";
import { printTypeAnnotationProperty } from "./print/type-annotation.js";
import { shouldPrintLeadingSemicolon } from "./print/semicolon.js";
/**
* @typedef {import("../common/ast-path.js").default} AstPath
@@ -143,7 +144,7 @@ function genericPrint(path, options, print, args) {
}
const needsParens = pathNeedsParens(path, options);
const needsSemi = args?.needsSemi;
const needsSemi = shouldPrintLeadingSemicolon(path, options);
if (!needsParens) {
if (needsSemi) {