diff --git a/lib/output.js b/lib/output.js index ae0b1036fcb..46e1e656e9d 100644 --- a/lib/output.js +++ b/lib/output.js @@ -259,7 +259,9 @@ function OutputStream(options) { if (line_fixed || flush) flush_mappings(); } : noop; - var require_semicolon = makePredicate("( [ + * / - , ."); + var stat_end_chars = makePredicate("; }"); + var asi_skip_chars = makePredicate("( [ + * / - , . `"); + var asi_skip_words = makePredicate("in instanceof"); function require_space(prev, ch, str) { return is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\") @@ -295,8 +297,8 @@ function OutputStream(options) { var prev = last.slice(-1); if (might_need_semicolon) { might_need_semicolon = false; - if (prev == ":" && ch == "}" || prev != ";" && (!ch || ";}".indexOf(ch) < 0)) { - var need_semicolon = require_semicolon[ch]; + if (prev != ";" && !stat_end_chars[ch]) { + var need_semicolon = asi_skip_chars[ch] || asi_skip_words[str]; if (need_semicolon || options.semicolons) { output += ";"; current_col++; @@ -941,7 +943,12 @@ function OutputStream(options) { DEFPRINT(AST_LabeledStatement, function(output) { this.label.print(output); output.colon(); - this.body.print(output); + var body = this.body; + if (body instanceof AST_EmptyStatement) { + output.force_semicolon(); + } else { + body.print(output); + } }); DEFPRINT(AST_SimpleStatement, function(output) { this.body.print(output); diff --git a/test/compress/classes.js b/test/compress/classes.js index 4335809bb63..c1d7fdde361 100644 --- a/test/compress/classes.js +++ b/test/compress/classes.js @@ -4112,3 +4112,68 @@ issue_5876_2: { ] node_version: ">=12" } + +issue_5878_1: { + beautify = { + semicolons: false, + } + input: { + new class { + p = {}; + in = console.log("PASS"); + }(); + } + expect_exact: 'new class{p={};in=console.log("PASS")}\n' + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_5878_2: { + beautify = { + semicolons: false, + } + input: { + class A { + static p = console.log("PASS"); + instanceof() {} + } + } + expect_exact: 'class A{static p=console.log("PASS");instanceof(){}}' + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_5878_3: { + beautify = { + semicolons: false, + } + input: { + new class { + p; + in = console.log("PASS"); + }(); + } + expect_exact: 'new class{p;in=console.log("PASS")}\n' + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_5878_4: { + beautify = { + semicolons: false, + } + input: { + "use strict"; + console.log(typeof class { + f() {} + instanceof() {} + }); + } + expect_exact: [ + '"use strict"', + "console.log(typeof class{f(){}instanceof(){}})", + "", + ] + expect_stdout: "function" + node_version: ">=4" +} diff --git a/test/compress/labels.js b/test/compress/labels.js index c455f3d1de1..5d7d2e60620 100644 --- a/test/compress/labels.js +++ b/test/compress/labels.js @@ -431,3 +431,41 @@ issue_5524: { } expect_stdout: "PASS" } + +issue_5878_1: { + beautify = { + semicolons: false, + } + input: { + console.log("PASS"); + L:; + } + expect_exact: 'console.log("PASS")\nL:;' + expect_stdout: "PASS" +} + +issue_5878_2: { + beautify = { + semicolons: false, + } + input: { + L: ; + L: console.log("PASS"); + } + expect_exact: 'L:;L:console.log("PASS")\n' + expect_stdout: "PASS" +} + +issue_5878_3: { + beautify = { + semicolons: false, + } + input: { + if (console.log("PASS")) + A:; + else + B:; + } + expect_exact: 'if(console.log("PASS"))A:;else B:;' + expect_stdout: "PASS" +} diff --git a/test/compress/regexp.js b/test/compress/regexp.js index 3324a0c8e35..05630243ed0 100644 --- a/test/compress/regexp.js +++ b/test/compress/regexp.js @@ -489,3 +489,15 @@ reset_state_between_evaluate: { } expect_stdout: "PASS" } + +issue_5878: { + beautify = { + semicolons: false, + } + input: { + console.log("PASS"); + /b/.exec({}); + } + expect_exact: 'console.log("PASS");/b/.exec({})\n' + expect_stdout: "PASS" +} diff --git a/test/compress/templates.js b/test/compress/templates.js index db3ab203c96..4658f0d8828 100644 --- a/test/compress/templates.js +++ b/test/compress/templates.js @@ -769,3 +769,16 @@ issue_5199: { expect_stdout: "undefined" node_version: ">=4" } + +issue_5878: { + beautify = { + semicolons: false, + } + input: { + console.log("PASS"); + `42`; + } + expect_exact: 'console.log("PASS");`42`\n' + expect_stdout: "PASS" + node_version: ">=4" +} diff --git a/test/sandbox.js b/test/sandbox.js index 71d1e09fff3..a4d799809aa 100644 --- a/test/sandbox.js +++ b/test/sandbox.js @@ -65,7 +65,7 @@ exports.patch_module_statements = function(code, module) { return ""; }).replace(/\bexport(?:\s*\{[^{}]*}\s*?(?:$|\n|;)|\s+default\b(?:\s*(\(|\{|class\s*\{|class\s+(?=extends\b)|(?:async\s+)?function\s*(?:\*\s*)?\())?|\b)/g, function(match, header) { if (/^export\s+default/.test(match)) has_default = "function _uglify_export_default_() {}"; - if (!header) return ""; + if (!header) return ";"; if (header.length == 1) return "0, " + header; var name = "_uglify_export_default_"; if (/^class\b/.test(header)) do { @@ -85,7 +85,7 @@ exports.patch_module_statements = function(code, module) { "} = new Proxy(Object.create(null), { get(_, value) { return { value }; } });", ].join("")); } - return ""; + return ";"; }); imports.push(""); return strict_mode + has_default + imports.join("\n") + code; diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js index eb38de375cd..15adc00d71e 100644 --- a/test/ufuzz/index.js +++ b/test/ufuzz/index.js @@ -2173,7 +2173,8 @@ function log_suspects(minify_options, component) { var defs = default_options[component]; var toplevel = sandbox.has_toplevel(minify_options); var suspects = Object.keys(defs).filter(function(name) { - var flip = component == "compress" && name == "keep_fargs"; + var flip = component == "compress" && name == "keep_fargs" + || component == "output" && name == "semicolons"; if (flip !== (name in options ? options : defs)[name]) { var m = JSON.parse(JSON.stringify(minify_options)); var o = JSON.parse(JSON.stringify(options)); diff --git a/test/ufuzz/options.json b/test/ufuzz/options.json index ef479f73c8b..9fad3d5e88a 100644 --- a/test/ufuzz/options.json +++ b/test/ufuzz/options.json @@ -23,6 +23,9 @@ { "ie": true, "module": false, + "output": { + "semicolons": false + }, "toplevel": true }, {