Destructuring consistency fixes (#1417)
- Use AST_Destructuring for lhf assignment patterns - Use AST_DefaultAssign for default assignments - Add more checks for lhs expressions - Add lots of testing - Cleanup ast (e.g. remove default property) - Fix #1402 based on a patch from @kzc - Refine spread allowance in array destructring pattern - Add destructuring AST tree checker
This commit is contained in:
committed by
Alex Lam S.L
parent
85c1cba760
commit
07734b000a
75
lib/ast.js
75
lib/ast.js
@@ -392,46 +392,56 @@ var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", {
|
||||
// We don't want anything which doesn't belong in a destructuring
|
||||
var root = this;
|
||||
return this.expressions.map(function to_fun_args(ex, _, __, default_seen_above) {
|
||||
var insert_default = function(ex, default_value) {
|
||||
if (default_value) {
|
||||
return new AST_DefaultAssign({
|
||||
start: ex.start,
|
||||
left: ex,
|
||||
operator: "=",
|
||||
right: default_value,
|
||||
end: default_value.end
|
||||
});
|
||||
}
|
||||
return ex;
|
||||
}
|
||||
if (ex instanceof AST_Object) {
|
||||
return new AST_Destructuring({
|
||||
return insert_default(new AST_Destructuring({
|
||||
start: ex.start,
|
||||
end: ex.end,
|
||||
is_array: false,
|
||||
default: default_seen_above,
|
||||
names: ex.properties.map(to_fun_args)
|
||||
});
|
||||
}), default_seen_above);
|
||||
} else if (ex instanceof AST_ObjectKeyVal) {
|
||||
if (ex.key instanceof AST_SymbolRef) {
|
||||
ex.key = to_fun_args(ex.key, 0, [ex.key], ex.default);
|
||||
ex.key = to_fun_args(ex.key, 0, [ex.key]);
|
||||
}
|
||||
ex.value = to_fun_args(ex.value, 0, [ex.key], ex.default);
|
||||
return ex;
|
||||
ex.value = to_fun_args(ex.value, 0, [ex.key]);
|
||||
return insert_default(ex, default_seen_above);
|
||||
} else if (ex instanceof AST_Hole) {
|
||||
return ex;
|
||||
} else if (ex instanceof AST_Destructuring) {
|
||||
if (ex.names.length == 0)
|
||||
croak("Invalid destructuring function parameter", ex.start.line, ex.start.col);
|
||||
ex.names = ex.names.map(to_fun_args);
|
||||
return ex;
|
||||
return insert_default(ex, default_seen_above);
|
||||
} else if (ex instanceof AST_SymbolRef) {
|
||||
return new AST_SymbolFunarg({
|
||||
return insert_default(new AST_SymbolFunarg({
|
||||
name: ex.name,
|
||||
default: default_seen_above,
|
||||
start: ex.start,
|
||||
end: ex.end
|
||||
});
|
||||
}), default_seen_above);
|
||||
} else if (ex instanceof AST_Expansion) {
|
||||
return ex;
|
||||
ex.expression = to_fun_args(ex.expression);
|
||||
return insert_default(ex, default_seen_above);
|
||||
} else if (ex instanceof AST_Array) {
|
||||
return new AST_Destructuring({
|
||||
return insert_default(new AST_Destructuring({
|
||||
start: ex.start,
|
||||
end: ex.end,
|
||||
is_array: true,
|
||||
default: default_seen_above,
|
||||
names: ex.elements.map(to_fun_args)
|
||||
});
|
||||
}), default_seen_above);
|
||||
} else if (ex instanceof AST_Assign) {
|
||||
return to_fun_args(ex.left, undefined, undefined, ex.right);
|
||||
return insert_default(to_fun_args(ex.left, undefined, undefined, ex.right), default_seen_above);
|
||||
} else {
|
||||
croak("Invalid function parameter", ex.start.line, ex.start.col);
|
||||
}
|
||||
@@ -447,7 +457,7 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator",
|
||||
$propdoc: {
|
||||
is_generator: "[boolean] is generatorFn or not",
|
||||
name: "[AST_SymbolDeclaration?] the name of this function",
|
||||
argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion*] array of function arguments, destructurings, or expanding arguments",
|
||||
argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion|AST_DefaultAssign*] array of function arguments, destructurings, or expanding arguments",
|
||||
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
|
||||
},
|
||||
args_as_names: function () {
|
||||
@@ -489,12 +499,11 @@ var AST_Defun = DEFNODE("Defun", null, {
|
||||
}, AST_Lambda);
|
||||
|
||||
/* -----[ DESTRUCTURING ]----- */
|
||||
var AST_Destructuring = DEFNODE("Destructuring", "names is_array default", {
|
||||
var AST_Destructuring = DEFNODE("Destructuring", "names is_array", {
|
||||
$documentation: "A destructuring of several names. Used in destructuring assignment and with destructuring function argument names",
|
||||
$propdoc: {
|
||||
"names": "[AST_Destructuring|AST_Expansion|AST_Hole|AST_ObjectKeyVal|AST_Symbol] Array of properties or elements",
|
||||
"is_array": "[Boolean] Whether the destructuring represents an object or array",
|
||||
"default": "[AST_Node?] Default assign value"
|
||||
"names": "[AST_Node*] Array of properties or elements",
|
||||
"is_array": "[Boolean] Whether the destructuring represents an object or array"
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
@@ -672,7 +681,7 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
|
||||
var AST_Catch = DEFNODE("Catch", "argname", {
|
||||
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
|
||||
$propdoc: {
|
||||
argname: "[AST_SymbolCatch] symbol for the exception"
|
||||
argname: "[AST_SymbolCatch|AST_Destructuring|AST_Expansion|AST_DefaultAssign] symbol for the exception"
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
@@ -957,6 +966,10 @@ var AST_Assign = DEFNODE("Assign", null, {
|
||||
$documentation: "An assignment expression — `a = b + 5`",
|
||||
}, AST_Binary);
|
||||
|
||||
var AST_DefaultAssign = DEFNODE("DefaultAssign", null, {
|
||||
$documentation: "A default assignment expression like in `(a = 3) => a`"
|
||||
}, AST_Binary);
|
||||
|
||||
/* -----[ LITERALS ]----- */
|
||||
|
||||
var AST_Array = DEFNODE("Array", "elements", {
|
||||
@@ -991,8 +1004,7 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
||||
$documentation: "Base class for literal object properties",
|
||||
$propdoc: {
|
||||
key: "[string|AST_Node] the property name converted to a string for ObjectKeyVal. For setters, getters and computed property this is an arbitrary AST_Node",
|
||||
value: "[AST_Node] property value. For setters and getters this is an AST_Function.",
|
||||
default: "[AST_Expression] The default for this parameter, only used when nested inside a binding pattern"
|
||||
value: "[AST_Node] property value. For setters and getters this is an AST_Function."
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
@@ -1003,11 +1015,10 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
||||
}
|
||||
});
|
||||
|
||||
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote default", {
|
||||
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
|
||||
$documentation: "A key: value object property",
|
||||
$propdoc: {
|
||||
quote: "[string] the original quote character",
|
||||
default: "[AST_Expression] The default parameter value, only used when nested inside a binding pattern"
|
||||
quote: "[string] the original quote character"
|
||||
}
|
||||
}, AST_ObjectProperty);
|
||||
|
||||
@@ -1066,19 +1077,13 @@ var AST_ClassExpression = DEFNODE("ClassExpression", null, {
|
||||
$documentation: "A class expression."
|
||||
}, AST_Class);
|
||||
|
||||
var AST_Symbol = DEFNODE("Symbol", "scope name thedef default", {
|
||||
var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
|
||||
$propdoc: {
|
||||
name: "[string] name of this symbol",
|
||||
scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
|
||||
thedef: "[SymbolDef/S] the definition of this symbol",
|
||||
default: "[AST_Expression] The default parameter value, only used when nested inside a binding pattern"
|
||||
thedef: "[SymbolDef/S] the definition of this symbol"
|
||||
},
|
||||
$documentation: "Base class for all symbols",
|
||||
_walk: function (visitor) {
|
||||
return visitor._visit(this, function() {
|
||||
if (this.default) this.default._walk(visitor);
|
||||
});
|
||||
}
|
||||
$documentation: "Base class for all symbols"
|
||||
});
|
||||
|
||||
var AST_NewTarget = DEFNODE("NewTarget", null, {
|
||||
|
||||
@@ -74,6 +74,7 @@ function Compressor(options, false_by_default) {
|
||||
pure_funcs : null,
|
||||
negate_iife : !false_by_default,
|
||||
screw_ie8 : true,
|
||||
ecma : 5,
|
||||
drop_console : false,
|
||||
angular : false,
|
||||
warnings : true,
|
||||
@@ -2317,7 +2318,7 @@ merge(Compressor.prototype, {
|
||||
if (!fun) return self;
|
||||
var args = fun.argnames.map(function(arg, i){
|
||||
return make_node(AST_String, self.args[i], {
|
||||
value: arg.print_to_string()
|
||||
value: arg.print_to_string({ecma: compressor.option("ecma")})
|
||||
});
|
||||
});
|
||||
var code = OutputStream();
|
||||
@@ -2886,6 +2887,22 @@ merge(Compressor.prototype, {
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_DefaultAssign, function(self, compressor){
|
||||
if (!compressor.option("evaluate")) {
|
||||
return self;
|
||||
}
|
||||
var evaluateRight = self.right.evaluate(compressor);
|
||||
|
||||
// `[x = undefined] = foo` ---> `[x] = foo`
|
||||
if (evaluateRight.length > 1 && evaluateRight[1] === undefined) {
|
||||
self = self.left;
|
||||
} else {
|
||||
self.right = evaluateRight[0];
|
||||
}
|
||||
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Conditional, function(self, compressor){
|
||||
if (!compressor.option("conditionals")) return self;
|
||||
if (self.condition instanceof AST_Seq) {
|
||||
|
||||
@@ -641,13 +641,10 @@ function OutputStream(options) {
|
||||
|| p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|
||||
|| p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
||||
|| p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|
||||
|| (p instanceof AST_SymbolConst && p.default === this) // const { xCover = (0, function() {}) }
|
||||
|| (p instanceof AST_SymbolLet && p.default === this) // let { xCover = (0, function() {}) }
|
||||
|| (p instanceof AST_SymbolVar && p.default === this) // var { xCover = (0, function() {}) }
|
||||
|| (p instanceof AST_SymbolCatch && p.default === this) // } catch (xCover = (0, function() {}) ) {
|
||||
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
|
||||
* ==> 20 (side effect, set a := 10 and b := 20) */
|
||||
|| p instanceof AST_Arrow // x => (x, x)
|
||||
|| p instanceof AST_DefaultAssign // x => (x = (0, function(){}))
|
||||
;
|
||||
});
|
||||
|
||||
@@ -761,6 +758,9 @@ function OutputStream(options) {
|
||||
// (a = foo)["prop"] —or— (a = foo).prop
|
||||
if (p instanceof AST_PropAccess && p.expression === this)
|
||||
return true;
|
||||
// ({a, b} = {a: 1, b: 2}), a destructuring assignment
|
||||
if (this instanceof AST_Assign && this.left instanceof AST_Destructuring && this.left.is_array === false)
|
||||
return true;
|
||||
});
|
||||
|
||||
/* -----[ PRINTERS ]----- */
|
||||
@@ -789,12 +789,6 @@ function OutputStream(options) {
|
||||
output.comma();
|
||||
})
|
||||
output.print(self.is_array ? "]" : "}");
|
||||
if (self.default) {
|
||||
output.space();
|
||||
output.print('=');
|
||||
output.space();
|
||||
self.default.print(output)
|
||||
}
|
||||
});
|
||||
|
||||
DEFPRINT(AST_Debugger, function(self, output){
|
||||
@@ -1003,7 +997,7 @@ function OutputStream(options) {
|
||||
parent instanceof AST_Unary ||
|
||||
(parent instanceof AST_Call && self === parent.expression);
|
||||
if (needs_parens) { output.print("(") }
|
||||
if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol && !self.argnames[0].default) {
|
||||
if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol) {
|
||||
self.argnames[0].print(output);
|
||||
} else {
|
||||
output.with_parens(function(){
|
||||
@@ -1497,15 +1491,27 @@ function OutputStream(options) {
|
||||
});
|
||||
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
||||
function get_name(self) {
|
||||
var def = self.value.definition();
|
||||
return def ? def.mangled_name || def.name : self.value.name;
|
||||
var def = self.definition();
|
||||
return def ? def.mangled_name || def.name : self.name;
|
||||
}
|
||||
if (output.option("shorthand") &&
|
||||
|
||||
var allowShortHand = output.option("shorthand");
|
||||
if (allowShortHand &&
|
||||
self.value instanceof AST_Symbol &&
|
||||
is_identifier_string(self.key) &&
|
||||
get_name(self) === self.key
|
||||
get_name(self.value) === self.key
|
||||
) {
|
||||
self.print_property_name(self.key, self.quote, output);
|
||||
|
||||
} else if (allowShortHand &&
|
||||
self.value instanceof AST_DefaultAssign &&
|
||||
self.value.left instanceof AST_Symbol &&
|
||||
is_identifier_string(self.key) &&
|
||||
get_name(self.value.left) === self.key
|
||||
) {
|
||||
self.print_property_name(self.key, self.quote, output);
|
||||
output.print("=");
|
||||
self.value.right.print(output);
|
||||
} else {
|
||||
if (!(self.key instanceof AST_Node)) {
|
||||
self.print_property_name(self.key, self.quote, output);
|
||||
@@ -1517,12 +1523,6 @@ function OutputStream(options) {
|
||||
output.colon();
|
||||
self.value.print(output);
|
||||
}
|
||||
if (self.default) {
|
||||
output.space();
|
||||
output.print('=');
|
||||
output.space();
|
||||
self.default.print(output);
|
||||
}
|
||||
});
|
||||
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, self, output) {
|
||||
if (self.static) {
|
||||
@@ -1573,12 +1573,6 @@ function OutputStream(options) {
|
||||
});
|
||||
DEFPRINT(AST_SymbolDeclaration, function(self, output){
|
||||
self._do_print(output);
|
||||
if (self.default) {
|
||||
output.space();
|
||||
output.print('=');
|
||||
output.space();
|
||||
self.default.print(output)
|
||||
}
|
||||
});
|
||||
DEFPRINT(AST_Undefined, function(self, output){
|
||||
output.print("void 0");
|
||||
|
||||
175
lib/parse.js
175
lib/parse.js
@@ -1106,11 +1106,9 @@ function parse($TEXT, options) {
|
||||
|
||||
case "export":
|
||||
return export_();
|
||||
|
||||
default:
|
||||
unexpected();
|
||||
}
|
||||
}
|
||||
unexpected();
|
||||
});
|
||||
|
||||
function labeled_statement() {
|
||||
@@ -1384,7 +1382,13 @@ function parse($TEXT, options) {
|
||||
if (is("operator", "=") && expand === false) {
|
||||
used_parameters.mark_default_assignment(S.token);
|
||||
next();
|
||||
param.default = expression(false);
|
||||
param = new AST_DefaultAssign({
|
||||
start: param.start,
|
||||
left: param,
|
||||
operator: "=",
|
||||
right: expression(false),
|
||||
end: S.token
|
||||
});
|
||||
}
|
||||
|
||||
if (expand !== false) {
|
||||
@@ -1458,7 +1462,13 @@ function parse($TEXT, options) {
|
||||
if (is("operator", "=") && is_expand === false) {
|
||||
used_parameters.mark_default_assignment(S.token);
|
||||
next();
|
||||
elements[elements.length - 1].default = expression(false);
|
||||
elements[elements.length - 1] = new AST_DefaultAssign({
|
||||
start: elements[elements.length - 1].start,
|
||||
left: elements[elements.length - 1],
|
||||
operator: "=",
|
||||
right: expression(false),
|
||||
end: S.token
|
||||
});
|
||||
}
|
||||
if (is_expand) {
|
||||
if (!is("punc", "]")) {
|
||||
@@ -1489,10 +1499,15 @@ function parse($TEXT, options) {
|
||||
}
|
||||
if (is("name") && (is_token(peek(), "punc") || is_token(peek(), "operator")) && [",", "}", "="].indexOf(peek().value) !== -1) {
|
||||
used_parameters.add_parameter(S.token);
|
||||
elements.push(new symbol_type({
|
||||
start: S.token,
|
||||
name: S.token.value,
|
||||
end: S.token
|
||||
elements.push(new AST_ObjectKeyVal({
|
||||
start: prev(),
|
||||
key: S.token.value,
|
||||
value: new symbol_type({
|
||||
start: S.token,
|
||||
name: S.token.value,
|
||||
end: S.token
|
||||
}),
|
||||
end: prev()
|
||||
}));
|
||||
next();
|
||||
} else if (is("punc", "}")) {
|
||||
@@ -1503,9 +1518,14 @@ function parse($TEXT, options) {
|
||||
if (property === null) {
|
||||
unexpected(prev());
|
||||
} else if (prev().type === "name" && !is("punc", ":")) {
|
||||
elements.push(new AST_SymbolFunarg({
|
||||
elements.push(new AST_ObjectKeyVal({
|
||||
start: prev(),
|
||||
name: property_token,
|
||||
key: property,
|
||||
value: new symbol_type({
|
||||
start: prev(),
|
||||
name: property,
|
||||
end: prev()
|
||||
}),
|
||||
end: prev()
|
||||
}));
|
||||
} else {
|
||||
@@ -1513,16 +1533,22 @@ function parse($TEXT, options) {
|
||||
elements.push(new AST_ObjectKeyVal({
|
||||
start: property_token,
|
||||
quote: property_token.quote,
|
||||
end: prev(),
|
||||
key: property,
|
||||
value: binding_element(used_parameters, symbol_type)
|
||||
value: binding_element(used_parameters, symbol_type),
|
||||
end: prev()
|
||||
}));
|
||||
}
|
||||
}
|
||||
if (is("operator", "=")) {
|
||||
used_parameters.mark_default_assignment(S.token);
|
||||
next();
|
||||
elements[elements.length - 1].default = expression(false);
|
||||
elements[elements.length - 1].value = new AST_DefaultAssign({
|
||||
start: elements[elements.length - 1].value.start,
|
||||
left: elements[elements.length - 1].value,
|
||||
operator: "=",
|
||||
right: expression(false),
|
||||
end: S.token
|
||||
});
|
||||
}
|
||||
}
|
||||
expect("}");
|
||||
@@ -1558,7 +1584,7 @@ function parse($TEXT, options) {
|
||||
next();
|
||||
a.push(new AST_Expansion({
|
||||
start: prev(),
|
||||
expression: binding_element(undefined, AST_SymbolFunarg),
|
||||
expression: expression(false),
|
||||
end: S.token,
|
||||
}));
|
||||
if (!is("punc", ")")) {
|
||||
@@ -1943,7 +1969,7 @@ function parse($TEXT, options) {
|
||||
a.push(new AST_Hole({ start: S.token, end: S.token }));
|
||||
} else if (is("expand", "...")) {
|
||||
next();
|
||||
a.push(new AST_Expansion({start: S.token, expression: expression(),end: S.token}));
|
||||
a.push(new AST_Expansion({start: prev(), expression: expression(),end: S.token}));
|
||||
} else {
|
||||
a.push(expression(false));
|
||||
}
|
||||
@@ -1970,63 +1996,51 @@ function parse($TEXT, options) {
|
||||
start = S.token;
|
||||
var type = start.type;
|
||||
var name = as_property_name();
|
||||
var value;
|
||||
|
||||
// Check property and fetch value
|
||||
if (!is("punc", ":")) {
|
||||
var concise = concise_method_or_getset(name, start);
|
||||
if (concise) {
|
||||
a.push(concise);
|
||||
continue;
|
||||
}
|
||||
if (!(start.type !== name)) {
|
||||
unexpected(S.token);
|
||||
}
|
||||
|
||||
value = new AST_SymbolRef({
|
||||
start: prev(),
|
||||
name: name,
|
||||
end: prev()
|
||||
});
|
||||
} else if (name === null) {
|
||||
unexpected(prev());
|
||||
} else {
|
||||
next(); // `:` - see first condition
|
||||
value = expression(false);
|
||||
}
|
||||
|
||||
if (type == "punc" && start.value == "[") {
|
||||
expect(":");
|
||||
a.push(new AST_ObjectKeyVal({
|
||||
start: start,
|
||||
key: name,
|
||||
value: expression(false),
|
||||
end: prev()
|
||||
}));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for default value and alter value accordingly if necessary
|
||||
if (is("operator", "=")) {
|
||||
next();
|
||||
a.push(new AST_Assign({
|
||||
value = new AST_Assign({
|
||||
start: start,
|
||||
// Symbol class doesn't matter. This is only meant to carry the symbol name into .as_params() since this is not normally valid.
|
||||
left: new AST_SymbolRef({
|
||||
start: start,
|
||||
end: start,
|
||||
name: name
|
||||
}),
|
||||
left: value,
|
||||
operator: "=",
|
||||
right: expression(false),
|
||||
end: prev()
|
||||
}));
|
||||
} else if (!is("punc", ":")) {
|
||||
// It's one of those object destructurings, the value is its own name
|
||||
a.push(new AST_ObjectKeyVal({
|
||||
start: start,
|
||||
key: name,
|
||||
value: new AST_SymbolRef({
|
||||
start: start,
|
||||
end: prev(),
|
||||
name: name
|
||||
}),
|
||||
end: start,
|
||||
}));
|
||||
} else {
|
||||
expect(":");
|
||||
a.push(new AST_ObjectKeyVal({
|
||||
start : start,
|
||||
quote : start.quote,
|
||||
key : name,
|
||||
value : expression(false),
|
||||
end : prev()
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// Create property
|
||||
a.push(new AST_ObjectKeyVal({
|
||||
start: start,
|
||||
quote: start.quote,
|
||||
key: name,
|
||||
value: value,
|
||||
end: prev()
|
||||
}));
|
||||
}
|
||||
next();
|
||||
return new AST_Object({ properties: a })
|
||||
@@ -2493,6 +2507,55 @@ function parse($TEXT, options) {
|
||||
|
||||
if (is("operator") && ASSIGNMENT(val)) {
|
||||
if (is_assignable(left)) {
|
||||
|
||||
var walk = function(node) {
|
||||
var newNode;
|
||||
if (node instanceof AST_Object) {
|
||||
newNode = new AST_Destructuring({
|
||||
start: node.start,
|
||||
names: node.properties.map(walk),
|
||||
is_array: false,
|
||||
end: node.end
|
||||
});
|
||||
node = newNode;
|
||||
} else if (node instanceof AST_Array) {
|
||||
var names = [];
|
||||
|
||||
for (var i = 0; i < node.elements.length; i++) {
|
||||
// Only allow expansion as last element
|
||||
if (node.elements[i] instanceof AST_Expansion) {
|
||||
if (i + 1 !== node.elements.length) {
|
||||
token_error(node.elements[i].start, "SyntaxError: Spread must the be last element in destructuring array");
|
||||
}
|
||||
node.elements[i].expression = walk(node.elements[i].expression);
|
||||
}
|
||||
|
||||
names.push(walk(node.elements[i]));
|
||||
}
|
||||
|
||||
newNode = new AST_Destructuring({
|
||||
start: node.start,
|
||||
names: names,
|
||||
is_array: true,
|
||||
end: node.end
|
||||
});
|
||||
node = newNode;
|
||||
} else if (node instanceof AST_ObjectProperty) {
|
||||
node.value = walk(node.value);
|
||||
} else if (node instanceof AST_Assign) {
|
||||
node = new AST_DefaultAssign({
|
||||
start: node.start,
|
||||
left: node.left,
|
||||
operator: "=",
|
||||
right: node.right,
|
||||
end: node.end
|
||||
});
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
left = walk(left);
|
||||
|
||||
next();
|
||||
return new AST_Assign({
|
||||
start : start,
|
||||
|
||||
@@ -50,6 +50,9 @@ destructuring_objects: {
|
||||
}
|
||||
|
||||
destructuring_objects_trailing_elision: {
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
var {cc,} = foo;
|
||||
}
|
||||
@@ -57,6 +60,9 @@ destructuring_objects_trailing_elision: {
|
||||
}
|
||||
|
||||
nested_destructuring_objects: {
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
const [{a},b] = c;
|
||||
let [{a},b] = c;
|
||||
@@ -66,6 +72,9 @@ nested_destructuring_objects: {
|
||||
}
|
||||
|
||||
destructuring_constdef_in_loops: {
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
for (const [x,y] in pairs);
|
||||
for (const [a] = 0;;);
|
||||
@@ -75,6 +84,9 @@ destructuring_constdef_in_loops: {
|
||||
}
|
||||
|
||||
destructuring_letdef_in_loops: {
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
for (let [x,y] in pairs);
|
||||
for (let [a] = 0;;);
|
||||
@@ -84,6 +96,9 @@ destructuring_letdef_in_loops: {
|
||||
}
|
||||
|
||||
destructuring_vardef_in_loops: {
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
for (var [x,y] in pairs);
|
||||
for (var [a] = 0;;);
|
||||
@@ -200,3 +215,39 @@ destructuring_remove_unused_2: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object_destructuring_may_need_parentheses: {
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
({a, b} = {a: 1, b: 2});
|
||||
}
|
||||
expect_exact: "({a,b}={a:1,b:2});"
|
||||
}
|
||||
|
||||
destructuring_with_undefined_as_default_assignment: {
|
||||
options = {
|
||||
evaluate: true
|
||||
}
|
||||
input: {
|
||||
[foo = undefined] = bar;
|
||||
[foo = void 0] = bar;
|
||||
}
|
||||
expect: {
|
||||
[foo] = bar;
|
||||
[foo] = bar;
|
||||
}
|
||||
}
|
||||
|
||||
destructuring_dont_evaluate_with_undefined_as_default_assignment: {
|
||||
options = {
|
||||
evaluate: false
|
||||
}
|
||||
input: {
|
||||
[foo = undefined] = bar;
|
||||
}
|
||||
expect: {
|
||||
[foo = void 0] = bar;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,11 @@ compress_new_function: {
|
||||
|
||||
compress_new_function_with_destruct: {
|
||||
options = {
|
||||
unsafe: true
|
||||
unsafe: true,
|
||||
ecma: 6
|
||||
}
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
new Function("aa, [bb]", 'return aa;');
|
||||
|
||||
@@ -82,3 +82,19 @@ new_with_unary_prefix: {
|
||||
}
|
||||
expect_exact: 'var bar=(+new Date).toString(32);';
|
||||
}
|
||||
|
||||
new_with_assignement_expression: {
|
||||
options = {
|
||||
evaluate: true
|
||||
}
|
||||
input: {
|
||||
var a;
|
||||
new x(a = 5 * 2, b = [1, 2, 3], c = {a: "a", b: "b", cd: "c" + "d"});
|
||||
new y([a, b] = [3, 4]);
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
new x(a = 10, b = [1, 2, 3], c = {a: "a", b: "b", cd: "cd"});
|
||||
new y([a, b] = [3, 4]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +170,9 @@ shorthand_properties: {
|
||||
}
|
||||
|
||||
concise_methods: {
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
x = {
|
||||
foo(a, b) {
|
||||
@@ -299,6 +302,9 @@ concise_methods_and_mangle_props: {
|
||||
}
|
||||
|
||||
concise_generators: {
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
x = {
|
||||
*foo(a, b) {
|
||||
|
||||
@@ -119,6 +119,9 @@ destructuring_arguments_2: {
|
||||
}
|
||||
|
||||
destructuring_arguments_3: {
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
function fn3({x: {y: {z: {} = 42}}}) {}
|
||||
const { cover = (function () {}), xCover = (0, function() {}) } = {};
|
||||
@@ -129,6 +132,9 @@ destructuring_arguments_3: {
|
||||
}
|
||||
|
||||
default_arguments: {
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
function x(a = 6) { }
|
||||
function x(a = (6 + 5)) { }
|
||||
@@ -138,6 +144,9 @@ default_arguments: {
|
||||
}
|
||||
|
||||
default_values_in_destructurings: {
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
function x({a=(4), b}) {}
|
||||
function x([b, c=(12)]) {}
|
||||
|
||||
@@ -376,4 +376,4 @@ template_ending_with_newline: {
|
||||
`;
|
||||
};
|
||||
} expect_exact: "function foo(e){return`this is a template string!\n`}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
catch_destructuring_with_sequence: {
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
try {
|
||||
throw {};
|
||||
|
||||
@@ -27,4 +27,253 @@ describe("arguments", function() {
|
||||
assert.strictEqual(ast.body[0].body[0].uses_arguments, true);
|
||||
assert.strictEqual(ast.body[0].body[0].body[0].uses_arguments, false);
|
||||
});
|
||||
});
|
||||
|
||||
it("Should parse a function containing default assignment correctly", function() {
|
||||
var ast = UglifyJS.parse("function foo(a = 123) {}");
|
||||
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
|
||||
assert.strictEqual(ast.body[0].argnames.length, 1);
|
||||
|
||||
// First argument
|
||||
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_DefaultAssign);
|
||||
assert(ast.body[0].argnames[0].left instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].argnames[0].operator, "=");
|
||||
assert(ast.body[0].argnames[0].right instanceof UglifyJS.AST_Number);
|
||||
|
||||
ast = UglifyJS.parse("function foo(a = a) {}");
|
||||
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
|
||||
assert.strictEqual(ast.body[0].argnames.length, 1);
|
||||
|
||||
// First argument
|
||||
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_DefaultAssign);
|
||||
assert(ast.body[0].argnames[0].left instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].argnames[0].operator, "=");
|
||||
assert(ast.body[0].argnames[0].right instanceof UglifyJS.AST_SymbolRef);
|
||||
});
|
||||
|
||||
it("Should parse a function containing default assignments in destructuring correctly", function() {
|
||||
var ast = UglifyJS.parse("function foo([a = 123]) {}");
|
||||
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
|
||||
assert.strictEqual(ast.body[0].argnames.length, 1);
|
||||
|
||||
// First argument
|
||||
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
|
||||
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_DefaultAssign);
|
||||
assert(ast.body[0].argnames[0].names[0].left instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[0].operator, "=");
|
||||
assert(ast.body[0].argnames[0].names[0].right instanceof UglifyJS.AST_Number);
|
||||
|
||||
|
||||
ast = UglifyJS.parse("function foo({a = 123}) {}");
|
||||
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
|
||||
assert.strictEqual(ast.body[0].argnames.length, 1);
|
||||
|
||||
// First argument
|
||||
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
|
||||
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
|
||||
|
||||
// Property a of first argument
|
||||
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_DefaultAssign);
|
||||
assert(ast.body[0].argnames[0].names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[0].value.operator, "=");
|
||||
assert(ast.body[0].argnames[0].names[0].value.right instanceof UglifyJS.AST_Number);
|
||||
|
||||
|
||||
ast = UglifyJS.parse("function foo({a: a = 123}) {}");
|
||||
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
|
||||
assert.strictEqual(ast.body[0].argnames.length, 1);
|
||||
|
||||
// First argument
|
||||
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
|
||||
|
||||
// Content destructuring of first argument
|
||||
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
|
||||
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_DefaultAssign);
|
||||
|
||||
// Property a of first argument
|
||||
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_DefaultAssign);
|
||||
assert(ast.body[0].argnames[0].names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[0].value.operator, "=");
|
||||
assert(ast.body[0].argnames[0].names[0].value.right instanceof UglifyJS.AST_Number);
|
||||
});
|
||||
|
||||
it("Should parse a function containing default assignments in complex destructuring correctly", function() {
|
||||
var ast = UglifyJS.parse("function foo([a, [b = 123]]){}");
|
||||
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
|
||||
assert.strictEqual(ast.body[0].argnames.length, 1);
|
||||
|
||||
// Check first argument
|
||||
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names.length, 2);
|
||||
|
||||
// Check whole destructuring structure of first argument
|
||||
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[1].is_array, true);
|
||||
|
||||
// Check content of second destructuring element (which is the nested destructuring pattern)
|
||||
assert(ast.body[0].argnames[0].names[1].names[0] instanceof UglifyJS.AST_DefaultAssign);
|
||||
assert(ast.body[0].argnames[0].names[1].names[0].left instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[1].names[0].operator, "=");
|
||||
assert(ast.body[0].argnames[0].names[1].names[0].right instanceof UglifyJS.AST_Number);
|
||||
|
||||
|
||||
ast = UglifyJS.parse("function foo([a, {b: c = 123}]){}");
|
||||
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
|
||||
assert.strictEqual(ast.body[0].argnames.length, 1);
|
||||
|
||||
// Check first argument
|
||||
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names.length, 2);
|
||||
|
||||
// Check whole destructuring structure of first argument
|
||||
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[1].is_array, false);
|
||||
|
||||
// Check content of second destructuring element (which is the nested destructuring pattern)
|
||||
assert(ast.body[0].argnames[0].names[1].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[1].names[0].key, "b");
|
||||
assert(ast.body[0].argnames[0].names[1].names[0].value instanceof UglifyJS.AST_DefaultAssign);
|
||||
|
||||
// Property b of second argument
|
||||
assert(ast.body[0].argnames[0].names[1].names[0].value instanceof UglifyJS.AST_DefaultAssign);
|
||||
assert(ast.body[0].argnames[0].names[1].names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[1].names[0].value.operator, "=");
|
||||
assert(ast.body[0].argnames[0].names[1].names[0].value.right instanceof UglifyJS.AST_Number);
|
||||
|
||||
|
||||
ast = UglifyJS.parse("function foo({a, b: {b = 123}}){}");
|
||||
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
|
||||
assert.strictEqual(ast.body[0].argnames.length, 1);
|
||||
|
||||
// Check first argument
|
||||
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names.length, 2);
|
||||
|
||||
// Check whole destructuring structure of first argument
|
||||
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
|
||||
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_ObjectKeyVal);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[1].key, "b");
|
||||
assert(ast.body[0].argnames[0].names[1].value instanceof UglifyJS.AST_Destructuring);
|
||||
|
||||
// Check content of nested destructuring in first parameter
|
||||
var content = ast.body[0].argnames[0].names[1].value
|
||||
assert.strictEqual(content.is_array, false);
|
||||
assert.strictEqual(content.names.length, 1);
|
||||
assert(content.names[0] instanceof UglifyJS.AST_ObjectKeyVal);
|
||||
assert.strictEqual(content.names[0].key, "b");
|
||||
assert(content.names[0].value instanceof UglifyJS.AST_DefaultAssign);
|
||||
assert(content.names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert.strictEqual(content.names[0].value.operator, "=");
|
||||
assert(content.names[0].value.right instanceof UglifyJS.AST_Number);
|
||||
|
||||
|
||||
ast = UglifyJS.parse("function foo({a: {b = 123}}){}");
|
||||
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
|
||||
assert.strictEqual(ast.body[0].argnames.length, 1);
|
||||
|
||||
// Check first argument
|
||||
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
|
||||
|
||||
// Check whole destructuring structure of first argument
|
||||
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
|
||||
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_Destructuring);
|
||||
|
||||
// Check content of nested destructuring
|
||||
content = ast.body[0].argnames[0].names[0].value
|
||||
assert.strictEqual(content.is_array, false);
|
||||
assert.strictEqual(content.names.length, 1);
|
||||
assert(content.names[0] instanceof UglifyJS.AST_ObjectKeyVal);
|
||||
assert.strictEqual(content.names[0].key, "b");
|
||||
assert(content.names[0].value instanceof UglifyJS.AST_DefaultAssign);
|
||||
assert(content.names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert.strictEqual(content.names[0].value.operator, "=");
|
||||
assert(content.names[0].value.right instanceof UglifyJS.AST_Number);
|
||||
});
|
||||
|
||||
it("Should parse spread correctly", function() {
|
||||
var ast = UglifyJS.parse("function foo(a, b, ...c){}");
|
||||
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
|
||||
assert.strictEqual(ast.body[0].argnames.length, 3);
|
||||
|
||||
// Check parameters
|
||||
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert(ast.body[0].argnames[1] instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert(ast.body[0].argnames[2] instanceof UglifyJS.AST_Expansion);
|
||||
assert(ast.body[0].argnames[2].expression instanceof UglifyJS.AST_SymbolFunarg);
|
||||
|
||||
|
||||
ast = UglifyJS.parse("function foo([a, b, ...c]){}");
|
||||
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
|
||||
assert.strictEqual(ast.body[0].argnames.length, 1);
|
||||
|
||||
// Check first parameter
|
||||
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
|
||||
|
||||
// Check content first parameter
|
||||
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert(ast.body[0].argnames[0].names[2] instanceof UglifyJS.AST_Expansion);
|
||||
assert(ast.body[0].argnames[0].names[2].expression instanceof UglifyJS.AST_SymbolFunarg);
|
||||
|
||||
|
||||
ast = UglifyJS.parse("function foo([a, b, [c, ...d]]){}");
|
||||
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
|
||||
assert.strictEqual(ast.body[0].argnames.length, 1);
|
||||
|
||||
// Check first parameter
|
||||
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
|
||||
|
||||
// Check content outer destructuring array
|
||||
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert(ast.body[0].argnames[0].names[2] instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[2].is_array, true);
|
||||
|
||||
// Check content nested destructuring array
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[2].names.length, 2);
|
||||
assert(ast.body[0].argnames[0].names[2].names[0] instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert(ast.body[0].argnames[0].names[2].names[1] instanceof UglifyJS.AST_Expansion);
|
||||
assert(ast.body[0].argnames[0].names[2].names[1].expression instanceof UglifyJS.AST_SymbolFunarg);
|
||||
|
||||
|
||||
ast = UglifyJS.parse("function foo({a: [b, ...c]}){}");
|
||||
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
|
||||
assert.strictEqual(ast.body[0].argnames.length, 1);
|
||||
|
||||
// Check first parameter
|
||||
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
|
||||
|
||||
// Check outer destructuring object
|
||||
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
|
||||
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
|
||||
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[0].value.is_array, true);
|
||||
|
||||
// Check content nested destructuring array
|
||||
assert.strictEqual(ast.body[0].argnames[0].names[0].value.names.length, 2);
|
||||
assert(ast.body[0].argnames[0].names[0].value.names[0] instanceof UglifyJS.AST_SymbolFunarg);
|
||||
assert(ast.body[0].argnames[0].names[0].value.names[1] instanceof UglifyJS.AST_Expansion);
|
||||
assert(ast.body[0].argnames[0].names[0].value.names[1].expression instanceof UglifyJS.AST_SymbolFunarg);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -80,4 +80,306 @@ describe("Arrow functions", function() {
|
||||
assert.throws(test(tests[i]), error);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should parse a function containing default assignment correctly", function() {
|
||||
var ast = uglify.parse("var a = (a = 123) => {}");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert.strictEqual(ast.body[0].definitions.length, 1);
|
||||
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
|
||||
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
|
||||
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
|
||||
|
||||
// First argument
|
||||
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_DefaultAssign);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].left instanceof uglify.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].operator, "=");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].right instanceof uglify.AST_Number);
|
||||
|
||||
ast = uglify.parse("var a = (a = a) => {}");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert.strictEqual(ast.body[0].definitions.length, 1);
|
||||
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
|
||||
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
|
||||
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
|
||||
|
||||
// First argument
|
||||
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_DefaultAssign);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].left instanceof uglify.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].operator, "=");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].right instanceof uglify.AST_SymbolRef);
|
||||
});
|
||||
|
||||
it("Should parse a function containing default assignments in destructuring correctly", function() {
|
||||
var ast = uglify.parse("var a = ([a = 123]) => {}");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert.strictEqual(ast.body[0].definitions.length, 1);
|
||||
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
|
||||
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
|
||||
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
|
||||
|
||||
// First argument
|
||||
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_DefaultAssign);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].left instanceof uglify.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].operator, "=");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].right instanceof uglify.AST_Number);
|
||||
|
||||
|
||||
ast = uglify.parse("var a = ({a = 123}) => {}");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert.strictEqual(ast.body[0].definitions.length, 1);
|
||||
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
|
||||
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
|
||||
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
|
||||
|
||||
// First argument
|
||||
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
|
||||
|
||||
// First object element in first argument
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_DefaultAssign);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.left instanceof uglify.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.operator, "=");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.right instanceof uglify.AST_Number);
|
||||
|
||||
|
||||
ast = uglify.parse("var a = ({a: a = 123}) => {}");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert.strictEqual(ast.body[0].definitions.length, 1);
|
||||
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
|
||||
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
|
||||
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
|
||||
|
||||
// First argument
|
||||
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
|
||||
|
||||
// Content destructuring of first argument
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectProperty);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_DefaultAssign);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.left instanceof uglify.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.operator, "=");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.right instanceof uglify.AST_Number);
|
||||
});
|
||||
|
||||
it("Should parse a function containing default assignments in complex destructuring correctly", function() {
|
||||
var ast = uglify.parse("var a = ([a, [b = 123]]) => {}");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert.strictEqual(ast.body[0].definitions.length, 1);
|
||||
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
|
||||
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
|
||||
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
|
||||
|
||||
// Check first argument
|
||||
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 2);
|
||||
|
||||
// Check whole destructuring structure of first argument
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].is_array, true);
|
||||
|
||||
// Check content of second destructuring element (which is the nested destructuring pattern)
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0] instanceof uglify.AST_DefaultAssign);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].left instanceof uglify.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].operator, "=");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].right instanceof uglify.AST_Number);
|
||||
|
||||
|
||||
ast = uglify.parse("var a = ([a, {b: c = 123}]) => {}");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert.strictEqual(ast.body[0].definitions.length, 1);
|
||||
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
|
||||
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
|
||||
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
|
||||
|
||||
// Check first argument
|
||||
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 2);
|
||||
|
||||
// Check whole destructuring structure of first argument
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].is_array, false);
|
||||
|
||||
// Check content of second destructuring element (which is the nested destructuring pattern)
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0] instanceof uglify.AST_ObjectKeyVal);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].key, "b");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value instanceof uglify.AST_DefaultAssign);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value.left instanceof uglify.AST_SymbolFunarg);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value.operator, "=");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value.right instanceof uglify.AST_Number);
|
||||
|
||||
|
||||
ast = uglify.parse("var a = ({a, b: {b = 123}}) => {}");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert.strictEqual(ast.body[0].definitions.length, 1);
|
||||
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
|
||||
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
|
||||
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
|
||||
|
||||
// Check first argument
|
||||
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 2);
|
||||
|
||||
// First argument, property 1
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_SymbolFunarg);
|
||||
|
||||
// First argument, property 2
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_ObjectKeyVal);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].key, "b");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[1].value instanceof uglify.AST_Destructuring);
|
||||
|
||||
// Check content of nested destructuring
|
||||
var content = ast.body[0].definitions[0].value.argnames[0].names[1].value
|
||||
assert.strictEqual(content.is_array, false);
|
||||
assert.strictEqual(content.names.length, 1);
|
||||
assert(content.names[0] instanceof uglify.AST_ObjectKeyVal);
|
||||
|
||||
// Content of first property in nested destructuring
|
||||
assert.strictEqual(content.names[0].key, "b");
|
||||
assert(content.names[0].value instanceof uglify.AST_DefaultAssign);
|
||||
assert(content.names[0].value.left instanceof uglify.AST_SymbolFunarg);
|
||||
assert.strictEqual(content.names[0].value.operator, "=");
|
||||
assert(content.names[0].value.right instanceof uglify.AST_Number);
|
||||
|
||||
|
||||
ast = uglify.parse("var a = ({a: {b = 123}}) => {}");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert.strictEqual(ast.body[0].definitions.length, 1);
|
||||
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
|
||||
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
|
||||
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
|
||||
|
||||
// Check first argument
|
||||
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
|
||||
|
||||
// Check whole destructuring structure of first argument
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_Destructuring);
|
||||
|
||||
// Check content of nested destructuring
|
||||
content = ast.body[0].definitions[0].value.argnames[0].names[0].value
|
||||
assert.strictEqual(content.is_array, false);
|
||||
assert.strictEqual(content.names.length, 1);
|
||||
assert(content.names[0] instanceof uglify.AST_ObjectKeyVal);
|
||||
|
||||
// Check first property of nested destructuring
|
||||
assert.strictEqual(content.names[0].key, "b");
|
||||
assert(content.names[0].value instanceof uglify.AST_DefaultAssign);
|
||||
assert(content.names[0].value.left instanceof uglify.AST_SymbolFunarg);
|
||||
assert.strictEqual(content.names[0].value.operator, "=");
|
||||
assert(content.names[0].value.right instanceof uglify.AST_Number);
|
||||
});
|
||||
|
||||
it("Should parse spread correctly", function() {
|
||||
var ast = uglify.parse("var a = (a, b, ...c) => {}");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert.strictEqual(ast.body[0].definitions.length, 1);
|
||||
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
|
||||
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
|
||||
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 3);
|
||||
|
||||
// Check parameters
|
||||
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_SymbolFunarg);
|
||||
assert(ast.body[0].definitions[0].value.argnames[1] instanceof uglify.AST_SymbolFunarg);
|
||||
assert(ast.body[0].definitions[0].value.argnames[2] instanceof uglify.AST_Expansion);
|
||||
assert(ast.body[0].definitions[0].value.argnames[2].expression instanceof uglify.AST_SymbolFunarg);
|
||||
|
||||
|
||||
ast = uglify.parse("var a = ([a, b, ...c]) => {}");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert.strictEqual(ast.body[0].definitions.length, 1);
|
||||
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
|
||||
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
|
||||
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
|
||||
|
||||
// Check first parameter
|
||||
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
|
||||
|
||||
// Check content first parameter
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_SymbolFunarg);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[2] instanceof uglify.AST_Expansion);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[2].expression instanceof uglify.AST_SymbolFunarg);
|
||||
|
||||
|
||||
ast = uglify.parse("var a = ([a, b, [c, ...d]]) => {}");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert.strictEqual(ast.body[0].definitions.length, 1);
|
||||
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
|
||||
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
|
||||
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
|
||||
|
||||
// Check first parameter
|
||||
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
|
||||
|
||||
// Check content outer destructuring array
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_SymbolFunarg);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[2] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[2].is_array, true);
|
||||
|
||||
// Check content nested destructuring array
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[2].names.length, 2);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[2].names[0] instanceof uglify.AST_SymbolFunarg);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[2].names[1] instanceof uglify.AST_Expansion);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[2].names[1].expression instanceof uglify.AST_SymbolFunarg);
|
||||
|
||||
|
||||
ast = uglify.parse("var a = ({a: [b, ...c]}) => {}");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert.strictEqual(ast.body[0].definitions.length, 1);
|
||||
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
|
||||
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
|
||||
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
|
||||
|
||||
// Check first parameter
|
||||
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
|
||||
|
||||
// Check outer destructuring object
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.is_array, true);
|
||||
|
||||
// Check content nested destructuring array
|
||||
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.names.length, 2);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.names[0] instanceof uglify.AST_SymbolFunarg);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.names[1] instanceof uglify.AST_Expansion);
|
||||
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.names[1].expression instanceof uglify.AST_SymbolFunarg);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
138
test/mocha/destructuring.js
Normal file
138
test/mocha/destructuring.js
Normal file
@@ -0,0 +1,138 @@
|
||||
var assert = require("assert");
|
||||
var uglify = require("../../");
|
||||
|
||||
describe("Destructuring", function() {
|
||||
it("Should generate similar trees for destructuring in left hand side expressions, definitions, functions and arrow functions", function() {
|
||||
var patterns = [
|
||||
"[]",
|
||||
"{}",
|
||||
|
||||
"[a, b, c]",
|
||||
"{a: b, c: d}",
|
||||
"{a}",
|
||||
"{a, b}",
|
||||
|
||||
"{a: {}}",
|
||||
"{a: []}",
|
||||
"[{}]",
|
||||
"[[]]",
|
||||
"{a: {b}}",
|
||||
|
||||
// Can't do `a = 123` with lhs expression, so only test in destructuring
|
||||
"[foo = bar]",
|
||||
"{a = 123}",
|
||||
"[{foo: abc = 123}]",
|
||||
"{foo: [abc = 123]}",
|
||||
|
||||
"[...foo]",
|
||||
"[...{}]",
|
||||
"[...[]]"
|
||||
|
||||
// Can't do `...` because that is an invalid lhs expression, spread in array destructuring should be fine though
|
||||
];
|
||||
|
||||
var types = [
|
||||
{
|
||||
name: "lhs",
|
||||
symbolType: uglify.AST_SymbolRef,
|
||||
tree: function (ast) {
|
||||
return ast.body[0].body.left;
|
||||
},
|
||||
generate: function (code) {
|
||||
return "(" + code + " = a)";
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "var",
|
||||
symbolType: uglify.AST_SymbolVar,
|
||||
tree: function (ast) {
|
||||
return ast.body[0].definitions[0].name;
|
||||
},
|
||||
generate: function (code) {
|
||||
return "var " + code + " = a";
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "function",
|
||||
symbolType: uglify.AST_SymbolFunarg,
|
||||
tree: function (ast) {
|
||||
return ast.body[0].argnames[0];
|
||||
},
|
||||
generate: function (code) {
|
||||
return "function a(" + code + ") {}";
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "arrow",
|
||||
symbolType: uglify.AST_SymbolFunarg,
|
||||
tree: function (ast) {
|
||||
return ast.body[0].definitions[0].value.argnames[0];
|
||||
},
|
||||
generate: function (code) {
|
||||
return "var a = (" + code + ") => {}";
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
var walker = function(type, ref, code, result) {
|
||||
var w = new uglify.TreeWalker(function(node) {
|
||||
if (w.parent() instanceof uglify.AST_DefaultAssign &&
|
||||
w.parent().right === node
|
||||
) {
|
||||
return true; // Don't check the content of the default assignment
|
||||
|
||||
} else if (node instanceof uglify.AST_Symbol) {
|
||||
assert(node instanceof type.symbolType,
|
||||
node.TYPE + " while " + type.symbolType.TYPE + " expected at pos " +
|
||||
node.start.pos + " in `" + code + "` (" + ref + ")"
|
||||
);
|
||||
|
||||
result.push([
|
||||
new uglify.AST_Symbol({
|
||||
start: node.start,
|
||||
name: node.name,
|
||||
end: node.end
|
||||
}),
|
||||
w.parent()
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
result.push([node, w.parent()]);
|
||||
});
|
||||
|
||||
return w;
|
||||
};
|
||||
|
||||
var getNodeType = function(node) {
|
||||
return node[0].TYPE + (node[1] ? " " + node[1].TYPE : "");
|
||||
}
|
||||
|
||||
for (var i = 0; i < patterns.length; i++) {
|
||||
var results = [];
|
||||
|
||||
for (var j = 0; j < types.length; j++) {
|
||||
var code = types[j].generate(patterns[i])
|
||||
var ast = types[j].tree(
|
||||
uglify.parse(code)
|
||||
);
|
||||
results.push([]);
|
||||
ast.walk(walker(
|
||||
types[j],
|
||||
"`" + patterns[i] + "` on " + types[j].name,
|
||||
code,
|
||||
results[j]
|
||||
));
|
||||
|
||||
if (j > 0) {
|
||||
assert.deepEqual(
|
||||
results[0].map(getNodeType),
|
||||
results[j].map(getNodeType),
|
||||
"AST disagree on " + patterns[i] + " with " + types[j].name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -47,64 +47,83 @@ describe("Function", function() {
|
||||
assert.equal(destruct3.is_array, false);
|
||||
assert.equal(destruct4.is_array, true);
|
||||
|
||||
var aAndB = [
|
||||
['SymbolFunarg', 'a'],
|
||||
['SymbolFunarg', 'b']
|
||||
];
|
||||
|
||||
// destruct 1
|
||||
assert.deepEqual(
|
||||
[
|
||||
destruct1.names[0].TYPE,
|
||||
destruct1.names[0].name
|
||||
destruct1.names[0].key,
|
||||
destruct1.names[0].value.name
|
||||
],
|
||||
aAndB[0]);
|
||||
['ObjectKeyVal', 'a', 'a']
|
||||
);
|
||||
assert.deepEqual(
|
||||
[
|
||||
destruct1.names[1].TYPE,
|
||||
destruct1.names[1].name
|
||||
destruct1.names[1].key,
|
||||
destruct1.names[1].value.name
|
||||
],
|
||||
aAndB[1]);
|
||||
['ObjectKeyVal', 'b', 'b']
|
||||
);
|
||||
|
||||
// destruct 2
|
||||
assert.deepEqual(
|
||||
[
|
||||
destruct2.names[0].TYPE,
|
||||
destruct2.names[0].name
|
||||
],
|
||||
aAndB[0]);
|
||||
['SymbolFunarg', 'a']
|
||||
);
|
||||
assert.deepEqual(
|
||||
[
|
||||
destruct2.names[1].names[0].TYPE,
|
||||
destruct2.names[1].names[0].name
|
||||
],
|
||||
aAndB[1]);
|
||||
['SymbolFunarg', 'b']
|
||||
);
|
||||
|
||||
// destruct 3
|
||||
assert.strictEqual(typeof destruct3.names[0].key, "string");
|
||||
assert.strictEqual(destruct3.names[0].key, "a");
|
||||
assert.strictEqual(destruct3.names[0].value.TYPE, "SymbolFunarg");
|
||||
assert.strictEqual(destruct3.names[0].value.name, "a");
|
||||
assert.strictEqual(typeof destruct3.names[1].key, "string");
|
||||
assert.strictEqual(destruct3.names[1].key, "b");
|
||||
assert.strictEqual(destruct3.names[1].value.TYPE, "SymbolFunarg");
|
||||
assert.strictEqual(destruct3.names[1].value.name, "b");
|
||||
|
||||
// destruct 4
|
||||
assert.deepEqual(
|
||||
[
|
||||
destruct4.names[0].TYPE,
|
||||
destruct4.names[0].name
|
||||
],
|
||||
aAndB[0]);
|
||||
['SymbolFunarg', 'a']
|
||||
);
|
||||
assert.strictEqual(destruct4.names[1].TYPE, "Destructuring");
|
||||
assert.deepEqual(
|
||||
[
|
||||
destruct4.names[1].names[0].TYPE,
|
||||
destruct4.names[1].names[0].name
|
||||
],
|
||||
aAndB[1]);
|
||||
['SymbolFunarg', 'b']
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
get_args(destr_fun1.args_as_names()),
|
||||
aAndB);
|
||||
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
|
||||
);
|
||||
assert.deepEqual(
|
||||
get_args(destr_fun2.args_as_names()),
|
||||
aAndB);
|
||||
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
|
||||
);
|
||||
assert.deepEqual(
|
||||
get_args(destr_fun3.args_as_names()),
|
||||
aAndB);
|
||||
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
|
||||
);
|
||||
assert.deepEqual(
|
||||
get_args(destr_fun4.args_as_names()),
|
||||
aAndB);
|
||||
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
|
||||
);
|
||||
|
||||
// Making sure we don't accidentally accept things which
|
||||
// Aren't argument destructurings
|
||||
|
||||
@@ -7,17 +7,26 @@ describe("Left-hand side expressions", function () {
|
||||
|
||||
assert.equal(decls.body[0].TYPE, 'Var');
|
||||
assert.equal(decls.body[0].definitions.length, 2);
|
||||
|
||||
// Item 1
|
||||
assert.equal(decls.body[0].definitions[0].name.TYPE, 'Destructuring');
|
||||
assert.equal(decls.body[0].definitions[0].value.TYPE, 'SymbolRef');
|
||||
|
||||
// Item 2
|
||||
assert.equal(decls.body[0].definitions[1].name.TYPE, 'Destructuring');
|
||||
assert.equal(decls.body[0].definitions[1].value.TYPE, 'SymbolRef');
|
||||
|
||||
var nested_def = uglify.parse('var [{x}] = foo').body[0].definitions[0];
|
||||
|
||||
assert.equal(nested_def.name.names[0].names[0].TYPE, 'SymbolVar');
|
||||
assert.equal(nested_def.name.names[0].names[0].name, 'x');
|
||||
assert.equal(nested_def.name.names[0].names[0].TYPE, 'ObjectKeyVal');
|
||||
assert.equal(nested_def.name.names[0].names[0].value.TYPE, 'SymbolVar');
|
||||
assert.equal(nested_def.name.names[0].names[0].key, 'x');
|
||||
assert.equal(nested_def.name.names[0].names[0].value.name, 'x');
|
||||
|
||||
var holey_def = uglify.parse('const [,,third] = [1,2,3]').body[0].definitions[0];
|
||||
|
||||
assert.equal(holey_def.name.names[0].TYPE, 'Hole');
|
||||
assert.equal(holey_def.name.names[1].TYPE, 'Hole');
|
||||
assert.equal(holey_def.name.names[2].TYPE, 'SymbolConst');
|
||||
|
||||
var expanding_def = uglify.parse('var [first, ...rest] = [1,2,3]').body[0].definitions[0];
|
||||
@@ -26,4 +35,231 @@ describe("Left-hand side expressions", function () {
|
||||
assert.equal(expanding_def.name.names[1].TYPE, 'Expansion');
|
||||
assert.equal(expanding_def.name.names[1].expression.TYPE, 'SymbolVar');
|
||||
});
|
||||
|
||||
it("Parser should use AST_Array for array literals", function() {
|
||||
var ast = uglify.parse('["foo", "bar"]');
|
||||
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
|
||||
assert(ast.body[0].body instanceof uglify.AST_Array);
|
||||
|
||||
ast = uglify.parse('a = ["foo"]');
|
||||
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
|
||||
|
||||
assert(ast.body[0].body instanceof uglify.AST_Assign);
|
||||
assert(ast.body[0].body.left instanceof uglify.AST_SymbolRef);
|
||||
assert.equal(ast.body[0].body.operator, "=");
|
||||
assert(ast.body[0].body.right instanceof uglify.AST_Array);
|
||||
});
|
||||
|
||||
it("Parser should use AST_Object for object literals", function() {
|
||||
var ast = uglify.parse('({foo: "bar"})');
|
||||
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
|
||||
assert(ast.body[0].body instanceof uglify.AST_Object);
|
||||
|
||||
// This example should be fine though
|
||||
ast = uglify.parse('a = {foo: "bar"}');
|
||||
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
|
||||
|
||||
assert(ast.body[0].body instanceof uglify.AST_Assign);
|
||||
assert(ast.body[0].body.left instanceof uglify.AST_SymbolRef);
|
||||
assert.equal(ast.body[0].body.operator, "=");
|
||||
assert(ast.body[0].body.right instanceof uglify.AST_Object);
|
||||
});
|
||||
|
||||
it("Parser should use AST_Destructuring for array assignment patterns", function() {
|
||||
var ast = uglify.parse('[foo, bar] = [1, 2]');
|
||||
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
|
||||
|
||||
assert(ast.body[0].body instanceof uglify.AST_Assign);
|
||||
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].body.left.is_array, true);
|
||||
assert.equal(ast.body[0].body.operator, "=");
|
||||
assert(ast.body[0].body.right instanceof uglify.AST_Array);
|
||||
});
|
||||
|
||||
it("Parser should use AST_Destructuring for object assignment patterns", function() {
|
||||
var ast = uglify.parse('({a: b, b: c} = {b: "c", c: "d"})');
|
||||
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
|
||||
|
||||
assert(ast.body[0].body instanceof uglify.AST_Assign);
|
||||
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].body.left.is_array, false);
|
||||
assert.equal(ast.body[0].body.operator, "=");
|
||||
assert(ast.body[0].body.right instanceof uglify.AST_Object);
|
||||
});
|
||||
|
||||
it("Parser should be able to handle nested destructuring", function() {
|
||||
var ast = uglify.parse('[{a,b},[d, e, f, {g, h}]] = [{a: 1, b: 2}, [3, 4, 5, {g: 6, h: 7}]]');
|
||||
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
|
||||
|
||||
assert(ast.body[0].body instanceof uglify.AST_Assign);
|
||||
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].body.left.is_array, true);
|
||||
assert.equal(ast.body[0].body.operator, "=");
|
||||
assert(ast.body[0].body.right instanceof uglify.AST_Array);
|
||||
|
||||
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].body.left.names[0].is_array, false);
|
||||
|
||||
assert(ast.body[0].body.left.names[1] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].body.left.names[1].is_array, true);
|
||||
|
||||
assert(ast.body[0].body.left.names[1].names[3] instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].body.left.names[1].names[3].is_array, false);
|
||||
});
|
||||
|
||||
it("Should handle spread operator in destructuring", function() {
|
||||
var ast = uglify.parse("[a, b, ...c] = [1, 2, 3, 4, 5]");
|
||||
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
|
||||
|
||||
assert(ast.body[0].body instanceof uglify.AST_Assign);
|
||||
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].body.left.is_array, true);
|
||||
assert.equal(ast.body[0].body.operator, "=");
|
||||
assert(ast.body[0].body.right instanceof uglify.AST_Array);
|
||||
|
||||
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_SymbolRef);
|
||||
assert(ast.body[0].body.left.names[1] instanceof uglify.AST_SymbolRef);
|
||||
|
||||
assert(ast.body[0].body.left.names[2] instanceof uglify.AST_Expansion);
|
||||
});
|
||||
|
||||
it("Should handle default assignments in destructuring", function() {
|
||||
var ast = uglify.parse("({x: v, z = z + 5} = obj);");
|
||||
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
|
||||
|
||||
assert(ast.body[0].body instanceof uglify.AST_Assign);
|
||||
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].body.left.is_array, false);
|
||||
assert.equal(ast.body[0].body.operator, "=");
|
||||
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
|
||||
|
||||
assert(ast.body[0].body.left.names[0].value instanceof uglify.AST_SymbolRef);
|
||||
assert.strictEqual(ast.body[0].body.left.names[0].start.value, "x");
|
||||
|
||||
assert(ast.body[0].body.left.names[1].value instanceof uglify.AST_DefaultAssign);
|
||||
assert.strictEqual(ast.body[0].body.left.names[1].start.value, "z");
|
||||
assert(ast.body[0].body.left.names[1].value.left instanceof uglify.AST_SymbolRef);
|
||||
assert.strictEqual(ast.body[0].body.left.names[1].value.operator, "=");
|
||||
assert(ast.body[0].body.left.names[1].value.right instanceof uglify.AST_Binary);
|
||||
|
||||
|
||||
ast = uglify.parse("({x = 123} = obj);");
|
||||
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
|
||||
assert(ast.body[0].body instanceof uglify.AST_Assign);
|
||||
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].body.left.is_array, false);
|
||||
assert.equal(ast.body[0].body.operator, "=");
|
||||
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
|
||||
|
||||
assert(ast.body[0].body.left.names[0].value instanceof uglify.AST_DefaultAssign);
|
||||
assert.strictEqual(ast.body[0].body.left.names[0].value.operator, "=");
|
||||
assert(ast.body[0].body.left.names[0].value.left instanceof uglify.AST_SymbolRef);
|
||||
|
||||
|
||||
ast = uglify.parse("[x, y = 5] = foo");
|
||||
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
|
||||
|
||||
assert(ast.body[0].body instanceof uglify.AST_Assign);
|
||||
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].body.left.is_array, true);
|
||||
assert.equal(ast.body[0].body.operator, "=");
|
||||
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
|
||||
|
||||
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_SymbolRef);
|
||||
assert.strictEqual(ast.body[0].body.left.names[0].start.value, "x");
|
||||
|
||||
assert(ast.body[0].body.left.names[1] instanceof uglify.AST_DefaultAssign);
|
||||
assert(ast.body[0].body.left.names[1].left instanceof uglify.AST_SymbolRef);
|
||||
assert.strictEqual(ast.body[0].body.left.names[1].start.value, "y");
|
||||
});
|
||||
|
||||
it("Should handle default assignments containing assignments in a destructuring", function() {
|
||||
var ast = uglify.parse("[x, y = z = 2] = a;");
|
||||
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
|
||||
|
||||
assert(ast.body[0].body instanceof uglify.AST_Assign);
|
||||
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].body.left.is_array, true);
|
||||
assert.equal(ast.body[0].body.operator, "=");
|
||||
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
|
||||
|
||||
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_SymbolRef);
|
||||
|
||||
assert(ast.body[0].body.left.names[1] instanceof uglify.AST_DefaultAssign);
|
||||
assert(ast.body[0].body.left.names[1].left instanceof uglify.AST_SymbolRef);
|
||||
assert.equal(ast.body[0].body.left.names[1].operator, "=");
|
||||
assert(ast.body[0].body.left.names[1].right instanceof uglify.AST_Assign);
|
||||
|
||||
assert(ast.body[0].body.left.names[1].right.left instanceof uglify.AST_SymbolRef);
|
||||
assert.equal(ast.body[0].body.left.names[1].right.operator, "=");
|
||||
assert(ast.body[0].body.left.names[1].right.right instanceof uglify.AST_Number);
|
||||
|
||||
ast = uglify.parse("({a: a = 123} = obj)");
|
||||
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
|
||||
|
||||
assert(ast.body[0].body instanceof uglify.AST_Assign);
|
||||
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
|
||||
assert.strictEqual(ast.body[0].body.left.is_array, false);
|
||||
assert.equal(ast.body[0].body.operator, "=");
|
||||
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
|
||||
|
||||
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_ObjectProperty);
|
||||
assert.strictEqual(ast.body[0].body.left.names[0].key, "a");
|
||||
assert(ast.body[0].body.left.names[0].value instanceof uglify.AST_DefaultAssign);
|
||||
assert(ast.body[0].body.left.names[0].value.left instanceof uglify.AST_SymbolRef);
|
||||
assert.strictEqual(ast.body[0].body.left.names[0].value.operator, "=");
|
||||
assert(ast.body[0].body.left.names[0].value.right instanceof uglify.AST_Number);
|
||||
});
|
||||
|
||||
it("Should allow multiple spread in array literals", function() {
|
||||
var ast = uglify.parse("var a = [1, 2, 3], b = [4, 5, 6], joined; joined = [...a, ...b]");
|
||||
assert(ast.body[0] instanceof uglify.AST_Var);
|
||||
assert(ast.body[1] instanceof uglify.AST_SimpleStatement);
|
||||
|
||||
// Check statement containing array with spreads
|
||||
assert(ast.body[1].body instanceof uglify.AST_Assign);
|
||||
assert(ast.body[1].body.left instanceof uglify.AST_SymbolRef);
|
||||
assert.equal(ast.body[1].body.operator, "=");
|
||||
assert(ast.body[1].body.right instanceof uglify.AST_Array);
|
||||
|
||||
// Check array content
|
||||
assert.strictEqual(ast.body[1].body.right.elements.length, 2);
|
||||
assert(ast.body[1].body.right.elements[0] instanceof uglify.AST_Expansion);
|
||||
assert(ast.body[1].body.right.elements[0].expression instanceof uglify.AST_SymbolRef);
|
||||
assert(ast.body[1].body.right.elements[0].expression.name, "a");
|
||||
assert(ast.body[1].body.right.elements[1] instanceof uglify.AST_Expansion);
|
||||
assert(ast.body[1].body.right.elements[1].expression instanceof uglify.AST_SymbolRef);
|
||||
assert(ast.body[1].body.right.elements[1].expression.name, "b");
|
||||
});
|
||||
|
||||
it("Should not allow spread on invalid locations", function() {
|
||||
var expect = function(input, expected) {
|
||||
var execute = function(input) {
|
||||
return function() {
|
||||
uglify.parse(input);
|
||||
}
|
||||
}
|
||||
var check = function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error &&
|
||||
e.message === expected;
|
||||
}
|
||||
|
||||
assert.throws(execute(input), check);
|
||||
}
|
||||
|
||||
// Spreads are not allowed in destructuring array if it's not the last element
|
||||
expect("[...a, ...b] = [1, 2, 3, 4]", "SyntaxError: Spread must the be last element in destructuring array");
|
||||
|
||||
// Multiple spreads are not allowed in destructuring array
|
||||
expect("[...a, ...b] = [1, 2, 3, 4]", "SyntaxError: Spread must the be last element in destructuring array");
|
||||
|
||||
// Spread in obvious object pattern
|
||||
expect("({...a} = foo)", "SyntaxError: Unexpected token: expand (...)");
|
||||
|
||||
// Spread in block should not be allowed
|
||||
expect("{...a} = foo", "SyntaxError: Unexpected token: expand (...)");
|
||||
|
||||
// Not in standard yet
|
||||
expect("let foo = {bar: 42}, bar; bar = {...foo}", "SyntaxError: Unexpected token: expand (...)");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user