"use strict";
-var KEYWORDS = "break case catch class const continue debugger default delete do else extends finally for function if in instanceof let new return switch throw try typeof var void while with";
+var KEYWORDS = "break case catch class const continue debugger default delete do else extends finally for function if in instanceof new return switch throw try typeof var void while with";
var KEYWORDS_ATOM = "false null true";
var RESERVED_WORDS = [
- "abstract async await boolean byte char double enum export final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield",
+ "abstract async await boolean byte char double enum export final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield",
KEYWORDS_ATOM,
KEYWORDS,
].join(" ");
next();
return import_();
}
+ break;
+ case "let":
+ if (is_vardefs()) {
+ next();
+ var node = let_();
+ semicolon();
+ return node;
+ }
+ break;
case "yield":
if (S.in_generator) return simple_statement();
break;
next();
return if_();
- case "let":
- next();
- var node = let_();
- semicolon();
- return node;
-
case "return":
if (S.in_function == 0 && !options.bare_returns)
croak("'return' outside of function");
if (await || !is("punc", ";")) {
init = is("keyword", "const")
? (next(), const_(true))
- : is("keyword", "let")
+ : is("name", "let") && is_vardefs()
? (next(), let_(true))
: is("keyword", "var")
? (next(), var_(true))
}
var export_decl = embed_tokens(function() {
- if (is("name", "async")) {
+ if (is("name")) switch (S.token.value) {
+ case "async":
next();
expect_token("keyword", "function");
if (!is("operator", "*")) return function_(AST_AsyncDefun);
next();
return function_(AST_AsyncGeneratorDefun);
+ case "let":
+ next();
+ var node = let_();
+ semicolon();
+ return node;
} else if (is("keyword")) switch (S.token.value) {
case "class":
next();
if (!is("operator", "*")) return function_(AST_Defun);
next();
return function_(AST_GeneratorDefun);
- case "let":
- next();
- var node = let_();
- semicolon();
- return node;
case "var":
next();
var node = var_();
return a;
}
+ function is_vardefs() {
+ var token = peek();
+ return is_token(token, "name") || is_token(token, "punc", "[") || is_token(token, "punc", "{");
+ }
+
var const_ = function(no_in) {
return new AST_Const({
start : prev(),
}
function strict_verify_symbol(sym) {
- if (sym.name == "arguments" || sym.name == "eval")
+ if (sym.name == "arguments" || sym.name == "eval" || sym.name == "let")
token_error(sym.start, "Unexpected " + sym.name + " in strict mode");
}
var assert = require("assert");
-var UglifyJS = require("../..");
+var UglifyJS = require("../node");
describe("let", function() {
this.timeout(30000);
assert.notStrictEqual(result.indexOf('v["' + name + '"]'), -1);
});
});
+ it("Should parse `let` as name correctly", function() {
+ [
+ "for(var let;let;let)let;",
+ "function let(let){let}",
+ ].forEach(function(code) {
+ var ast = UglifyJS.parse(code);
+ assert.strictEqual(ast.print_to_string(), code);
+ assert.throws(function() {
+ UglifyJS.parse('"use strict";' + code);
+ }, function(e) {
+ return e instanceof UglifyJS.JS_Parse_Error && e.message == "Unexpected let in strict mode";
+ }, code);
+ });
+ });
+ it("Should throw on ambiguous use of `let`", function() {
+ [
+ "export let",
+ [
+ "let",
+ "console.log(42)",
+ ].join("\n"),
+ [
+ "let",
+ "[ console.log(42) ]",
+ ].join("\n"),
+ [
+ "let",
+ "{",
+ " console.log(42)",
+ "}",
+ ].join("\n"),
+ ].forEach(function(code) {
+ assert.throws(function() {
+ UglifyJS.parse(code);
+ }, function(e) {
+ return e instanceof UglifyJS.JS_Parse_Error;
+ }, code);
+ });
+ });
});