Enable no-semi mode and protect against ASI failures (#1129)
parent
e3aa878187
commit
36bec87d17
|
@ -241,7 +241,11 @@ prettier.format(source, {
|
||||||
jsxBracketSameLine: false,
|
jsxBracketSameLine: false,
|
||||||
|
|
||||||
// Which parser to use. Valid options are 'flow' and 'babylon'
|
// Which parser to use. Valid options are 'flow' and 'babylon'
|
||||||
parser: 'babylon'
|
parser: 'babylon',
|
||||||
|
|
||||||
|
// Whether to add a semicolon at the end of every line (semi: true),
|
||||||
|
// or only at the beginning of lines that may introduce ASI failures (semi: false)
|
||||||
|
semi: true
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@ var defaults = {
|
||||||
trailingComma: "none",
|
trailingComma: "none",
|
||||||
bracketSpacing: true,
|
bracketSpacing: true,
|
||||||
jsxBracketSameLine: false,
|
jsxBracketSameLine: false,
|
||||||
parser: "babylon"
|
parser: "babylon",
|
||||||
|
semi: true
|
||||||
};
|
};
|
||||||
|
|
||||||
var exampleConfig = Object.assign({}, defaults, {
|
var exampleConfig = Object.assign({}, defaults, {
|
||||||
|
|
230
src/printer.js
230
src/printer.js
|
@ -109,6 +109,14 @@ function genericPrint(path, options, printPath, args) {
|
||||||
needsParens = path.needsParens();
|
needsParens = path.needsParens();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node.type) {
|
||||||
|
// HACK: ASI prevention in no-semi mode relies on knowledge of whether
|
||||||
|
// or not a paren has been inserted (see `exprNeedsASIProtection()`).
|
||||||
|
// For now, we're just passing that information by mutating the AST here,
|
||||||
|
// but it would be nice to find a cleaner way to do this.
|
||||||
|
node.needsParens = needsParens;
|
||||||
|
}
|
||||||
|
|
||||||
if (needsParens) {
|
if (needsParens) {
|
||||||
parts.unshift("(");
|
parts.unshift("(");
|
||||||
}
|
}
|
||||||
|
@ -124,6 +132,9 @@ function genericPrint(path, options, printPath, args) {
|
||||||
|
|
||||||
function genericPrintNoParens(path, options, print, args) {
|
function genericPrintNoParens(path, options, print, args) {
|
||||||
var n = path.getValue();
|
var n = path.getValue();
|
||||||
|
const semi = options.semi
|
||||||
|
? ";"
|
||||||
|
: "";
|
||||||
|
|
||||||
if (!n) {
|
if (!n) {
|
||||||
return "";
|
return "";
|
||||||
|
@ -146,7 +157,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
if (n.directives) {
|
if (n.directives) {
|
||||||
path.each(
|
path.each(
|
||||||
function(childPath) {
|
function(childPath) {
|
||||||
parts.push(print(childPath), ";", hardline);
|
parts.push(print(childPath), semi, hardline);
|
||||||
if (
|
if (
|
||||||
util.isNextLineEmpty(options.originalText, childPath.getValue())
|
util.isNextLineEmpty(options.originalText, childPath.getValue())
|
||||||
) {
|
) {
|
||||||
|
@ -181,7 +192,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
case "EmptyStatement":
|
case "EmptyStatement":
|
||||||
return "";
|
return "";
|
||||||
case "ExpressionStatement":
|
case "ExpressionStatement":
|
||||||
return concat([path.call(print, "expression"), ";"]); // Babel extension.
|
return concat([path.call(print, "expression"), semi]); // Babel extension.
|
||||||
case "ParenthesizedExpression":
|
case "ParenthesizedExpression":
|
||||||
return concat(["(", path.call(print, "expression"), ")"]);
|
return concat(["(", path.call(print, "expression"), ")"]);
|
||||||
case "AssignmentExpression":
|
case "AssignmentExpression":
|
||||||
|
@ -295,17 +306,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
parts.push(path.call(print, "typeParameters"));
|
parts.push(path.call(print, "typeParameters"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (canPrintParamsWithoutParens(n)) {
|
||||||
n.params.length === 1 &&
|
|
||||||
!n.rest &&
|
|
||||||
n.params[0].type === "Identifier" &&
|
|
||||||
!n.params[0].typeAnnotation &&
|
|
||||||
!n.params[0].leadingComments &&
|
|
||||||
!n.params[0].trailingComments &&
|
|
||||||
!n.params[0].optional &&
|
|
||||||
!n.predicate &&
|
|
||||||
!n.returnType
|
|
||||||
) {
|
|
||||||
parts.push(path.call(print, "params", 0));
|
parts.push(path.call(print, "params", 0));
|
||||||
} else {
|
} else {
|
||||||
parts.push(
|
parts.push(
|
||||||
|
@ -461,7 +462,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
parts.push(" as ", path.call(print, "exported"));
|
parts.push(" as ", path.call(print, "exported"));
|
||||||
}
|
}
|
||||||
|
|
||||||
parts.push(" from ", path.call(print, "source"), ";");
|
parts.push(" from ", path.call(print, "source"), semi);
|
||||||
|
|
||||||
return concat(parts);
|
return concat(parts);
|
||||||
case "ExportNamespaceSpecifier":
|
case "ExportNamespaceSpecifier":
|
||||||
|
@ -526,7 +527,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
parts.push("{} from ");
|
parts.push("{} from ");
|
||||||
}
|
}
|
||||||
|
|
||||||
fromParts.push(path.call(print, "source"), ";");
|
fromParts.push(path.call(print, "source"), semi);
|
||||||
|
|
||||||
// If there's a very long import, break the following way:
|
// If there's a very long import, break the following way:
|
||||||
//
|
//
|
||||||
|
@ -581,7 +582,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
function(childPath) {
|
function(childPath) {
|
||||||
parts.push(
|
parts.push(
|
||||||
indent(
|
indent(
|
||||||
concat([hardline, print(childPath), ";"])
|
concat([hardline, print(childPath), semi])
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -647,7 +648,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
parts.push(";");
|
parts.push(semi);
|
||||||
|
|
||||||
return concat(parts);
|
return concat(parts);
|
||||||
case "CallExpression": {
|
case "CallExpression": {
|
||||||
|
@ -986,7 +987,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
namedTypes.ForAwaitStatement.check(parentNode));
|
namedTypes.ForAwaitStatement.check(parentNode));
|
||||||
|
|
||||||
if (!(isParentForLoop && parentNode.body !== n)) {
|
if (!(isParentForLoop && parentNode.body !== n)) {
|
||||||
parts.push(";");
|
parts.push(semi);
|
||||||
}
|
}
|
||||||
|
|
||||||
return group(concat(parts));
|
return group(concat(parts));
|
||||||
|
@ -1132,9 +1133,9 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
const hasBraces = isCurlyBracket(clause);
|
const hasBraces = isCurlyBracket(clause);
|
||||||
|
|
||||||
if (hasBraces) parts.push(" while");
|
if (hasBraces) parts.push(" while");
|
||||||
else parts.push(concat([line, "while"]));
|
else parts.push(concat([hardline, "while"]));
|
||||||
|
|
||||||
parts.push(" (", path.call(print, "test"), ");");
|
parts.push(" (", path.call(print, "test"), ")", semi);
|
||||||
|
|
||||||
return concat(parts);
|
return concat(parts);
|
||||||
case "DoExpression":
|
case "DoExpression":
|
||||||
|
@ -1144,7 +1145,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
|
|
||||||
if (n.label) parts.push(" ", path.call(print, "label"));
|
if (n.label) parts.push(" ", path.call(print, "label"));
|
||||||
|
|
||||||
parts.push(";");
|
parts.push(semi);
|
||||||
|
|
||||||
return concat(parts);
|
return concat(parts);
|
||||||
case "ContinueStatement":
|
case "ContinueStatement":
|
||||||
|
@ -1152,7 +1153,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
|
|
||||||
if (n.label) parts.push(" ", path.call(print, "label"));
|
if (n.label) parts.push(" ", path.call(print, "label"));
|
||||||
|
|
||||||
parts.push(";");
|
parts.push(semi);
|
||||||
|
|
||||||
return concat(parts);
|
return concat(parts);
|
||||||
case "LabeledStatement":
|
case "LabeledStatement":
|
||||||
|
@ -1195,7 +1196,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
|
|
||||||
return concat(parts);
|
return concat(parts);
|
||||||
case "ThrowStatement":
|
case "ThrowStatement":
|
||||||
return concat(["throw ", path.call(print, "argument"), ";"]);
|
return concat(["throw ", path.call(print, "argument"), semi]);
|
||||||
// Note: ignoring n.lexical because it has no printing consequences.
|
// Note: ignoring n.lexical because it has no printing consequences.
|
||||||
case "SwitchStatement":
|
case "SwitchStatement":
|
||||||
return concat([
|
return concat([
|
||||||
|
@ -1238,7 +1239,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
return concat(parts);
|
return concat(parts);
|
||||||
// JSX extensions below.
|
// JSX extensions below.
|
||||||
case "DebuggerStatement":
|
case "DebuggerStatement":
|
||||||
return "debugger;";
|
return concat(["debugger", semi]);
|
||||||
case "JSXAttribute":
|
case "JSXAttribute":
|
||||||
parts.push(path.call(print, "name"));
|
parts.push(path.call(print, "name"));
|
||||||
|
|
||||||
|
@ -1396,7 +1397,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
case "ClassPropertyDefinition":
|
case "ClassPropertyDefinition":
|
||||||
parts.push("static ", path.call(print, "definition"));
|
parts.push("static ", path.call(print, "definition"));
|
||||||
|
|
||||||
if (!namedTypes.MethodDefinition.check(n.definition)) parts.push(";");
|
if (!namedTypes.MethodDefinition.check(n.definition)) parts.push(semi);
|
||||||
|
|
||||||
return concat(parts);
|
return concat(parts);
|
||||||
case "ClassProperty":
|
case "ClassProperty":
|
||||||
|
@ -1428,7 +1429,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
|
|
||||||
if (n.value) parts.push(" = ", path.call(print, "value"));
|
if (n.value) parts.push(" = ", path.call(print, "value"));
|
||||||
|
|
||||||
parts.push(";");
|
parts.push(semi);
|
||||||
|
|
||||||
return concat(parts);
|
return concat(parts);
|
||||||
case "ClassDeclaration":
|
case "ClassDeclaration":
|
||||||
|
@ -1572,7 +1573,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
path.call(print, "id"),
|
path.call(print, "id"),
|
||||||
n.predicate ? " " : "",
|
n.predicate ? " " : "",
|
||||||
path.call(print, "predicate"),
|
path.call(print, "predicate"),
|
||||||
";"
|
semi
|
||||||
]);
|
]);
|
||||||
case "DeclareModule":
|
case "DeclareModule":
|
||||||
return printFlowDeclaration(path, [
|
return printFlowDeclaration(path, [
|
||||||
|
@ -1585,10 +1586,10 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
return printFlowDeclaration(path, [
|
return printFlowDeclaration(path, [
|
||||||
"module.exports",
|
"module.exports",
|
||||||
path.call(print, "typeAnnotation"),
|
path.call(print, "typeAnnotation"),
|
||||||
";"
|
semi
|
||||||
]);
|
]);
|
||||||
case "DeclareVariable":
|
case "DeclareVariable":
|
||||||
return printFlowDeclaration(path, ["var ", path.call(print, "id"), ";"]);
|
return printFlowDeclaration(path, ["var ", path.call(print, "id"), semi]);
|
||||||
case "DeclareExportAllDeclaration":
|
case "DeclareExportAllDeclaration":
|
||||||
return concat(["declare export * from ", path.call(print, "source")]);
|
return concat(["declare export * from ", path.call(print, "source")]);
|
||||||
case "DeclareExportDeclaration":
|
case "DeclareExportDeclaration":
|
||||||
|
@ -1828,7 +1829,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
concat([hardline, path.call(print, "right")])
|
concat([hardline, path.call(print, "right")])
|
||||||
)
|
)
|
||||||
: concat([" ", path.call(print, "right")]),
|
: concat([" ", path.call(print, "right")]),
|
||||||
";"
|
semi
|
||||||
);
|
);
|
||||||
|
|
||||||
return concat(parts);
|
return concat(parts);
|
||||||
|
@ -1998,7 +1999,10 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
function printStatementSequence(path, options, print) {
|
function printStatementSequence(path, options, print) {
|
||||||
let printed = [];
|
let printed = [];
|
||||||
|
|
||||||
path.map(stmtPath => {
|
const bodyNode = path.getNode();
|
||||||
|
const isClass = bodyNode.type === "ClassBody";
|
||||||
|
|
||||||
|
path.map((stmtPath, i) => {
|
||||||
var stmt = stmtPath.getValue();
|
var stmt = stmtPath.getValue();
|
||||||
|
|
||||||
// Just in case the AST has been modified to contain falsy
|
// Just in case the AST has been modified to contain falsy
|
||||||
|
@ -2017,8 +2021,24 @@ function printStatementSequence(path, options, print) {
|
||||||
const text = options.originalText;
|
const text = options.originalText;
|
||||||
const parts = [];
|
const parts = [];
|
||||||
|
|
||||||
|
// in no-semi mode, prepend statement with semicolon if it might break ASI
|
||||||
|
if (!options.semi && !isClass && stmtNeedsASIProtection(stmtPath)) {
|
||||||
|
parts.push(";");
|
||||||
|
}
|
||||||
|
|
||||||
parts.push(stmtPrinted);
|
parts.push(stmtPrinted);
|
||||||
|
|
||||||
|
if (!options.semi && isClass) {
|
||||||
|
if (classPropMayCauseASIProblems(stmtPath)) {
|
||||||
|
parts.push(";");
|
||||||
|
} else if (stmt.type === "ClassProperty") {
|
||||||
|
const nextChild = bodyNode.body[i + 1];
|
||||||
|
if (classChildNeedsASIProtection(nextChild)) {
|
||||||
|
parts.push(";");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (util.isNextLineEmpty(text, stmt) && !isLastStatement(stmtPath)) {
|
if (util.isNextLineEmpty(text, stmt) && !isLastStatement(stmtPath)) {
|
||||||
parts.push(hardline);
|
parts.push(hardline);
|
||||||
}
|
}
|
||||||
|
@ -2324,6 +2344,20 @@ function printFunctionParams(path, print, options) {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canPrintParamsWithoutParens(node) {
|
||||||
|
return (
|
||||||
|
node.params.length === 1 &&
|
||||||
|
!node.rest &&
|
||||||
|
node.params[0].type === "Identifier" &&
|
||||||
|
!node.params[0].typeAnnotation &&
|
||||||
|
!node.params[0].leadingComments &&
|
||||||
|
!node.params[0].trailingComments &&
|
||||||
|
!node.params[0].optional &&
|
||||||
|
!node.predicate &&
|
||||||
|
!node.returnType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function printFunctionDeclaration(path, print, options) {
|
function printFunctionDeclaration(path, print, options) {
|
||||||
var n = path.getValue();
|
var n = path.getValue();
|
||||||
var parts = [];
|
var parts = [];
|
||||||
|
@ -2414,6 +2448,7 @@ function typeIsFunction(type) {
|
||||||
|
|
||||||
function printExportDeclaration(path, options, print) {
|
function printExportDeclaration(path, options, print) {
|
||||||
const decl = path.getValue();
|
const decl = path.getValue();
|
||||||
|
const semi = options.semi ? ";" : "";
|
||||||
let parts = ["export "];
|
let parts = ["export "];
|
||||||
|
|
||||||
namedTypes.Declaration.assert(decl);
|
namedTypes.Declaration.assert(decl);
|
||||||
|
@ -2434,7 +2469,7 @@ function printExportDeclaration(path, options, print) {
|
||||||
(decl.declaration.type !== "ClassDeclaration" &&
|
(decl.declaration.type !== "ClassDeclaration" &&
|
||||||
decl.declaration.type !== "FunctionDeclaration")
|
decl.declaration.type !== "FunctionDeclaration")
|
||||||
) {
|
) {
|
||||||
parts.push(";");
|
parts.push(semi);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (decl.specifiers && decl.specifiers.length > 0) {
|
if (decl.specifiers && decl.specifiers.length > 0) {
|
||||||
|
@ -2496,7 +2531,7 @@ function printExportDeclaration(path, options, print) {
|
||||||
parts.push(" from ", path.call(print, "source"));
|
parts.push(" from ", path.call(print, "source"));
|
||||||
}
|
}
|
||||||
|
|
||||||
parts.push(";");
|
parts.push(semi);
|
||||||
}
|
}
|
||||||
|
|
||||||
return concat(parts);
|
return concat(parts);
|
||||||
|
@ -3346,6 +3381,118 @@ function hasLeadingOwnLineComment(text, node) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasNakedLeftSide(node) {
|
||||||
|
return (
|
||||||
|
node.type === "AssignmentExpression" ||
|
||||||
|
node.type === "BinaryExpression" ||
|
||||||
|
node.type === "LogicalExpression" ||
|
||||||
|
node.type === "ConditionalExpression" ||
|
||||||
|
node.type === "CallExpression" ||
|
||||||
|
node.type === "MemberExpression" ||
|
||||||
|
node.type === "SequenceExpression" ||
|
||||||
|
node.type === "TaggedTemplateExpression"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLeftSide(node) {
|
||||||
|
if (node.expressions) {
|
||||||
|
return node.expressions[0];
|
||||||
|
}
|
||||||
|
return node.left || node.test || node.callee || node.object || node.tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
function exprNeedsASIProtection(node) {
|
||||||
|
// HACK: node.needsParens is added in `genericPrint()` for the sole purpose
|
||||||
|
// of being used here. It'd be preferable to find a cleaner way to do this.
|
||||||
|
const maybeASIProblem = node.needsParens ||
|
||||||
|
node.type === "ParenthesizedExpression" ||
|
||||||
|
node.type === "TypeCastExpression" ||
|
||||||
|
(node.type === "ArrowFunctionExpression" &&
|
||||||
|
!canPrintParamsWithoutParens(node)) ||
|
||||||
|
node.type === "ArrayExpression" ||
|
||||||
|
node.type === "ArrayPattern" ||
|
||||||
|
(node.type === "UnaryExpression" &&
|
||||||
|
node.prefix &&
|
||||||
|
(node.operator === "+" || node.operator === "-")) ||
|
||||||
|
node.type === "TemplateLiteral" ||
|
||||||
|
node.type === "TemplateElement" ||
|
||||||
|
node.type === "JSXElement" ||
|
||||||
|
node.type === "RegExpLiteral" ||
|
||||||
|
(node.type === "Literal" && node.pattern) ||
|
||||||
|
(node.type === "Literal" && node.regex);
|
||||||
|
|
||||||
|
if (maybeASIProblem) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasNakedLeftSide(node)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return exprNeedsASIProtection(getLeftSide(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
function stmtNeedsASIProtection(path) {
|
||||||
|
if (!path) return false;
|
||||||
|
const node = path.getNode();
|
||||||
|
|
||||||
|
if (node.type !== "ExpressionStatement") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return exprNeedsASIProtection(node.expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
function classPropMayCauseASIProblems(path) {
|
||||||
|
const node = path.getNode();
|
||||||
|
|
||||||
|
if (node.type !== "ClassProperty") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = node.key && node.key.name;
|
||||||
|
if (!name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this isn't actually possible yet with most parsers available today
|
||||||
|
// so isn't properly tested yet.
|
||||||
|
if (name === "static" || name === "get" || name === "set") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function classChildNeedsASIProtection(node) {
|
||||||
|
if (!node) return;
|
||||||
|
|
||||||
|
let isAsync, isGenerator;
|
||||||
|
switch (node.type) {
|
||||||
|
case "ClassProperty":
|
||||||
|
return node.computed;
|
||||||
|
|
||||||
|
// flow
|
||||||
|
case "MethodDefinition":
|
||||||
|
// babylon
|
||||||
|
case "ClassMethod": {
|
||||||
|
const isAsync = node.value
|
||||||
|
? node.value.async
|
||||||
|
: node.async;
|
||||||
|
const isGenerator = node.value
|
||||||
|
? node.value.generator
|
||||||
|
: node.generator;
|
||||||
|
if (isAsync || node.static || node.kind === "get" || node.kind === "set") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (node.computed || isGenerator) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This recurses the return argument, looking for the first token
|
// This recurses the return argument, looking for the first token
|
||||||
// (the leftmost leaf node) and, if it (or its parents) has any
|
// (the leftmost leaf node) and, if it (or its parents) has any
|
||||||
// leadingComments, returns true (so it can be wrapped in parens).
|
// leadingComments, returns true (so it can be wrapped in parens).
|
||||||
|
@ -3354,22 +3501,7 @@ function returnArgumentHasLeadingComment(options, argument) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasCommentableLeftSide = argument.type === "BinaryExpression" ||
|
if (hasNakedLeftSide(argument)) {
|
||||||
argument.type === "LogicalExpression" ||
|
|
||||||
argument.type === "ConditionalExpression" ||
|
|
||||||
argument.type === "CallExpression" ||
|
|
||||||
argument.type === "MemberExpression" ||
|
|
||||||
argument.type === "SequenceExpression" ||
|
|
||||||
argument.type === "TaggedTemplateExpression";
|
|
||||||
|
|
||||||
if (hasCommentableLeftSide) {
|
|
||||||
const getLeftSide = (node) => {
|
|
||||||
if (node.expressions) {
|
|
||||||
return node.expressions[0];
|
|
||||||
}
|
|
||||||
return node.left || node.test || node.callee || node.object || node.tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
let leftMost = argument;
|
let leftMost = argument;
|
||||||
let newLeftMost;
|
let newLeftMost;
|
||||||
while (newLeftMost = getLeftSide(leftMost)) {
|
while (newLeftMost = getLeftSide(leftMost)) {
|
||||||
|
|
|
@ -0,0 +1,965 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`no-semi.js 1`] = `
|
||||||
|
"
|
||||||
|
// with preexisting semi
|
||||||
|
|
||||||
|
x; [1, 2, 3].forEach(fn)
|
||||||
|
x; [a, b, ...c] = [1, 2]
|
||||||
|
x; /r/i.test('r')
|
||||||
|
x; +1
|
||||||
|
x; - 1
|
||||||
|
x; ('h' + 'i').repeat(10)
|
||||||
|
x; (1, 2)
|
||||||
|
x; (() => {})()
|
||||||
|
x; ({ a: 1 }).entries()
|
||||||
|
x; ({ a: 1 }).entries()
|
||||||
|
x; <Hello />
|
||||||
|
x; \`string\`
|
||||||
|
x; (x, y) => x
|
||||||
|
|
||||||
|
// doesn't have to be preceded by a semicolon
|
||||||
|
|
||||||
|
class X {} [1, 2, 3].forEach(fn)
|
||||||
|
|
||||||
|
// TODO: enable lang features
|
||||||
|
// x; ::b.c
|
||||||
|
// TODO: upgrade parser
|
||||||
|
// class A {
|
||||||
|
// async; // The semicolon is *not* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class B {
|
||||||
|
// static; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// get; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// set; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// don't semicolon if it doesn't start statement
|
||||||
|
|
||||||
|
if (true) (() => {})()
|
||||||
|
|
||||||
|
class A {
|
||||||
|
a = 0;
|
||||||
|
[b](){}
|
||||||
|
|
||||||
|
c = 0;
|
||||||
|
*d(){}
|
||||||
|
|
||||||
|
e = 0;
|
||||||
|
[f] = 0
|
||||||
|
|
||||||
|
// none of the semicolons above this comment can be omitted.
|
||||||
|
// none of the semicolons below this comment are necessary.
|
||||||
|
|
||||||
|
q() {};
|
||||||
|
[h](){}
|
||||||
|
|
||||||
|
p() {};
|
||||||
|
*i(){}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
get ['y']() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
static ['y']() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
set ['z'](z) {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
async ['a']() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
async *g() {}
|
||||||
|
|
||||||
|
a = 0;
|
||||||
|
b = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// being first/last shouldn't break things
|
||||||
|
class G {
|
||||||
|
x = 1
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
*x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
[x] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// check indentation
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
x; (() => {})()
|
||||||
|
}
|
||||||
|
|
||||||
|
// flow
|
||||||
|
|
||||||
|
(x: void);
|
||||||
|
(y: void)
|
||||||
|
|
||||||
|
// check statement clauses
|
||||||
|
|
||||||
|
do break; while (false)
|
||||||
|
if (true) do break; while (false)
|
||||||
|
|
||||||
|
if (true) 1; else 2
|
||||||
|
for (;;) ;
|
||||||
|
for (x of y) ;
|
||||||
|
|
||||||
|
debugger
|
||||||
|
|
||||||
|
// check that it doesn't break non-ASI
|
||||||
|
|
||||||
|
1
|
||||||
|
- 1
|
||||||
|
|
||||||
|
1
|
||||||
|
+ 1
|
||||||
|
|
||||||
|
1
|
||||||
|
/ 1
|
||||||
|
|
||||||
|
arr
|
||||||
|
[0]
|
||||||
|
|
||||||
|
fn
|
||||||
|
(x)
|
||||||
|
|
||||||
|
!1
|
||||||
|
|
||||||
|
1
|
||||||
|
< 1
|
||||||
|
|
||||||
|
tag
|
||||||
|
\`string\`
|
||||||
|
|
||||||
|
x; x => x
|
||||||
|
|
||||||
|
while (false)
|
||||||
|
(function(){}())
|
||||||
|
|
||||||
|
aReallyLongLine012345678901234567890123456789012345678901234567890123456789 *
|
||||||
|
(b + c)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// with preexisting semi
|
||||||
|
|
||||||
|
x;
|
||||||
|
[1, 2, 3].forEach(fn);
|
||||||
|
x;
|
||||||
|
[a, b, ...c] = [1, 2];
|
||||||
|
x;
|
||||||
|
/r/i.test(\\"r\\");
|
||||||
|
x;
|
||||||
|
+1;
|
||||||
|
x;
|
||||||
|
-1;
|
||||||
|
x;
|
||||||
|
(\\"h\\" + \\"i\\").repeat(10);
|
||||||
|
x;
|
||||||
|
1, 2;
|
||||||
|
x;
|
||||||
|
(() => {})();
|
||||||
|
x;
|
||||||
|
({ a: 1 }.entries());
|
||||||
|
x;
|
||||||
|
({ a: 1 }.entries());
|
||||||
|
x;
|
||||||
|
<Hello />;
|
||||||
|
x;
|
||||||
|
\`string\`;
|
||||||
|
x;
|
||||||
|
(x, y) => x;
|
||||||
|
|
||||||
|
// doesn't have to be preceded by a semicolon
|
||||||
|
|
||||||
|
class X {}
|
||||||
|
[1, 2, 3].forEach(fn);
|
||||||
|
|
||||||
|
// TODO: enable lang features
|
||||||
|
// x; ::b.c
|
||||||
|
// TODO: upgrade parser
|
||||||
|
// class A {
|
||||||
|
// async; // The semicolon is *not* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class B {
|
||||||
|
// static; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// get; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// set; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
|
||||||
|
// don't semicolon if it doesn't start statement
|
||||||
|
|
||||||
|
if (true) (() => {})();
|
||||||
|
|
||||||
|
class A {
|
||||||
|
a = 0;
|
||||||
|
[b]() {}
|
||||||
|
|
||||||
|
c = 0;
|
||||||
|
*d() {}
|
||||||
|
|
||||||
|
e = 0;
|
||||||
|
[f] = 0;
|
||||||
|
|
||||||
|
// none of the semicolons above this comment can be omitted.
|
||||||
|
// none of the semicolons below this comment are necessary.
|
||||||
|
q() {}
|
||||||
|
[h]() {}
|
||||||
|
|
||||||
|
p() {}
|
||||||
|
*i() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
get [\\"y\\"]() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
static [\\"y\\"]() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
set [\\"z\\"](z) {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
async [\\"a\\"]() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
async *g() {}
|
||||||
|
|
||||||
|
a = 0;
|
||||||
|
b = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// being first/last shouldn't break things
|
||||||
|
class G {
|
||||||
|
x = 1;
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
*x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
[x] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check indentation
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
x;
|
||||||
|
(() => {})();
|
||||||
|
}
|
||||||
|
|
||||||
|
// flow
|
||||||
|
|
||||||
|
(x: void);
|
||||||
|
(y: void);
|
||||||
|
|
||||||
|
// check statement clauses
|
||||||
|
|
||||||
|
do
|
||||||
|
break;
|
||||||
|
while (false);
|
||||||
|
if (true)
|
||||||
|
do
|
||||||
|
break;
|
||||||
|
while (false);
|
||||||
|
|
||||||
|
if (true) 1;
|
||||||
|
else 2;
|
||||||
|
for (;;);
|
||||||
|
for (x of y);
|
||||||
|
|
||||||
|
debugger;
|
||||||
|
|
||||||
|
// check that it doesn't break non-ASI
|
||||||
|
|
||||||
|
1 - 1;
|
||||||
|
|
||||||
|
1 + 1;
|
||||||
|
|
||||||
|
1 / 1;
|
||||||
|
|
||||||
|
arr[0];
|
||||||
|
|
||||||
|
fn(x);
|
||||||
|
|
||||||
|
!1;
|
||||||
|
|
||||||
|
1 < 1;
|
||||||
|
|
||||||
|
tag\`string\`;
|
||||||
|
|
||||||
|
x;
|
||||||
|
x => x;
|
||||||
|
|
||||||
|
while (false)
|
||||||
|
(function() {})();
|
||||||
|
|
||||||
|
aReallyLongLine012345678901234567890123456789012345678901234567890123456789 *
|
||||||
|
(b + c);
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`no-semi.js 2`] = `
|
||||||
|
"
|
||||||
|
// with preexisting semi
|
||||||
|
|
||||||
|
x; [1, 2, 3].forEach(fn)
|
||||||
|
x; [a, b, ...c] = [1, 2]
|
||||||
|
x; /r/i.test('r')
|
||||||
|
x; +1
|
||||||
|
x; - 1
|
||||||
|
x; ('h' + 'i').repeat(10)
|
||||||
|
x; (1, 2)
|
||||||
|
x; (() => {})()
|
||||||
|
x; ({ a: 1 }).entries()
|
||||||
|
x; ({ a: 1 }).entries()
|
||||||
|
x; <Hello />
|
||||||
|
x; \`string\`
|
||||||
|
x; (x, y) => x
|
||||||
|
|
||||||
|
// doesn't have to be preceded by a semicolon
|
||||||
|
|
||||||
|
class X {} [1, 2, 3].forEach(fn)
|
||||||
|
|
||||||
|
// TODO: enable lang features
|
||||||
|
// x; ::b.c
|
||||||
|
// TODO: upgrade parser
|
||||||
|
// class A {
|
||||||
|
// async; // The semicolon is *not* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class B {
|
||||||
|
// static; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// get; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// set; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// don't semicolon if it doesn't start statement
|
||||||
|
|
||||||
|
if (true) (() => {})()
|
||||||
|
|
||||||
|
class A {
|
||||||
|
a = 0;
|
||||||
|
[b](){}
|
||||||
|
|
||||||
|
c = 0;
|
||||||
|
*d(){}
|
||||||
|
|
||||||
|
e = 0;
|
||||||
|
[f] = 0
|
||||||
|
|
||||||
|
// none of the semicolons above this comment can be omitted.
|
||||||
|
// none of the semicolons below this comment are necessary.
|
||||||
|
|
||||||
|
q() {};
|
||||||
|
[h](){}
|
||||||
|
|
||||||
|
p() {};
|
||||||
|
*i(){}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
get ['y']() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
static ['y']() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
set ['z'](z) {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
async ['a']() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
async *g() {}
|
||||||
|
|
||||||
|
a = 0;
|
||||||
|
b = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// being first/last shouldn't break things
|
||||||
|
class G {
|
||||||
|
x = 1
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
*x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
[x] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// check indentation
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
x; (() => {})()
|
||||||
|
}
|
||||||
|
|
||||||
|
// flow
|
||||||
|
|
||||||
|
(x: void);
|
||||||
|
(y: void)
|
||||||
|
|
||||||
|
// check statement clauses
|
||||||
|
|
||||||
|
do break; while (false)
|
||||||
|
if (true) do break; while (false)
|
||||||
|
|
||||||
|
if (true) 1; else 2
|
||||||
|
for (;;) ;
|
||||||
|
for (x of y) ;
|
||||||
|
|
||||||
|
debugger
|
||||||
|
|
||||||
|
// check that it doesn't break non-ASI
|
||||||
|
|
||||||
|
1
|
||||||
|
- 1
|
||||||
|
|
||||||
|
1
|
||||||
|
+ 1
|
||||||
|
|
||||||
|
1
|
||||||
|
/ 1
|
||||||
|
|
||||||
|
arr
|
||||||
|
[0]
|
||||||
|
|
||||||
|
fn
|
||||||
|
(x)
|
||||||
|
|
||||||
|
!1
|
||||||
|
|
||||||
|
1
|
||||||
|
< 1
|
||||||
|
|
||||||
|
tag
|
||||||
|
\`string\`
|
||||||
|
|
||||||
|
x; x => x
|
||||||
|
|
||||||
|
while (false)
|
||||||
|
(function(){}())
|
||||||
|
|
||||||
|
aReallyLongLine012345678901234567890123456789012345678901234567890123456789 *
|
||||||
|
(b + c)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// with preexisting semi
|
||||||
|
|
||||||
|
x
|
||||||
|
;[1, 2, 3].forEach(fn)
|
||||||
|
x
|
||||||
|
;[a, b, ...c] = [1, 2]
|
||||||
|
x
|
||||||
|
;/r/i.test(\\"r\\")
|
||||||
|
x
|
||||||
|
;+1
|
||||||
|
x
|
||||||
|
;-1
|
||||||
|
x
|
||||||
|
;(\\"h\\" + \\"i\\").repeat(10)
|
||||||
|
x
|
||||||
|
1, 2
|
||||||
|
x
|
||||||
|
;(() => {})()
|
||||||
|
x
|
||||||
|
;({ a: 1 }.entries())
|
||||||
|
x
|
||||||
|
;({ a: 1 }.entries())
|
||||||
|
x
|
||||||
|
;<Hello />
|
||||||
|
x
|
||||||
|
;\`string\`
|
||||||
|
x
|
||||||
|
;(x, y) => x
|
||||||
|
|
||||||
|
// doesn't have to be preceded by a semicolon
|
||||||
|
|
||||||
|
class X {}
|
||||||
|
;[1, 2, 3].forEach(fn)
|
||||||
|
|
||||||
|
// TODO: enable lang features
|
||||||
|
// x; ::b.c
|
||||||
|
// TODO: upgrade parser
|
||||||
|
// class A {
|
||||||
|
// async; // The semicolon is *not* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class B {
|
||||||
|
// static; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// get; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// set; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
|
||||||
|
// don't semicolon if it doesn't start statement
|
||||||
|
|
||||||
|
if (true) (() => {})()
|
||||||
|
|
||||||
|
class A {
|
||||||
|
a = 0;
|
||||||
|
[b]() {}
|
||||||
|
|
||||||
|
c = 0;
|
||||||
|
*d() {}
|
||||||
|
|
||||||
|
e = 0;
|
||||||
|
[f] = 0
|
||||||
|
|
||||||
|
// none of the semicolons above this comment can be omitted.
|
||||||
|
// none of the semicolons below this comment are necessary.
|
||||||
|
q() {}
|
||||||
|
[h]() {}
|
||||||
|
|
||||||
|
p() {}
|
||||||
|
*i() {}
|
||||||
|
|
||||||
|
a = 1
|
||||||
|
get [\\"y\\"]() {}
|
||||||
|
|
||||||
|
a = 1
|
||||||
|
static [\\"y\\"]() {}
|
||||||
|
|
||||||
|
a = 1
|
||||||
|
set [\\"z\\"](z) {}
|
||||||
|
|
||||||
|
a = 1
|
||||||
|
async [\\"a\\"]() {}
|
||||||
|
|
||||||
|
a = 1
|
||||||
|
async *g() {}
|
||||||
|
|
||||||
|
a = 0
|
||||||
|
b = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// being first/last shouldn't break things
|
||||||
|
class G {
|
||||||
|
x = 1
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
*x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
[x] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// check indentation
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
x
|
||||||
|
;(() => {})()
|
||||||
|
}
|
||||||
|
|
||||||
|
;// flow
|
||||||
|
|
||||||
|
(x: void)
|
||||||
|
;(y: void)
|
||||||
|
|
||||||
|
// check statement clauses
|
||||||
|
|
||||||
|
do
|
||||||
|
break
|
||||||
|
while (false)
|
||||||
|
if (true)
|
||||||
|
do
|
||||||
|
break
|
||||||
|
while (false)
|
||||||
|
|
||||||
|
if (true) 1
|
||||||
|
else 2
|
||||||
|
for (;;);
|
||||||
|
for (x of y);
|
||||||
|
|
||||||
|
debugger
|
||||||
|
|
||||||
|
// check that it doesn't break non-ASI
|
||||||
|
|
||||||
|
1 - 1
|
||||||
|
|
||||||
|
1 + 1
|
||||||
|
|
||||||
|
1 / 1
|
||||||
|
|
||||||
|
arr[0]
|
||||||
|
|
||||||
|
fn(x)
|
||||||
|
|
||||||
|
!1
|
||||||
|
|
||||||
|
1 < 1
|
||||||
|
|
||||||
|
tag\`string\`
|
||||||
|
|
||||||
|
x
|
||||||
|
x => x
|
||||||
|
|
||||||
|
while (false)
|
||||||
|
(function() {})()
|
||||||
|
|
||||||
|
aReallyLongLine012345678901234567890123456789012345678901234567890123456789 *
|
||||||
|
(b + c)
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`no-semi.js 3`] = `
|
||||||
|
"
|
||||||
|
// with preexisting semi
|
||||||
|
|
||||||
|
x; [1, 2, 3].forEach(fn)
|
||||||
|
x; [a, b, ...c] = [1, 2]
|
||||||
|
x; /r/i.test('r')
|
||||||
|
x; +1
|
||||||
|
x; - 1
|
||||||
|
x; ('h' + 'i').repeat(10)
|
||||||
|
x; (1, 2)
|
||||||
|
x; (() => {})()
|
||||||
|
x; ({ a: 1 }).entries()
|
||||||
|
x; ({ a: 1 }).entries()
|
||||||
|
x; <Hello />
|
||||||
|
x; \`string\`
|
||||||
|
x; (x, y) => x
|
||||||
|
|
||||||
|
// doesn't have to be preceded by a semicolon
|
||||||
|
|
||||||
|
class X {} [1, 2, 3].forEach(fn)
|
||||||
|
|
||||||
|
// TODO: enable lang features
|
||||||
|
// x; ::b.c
|
||||||
|
// TODO: upgrade parser
|
||||||
|
// class A {
|
||||||
|
// async; // The semicolon is *not* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class B {
|
||||||
|
// static; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// get; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// set; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// don't semicolon if it doesn't start statement
|
||||||
|
|
||||||
|
if (true) (() => {})()
|
||||||
|
|
||||||
|
class A {
|
||||||
|
a = 0;
|
||||||
|
[b](){}
|
||||||
|
|
||||||
|
c = 0;
|
||||||
|
*d(){}
|
||||||
|
|
||||||
|
e = 0;
|
||||||
|
[f] = 0
|
||||||
|
|
||||||
|
// none of the semicolons above this comment can be omitted.
|
||||||
|
// none of the semicolons below this comment are necessary.
|
||||||
|
|
||||||
|
q() {};
|
||||||
|
[h](){}
|
||||||
|
|
||||||
|
p() {};
|
||||||
|
*i(){}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
get ['y']() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
static ['y']() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
set ['z'](z) {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
async ['a']() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
async *g() {}
|
||||||
|
|
||||||
|
a = 0;
|
||||||
|
b = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// being first/last shouldn't break things
|
||||||
|
class G {
|
||||||
|
x = 1
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
*x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
[x] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// check indentation
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
x; (() => {})()
|
||||||
|
}
|
||||||
|
|
||||||
|
// flow
|
||||||
|
|
||||||
|
(x: void);
|
||||||
|
(y: void)
|
||||||
|
|
||||||
|
// check statement clauses
|
||||||
|
|
||||||
|
do break; while (false)
|
||||||
|
if (true) do break; while (false)
|
||||||
|
|
||||||
|
if (true) 1; else 2
|
||||||
|
for (;;) ;
|
||||||
|
for (x of y) ;
|
||||||
|
|
||||||
|
debugger
|
||||||
|
|
||||||
|
// check that it doesn't break non-ASI
|
||||||
|
|
||||||
|
1
|
||||||
|
- 1
|
||||||
|
|
||||||
|
1
|
||||||
|
+ 1
|
||||||
|
|
||||||
|
1
|
||||||
|
/ 1
|
||||||
|
|
||||||
|
arr
|
||||||
|
[0]
|
||||||
|
|
||||||
|
fn
|
||||||
|
(x)
|
||||||
|
|
||||||
|
!1
|
||||||
|
|
||||||
|
1
|
||||||
|
< 1
|
||||||
|
|
||||||
|
tag
|
||||||
|
\`string\`
|
||||||
|
|
||||||
|
x; x => x
|
||||||
|
|
||||||
|
while (false)
|
||||||
|
(function(){}())
|
||||||
|
|
||||||
|
aReallyLongLine012345678901234567890123456789012345678901234567890123456789 *
|
||||||
|
(b + c)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// with preexisting semi
|
||||||
|
|
||||||
|
x
|
||||||
|
;[1, 2, 3].forEach(fn)
|
||||||
|
x
|
||||||
|
;[a, b, ...c] = [1, 2]
|
||||||
|
x
|
||||||
|
;/r/i.test(\\"r\\")
|
||||||
|
x
|
||||||
|
;+1
|
||||||
|
x
|
||||||
|
;-1
|
||||||
|
x
|
||||||
|
;(\\"h\\" + \\"i\\").repeat(10)
|
||||||
|
x
|
||||||
|
1, 2
|
||||||
|
x
|
||||||
|
;(() => {})()
|
||||||
|
x
|
||||||
|
;({ a: 1 }.entries())
|
||||||
|
x
|
||||||
|
;({ a: 1 }.entries())
|
||||||
|
x
|
||||||
|
;<Hello />
|
||||||
|
x
|
||||||
|
;\`string\`
|
||||||
|
x
|
||||||
|
;(x, y) => x
|
||||||
|
|
||||||
|
// doesn't have to be preceded by a semicolon
|
||||||
|
|
||||||
|
class X {}
|
||||||
|
;[1, 2, 3].forEach(fn)
|
||||||
|
|
||||||
|
// TODO: enable lang features
|
||||||
|
// x; ::b.c
|
||||||
|
// TODO: upgrade parser
|
||||||
|
// class A {
|
||||||
|
// async; // The semicolon is *not* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class B {
|
||||||
|
// static; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// get; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// set; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
|
||||||
|
// don't semicolon if it doesn't start statement
|
||||||
|
|
||||||
|
if (true) (() => {})()
|
||||||
|
|
||||||
|
class A {
|
||||||
|
a = 0;
|
||||||
|
[b]() {}
|
||||||
|
|
||||||
|
c = 0;
|
||||||
|
*d() {}
|
||||||
|
|
||||||
|
e = 0;
|
||||||
|
[f] = 0
|
||||||
|
|
||||||
|
// none of the semicolons above this comment can be omitted.
|
||||||
|
// none of the semicolons below this comment are necessary.
|
||||||
|
|
||||||
|
q() {}
|
||||||
|
[h]() {}
|
||||||
|
|
||||||
|
p() {}
|
||||||
|
*i() {}
|
||||||
|
|
||||||
|
a = 1
|
||||||
|
get [\\"y\\"]() {}
|
||||||
|
|
||||||
|
a = 1
|
||||||
|
static [\\"y\\"]() {}
|
||||||
|
|
||||||
|
a = 1
|
||||||
|
set [\\"z\\"](z) {}
|
||||||
|
|
||||||
|
a = 1
|
||||||
|
async [\\"a\\"]() {}
|
||||||
|
|
||||||
|
a = 1
|
||||||
|
async *g() {}
|
||||||
|
|
||||||
|
a = 0
|
||||||
|
b = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// being first/last shouldn't break things
|
||||||
|
class G {
|
||||||
|
x = 1
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
*x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
[x] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// check indentation
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
x
|
||||||
|
;(() => {})()
|
||||||
|
}
|
||||||
|
|
||||||
|
;// flow
|
||||||
|
|
||||||
|
(x: void)
|
||||||
|
;(y: void)
|
||||||
|
|
||||||
|
// check statement clauses
|
||||||
|
|
||||||
|
do
|
||||||
|
break
|
||||||
|
while (false)
|
||||||
|
if (true)
|
||||||
|
do
|
||||||
|
break
|
||||||
|
while (false)
|
||||||
|
|
||||||
|
if (true) 1
|
||||||
|
else 2
|
||||||
|
for (;;);
|
||||||
|
for (x of y);
|
||||||
|
|
||||||
|
debugger
|
||||||
|
|
||||||
|
// check that it doesn't break non-ASI
|
||||||
|
|
||||||
|
1 - 1
|
||||||
|
|
||||||
|
1 + 1
|
||||||
|
|
||||||
|
1 / 1
|
||||||
|
|
||||||
|
arr[0]
|
||||||
|
|
||||||
|
fn(x)
|
||||||
|
|
||||||
|
!1
|
||||||
|
|
||||||
|
1 < 1
|
||||||
|
|
||||||
|
tag\`string\`
|
||||||
|
|
||||||
|
x
|
||||||
|
x => x
|
||||||
|
|
||||||
|
while (false)
|
||||||
|
(function() {})()
|
||||||
|
|
||||||
|
aReallyLongLine012345678901234567890123456789012345678901234567890123456789 *
|
||||||
|
(b + c)
|
||||||
|
"
|
||||||
|
`;
|
|
@ -0,0 +1,3 @@
|
||||||
|
run_spec(__dirname);
|
||||||
|
run_spec(__dirname, { semi: false, parser: "flow" });
|
||||||
|
run_spec(__dirname, { semi: false, parser: "babylon" });
|
|
@ -0,0 +1,152 @@
|
||||||
|
|
||||||
|
// with preexisting semi
|
||||||
|
|
||||||
|
x; [1, 2, 3].forEach(fn)
|
||||||
|
x; [a, b, ...c] = [1, 2]
|
||||||
|
x; /r/i.test('r')
|
||||||
|
x; +1
|
||||||
|
x; - 1
|
||||||
|
x; ('h' + 'i').repeat(10)
|
||||||
|
x; (1, 2)
|
||||||
|
x; (() => {})()
|
||||||
|
x; ({ a: 1 }).entries()
|
||||||
|
x; ({ a: 1 }).entries()
|
||||||
|
x; <Hello />
|
||||||
|
x; `string`
|
||||||
|
x; (x, y) => x
|
||||||
|
|
||||||
|
// doesn't have to be preceded by a semicolon
|
||||||
|
|
||||||
|
class X {} [1, 2, 3].forEach(fn)
|
||||||
|
|
||||||
|
// TODO: enable lang features
|
||||||
|
// x; ::b.c
|
||||||
|
// TODO: upgrade parser
|
||||||
|
// class A {
|
||||||
|
// async; // The semicolon is *not* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class B {
|
||||||
|
// static; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// get; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
// class C {
|
||||||
|
// set; // The semicolon *is* necessary
|
||||||
|
// x(){}
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// don't semicolon if it doesn't start statement
|
||||||
|
|
||||||
|
if (true) (() => {})()
|
||||||
|
|
||||||
|
class A {
|
||||||
|
a = 0;
|
||||||
|
[b](){}
|
||||||
|
|
||||||
|
c = 0;
|
||||||
|
*d(){}
|
||||||
|
|
||||||
|
e = 0;
|
||||||
|
[f] = 0
|
||||||
|
|
||||||
|
// none of the semicolons above this comment can be omitted.
|
||||||
|
// none of the semicolons below this comment are necessary.
|
||||||
|
|
||||||
|
q() {};
|
||||||
|
[h](){}
|
||||||
|
|
||||||
|
p() {};
|
||||||
|
*i(){}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
get ['y']() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
static ['y']() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
set ['z'](z) {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
async ['a']() {}
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
async *g() {}
|
||||||
|
|
||||||
|
a = 0;
|
||||||
|
b = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// being first/last shouldn't break things
|
||||||
|
class G {
|
||||||
|
x = 1
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
*x() {}
|
||||||
|
}
|
||||||
|
class G {
|
||||||
|
[x] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// check indentation
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
x; (() => {})()
|
||||||
|
}
|
||||||
|
|
||||||
|
// flow
|
||||||
|
|
||||||
|
(x: void);
|
||||||
|
(y: void)
|
||||||
|
|
||||||
|
// check statement clauses
|
||||||
|
|
||||||
|
do break; while (false)
|
||||||
|
if (true) do break; while (false)
|
||||||
|
|
||||||
|
if (true) 1; else 2
|
||||||
|
for (;;) ;
|
||||||
|
for (x of y) ;
|
||||||
|
|
||||||
|
debugger
|
||||||
|
|
||||||
|
// check that it doesn't break non-ASI
|
||||||
|
|
||||||
|
1
|
||||||
|
- 1
|
||||||
|
|
||||||
|
1
|
||||||
|
+ 1
|
||||||
|
|
||||||
|
1
|
||||||
|
/ 1
|
||||||
|
|
||||||
|
arr
|
||||||
|
[0]
|
||||||
|
|
||||||
|
fn
|
||||||
|
(x)
|
||||||
|
|
||||||
|
!1
|
||||||
|
|
||||||
|
1
|
||||||
|
< 1
|
||||||
|
|
||||||
|
tag
|
||||||
|
`string`
|
||||||
|
|
||||||
|
x; x => x
|
||||||
|
|
||||||
|
while (false)
|
||||||
|
(function(){}())
|
||||||
|
|
||||||
|
aReallyLongLine012345678901234567890123456789012345678901234567890123456789 *
|
||||||
|
(b + c)
|
Loading…
Reference in New Issue