Merge 774dc23acb into 7eb52d2837
This commit is contained in:
commit
e2dddd3103
356
lib/ast.js
356
lib/ast.js
|
|
@ -143,7 +143,7 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
|
|||
}, AST_Statement);
|
||||
|
||||
function walk_body(node, visitor) {
|
||||
if (node.body instanceof AST_Statement) {
|
||||
if (node.body instanceof AST_Node) {
|
||||
node.body._walk(visitor);
|
||||
}
|
||||
else node.body.forEach(function(stat){
|
||||
|
|
@ -263,6 +263,10 @@ var AST_ForIn = DEFNODE("ForIn", "init name object", {
|
|||
}
|
||||
}, AST_IterationStatement);
|
||||
|
||||
var AST_ForOf = DEFNODE("ForOf", null, {
|
||||
$documentation: "A `for ... of` statement",
|
||||
}, AST_ForIn);
|
||||
|
||||
var AST_With = DEFNODE("With", "expression", {
|
||||
$documentation: "A `with` statement",
|
||||
$propdoc: {
|
||||
|
|
@ -278,9 +282,10 @@ var AST_With = DEFNODE("With", "expression", {
|
|||
|
||||
/* -----[ scope and functions ]----- */
|
||||
|
||||
var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
|
||||
var AST_Scope = DEFNODE("Scope", "is_block_scope directives variables functions uses_with uses_eval parent_scope enclosed cname", {
|
||||
$documentation: "Base class for all statements introducing a lexical scope",
|
||||
$propdoc: {
|
||||
is_block_scope: "[boolean] identifies a block scope",
|
||||
directives: "[string*/S] an array of directives declared in this scope",
|
||||
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
|
||||
functions: "[Object/S] like `variables`, but only lists function declarations",
|
||||
|
|
@ -290,6 +295,13 @@ var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_
|
|||
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
||||
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
|
||||
},
|
||||
get_defun_scope: function () {
|
||||
var self = this;
|
||||
while (self.is_block_scope && self.parent_scope) {
|
||||
self = self.parent_scope;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
}, AST_Block);
|
||||
|
||||
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
|
|
@ -359,13 +371,99 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
|||
}
|
||||
}, AST_Scope);
|
||||
|
||||
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
|
||||
var AST_Expansion = DEFNODE("Expansion", "expression", {
|
||||
$documentation: "An expandible argument, such as ...rest, a splat, such as [1,2,...all], or an expansion in a variable declaration, such as var [first, ...rest] = list",
|
||||
$propdoc: {
|
||||
expression: "AST_Symbol the thing to be expanded"
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
var self = this;
|
||||
return visitor._visit(this, function(){
|
||||
self.expression.walk(visitor);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", {
|
||||
$documentation: "A set of arrow function parameters or a sequence expression. This is used because when the parser sees a \"(\" it could be the start of a seq, or the start of a parameter list of an arrow function.",
|
||||
$propdoc: {
|
||||
expressions: "[AST_Expression|AST_Destructuring|AST_Expansion*] array of expressions or argument names or destructurings."
|
||||
},
|
||||
as_params: function (croak) {
|
||||
// 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) {
|
||||
if (ex instanceof AST_Object) {
|
||||
if (ex.properties.length == 0)
|
||||
croak("Invalid destructuring function parameter", ex.start.line, ex.start.col);
|
||||
return new AST_Destructuring({
|
||||
start: ex.start,
|
||||
end: ex.end,
|
||||
is_array: false,
|
||||
default: default_seen_above,
|
||||
names: ex.properties.map(to_fun_args)
|
||||
});
|
||||
} else if (ex instanceof AST_ObjectKeyVal && ex.shorthand) {
|
||||
return new AST_SymbolFunarg({
|
||||
name: ex.key,
|
||||
start: ex.start,
|
||||
end: ex.end
|
||||
});
|
||||
} 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;
|
||||
} else if (ex instanceof AST_SymbolRef) {
|
||||
return new AST_SymbolFunarg({
|
||||
name: ex.name,
|
||||
default: default_seen_above,
|
||||
start: ex.start,
|
||||
end: ex.end
|
||||
});
|
||||
} else if (ex instanceof AST_Expansion) {
|
||||
return ex;
|
||||
} else if (ex instanceof AST_Array) {
|
||||
if (ex.elements.length === 0)
|
||||
croak("Invalid destructuring function parameter", ex.start.line, ex.start.col);
|
||||
return new AST_Destructuring({
|
||||
start: ex.start,
|
||||
end: ex.end,
|
||||
is_array: true,
|
||||
default: default_seen_above,
|
||||
names: ex.elements.map(to_fun_args)
|
||||
});
|
||||
} else if (ex instanceof AST_Assign) {
|
||||
return to_fun_args(ex.left, undefined, undefined, ex.right);
|
||||
} else {
|
||||
croak("Invalid function parameter", ex.start.line, ex.start.col);
|
||||
}
|
||||
});
|
||||
},
|
||||
as_expr: function (croak) {
|
||||
return AST_Seq.from_array(this.expressions);
|
||||
}
|
||||
});
|
||||
|
||||
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator", {
|
||||
$documentation: "Base class for functions",
|
||||
$propdoc: {
|
||||
is_generator: "is generatorFn or not",
|
||||
name: "[AST_SymbolDeclaration?] the name of this function",
|
||||
argnames: "[AST_SymbolFunarg*] array of function arguments",
|
||||
argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion*] array of function arguments, destructurings, or expanding arguments",
|
||||
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
|
||||
},
|
||||
args_as_names: function () {
|
||||
var out = [];
|
||||
for (var i = 0; i < this.argnames.length; i++) {
|
||||
if (this.argnames[i] instanceof AST_Destructuring) {
|
||||
out = out.concat(this.argnames[i].all_symbols());
|
||||
} else {
|
||||
out.push(this.argnames[i]);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
if (this.name) this.name._walk(visitor);
|
||||
|
|
@ -385,10 +483,74 @@ var AST_Function = DEFNODE("Function", null, {
|
|||
$documentation: "A function expression"
|
||||
}, AST_Lambda);
|
||||
|
||||
var AST_Arrow = DEFNODE("Arrow", null, {
|
||||
$documentation: "An ES6 Arrow function ((a) => b)"
|
||||
}, AST_Lambda);
|
||||
|
||||
var AST_ConciseMethod = DEFNODE("ConciseMethod", "is_generator static", {
|
||||
$propdoc: {
|
||||
is_generator: "is generatorFn or not",
|
||||
static: "[boolean] whether this method is static (classes only)",
|
||||
},
|
||||
$documentation: "An ES6 concise method inside an object or class"
|
||||
}, AST_Lambda);
|
||||
|
||||
var AST_Defun = DEFNODE("Defun", null, {
|
||||
$documentation: "A function definition"
|
||||
}, AST_Lambda);
|
||||
|
||||
/* -----[ DESTRUCTURING ]----- */
|
||||
var AST_Destructuring = DEFNODE("Destructuring", "names is_array default", {
|
||||
$documentation: "A destructuring of several names. Used in destructuring assignment and with destructuring function argument names",
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
this.names.forEach(function(name){
|
||||
name._walk(visitor);
|
||||
});
|
||||
});
|
||||
},
|
||||
all_symbols: function() {
|
||||
var out = [];
|
||||
this.walk(new TreeWalker(function (node) {
|
||||
if (node instanceof AST_Symbol) {
|
||||
out.push(node);
|
||||
}
|
||||
if (node instanceof AST_Expansion) {
|
||||
out.push(node.expression);
|
||||
}
|
||||
}));
|
||||
return out;
|
||||
}
|
||||
});
|
||||
|
||||
var AST_PrefixedTemplateString = DEFNODE("PrefixedTemplateString", "template_string prefix", {
|
||||
$documentation: "A templatestring with a prefix, such as String.raw`foobarbaz`",
|
||||
$propdoc: {
|
||||
template_string: "[AST_TemplateString] The template string",
|
||||
prefix: "[AST_SymbolRef|AST_PropAccess] The prefix, which can be a symbol such as `foo` or a dotted expression such as `String.raw`."
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
this.prefix._walk(visitor);
|
||||
this.template_string._walk(visitor);
|
||||
}
|
||||
})
|
||||
|
||||
var AST_TemplateString = DEFNODE("TemplateString", "segments", {
|
||||
$documentation: "A template string literal",
|
||||
$propdoc: {
|
||||
segments: "[string|AST_Expression]* One or more segments. They can be the parts that are evaluated, or the raw string parts."
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
this.segments.forEach(function(seg, i){
|
||||
if (i % 2 !== 0) {
|
||||
seg._walk(visitor);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* -----[ JUMPS ]----- */
|
||||
|
||||
var AST_Jump = DEFNODE("Jump", null, {
|
||||
|
|
@ -542,16 +704,78 @@ var AST_Var = DEFNODE("Var", null, {
|
|||
$documentation: "A `var` statement"
|
||||
}, AST_Definitions);
|
||||
|
||||
var AST_Let = DEFNODE("Let", null, {
|
||||
$documentation: "A `let` statement"
|
||||
}, AST_Definitions);
|
||||
|
||||
var AST_Const = DEFNODE("Const", null, {
|
||||
$documentation: "A `const` statement"
|
||||
}, AST_Definitions);
|
||||
|
||||
var AST_NameImport = DEFNODE("NameImport", "foreign_name name", {
|
||||
$documentation: "The part of the import statement that imports names from a module.",
|
||||
$propdoc: {
|
||||
foreign_name: "[AST_SymbolImportForeign] The name being imported (as specified in the module)",
|
||||
name: "[AST_SymbolImport] The name as it becomes available to this module."
|
||||
},
|
||||
_walk: function (visitor) {
|
||||
return visitor._visit(this, function() {
|
||||
this.foreign_name._walk(visitor);
|
||||
this.name._walk(visitor);
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
|
||||
$documentation: "An `import` statement",
|
||||
$propdoc: {
|
||||
imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.",
|
||||
imported_names: "[AST_NameImport*] The names of non-default imported variables",
|
||||
module_name: "[AST_String] String literal describing where this module came from",
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function() {
|
||||
if (this.imported_name) {
|
||||
this.imported_name._walk(visitor);
|
||||
}
|
||||
if (this.imported_names) {
|
||||
this.imported_names.forEach(function (name_import) {
|
||||
name_import._walk(visitor);
|
||||
});
|
||||
}
|
||||
this.module_name._walk(visitor);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default", {
|
||||
$documentation: "An `export` statement",
|
||||
$propdoc: {
|
||||
exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
|
||||
exported_value: "[AST_Node?] An exported value",
|
||||
is_default: "[Boolean] Whether this is the default exported value of this module"
|
||||
},
|
||||
_walk: function (visitor) {
|
||||
visitor._visit(this, function () {
|
||||
if (this.exported_definition) {
|
||||
this.exported_definition._walk(visitor);
|
||||
}
|
||||
if (this.exported_value) {
|
||||
this.exported_value._walk(visitor);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, AST_Statement);
|
||||
|
||||
var AST_VarDef = DEFNODE("VarDef", "name value", {
|
||||
$documentation: "A variable declaration; only appears in a AST_Definitions node",
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolVar|AST_SymbolConst] name of the variable",
|
||||
name: "[AST_SymbolVar|AST_SymbolConst|AST_Destructuring] name of the variable",
|
||||
value: "[AST_Node?] initializer, or null of there's no initializer"
|
||||
},
|
||||
is_destructuring: function() {
|
||||
return this.name instanceof AST_Destructuring;
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
this.name._walk(visitor);
|
||||
|
|
@ -774,21 +998,68 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
|||
}
|
||||
});
|
||||
|
||||
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
|
||||
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote shorthand", {
|
||||
$documentation: "A key: value object property",
|
||||
$propdoc: {
|
||||
quote: "[string] the original quote character"
|
||||
quote: "[string] the original quote character",
|
||||
shorthand: "[boolean] whether this is a shorthand key:value pair, expressed as just the key."
|
||||
}
|
||||
}, AST_ObjectProperty);
|
||||
|
||||
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
|
||||
var AST_ObjectComputedKeyVal = DEFNODE("ObjectComputedKeyVal", null, {
|
||||
$documentation: "An object property whose key is computed. Like `[Symbol.iterator]: function...` or `[routes.homepage]: renderHomepage`",
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
this.key._walk(visitor);
|
||||
this.value._walk(visitor);
|
||||
});
|
||||
}
|
||||
}, AST_ObjectProperty);
|
||||
|
||||
var AST_ObjectSetter = DEFNODE("ObjectSetter", "static", {
|
||||
$propdoc: {
|
||||
static: "[boolean] whether this is a static setter (classes only)"
|
||||
},
|
||||
$documentation: "An object setter property",
|
||||
}, AST_ObjectProperty);
|
||||
|
||||
var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
|
||||
var AST_ObjectGetter = DEFNODE("ObjectGetter", "static", {
|
||||
$propdoc: {
|
||||
static: "[boolean] whether this is a static getter (classes only)"
|
||||
},
|
||||
$documentation: "An object getter property",
|
||||
}, AST_ObjectProperty);
|
||||
|
||||
var AST_Class = DEFNODE("Class", "name extends properties", {
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolClass|AST_SymbolDefClass?] optional class name.",
|
||||
extends: "[AST_Node]? optional parent class",
|
||||
properties: "[AST_ObjectProperty*] array of properties"
|
||||
},
|
||||
$documentation: "An ES6 class",
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
if (this.name) {
|
||||
this.name._walk(visitor);
|
||||
}
|
||||
if (this.extends) {
|
||||
this.extends._walk(visitor);
|
||||
}
|
||||
this.properties.forEach(function(prop){
|
||||
prop._walk(visitor);
|
||||
});
|
||||
});
|
||||
},
|
||||
}, AST_Scope);
|
||||
|
||||
var AST_DefClass = DEFNODE("DefClass", null, {
|
||||
$documentation: "A class definition",
|
||||
}, AST_Class);
|
||||
|
||||
var AST_ClassExpression = DEFNODE("ClassExpression", null, {
|
||||
$documentation: "A class expression."
|
||||
}, AST_Class);
|
||||
|
||||
var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
|
||||
$propdoc: {
|
||||
name: "[string] name of this symbol",
|
||||
|
|
@ -798,14 +1069,24 @@ var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
|
|||
$documentation: "Base class for all symbols",
|
||||
});
|
||||
|
||||
var AST_NewTarget = DEFNODE("NewTarget", null, {
|
||||
$documentation: "A reference to new.target"
|
||||
});
|
||||
|
||||
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
|
||||
$documentation: "The name of a property accessor (setter/getter function)"
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
|
||||
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init default", {
|
||||
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
|
||||
$propdoc: {
|
||||
init: "[AST_Node*/S] array of initializers for this declaration."
|
||||
init: "[AST_Node*/S] array of initializers for this declaration.",
|
||||
default: "[AST_Expression] The default for this parameter. For example, `= 6`"
|
||||
},
|
||||
_walk: function (visitor) {
|
||||
return visitor._visit(this, function() {
|
||||
if (this.default) this.default._walk(visitor);
|
||||
});
|
||||
}
|
||||
}, AST_Symbol);
|
||||
|
||||
|
|
@ -813,9 +1094,17 @@ var AST_SymbolVar = DEFNODE("SymbolVar", null, {
|
|||
$documentation: "Symbol defining a variable",
|
||||
}, AST_SymbolDeclaration);
|
||||
|
||||
var AST_SymbolBlockDeclaration = DEFNODE("SymbolBlockDeclaration", null, {
|
||||
$documentation: "Base class for block-scoped declaration symbols"
|
||||
}, AST_SymbolDeclaration);
|
||||
|
||||
var AST_SymbolConst = DEFNODE("SymbolConst", null, {
|
||||
$documentation: "A constant declaration"
|
||||
}, AST_SymbolDeclaration);
|
||||
}, AST_SymbolBlockDeclaration);
|
||||
|
||||
var AST_SymbolLet = DEFNODE("SymbolLet", null, {
|
||||
$documentation: "A block-scoped `let` declaration"
|
||||
}, AST_SymbolBlockDeclaration);
|
||||
|
||||
var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
|
||||
$documentation: "Symbol naming a function argument",
|
||||
|
|
@ -825,13 +1114,33 @@ var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
|
|||
$documentation: "Symbol defining a function",
|
||||
}, AST_SymbolDeclaration);
|
||||
|
||||
var AST_SymbolMethod = DEFNODE("SymbolMethod", null, {
|
||||
$documentation: "Symbol in an object defining a method",
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
|
||||
$documentation: "Symbol naming a function expression",
|
||||
}, AST_SymbolDeclaration);
|
||||
|
||||
var AST_SymbolDefClass = DEFNODE("SymbolDefClass", null, {
|
||||
$documentation: "Symbol naming a class's name in a class declaration. Lexically scoped to its containing scope, and accessible within the class."
|
||||
}, AST_SymbolBlockDeclaration);
|
||||
|
||||
var AST_SymbolClass = DEFNODE("SymbolClass", null, {
|
||||
$documentation: "Symbol naming a class's name. Lexically scoped to the class."
|
||||
}, AST_SymbolDeclaration);
|
||||
|
||||
var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
|
||||
$documentation: "Symbol naming the exception in catch",
|
||||
}, AST_SymbolDeclaration);
|
||||
}, AST_SymbolBlockDeclaration);
|
||||
|
||||
var AST_SymbolImport = DEFNODE("SymbolImport", null, {
|
||||
$documentation: "Symbol refering to an imported name",
|
||||
}, AST_SymbolBlockDeclaration);
|
||||
|
||||
var AST_SymbolImportForeign = DEFNODE("SymbolImportForeign", null, {
|
||||
$documentation: "A symbol imported from a module, but it is defined in the other module, and its real name is irrelevant for this module's purposes",
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_Label = DEFNODE("Label", "references", {
|
||||
$documentation: "Symbol naming a label (declaration)",
|
||||
|
|
@ -856,6 +1165,10 @@ var AST_This = DEFNODE("This", null, {
|
|||
$documentation: "The `this` symbol",
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_Super = DEFNODE("Super", null, {
|
||||
$documentation: "The `super` symbol",
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_Constant = DEFNODE("Constant", null, {
|
||||
$documentation: "Base class for all constants",
|
||||
getValue: function() {
|
||||
|
|
@ -929,6 +1242,21 @@ var AST_True = DEFNODE("True", null, {
|
|||
value: true
|
||||
}, AST_Boolean);
|
||||
|
||||
/* -----[ Yield ]----- */
|
||||
|
||||
var AST_Yield = DEFNODE("Yield", "expression is_star", {
|
||||
$documentation: "A `yield` statement",
|
||||
$propdoc: {
|
||||
expression: "[AST_Node?] the value returned or thrown by this statement; could be null (representing undefined) but only when is_star is set to false",
|
||||
is_star: "[Boolean] Whether this is a yield or yield* statement"
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, this.expression && function(){
|
||||
this.expression._walk(visitor);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* -----[ TreeWalker ]----- */
|
||||
|
||||
function TreeWalker(callback) {
|
||||
|
|
@ -979,7 +1307,7 @@ TreeWalker.prototype = {
|
|||
var dir = this.directives[type];
|
||||
if (dir) return dir;
|
||||
var node = this.stack[this.stack.length - 1];
|
||||
if (node instanceof AST_Scope) {
|
||||
if (node instanceof AST_Scope && node.body) {
|
||||
for (var i = 0; i < node.body.length; ++i) {
|
||||
var st = node.body[i];
|
||||
if (!(st instanceof AST_Directive)) break;
|
||||
|
|
|
|||
134
lib/compress.js
134
lib/compress.js
|
|
@ -246,6 +246,14 @@ merge(Compressor.prototype, {
|
|||
return false;
|
||||
};
|
||||
|
||||
function can_be_evicted_from_block(node) {
|
||||
return !(
|
||||
node instanceof AST_DefClass ||
|
||||
node instanceof AST_Let ||
|
||||
node instanceof AST_Const
|
||||
);
|
||||
}
|
||||
|
||||
function loop_body(x) {
|
||||
if (x instanceof AST_Switch) return x;
|
||||
if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
|
||||
|
|
@ -353,7 +361,7 @@ merge(Compressor.prototype, {
|
|||
if (side_effects_encountered |= lvalues_encountered) continue;
|
||||
|
||||
// Non-constant single use vars can only be replaced in same scope.
|
||||
if (ref.scope !== self) {
|
||||
if (ref.scope.get_defun_scope() !== self) {
|
||||
side_effects_encountered |= var_decl.value.has_side_effects(compressor);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -444,8 +452,16 @@ merge(Compressor.prototype, {
|
|||
return /@ngInject/.test(comment.value);
|
||||
}
|
||||
function make_arguments_names_list(func) {
|
||||
var foundDestructuring = false;
|
||||
return func.argnames.map(function(sym){
|
||||
if (sym instanceof AST_Destructuring) {
|
||||
compressor.warn("Function with destructuring arguments marked with @ngInject [{file}:{line},{col}]", token);
|
||||
foundDestructuring = true;
|
||||
}
|
||||
if (foundDestructuring) { return null; }
|
||||
return make_node(AST_String, sym, { value: sym.name });
|
||||
}).filter(function (name) {
|
||||
return name !== null;
|
||||
});
|
||||
}
|
||||
function make_array(orig, elements) {
|
||||
|
|
@ -516,7 +532,7 @@ merge(Compressor.prototype, {
|
|||
function eliminate_spurious_blocks(statements) {
|
||||
var seen_dirs = [];
|
||||
return statements.reduce(function(a, stat){
|
||||
if (stat instanceof AST_BlockStatement) {
|
||||
if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
|
||||
CHANGED = true;
|
||||
a.push.apply(a, eliminate_spurious_blocks(stat.body));
|
||||
} else if (stat instanceof AST_EmptyStatement) {
|
||||
|
|
@ -886,7 +902,7 @@ merge(Compressor.prototype, {
|
|||
compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
|
||||
}
|
||||
stat.walk(new TreeWalker(function(node){
|
||||
if (node instanceof AST_Definitions) {
|
||||
if (node instanceof AST_Var) {
|
||||
compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
|
||||
node.remove_initializers();
|
||||
target.push(node);
|
||||
|
|
@ -1023,6 +1039,12 @@ merge(Compressor.prototype, {
|
|||
// places too. :-( Wish JS had multiple inheritance.
|
||||
throw def;
|
||||
});
|
||||
def(AST_Arrow, function() {
|
||||
throw def;
|
||||
});
|
||||
def(AST_Class, function() {
|
||||
throw def;
|
||||
});
|
||||
function ev(node, compressor) {
|
||||
if (!compressor) throw new Error("Compressor must be passed");
|
||||
|
||||
|
|
@ -1041,7 +1063,8 @@ merge(Compressor.prototype, {
|
|||
case "typeof":
|
||||
// Function would be evaluated to an array and so typeof would
|
||||
// incorrectly return 'object'. Hence making is a special case.
|
||||
if (e instanceof AST_Function) return typeof function(){};
|
||||
if (e instanceof AST_Function ||
|
||||
e instanceof AST_Arrow) return typeof function(){};
|
||||
|
||||
e = ev(e, compressor);
|
||||
|
||||
|
|
@ -1067,6 +1090,7 @@ merge(Compressor.prototype, {
|
|||
case "^" : result = ev(left, c) ^ ev(right, c); break;
|
||||
case "+" : result = ev(left, c) + ev(right, c); break;
|
||||
case "*" : result = ev(left, c) * ev(right, c); break;
|
||||
case "**" : result = Math.pow(ev(left, c), ev(right, c)); break;
|
||||
case "/" : result = ev(left, c) / ev(right, c); break;
|
||||
case "%" : result = ev(left, c) % ev(right, c); break;
|
||||
case "-" : result = ev(left, c) - ev(right, c); break;
|
||||
|
|
@ -1215,6 +1239,8 @@ merge(Compressor.prototype, {
|
|||
});
|
||||
def(AST_Defun, function(compressor){ return true });
|
||||
def(AST_Function, function(compressor){ return false });
|
||||
def(AST_Class, function(compressor){ return false });
|
||||
def(AST_DefClass, function(compressor){ return true });
|
||||
def(AST_Binary, function(compressor){
|
||||
return this.left.has_side_effects(compressor)
|
||||
|| this.right.has_side_effects(compressor);
|
||||
|
|
@ -1226,9 +1252,7 @@ merge(Compressor.prototype, {
|
|||
|| this.alternative.has_side_effects(compressor);
|
||||
});
|
||||
def(AST_Unary, function(compressor){
|
||||
return this.operator == "delete"
|
||||
|| this.operator == "++"
|
||||
|| this.operator == "--"
|
||||
return member(this.operator, ["delete", "++", "--"])
|
||||
|| this.expression.has_side_effects(compressor);
|
||||
});
|
||||
def(AST_SymbolRef, function(compressor){
|
||||
|
|
@ -1241,6 +1265,9 @@ merge(Compressor.prototype, {
|
|||
return false;
|
||||
});
|
||||
def(AST_ObjectProperty, function(compressor){
|
||||
if (this instanceof AST_ObjectComputedKeyVal &&
|
||||
this.key.has_side_effects(compressor))
|
||||
return true;
|
||||
return this.value.has_side_effects(compressor);
|
||||
});
|
||||
def(AST_Array, function(compressor){
|
||||
|
|
@ -1280,6 +1307,7 @@ merge(Compressor.prototype, {
|
|||
var n = this.body.length;
|
||||
return n > 0 && aborts(this.body[n - 1]);
|
||||
};
|
||||
def(AST_Import, function(){ return null; });
|
||||
def(AST_BlockStatement, block_aborts);
|
||||
def(AST_SwitchBranch, block_aborts);
|
||||
def(AST_If, function(){
|
||||
|
|
@ -1313,6 +1341,7 @@ merge(Compressor.prototype, {
|
|||
});
|
||||
|
||||
OPT(AST_Block, function(self, compressor){
|
||||
if (self.body instanceof AST_Node) { return self; }
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
return self;
|
||||
});
|
||||
|
|
@ -1320,7 +1349,11 @@ merge(Compressor.prototype, {
|
|||
OPT(AST_BlockStatement, function(self, compressor){
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
switch (self.body.length) {
|
||||
case 1: return self.body[0];
|
||||
case 1:
|
||||
if (can_be_evicted_from_block(self.body[0])) {
|
||||
return self.body[0];
|
||||
}
|
||||
break;
|
||||
case 0: return make_node(AST_EmptyStatement, self);
|
||||
}
|
||||
return self;
|
||||
|
|
@ -1330,7 +1363,6 @@ merge(Compressor.prototype, {
|
|||
var self = this;
|
||||
if (compressor.has_directive("use asm")) return self;
|
||||
if (compressor.option("unused")
|
||||
&& !(self instanceof AST_Toplevel)
|
||||
&& !self.uses_eval
|
||||
&& !self.uses_with
|
||||
) {
|
||||
|
|
@ -1342,12 +1374,13 @@ merge(Compressor.prototype, {
|
|||
var scope = this;
|
||||
var tw = new TreeWalker(function(node, descend){
|
||||
if (node !== self) {
|
||||
if (node instanceof AST_Defun) {
|
||||
if (node instanceof AST_Defun || node instanceof AST_DefClass) {
|
||||
initializations.add(node.name.name, node);
|
||||
return true; // don't go in nested scopes
|
||||
}
|
||||
if (node instanceof AST_Definitions && scope === self) {
|
||||
node.definitions.forEach(function(def){
|
||||
if (def.is_destructuring()) return; /* Destructurings are type assertions! */
|
||||
if (def.value) {
|
||||
initializations.add(def.name.name, def.value);
|
||||
if (def.value.has_side_effects(compressor)) {
|
||||
|
|
@ -1402,22 +1435,35 @@ merge(Compressor.prototype, {
|
|||
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
|
||||
if (!compressor.option("keep_fargs")) {
|
||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||
var sym = a[i];
|
||||
if (sym.unreferenced()) {
|
||||
a.pop();
|
||||
compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
|
||||
name : sym.name,
|
||||
file : sym.start.file,
|
||||
line : sym.start.line,
|
||||
col : sym.start.col
|
||||
});
|
||||
if (a[i] instanceof AST_Destructuring) {
|
||||
// Do not drop destructuring arguments.
|
||||
// They constitute a type assertion, so dropping
|
||||
// them would stop that TypeError which would happen
|
||||
// if someone called it with an incorrectly formatted
|
||||
// parameter.
|
||||
break;
|
||||
} else {
|
||||
var sym = a[i];
|
||||
if (sym instanceof AST_Expansion) {
|
||||
sym = sym.symbol;
|
||||
}
|
||||
if (sym.unreferenced()) {
|
||||
a.pop();
|
||||
compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
|
||||
name : sym.name,
|
||||
file : sym.start.file,
|
||||
line : sym.start.line,
|
||||
col : sym.start.col
|
||||
});
|
||||
}
|
||||
else break;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_Defun && node !== self) {
|
||||
if (!(node.name.definition().id in in_use_ids)) {
|
||||
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
|
||||
var keep = (node.name.definition().id in in_use_ids) || node.name.definition().global;
|
||||
if (!keep) {
|
||||
compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
|
||||
name : node.name.name,
|
||||
file : node.name.start.file,
|
||||
|
|
@ -1430,7 +1476,10 @@ merge(Compressor.prototype, {
|
|||
}
|
||||
if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
|
||||
var def = node.definitions.filter(function(def){
|
||||
if (def.is_destructuring()) return true;
|
||||
if (def.name.definition().id in in_use_ids) return true;
|
||||
if (def.name.definition().global) return true;
|
||||
|
||||
var w = {
|
||||
name : def.name.name,
|
||||
file : def.name.start.file,
|
||||
|
|
@ -1509,6 +1558,12 @@ merge(Compressor.prototype, {
|
|||
});
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_BlockStatement) {
|
||||
descend(node, this);
|
||||
if (in_list && all(node.body, can_be_evicted_from_block)) {
|
||||
return MAP.splice(node.body);
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_Scope && node !== self)
|
||||
return node;
|
||||
}
|
||||
|
|
@ -1520,8 +1575,12 @@ merge(Compressor.prototype, {
|
|||
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
|
||||
var self = this;
|
||||
if (compressor.has_directive("use asm")) return self;
|
||||
// Hoisting makes no sense in an arrow func
|
||||
if (!Array.isArray(self.body)) return self;
|
||||
|
||||
var hoist_funs = compressor.option("hoist_funs");
|
||||
var hoist_vars = compressor.option("hoist_vars");
|
||||
|
||||
if (hoist_funs || hoist_vars) {
|
||||
var dirs = [];
|
||||
var hoisted = [];
|
||||
|
|
@ -1550,6 +1609,7 @@ merge(Compressor.prototype, {
|
|||
}
|
||||
if (node instanceof AST_Var && hoist_vars) {
|
||||
node.definitions.forEach(function(def){
|
||||
if (def.is_destructuring()) { return; }
|
||||
vars.set(def.name.name, def);
|
||||
++vars_found;
|
||||
});
|
||||
|
|
@ -1582,7 +1642,7 @@ merge(Compressor.prototype, {
|
|||
vars.each(function(def, name){
|
||||
if (self instanceof AST_Lambda
|
||||
&& find_if(function(x){ return x.name == def.name.name },
|
||||
self.argnames)) {
|
||||
self.args_as_names())) {
|
||||
vars.del(name);
|
||||
} else {
|
||||
def = def.clone();
|
||||
|
|
@ -2004,13 +2064,23 @@ merge(Compressor.prototype, {
|
|||
|
||||
AST_Definitions.DEFMETHOD("to_assignments", function(){
|
||||
var assignments = this.definitions.reduce(function(a, def){
|
||||
if (def.value) {
|
||||
if (def.value && !def.is_destructuring()) {
|
||||
var name = make_node(AST_SymbolRef, def.name, def.name);
|
||||
a.push(make_node(AST_Assign, def, {
|
||||
operator : "=",
|
||||
left : name,
|
||||
right : def.value
|
||||
}));
|
||||
} else if (def.value) {
|
||||
// Because it's a destructuring, do not turn into an assignment.
|
||||
var varDef = make_node(AST_VarDef, def, {
|
||||
name: def.name,
|
||||
value: def.value
|
||||
});
|
||||
var var_ = make_node(AST_Var, def, {
|
||||
definitions: [ varDef ]
|
||||
});
|
||||
a.push(var_);
|
||||
}
|
||||
return a;
|
||||
}, []);
|
||||
|
|
@ -2024,6 +2094,10 @@ merge(Compressor.prototype, {
|
|||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Import, function(self, compressor) {
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Function, function(self, compressor){
|
||||
self = AST_Lambda.prototype.optimize.call(self, compressor);
|
||||
if (compressor.option("unused") && !compressor.option("keep_fnames")) {
|
||||
|
|
@ -2137,6 +2211,11 @@ merge(Compressor.prototype, {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case "Symbol":
|
||||
// Symbol's argument is only used for debugging.
|
||||
self.args = [];
|
||||
return self;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
|
||||
|
|
@ -2902,4 +2981,11 @@ merge(Compressor.prototype, {
|
|||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Yield, function(self, compressor){
|
||||
if (!self.is_star && self.expression instanceof AST_Undefined) {
|
||||
self.expression = null;
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
|
|||
309
lib/output.js
309
lib/output.js
|
|
@ -67,7 +67,8 @@ function OutputStream(options) {
|
|||
screw_ie8 : true,
|
||||
preamble : null,
|
||||
quote_style : 0,
|
||||
keep_quoted_props: false
|
||||
keep_quoted_props: false,
|
||||
ecma : 5,
|
||||
}, true);
|
||||
|
||||
var indentation = 0;
|
||||
|
|
@ -77,9 +78,16 @@ function OutputStream(options) {
|
|||
var OUTPUT = "";
|
||||
|
||||
function to_ascii(str, identifier) {
|
||||
return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
|
||||
var code = ch.charCodeAt(0).toString(16);
|
||||
if (code.length <= 2 && !identifier) {
|
||||
return str.replace(/[\ud800-\udbff][\udc00-\udfff]|[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
|
||||
var code = get_full_char_code(ch, 0).toString(16);
|
||||
|
||||
if ((identifier && code.length === 1 && !options.es5) || code.length > 4) {
|
||||
if (options.ecma < 6) {
|
||||
return "\\u" + ch.charCodeAt(0).toString(16) + "\\u"
|
||||
+ ch.charCodeAt(1).toString(16);
|
||||
}
|
||||
return "\\u{" + code + "}";
|
||||
} else if (code.length <= 2 && !identifier) {
|
||||
while (code.length < 2) code = "0" + code;
|
||||
return "\\x" + code;
|
||||
} else {
|
||||
|
|
@ -107,7 +115,7 @@ function OutputStream(options) {
|
|||
case "\u2029": return "\\u2029";
|
||||
case "\ufeff": return "\\ufeff";
|
||||
case "\0":
|
||||
return /[0-7]/.test(str.charAt(i+1)) ? "\\x00" : "\\0";
|
||||
return /[0-7]/.test(get_full_char(str, i+1)) ? "\\x00" : "\\0";
|
||||
}
|
||||
return s;
|
||||
});
|
||||
|
|
@ -158,7 +166,13 @@ function OutputStream(options) {
|
|||
var last = null;
|
||||
|
||||
function last_char() {
|
||||
return last.charAt(last.length - 1);
|
||||
var char = last.charAt(last.length - 1);
|
||||
|
||||
if (is_surrogate_pair_tail(char)) {
|
||||
return last.charAt(last.length - 2) + char;
|
||||
}
|
||||
|
||||
return char;
|
||||
};
|
||||
|
||||
function maybe_newline() {
|
||||
|
|
@ -170,7 +184,7 @@ function OutputStream(options) {
|
|||
|
||||
function print(str) {
|
||||
str = String(str);
|
||||
var ch = str.charAt(0);
|
||||
var ch = get_full_char(str, 0);
|
||||
if (might_need_semicolon) {
|
||||
might_need_semicolon = false;
|
||||
|
||||
|
|
@ -232,6 +246,10 @@ function OutputStream(options) {
|
|||
OUTPUT += str;
|
||||
};
|
||||
|
||||
var star = function(){
|
||||
print("*");
|
||||
}
|
||||
|
||||
var space = options.beautify ? function() {
|
||||
print(" ");
|
||||
} : function() {
|
||||
|
|
@ -349,6 +367,7 @@ function OutputStream(options) {
|
|||
should_break : function() { return options.width && this.current_width() >= options.width },
|
||||
newline : newline,
|
||||
print : print,
|
||||
star : star,
|
||||
space : space,
|
||||
comma : comma,
|
||||
colon : colon,
|
||||
|
|
@ -533,7 +552,13 @@ function OutputStream(options) {
|
|||
PARENS([ AST_Unary, AST_Undefined ], function(output){
|
||||
var p = output.parent();
|
||||
return p instanceof AST_PropAccess && p.expression === this
|
||||
|| p instanceof AST_New;
|
||||
|| p instanceof AST_New
|
||||
|| p instanceof AST_Binary
|
||||
&& p.operator === "**"
|
||||
&& this instanceof AST_UnaryPrefix
|
||||
&& p.left === this
|
||||
&& this.operator !== "++"
|
||||
&& this.operator !== "--";
|
||||
});
|
||||
|
||||
PARENS(AST_Seq, function(output){
|
||||
|
|
@ -573,6 +598,20 @@ function OutputStream(options) {
|
|||
}
|
||||
});
|
||||
|
||||
PARENS(AST_Yield, function(output){
|
||||
var p = output.parent();
|
||||
// (yield 1) + (yield 2)
|
||||
// a = yield 3
|
||||
if (p instanceof AST_Binary && p.operator !== "=")
|
||||
return true;
|
||||
// (yield 1) ? yield 2 : yield 3
|
||||
if (p instanceof AST_Conditional && p.condition === this)
|
||||
return true;
|
||||
// -(yield 4)
|
||||
if (p instanceof AST_Unary)
|
||||
return true;
|
||||
});
|
||||
|
||||
PARENS(AST_PropAccess, function(output){
|
||||
var p = output.parent();
|
||||
if (p instanceof AST_New && p.expression === this) {
|
||||
|
|
@ -650,6 +689,28 @@ function OutputStream(options) {
|
|||
output.print_string(self.value, self.quote);
|
||||
output.semicolon();
|
||||
});
|
||||
|
||||
DEFPRINT(AST_Expansion, function (self, output) {
|
||||
output.print('...');
|
||||
self.expression.print(output);
|
||||
});
|
||||
|
||||
DEFPRINT(AST_Destructuring, function (self, output) {
|
||||
output.print(self.is_array ? "[" : "{");
|
||||
var first = true;
|
||||
self.names.forEach(function (name) {
|
||||
if (first) first = false; else { output.comma(); output.space(); }
|
||||
name.print(output);
|
||||
})
|
||||
output.print(self.is_array ? "]" : "}");
|
||||
if (self.default) {
|
||||
output.space();
|
||||
output.print('=');
|
||||
output.space();
|
||||
self.default.print(output)
|
||||
}
|
||||
})
|
||||
|
||||
DEFPRINT(AST_Debugger, function(self, output){
|
||||
output.print("debugger");
|
||||
output.semicolon();
|
||||
|
|
@ -774,7 +835,11 @@ function OutputStream(options) {
|
|||
output.with_parens(function(){
|
||||
self.init.print(output);
|
||||
output.space();
|
||||
output.print("in");
|
||||
if (self instanceof AST_ForOf) {
|
||||
output.print("of");
|
||||
} else {
|
||||
output.print("in");
|
||||
}
|
||||
output.space();
|
||||
self.object.print(output);
|
||||
});
|
||||
|
|
@ -796,10 +861,19 @@ function OutputStream(options) {
|
|||
var self = this;
|
||||
if (!nokeyword) {
|
||||
output.print("function");
|
||||
if (this.is_generator) {
|
||||
output.star();
|
||||
}
|
||||
if (self.name) {
|
||||
output.space();
|
||||
}
|
||||
}
|
||||
if (self.name) {
|
||||
output.space();
|
||||
if (self.name instanceof AST_Symbol) {
|
||||
self.name.print(output);
|
||||
} else if (nokeyword && self.name instanceof AST_Node) {
|
||||
output.with_square(function() {
|
||||
self.name.print(output);
|
||||
});
|
||||
}
|
||||
output.with_parens(function(){
|
||||
self.argnames.forEach(function(arg, i){
|
||||
|
|
@ -814,6 +888,62 @@ function OutputStream(options) {
|
|||
self._do_print(output);
|
||||
});
|
||||
|
||||
DEFPRINT(AST_PrefixedTemplateString, function(self, output) {
|
||||
self.prefix.print(output);
|
||||
self.template_string.print(output);
|
||||
});
|
||||
DEFPRINT(AST_TemplateString, function(self, output) {
|
||||
output.print("`");
|
||||
for (var i = 0; i < self.segments.length; i++) {
|
||||
if (typeof self.segments[i] !== "string") {
|
||||
output.print("${");
|
||||
self.segments[i].print(output);
|
||||
output.print("}");
|
||||
} else {
|
||||
output.print(self.segments[i]);
|
||||
}
|
||||
}
|
||||
output.print("`");
|
||||
});
|
||||
|
||||
AST_Arrow.DEFMETHOD("_do_print", function(output){
|
||||
var self = this;
|
||||
var parent = output.parent();
|
||||
var needs_parens = parent instanceof AST_Binary ||
|
||||
parent instanceof AST_Unary ||
|
||||
parent instanceof AST_Call;
|
||||
if (needs_parens) { output.print("(") }
|
||||
if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol && !self.argnames[0].default) {
|
||||
self.argnames[0].print(output);
|
||||
} else {
|
||||
output.with_parens(function(){
|
||||
self.argnames.forEach(function(arg, i){
|
||||
if (i) output.comma();
|
||||
arg.print(output);
|
||||
});
|
||||
});
|
||||
}
|
||||
output.space();
|
||||
output.print('=>');
|
||||
output.space();
|
||||
if (self.body instanceof AST_Node) {
|
||||
this.body.print(output);
|
||||
} else {
|
||||
print_bracketed(this.body, output);
|
||||
}
|
||||
if (needs_parens) { output.print(")") }
|
||||
});
|
||||
DEFPRINT(AST_ConciseMethod, function(self, output){
|
||||
if (self.static) {
|
||||
output.print("static");
|
||||
output.space();
|
||||
}
|
||||
if (self.is_generator) {
|
||||
output.print("*");
|
||||
}
|
||||
self._do_print(output, true /* do not print "function" */);
|
||||
});
|
||||
|
||||
/* -----[ exits ]----- */
|
||||
AST_Exit.DEFMETHOD("_do_print", function(output, kind){
|
||||
output.print(kind);
|
||||
|
|
@ -830,6 +960,17 @@ function OutputStream(options) {
|
|||
self._do_print(output, "throw");
|
||||
});
|
||||
|
||||
/* -----[ yield ]----- */
|
||||
|
||||
DEFPRINT(AST_Yield, function(self, output){
|
||||
var star = self.is_star ? "*" : "";
|
||||
output.print("yield" + star);
|
||||
if (self.expression) {
|
||||
output.space();
|
||||
self.expression.print(output);
|
||||
}
|
||||
});
|
||||
|
||||
/* -----[ loop control ]----- */
|
||||
AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
|
||||
output.print(kind);
|
||||
|
|
@ -986,12 +1127,77 @@ function OutputStream(options) {
|
|||
if (!avoid_semicolon)
|
||||
output.semicolon();
|
||||
});
|
||||
DEFPRINT(AST_Let, function(self, output){
|
||||
self._do_print(output, "let");
|
||||
});
|
||||
DEFPRINT(AST_Var, function(self, output){
|
||||
self._do_print(output, "var");
|
||||
});
|
||||
DEFPRINT(AST_Const, function(self, output){
|
||||
self._do_print(output, "const");
|
||||
});
|
||||
DEFPRINT(AST_Import, function(self, output) {
|
||||
output.print("import");
|
||||
output.space();
|
||||
if (self.imported_name) {
|
||||
self.imported_name.print(output);
|
||||
}
|
||||
if (self.imported_name && self.imported_names) {
|
||||
output.print(",");
|
||||
output.space();
|
||||
}
|
||||
if (self.imported_names) {
|
||||
output.print("{");
|
||||
self.imported_names.forEach(function(name_import, i) {
|
||||
output.space();
|
||||
name_import.print(output);
|
||||
if (i < self.imported_names.length - 1) {
|
||||
output.print(",");
|
||||
output.space();
|
||||
}
|
||||
});
|
||||
output.space();
|
||||
output.print("}");
|
||||
}
|
||||
if (self.imported_name || self.imported_names) {
|
||||
output.space();
|
||||
output.print("from")
|
||||
output.space();
|
||||
}
|
||||
self.module_name.print(output);
|
||||
output.semicolon();
|
||||
});
|
||||
|
||||
DEFPRINT(AST_NameImport, function(self, output) {
|
||||
var definition = self.name.definition();
|
||||
var names_are_different =
|
||||
(definition && definition.mangled_name || self.name.name) !==
|
||||
self.foreign_name.name;
|
||||
if (names_are_different) {
|
||||
output.print(self.foreign_name.name);
|
||||
output.space();
|
||||
output.print("as");
|
||||
output.space();
|
||||
self.name.print(output);
|
||||
} else {
|
||||
self.name.print(output);
|
||||
}
|
||||
});
|
||||
|
||||
DEFPRINT(AST_Export, function(self, output) {
|
||||
output.print("export");
|
||||
output.space();
|
||||
if (self.is_default) {
|
||||
output.print("default");
|
||||
output.space();
|
||||
}
|
||||
if (self.exported_value) {
|
||||
self.exported_value.print(output);
|
||||
} else if (self.exported_definition) {
|
||||
self.exported_definition.print(output);
|
||||
}
|
||||
output.semicolon();
|
||||
});
|
||||
|
||||
function parenthesize_for_noin(node, output, noin) {
|
||||
if (!noin) node.print(output);
|
||||
|
|
@ -1163,9 +1369,45 @@ function OutputStream(options) {
|
|||
});
|
||||
else output.print("{}");
|
||||
});
|
||||
DEFPRINT(AST_Class, function(self, output){
|
||||
output.print("class");
|
||||
output.space();
|
||||
if (self.name) {
|
||||
self.name.print(output);
|
||||
output.space();
|
||||
}
|
||||
if (self.extends) {
|
||||
output.print("extends");
|
||||
output.space();
|
||||
self.extends.print(output);
|
||||
output.space();
|
||||
}
|
||||
if (self.properties.length > 0) output.with_block(function(){
|
||||
self.properties.forEach(function(prop, i){
|
||||
if (i) {
|
||||
output.newline();
|
||||
}
|
||||
output.indent();
|
||||
prop.print(output);
|
||||
});
|
||||
output.newline();
|
||||
});
|
||||
else output.print("{}");
|
||||
});
|
||||
DEFPRINT(AST_NewTarget, function(self, output) {
|
||||
output.print("new.target");
|
||||
});
|
||||
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
||||
var key = self.key;
|
||||
var quote = self.quote;
|
||||
var print_as_shorthand = self.shorthand &&
|
||||
self.value instanceof AST_Symbol &&
|
||||
self.key == self.value.print_to_string();
|
||||
|
||||
if (print_as_shorthand) {
|
||||
output.print_name(key);
|
||||
return;
|
||||
}
|
||||
if (output.option("quote_keys")) {
|
||||
output.print_string(key + "");
|
||||
} else if ((typeof key == "number"
|
||||
|
|
@ -1186,20 +1428,56 @@ function OutputStream(options) {
|
|||
self.value.print(output);
|
||||
});
|
||||
DEFPRINT(AST_ObjectSetter, function(self, output){
|
||||
if (self.static) {
|
||||
output.print("static");
|
||||
output.space();
|
||||
}
|
||||
output.print("set");
|
||||
output.space();
|
||||
self.key.print(output);
|
||||
self.value._do_print(output, true);
|
||||
});
|
||||
DEFPRINT(AST_ObjectGetter, function(self, output){
|
||||
if (self.static) {
|
||||
output.print("static");
|
||||
output.space();
|
||||
}
|
||||
output.print("get");
|
||||
output.space();
|
||||
self.key.print(output);
|
||||
self.value._do_print(output, true);
|
||||
});
|
||||
DEFPRINT(AST_Symbol, function(self, output){
|
||||
var def = self.definition();
|
||||
output.print_name(def ? def.mangled_name || def.name : self.name);
|
||||
DEFPRINT(AST_ObjectComputedKeyVal, function(self, output) {
|
||||
output.print("[");
|
||||
self.key.print(output);
|
||||
output.print("]:");
|
||||
output.space();
|
||||
self.value.print(output);
|
||||
});
|
||||
AST_Symbol.DEFMETHOD("_do_print", function(output){
|
||||
var def = this.definition();
|
||||
output.print_name(def ? def.mangled_name || def.name : this.name);
|
||||
});
|
||||
DEFPRINT(AST_Symbol, function (self, output) {
|
||||
self._do_print(output);
|
||||
});
|
||||
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_SymbolMethod, function(self, output) {
|
||||
if (self.name instanceof AST_Node) {
|
||||
output.with_square(function() {
|
||||
self.name.print(output);
|
||||
});
|
||||
} else {
|
||||
self._do_print(output);
|
||||
}
|
||||
});
|
||||
DEFPRINT(AST_Undefined, function(self, output){
|
||||
output.print("void 0");
|
||||
|
|
@ -1214,6 +1492,9 @@ function OutputStream(options) {
|
|||
DEFPRINT(AST_This, function(self, output){
|
||||
output.print("this");
|
||||
});
|
||||
DEFPRINT(AST_Super, function(self, output){
|
||||
output.print("super");
|
||||
});
|
||||
DEFPRINT(AST_Constant, function(self, output){
|
||||
output.print(self.getValue());
|
||||
});
|
||||
|
|
|
|||
958
lib/parse.js
958
lib/parse.js
File diff suppressed because one or more lines are too long
|
|
@ -44,10 +44,16 @@
|
|||
"use strict";
|
||||
|
||||
function find_builtins() {
|
||||
|
||||
// Compatibility fix for es5.1 and earlier where Symbol isn't defined
|
||||
if (!global.Symbol) {
|
||||
global.Symbol = new Function();
|
||||
}
|
||||
|
||||
var a = [];
|
||||
[ Object, Array, Function, Number,
|
||||
String, Boolean, Error, Math,
|
||||
Date, RegExp
|
||||
Date, RegExp, Symbol
|
||||
].forEach(function(ctor){
|
||||
Object.getOwnPropertyNames(ctor).map(add);
|
||||
if (ctor.prototype) {
|
||||
|
|
@ -108,6 +114,9 @@ function mangle_properties(ast, options) {
|
|||
addStrings(node.property);
|
||||
}
|
||||
}
|
||||
else if (node instanceof AST_ConciseMethod) {
|
||||
add(node.name.name);
|
||||
}
|
||||
}));
|
||||
|
||||
// step 2: transform the tree, renaming properties
|
||||
|
|
@ -127,6 +136,11 @@ function mangle_properties(ast, options) {
|
|||
if (!ignore_quoted)
|
||||
node.property = mangleStrings(node.property);
|
||||
}
|
||||
else if (node instanceof AST_ConciseMethod) {
|
||||
if (should_mangle(node.name.name)) {
|
||||
node.name.name = mangle(node.name.name);
|
||||
}
|
||||
}
|
||||
// else if (node instanceof AST_String) {
|
||||
// if (should_mangle(node.value)) {
|
||||
// AST_Node.warn(
|
||||
|
|
|
|||
111
lib/scope.js
111
lib/scope.js
|
|
@ -49,7 +49,9 @@ function SymbolDef(scope, index, orig) {
|
|||
this.scope = scope;
|
||||
this.references = [];
|
||||
this.global = false;
|
||||
this.export = false;
|
||||
this.mangled_name = null;
|
||||
this.object_destructuring_arg = false;
|
||||
this.undeclared = false;
|
||||
this.constant = false;
|
||||
this.index = index;
|
||||
|
|
@ -63,11 +65,17 @@ SymbolDef.prototype = {
|
|||
if (!options) options = {};
|
||||
|
||||
return (this.global && !options.toplevel)
|
||||
|| this.export
|
||||
|| this.object_destructuring_arg
|
||||
|| this.undeclared
|
||||
|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with))
|
||||
|| (options.keep_fnames
|
||||
&& (this.orig[0] instanceof AST_SymbolLambda
|
||||
|| this.orig[0] instanceof AST_SymbolDefun));
|
||||
|| this.orig[0] instanceof AST_SymbolDefun))
|
||||
|| this.orig[0] instanceof AST_SymbolMethod
|
||||
|| (options.keep_classnames
|
||||
&& (this.orig[0] instanceof AST_SymbolClass
|
||||
|| this.orig[0] instanceof AST_SymbolDefClass));
|
||||
},
|
||||
mangle: function(options) {
|
||||
var cache = options.cache && options.cache.props;
|
||||
|
|
@ -99,16 +107,33 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
|||
var defun = null;
|
||||
var last_var_had_const_pragma = false;
|
||||
var nesting = 0;
|
||||
var in_destructuring = null;
|
||||
var in_export;
|
||||
var tw = new TreeWalker(function(node, descend){
|
||||
if (options.screw_ie8 && node instanceof AST_Catch) {
|
||||
var create_a_block_scope =
|
||||
(options.screw_ie8 && node instanceof AST_Catch) ||
|
||||
((node instanceof AST_Block) && node.creates_block_scope());
|
||||
if (create_a_block_scope) {
|
||||
var save_scope = scope;
|
||||
scope = new AST_Scope(node);
|
||||
scope.init_scope_vars(nesting);
|
||||
scope.parent_scope = save_scope;
|
||||
scope.is_block_scope = true;
|
||||
if (!(node instanceof AST_Scope)) {
|
||||
scope.uses_with = save_scope.uses_with;
|
||||
scope.uses_eval = save_scope.uses_eval;
|
||||
scope.directives = save_scope.directives;
|
||||
}
|
||||
descend();
|
||||
scope = save_scope;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Destructuring && node.is_array === false) {
|
||||
in_destructuring = node; // These don't nest
|
||||
descend();
|
||||
in_destructuring = null;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Scope) {
|
||||
node.init_scope_vars(nesting);
|
||||
var save_scope = node.parent_scope = scope;
|
||||
|
|
@ -122,6 +147,11 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
|||
labels = save_labels;
|
||||
return true; // don't descend again in TreeWalker
|
||||
}
|
||||
if (node instanceof AST_Export) {
|
||||
in_export = true;
|
||||
descend();
|
||||
in_export = false;
|
||||
}
|
||||
if (node instanceof AST_LabeledStatement) {
|
||||
var l = node.label;
|
||||
if (labels.has(l.name)) {
|
||||
|
|
@ -140,12 +170,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
|||
if (node instanceof AST_Symbol) {
|
||||
node.scope = scope;
|
||||
}
|
||||
if (node instanceof AST_SymbolFunarg) {
|
||||
node.object_destructuring_arg = !!in_destructuring;
|
||||
defun.def_variable(node, in_export);
|
||||
}
|
||||
if (node instanceof AST_Label) {
|
||||
node.thedef = node;
|
||||
node.references = [];
|
||||
}
|
||||
if (node instanceof AST_SymbolLambda) {
|
||||
defun.def_function(node);
|
||||
defun.def_function(node, in_export);
|
||||
}
|
||||
else if (node instanceof AST_SymbolDefun) {
|
||||
// Careful here, the scope where this should be defined is
|
||||
|
|
@ -153,15 +187,32 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
|||
// scope when we encounter the AST_Defun node (which is
|
||||
// instanceof AST_Scope) but we get to the symbol a bit
|
||||
// later.
|
||||
(node.scope = defun.parent_scope).def_function(node);
|
||||
var parent_lambda = defun.parent_scope;
|
||||
while (parent_lambda.is_block_scope) {
|
||||
parent_lambda = parent_lambda.parent_scope;
|
||||
}
|
||||
(node.scope = parent_lambda).def_function(node, in_export);
|
||||
}
|
||||
else if (node instanceof AST_SymbolClass) {
|
||||
defun.def_variable(node, in_export);
|
||||
}
|
||||
else if (node instanceof AST_SymbolImport) {
|
||||
scope.def_variable(node, in_export);
|
||||
}
|
||||
else if (node instanceof AST_SymbolDefClass) {
|
||||
// This deals with the name of the class being available
|
||||
// inside the class.
|
||||
(node.scope = defun.parent_scope).def_function(node, in_export);
|
||||
}
|
||||
else if (node instanceof AST_Var) {
|
||||
last_var_had_const_pragma = node.has_const_pragma();
|
||||
}
|
||||
else if (node instanceof AST_SymbolVar
|
||||
|| node instanceof AST_SymbolConst) {
|
||||
var def = defun.def_variable(node);
|
||||
|| node instanceof AST_SymbolConst
|
||||
|| node instanceof AST_SymbolLet) {
|
||||
var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node, in_export);
|
||||
def.constant = node instanceof AST_SymbolConst || last_var_had_const_pragma;
|
||||
def.destructuring = in_destructuring;
|
||||
def.init = tw.parent().value;
|
||||
}
|
||||
else if (node instanceof AST_SymbolCatch) {
|
||||
|
|
@ -182,6 +233,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
|||
|
||||
// pass 2: find back references and eval
|
||||
var func = null;
|
||||
var cls = null;
|
||||
var globals = self.globals = new Dictionary();
|
||||
var tw = new TreeWalker(function(node, descend){
|
||||
if (node instanceof AST_Lambda) {
|
||||
|
|
@ -191,6 +243,13 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
|||
func = prev_func;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Class) {
|
||||
var prev_cls = cls;
|
||||
cls = node;
|
||||
descend();
|
||||
cls = prev_cls;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_LoopControl && node.label) {
|
||||
node.label.thedef.references.push(node);
|
||||
return true;
|
||||
|
|
@ -242,6 +301,14 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
|
|||
this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
|
||||
});
|
||||
|
||||
AST_Block.DEFMETHOD("creates_block_scope", function() {
|
||||
return (
|
||||
!(this instanceof AST_Lambda) &&
|
||||
!(this instanceof AST_Toplevel) &&
|
||||
!(this instanceof AST_Class)
|
||||
);
|
||||
});
|
||||
|
||||
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
||||
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
||||
this.uses_arguments = false;
|
||||
|
|
@ -269,16 +336,20 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|
|||
|| (this.parent_scope && this.parent_scope.find_variable(name));
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("def_function", function(symbol){
|
||||
this.functions.set(symbol.name, this.def_variable(symbol));
|
||||
AST_Scope.DEFMETHOD("def_function", function(symbol, in_export){
|
||||
this.functions.set(symbol.name, this.def_variable(symbol, in_export));
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("def_variable", function(symbol){
|
||||
AST_Scope.DEFMETHOD("def_variable", function(symbol, in_export){
|
||||
var def;
|
||||
if (!this.variables.has(symbol.name)) {
|
||||
def = new SymbolDef(this, this.variables.size(), symbol);
|
||||
this.variables.set(symbol.name, def);
|
||||
def.global = !this.parent_scope;
|
||||
def.object_destructuring_arg = symbol.object_destructuring_arg;
|
||||
if (in_export) {
|
||||
def.export = true;
|
||||
}
|
||||
def.global = !this.parent_scope && !(symbol instanceof AST_SymbolBlockDeclaration);
|
||||
} else {
|
||||
def = this.variables.get(symbol.name);
|
||||
def.orig.push(symbol);
|
||||
|
|
@ -331,7 +402,8 @@ AST_Scope.DEFMETHOD("references", function(sym){
|
|||
});
|
||||
|
||||
AST_Symbol.DEFMETHOD("unmangleable", function(options){
|
||||
return this.definition().unmangleable(options);
|
||||
var def = this.definition();
|
||||
return def && def.unmangleable(options);
|
||||
});
|
||||
|
||||
// property accessors are not mangleable
|
||||
|
|
@ -382,7 +454,8 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
|
|||
sort : false, // Ignored. Flag retained for backwards compatibility.
|
||||
toplevel : false,
|
||||
screw_ie8 : true,
|
||||
keep_fnames : false
|
||||
keep_fnames : false,
|
||||
keep_classnames : false
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -431,13 +504,19 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
|||
node.mangled_name = name;
|
||||
return true;
|
||||
}
|
||||
if (options.screw_ie8 && node instanceof AST_SymbolCatch) {
|
||||
var mangle_with_block_scope =
|
||||
(options.screw_ie8 && node instanceof AST_SymbolCatch) ||
|
||||
node instanceof AST_SymbolBlockDeclaration;
|
||||
if (mangle_with_block_scope) {
|
||||
to_mangle.push(node.definition());
|
||||
return;
|
||||
}
|
||||
});
|
||||
this.walk(tw);
|
||||
to_mangle.forEach(function(def){ def.mangle(options) });
|
||||
to_mangle.forEach(function(def){
|
||||
if (def.destructuring && !def.destructuring.is_array) return;
|
||||
def.mangle(options);
|
||||
});
|
||||
|
||||
if (options.cache) {
|
||||
options.cache.cname = this.cname;
|
||||
|
|
@ -497,12 +576,16 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
|
|||
base54.consider("new");
|
||||
else if (node instanceof AST_This)
|
||||
base54.consider("this");
|
||||
else if (node instanceof AST_Super)
|
||||
base54.consider("super");
|
||||
else if (node instanceof AST_Try)
|
||||
base54.consider("try");
|
||||
else if (node instanceof AST_Catch)
|
||||
base54.consider("catch");
|
||||
else if (node instanceof AST_Finally)
|
||||
base54.consider("finally");
|
||||
else if (node instanceof AST_Yield)
|
||||
base54.consider("yield");
|
||||
else if (node instanceof AST_Symbol && node.unmangleable(options))
|
||||
base54.consider(node.name);
|
||||
else if (node instanceof AST_Unary || node instanceof AST_Binary)
|
||||
|
|
|
|||
|
|
@ -163,10 +163,18 @@ TreeTransformer.prototype = new TreeWalker;
|
|||
if (self.value) self.value = self.value.transform(tw);
|
||||
});
|
||||
|
||||
_(AST_Destructuring, function(self, tw) {
|
||||
self.names = do_list(self.names, tw);
|
||||
});
|
||||
|
||||
_(AST_Lambda, function(self, tw){
|
||||
if (self.name) self.name = self.name.transform(tw);
|
||||
self.argnames = do_list(self.argnames, tw);
|
||||
self.body = do_list(self.body, tw);
|
||||
if (self.body instanceof AST_Node) {
|
||||
self.body = self.body.transform(tw);
|
||||
} else {
|
||||
self.body = do_list(self.body, tw);
|
||||
}
|
||||
});
|
||||
|
||||
_(AST_Call, function(self, tw){
|
||||
|
|
@ -188,6 +196,10 @@ TreeTransformer.prototype = new TreeWalker;
|
|||
self.property = self.property.transform(tw);
|
||||
});
|
||||
|
||||
_(AST_Yield, function(self, tw){
|
||||
if (self.expression) self.expression = self.expression.transform(tw);
|
||||
});
|
||||
|
||||
_(AST_Unary, function(self, tw){
|
||||
self.expression = self.expression.transform(tw);
|
||||
});
|
||||
|
|
@ -215,4 +227,8 @@ TreeTransformer.prototype = new TreeWalker;
|
|||
self.value = self.value.transform(tw);
|
||||
});
|
||||
|
||||
_(AST_Expansion, function(self, tw){
|
||||
self.expression = self.expression.transform(tw);
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -72,3 +72,76 @@ constant_join_2: {
|
|||
var f = "strstr" + variable + "foobarmoo" + foo;
|
||||
}
|
||||
}
|
||||
|
||||
spread_with_variable_as_last_element: {
|
||||
input: {
|
||||
var values = [4, 5, 6];
|
||||
var a = [1, 2, 3, ...values];
|
||||
}
|
||||
expect: {
|
||||
var values = [4, 5, 6];
|
||||
var a = [1, 2, 3, ...values];
|
||||
}
|
||||
}
|
||||
|
||||
spread_with_variable_in_middle: {
|
||||
input: {
|
||||
var values = [4, 5, 6];
|
||||
var a = [1, 2, 3, ...values, 7,,,];
|
||||
}
|
||||
expect: {
|
||||
var values = [4, 5, 6];
|
||||
var a = [1, 2, 3, ...values, 7,,,];
|
||||
}
|
||||
}
|
||||
|
||||
spread_with_variable_at_front: {
|
||||
input: {
|
||||
var values = [1, 2, 3];
|
||||
var a = [...values, 4, 5, 6];
|
||||
}
|
||||
expect: {
|
||||
var values = [1, 2, 3];
|
||||
var a = [...values, 4, 5, 6];
|
||||
}
|
||||
}
|
||||
|
||||
spread_with_variable_at_front_after_elisions: {
|
||||
input: {
|
||||
var values = [1, 2, 3];
|
||||
var a = [,,,...values, 4, 5, 6];
|
||||
}
|
||||
expect: {
|
||||
var values = [1, 2, 3];
|
||||
var a = [,,,...values, 4, 5, 6];
|
||||
}
|
||||
}
|
||||
|
||||
spread_with_array_at_end: {
|
||||
input: {
|
||||
var a = [1, 2, ...[4, 5, 6]];
|
||||
}
|
||||
expect: {
|
||||
var a = [1, 2, ...[4, 5, 6]];
|
||||
}
|
||||
}
|
||||
|
||||
spread_with_logical_expression_at_end: {
|
||||
options = { evaluate: true }
|
||||
input: {
|
||||
var a = [1, 2, 3, ...[2+2]]
|
||||
}
|
||||
expect: {
|
||||
var a = [1, 2, 3, ...[4]]
|
||||
}
|
||||
}
|
||||
|
||||
spread_with_logical_expression_at_middle: {
|
||||
options = { evaluate: true }
|
||||
input: {
|
||||
var a = [1, 1, ...[1+1, 1+2, 2+3], 8]
|
||||
}
|
||||
expect: {
|
||||
var a = [1, 1, ...[2, 3, 5], 8]
|
||||
}
|
||||
}
|
||||
92
test/compress/arrow.js
Normal file
92
test/compress/arrow.js
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
arrow_functions_without_body: {
|
||||
input: {
|
||||
var a1 = () => 42;
|
||||
var a2 = (p) => p;
|
||||
var a3 = p => p;
|
||||
var a4 = (...p) => p;
|
||||
var a5 = (b, c) => b + c;
|
||||
var a6 = (b, ...c) => b + c[0];
|
||||
var a7 = (...b) => b.join();
|
||||
}
|
||||
expect: {
|
||||
var a1 = () => 42;
|
||||
var a2 = (p) => p;
|
||||
var a3 = p => p;
|
||||
var a4 = (...p) => p;
|
||||
var a5 = (b, c) => b + c;
|
||||
var a6 = (b, ...c) => b + c[0];
|
||||
var a7 = (...b) => b.join();
|
||||
}
|
||||
}
|
||||
|
||||
arrow_functions_with_body: {
|
||||
input: {
|
||||
var a1 = () => {
|
||||
var a = 42 * Math.random();
|
||||
return a;
|
||||
};
|
||||
var a2 = (p) => {
|
||||
var a = Math.random() * p;
|
||||
return a;
|
||||
};
|
||||
var a3 = p => {
|
||||
var a = Math.random() * p;
|
||||
return a;
|
||||
};
|
||||
var a4 = (...p) => {
|
||||
var a = Math.random() * p;
|
||||
return a;
|
||||
};
|
||||
var a5 = (b, c) => {
|
||||
var result = b * c + b / c;
|
||||
return result
|
||||
};
|
||||
var a6 = (b, ...c) => {
|
||||
var result = b;
|
||||
for (var i = 0; i < c.length; i++)
|
||||
result += c[i];
|
||||
return result
|
||||
};
|
||||
var a7 = (...b) => {
|
||||
b.join();
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var a1 = () => {
|
||||
var a = 42 * Math.random();
|
||||
return a;
|
||||
};
|
||||
var a2 = (p) => {
|
||||
var a = Math.random() * p;
|
||||
return a;
|
||||
};
|
||||
var a3 = p => {
|
||||
var a = Math.random() * p;
|
||||
return a;
|
||||
};
|
||||
var a4 = (...p) => {
|
||||
var a = Math.random() * p;
|
||||
return a;
|
||||
};
|
||||
var a5 = (b, c) => {
|
||||
var result = b * c + b / c;
|
||||
return result
|
||||
};
|
||||
var a6 = (b, ...c) => {
|
||||
var result = b;
|
||||
for (var i = 0; i < c.length; i++)
|
||||
result += c[i];
|
||||
return result
|
||||
};
|
||||
var a7 = (...b) => {
|
||||
b.join();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
arrow_function_with_single_parameter_with_default: {
|
||||
input: {
|
||||
var foo = (a = 0) => doSomething(a);
|
||||
}
|
||||
expect_exact: "var foo=(a=0)=>doSomething(a);"
|
||||
}
|
||||
132
test/compress/block-scope.js
Normal file
132
test/compress/block-scope.js
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
|
||||
let_statement: {
|
||||
input: {
|
||||
let x = 6;
|
||||
}
|
||||
expect_exact: "let x=6;"
|
||||
}
|
||||
|
||||
do_not_hoist_let: {
|
||||
options = {
|
||||
hoist_vars: true,
|
||||
};
|
||||
input: {
|
||||
function x() {
|
||||
if (FOO) {
|
||||
let let1;
|
||||
let let2;
|
||||
var var1;
|
||||
var var2;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function x() {
|
||||
var var1, var2;
|
||||
if (FOO) {
|
||||
let let1;
|
||||
let let2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do_not_remove_anon_blocks_if_they_have_decls: {
|
||||
input: {
|
||||
function x() {
|
||||
{
|
||||
let x;
|
||||
}
|
||||
{
|
||||
var x;
|
||||
}
|
||||
{
|
||||
const y;
|
||||
class Zee {};
|
||||
}
|
||||
}
|
||||
{
|
||||
let y;
|
||||
}
|
||||
{
|
||||
var y;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function x(){
|
||||
{
|
||||
let x
|
||||
}
|
||||
var x;
|
||||
{
|
||||
const y;
|
||||
class Zee {}
|
||||
}
|
||||
}
|
||||
{
|
||||
let y
|
||||
}
|
||||
var y;
|
||||
}
|
||||
}
|
||||
|
||||
remove_unused_in_global_block: {
|
||||
options = {
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
{
|
||||
let x;
|
||||
const y;
|
||||
class Zee {};
|
||||
var w;
|
||||
}
|
||||
let ex;
|
||||
const why;
|
||||
class Zed {};
|
||||
var wut;
|
||||
console.log(x, y, Zee);
|
||||
}
|
||||
expect: {
|
||||
var w;
|
||||
var wut;
|
||||
console.log(x, y, Zee);
|
||||
}
|
||||
}
|
||||
|
||||
regression_block_scope_resolves: {
|
||||
mangle = { };
|
||||
options = {
|
||||
dead_code: false
|
||||
};
|
||||
input: {
|
||||
(function () {
|
||||
if(1) {
|
||||
let x;
|
||||
const y;
|
||||
class Zee {};
|
||||
}
|
||||
if(1) {
|
||||
let ex;
|
||||
const why;
|
||||
class Zi {};
|
||||
}
|
||||
console.log(x, y, Zee, ex, why, Zi);
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
(function () {
|
||||
if (1) {
|
||||
let o;
|
||||
const n;
|
||||
class c {};
|
||||
}
|
||||
if (1) {
|
||||
let o;
|
||||
const n;
|
||||
class c {};
|
||||
}
|
||||
console.log(x, y, Zee, ex, why, Zi);
|
||||
}());
|
||||
}
|
||||
}
|
||||
|
|
@ -88,6 +88,28 @@ dead_code_constant_boolean_should_warn_more: {
|
|||
}
|
||||
}
|
||||
|
||||
dead_code_block_decls_die: {
|
||||
options = {
|
||||
dead_code : true,
|
||||
conditionals : true,
|
||||
booleans : true,
|
||||
evaluate : true
|
||||
};
|
||||
input: {
|
||||
if (0) {
|
||||
let foo = 6;
|
||||
const bar = 12;
|
||||
class Baz {};
|
||||
var qux;
|
||||
}
|
||||
console.log(foo, bar, Baz);
|
||||
}
|
||||
expect: {
|
||||
var qux;
|
||||
console.log(foo, bar, Baz);
|
||||
}
|
||||
}
|
||||
|
||||
dead_code_const_declaration: {
|
||||
options = {
|
||||
dead_code : true,
|
||||
|
|
|
|||
43
test/compress/destructuring.js
Normal file
43
test/compress/destructuring.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
destructuring_arrays: {
|
||||
input: {
|
||||
var [aa, bb] = cc;
|
||||
}
|
||||
expect: {
|
||||
var[aa,bb]=cc;
|
||||
}
|
||||
}
|
||||
|
||||
destructuring_objects: {
|
||||
input: {
|
||||
var {aa, bb} = {aa:1, bb:2};
|
||||
}
|
||||
expect: {
|
||||
var{aa,bb}={aa:1,bb:2};
|
||||
}
|
||||
}
|
||||
|
||||
nested_destructuring_objects: {
|
||||
input: {
|
||||
var [{a},b] = c;
|
||||
}
|
||||
expect_exact: 'var[{a},b]=c;';
|
||||
}
|
||||
|
||||
destructuring_vardef_in_loops: {
|
||||
input: {
|
||||
for (var [x,y] in pairs);
|
||||
for (var [a] = 0;;);
|
||||
for (var {c} of cees);
|
||||
}
|
||||
expect_exact: "for(var[x,y]in pairs);for(var[a]=0;;);for(var{c}of cees);"
|
||||
}
|
||||
destructuring_expressions: {
|
||||
input: {
|
||||
({a, b});
|
||||
[{a}];
|
||||
f({x});
|
||||
}
|
||||
expect_exact: "({a,b});[{a}];f({x});"
|
||||
}
|
||||
|
||||
|
|
@ -164,6 +164,87 @@ used_var_in_catch: {
|
|||
}
|
||||
}
|
||||
|
||||
unused_block_decls_in_catch: {
|
||||
options = { unused: true };
|
||||
input: {
|
||||
function foo() {
|
||||
try {
|
||||
foo();
|
||||
} catch(ex) {
|
||||
let x = 10;
|
||||
const y = 10;
|
||||
class Zee {};
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function foo() {
|
||||
try {
|
||||
foo();
|
||||
} catch(ex) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
used_block_decls_in_catch: {
|
||||
options = { unused: true };
|
||||
input: {
|
||||
function foo() {
|
||||
try {
|
||||
foo();
|
||||
} catch(ex) {
|
||||
let x = 10;
|
||||
const y = 10;
|
||||
class Zee {};
|
||||
}
|
||||
console.log(x, y, Zee);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function foo() {
|
||||
try {
|
||||
foo();
|
||||
} catch(ex) {}
|
||||
console.log(x, y, Zee);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unused_block_decls: {
|
||||
options = { unused: true };
|
||||
input: {
|
||||
function foo() {
|
||||
{
|
||||
const x;
|
||||
}
|
||||
{
|
||||
let y;
|
||||
}
|
||||
console.log(x, y);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function foo() {
|
||||
console.log(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unused_keep_harmony_destructuring: {
|
||||
options = { unused: true };
|
||||
input: {
|
||||
function foo() {
|
||||
var {x, y} = foo;
|
||||
var a = foo;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function foo() {
|
||||
var {x, y} = foo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keep_fnames: {
|
||||
options = { unused: true, keep_fnames: true, unsafe: true };
|
||||
input: {
|
||||
|
|
|
|||
|
|
@ -37,3 +37,91 @@ positive_zero: {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
pow: {
|
||||
options = { evaluate: true }
|
||||
input: {
|
||||
var a = 5 ** 3;
|
||||
}
|
||||
expect: {
|
||||
var a = 125;
|
||||
}
|
||||
}
|
||||
|
||||
pow_sequence: {
|
||||
options = {
|
||||
evaluate: true
|
||||
}
|
||||
input: {
|
||||
var a = 2 ** 3 ** 2;
|
||||
}
|
||||
expect: {
|
||||
var a = 512;
|
||||
}
|
||||
}
|
||||
|
||||
pow_mixed: {
|
||||
options = {
|
||||
evaluate: true
|
||||
}
|
||||
input: {
|
||||
var a = 5 + 2 ** 3 + 5;
|
||||
var b = 5 * 3 ** 2;
|
||||
var c = 5 ** 3 * 2;
|
||||
var d = 5 ** +3;
|
||||
}
|
||||
expect: {
|
||||
var a = 18;
|
||||
var b = 45;
|
||||
var c = 250;
|
||||
var d = 125;
|
||||
}
|
||||
}
|
||||
|
||||
pow_with_right_side_evaluating_to_unary: {
|
||||
options = {
|
||||
evaluate: true
|
||||
}
|
||||
input: {
|
||||
var a = (4 - 7) ** foo;
|
||||
var b = ++bar ** 3;
|
||||
var c = --baz ** 2;
|
||||
}
|
||||
expect_exact: "var a=(-3)**foo;var b=++bar**3;var c=--baz**2;"
|
||||
}
|
||||
|
||||
pow_with_number_constants: {
|
||||
options = {
|
||||
evaluate: true
|
||||
}
|
||||
input: {
|
||||
var a = 5 ** NaN; /* NaN exponent results to NaN */
|
||||
var b = 42 ** +0; /* +0 exponent results to NaN */
|
||||
var c = 42 ** -0; /* -0 exponent results to NaN */
|
||||
var d = NaN ** 1; /* NaN with non-zero exponent is NaN */
|
||||
var e = 2 ** Infinity; /* abs(base) > 1 with Infinity as exponent is Infinity */
|
||||
var f = 2 ** -Infinity; /* abs(base) > 1 with -Infinity as exponent is +0 */
|
||||
var g = (-7) ** (0.5);
|
||||
var h = 2324334 ** 34343443;
|
||||
var i = (-2324334) ** 34343443;
|
||||
var j = 2 ** (-3);
|
||||
var k = 2.0 ** -3;
|
||||
var l = 2.0 ** (5 - 7);
|
||||
var m = 3 ** -10; // Result will be 0.000016935087808430286, which is too long
|
||||
}
|
||||
expect: {
|
||||
var a = NaN;
|
||||
var b = 1;
|
||||
var c = 1;
|
||||
var d = NaN;
|
||||
var e = Infinity;
|
||||
var f = 0;
|
||||
var g = NaN;
|
||||
var h = Infinity;
|
||||
var i = -Infinity;
|
||||
var j = .125;
|
||||
var k = .125;
|
||||
var l = .25;
|
||||
var m = 3 ** -10;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
17
test/compress/expansions.js
Normal file
17
test/compress/expansions.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
expand_arguments: {
|
||||
input: {
|
||||
func(a, ...rest);
|
||||
func(...all);
|
||||
}
|
||||
expect_exact: "func(a,...rest);func(...all);"
|
||||
}
|
||||
|
||||
expand_parameters: {
|
||||
input: {
|
||||
(function (a, ...b){});
|
||||
(function (...args){});
|
||||
}
|
||||
expect_exact: "(function(a,...b){});(function(...args){});"
|
||||
}
|
||||
|
||||
52
test/compress/expression.js
Normal file
52
test/compress/expression.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
pow: {
|
||||
input: {
|
||||
var a = 2 ** 7;
|
||||
var b = 3;
|
||||
b **= 2;
|
||||
}
|
||||
expect: {
|
||||
var a = 2 ** 7;
|
||||
var b = 3;
|
||||
b **= 2;
|
||||
}
|
||||
}
|
||||
|
||||
pow_with_number_constants: {
|
||||
input: {
|
||||
var a = 5 ** NaN;
|
||||
var b = 42 ** +0;
|
||||
var c = 42 ** -0;
|
||||
var d = NaN ** 1;
|
||||
var e = 2 ** Infinity;
|
||||
var f = 2 ** -Infinity;
|
||||
}
|
||||
expect: {
|
||||
var a = 5 ** NaN;
|
||||
var b = 42 ** +0;
|
||||
var c = 42 ** -0;
|
||||
var d = NaN ** 1;
|
||||
var e = 2 ** (1/0);
|
||||
var f = 2 ** -(1/0);
|
||||
}
|
||||
}
|
||||
|
||||
pow_with_parentheses: {
|
||||
input: {
|
||||
var g = (-7) ** (0.5);
|
||||
var h = 2324334 ** 34343443;
|
||||
var i = (-2324334) ** 34343443;
|
||||
var j = 2 ** (-3);
|
||||
var k = 2.0 ** -3;
|
||||
var l = 2.0 ** (5 - 7);
|
||||
}
|
||||
expect_exact: "var g=(-7)**.5;var h=2324334**34343443;var i=(-2324334)**34343443;var j=2**-3;var k=2**-3;var l=2**(5-7);"
|
||||
}
|
||||
|
||||
pow_with_unary_between_brackets: {
|
||||
input: {
|
||||
var a = (-(+5)) ** 3;
|
||||
}
|
||||
expect: {
|
||||
var a = (-+5)**3;
|
||||
}
|
||||
}
|
||||
527
test/compress/harmony.js
Normal file
527
test/compress/harmony.js
Normal file
|
|
@ -0,0 +1,527 @@
|
|||
arrow_functions: {
|
||||
input: {
|
||||
(a) => b; // 1 args
|
||||
(a, b) => c; // n args
|
||||
() => b; // 0 args
|
||||
(a) => (b) => c; // func returns func returns func
|
||||
(a) => ((b) => c); // So these parens are dropped
|
||||
() => (b,c) => d; // func returns func returns func
|
||||
a=>{return b;}
|
||||
a => 'lel'; // Dropping the parens
|
||||
}
|
||||
expect_exact: "a=>b;(a,b)=>c;()=>b;a=>b=>c;a=>b=>c;()=>(b,c)=>d;a=>{return b};a=>\"lel\";"
|
||||
}
|
||||
|
||||
arrow_function_parens: {
|
||||
input: {
|
||||
something && (() => {});
|
||||
}
|
||||
expect_exact: "something&&(()=>{});"
|
||||
}
|
||||
arrow_function_parens_2: {
|
||||
input: {
|
||||
(() => null)();
|
||||
}
|
||||
expect_exact: "(()=>null)();"
|
||||
}
|
||||
|
||||
regression_arrow_functions_and_hoist: {
|
||||
options = {
|
||||
hoist_vars: true,
|
||||
hoist_funs: true
|
||||
}
|
||||
input: {
|
||||
(a) => b;
|
||||
}
|
||||
expect_exact: "a=>b;"
|
||||
}
|
||||
|
||||
regression_assign_arrow_functions: {
|
||||
input: {
|
||||
oninstall = e => false;
|
||||
oninstall = () => false;
|
||||
}
|
||||
expect: {
|
||||
oninstall=e=>false;
|
||||
oninstall=()=>false;
|
||||
}
|
||||
}
|
||||
|
||||
computed_property_names: {
|
||||
input: {
|
||||
obj({ ["x" + "x"]: 6 });
|
||||
}
|
||||
expect_exact: 'obj({["x"+"x"]:6});'
|
||||
}
|
||||
|
||||
shorthand_properties: {
|
||||
mangle = true;
|
||||
input: (function() {
|
||||
var prop = 1;
|
||||
const value = {prop};
|
||||
return value;
|
||||
})();
|
||||
expect: (function() {
|
||||
var n = 1;
|
||||
const r = {prop:n};
|
||||
return r;
|
||||
})();
|
||||
}
|
||||
|
||||
typeof_arrow_functions: {
|
||||
options = {
|
||||
evaluate: true
|
||||
}
|
||||
input: {
|
||||
var foo = typeof (x) => null;
|
||||
}
|
||||
expect_exact: "var foo=\"function\";"
|
||||
}
|
||||
|
||||
template_strings: {
|
||||
input: {
|
||||
``;
|
||||
`xx\`x`;
|
||||
`${ foo + 2 }`;
|
||||
` foo ${ bar + `baz ${ qux }` }`;
|
||||
}
|
||||
expect_exact: "``;`xx\\`x`;`${foo+2}`;` foo ${bar+`baz ${qux}`}`;";
|
||||
}
|
||||
|
||||
template_string_prefixes: {
|
||||
input: {
|
||||
String.raw`foo`;
|
||||
foo `bar`;
|
||||
}
|
||||
expect_exact: "String.raw`foo`;foo`bar`;";
|
||||
}
|
||||
|
||||
destructuring_arguments: {
|
||||
input: {
|
||||
(function ( a ) { });
|
||||
(function ( [ a ] ) { });
|
||||
(function ( [ a, b ] ) { });
|
||||
(function ( [ [ a ] ] ) { });
|
||||
(function ( [ [ a, b ] ] ) { });
|
||||
(function ( [ a, [ b ] ] ) { });
|
||||
(function ( [ [ b ], a ] ) { });
|
||||
|
||||
(function ( { a } ) { });
|
||||
(function ( { a, b } ) { });
|
||||
|
||||
(function ( [ { a } ] ) { });
|
||||
(function ( [ { a, b } ] ) { });
|
||||
(function ( [ a, { b } ] ) { });
|
||||
(function ( [ { b }, a ] ) { });
|
||||
|
||||
( [ a ] ) => { };
|
||||
( [ a, b ] ) => { };
|
||||
|
||||
( { a } ) => { };
|
||||
( { a, b, c, d, e } ) => { };
|
||||
|
||||
( [ a ] ) => b;
|
||||
( [ a, b ] ) => c;
|
||||
|
||||
( { a } ) => b;
|
||||
( { a, b } ) => c;
|
||||
}
|
||||
expect: {
|
||||
(function(a){});
|
||||
(function([a]){});
|
||||
(function([a,b]){});
|
||||
(function([[a]]){});
|
||||
(function([[a,b]]){});
|
||||
(function([a,[b]]){});
|
||||
(function([[b],a]){});
|
||||
|
||||
(function({a}){});
|
||||
(function({a,b}){});
|
||||
|
||||
(function([{a}]){});
|
||||
(function([{a,b}]){});
|
||||
(function([a,{b}]){});
|
||||
(function([{b},a]){});
|
||||
|
||||
([a])=>{};
|
||||
([a,b])=>{};
|
||||
|
||||
({a})=>{};
|
||||
({a,b,c,d,e})=>{};
|
||||
|
||||
([a])=>b;
|
||||
([a,b])=>c;
|
||||
|
||||
({a})=>b;
|
||||
({a,b})=>c;
|
||||
}
|
||||
}
|
||||
|
||||
default_arguments: {
|
||||
input: {
|
||||
function x(a = 6) { }
|
||||
function x(a = (6 + 5)) { }
|
||||
function x({ foo } = {}, [ bar ] = [ 1 ]) { }
|
||||
}
|
||||
expect_exact: "function x(a=6){}function x(a=6+5){}function x({foo}={},[bar]=[1]){}"
|
||||
}
|
||||
|
||||
default_values_in_destructurings: {
|
||||
input: {
|
||||
function x({a=(4), b}) {}
|
||||
function x([b, c=(12)]) {}
|
||||
var { x = (6), y } = x;
|
||||
var [ x, y = (6) ] = x;
|
||||
}
|
||||
expect_exact: "function x({a=4,b}){}function x([b,c=12]){}var{x=6,y}=x;var[x,y=6]=x;"
|
||||
}
|
||||
|
||||
concise_methods: {
|
||||
input: {
|
||||
x = {
|
||||
foo(a, b) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
y = {
|
||||
foo([{a}]) {
|
||||
return a;
|
||||
},
|
||||
bar(){}
|
||||
}
|
||||
}
|
||||
expect_exact: "x={foo(a,b){return x}};y={foo([{a}]){return a},bar(){}};"
|
||||
}
|
||||
|
||||
concise_methods_with_computed_property: {
|
||||
options = {
|
||||
evaluate: true
|
||||
}
|
||||
input: {
|
||||
var foo = {
|
||||
[Symbol.iterator]() {
|
||||
return { /* stuff */ }
|
||||
},
|
||||
[1 + 2]() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var foo = {
|
||||
[Symbol.iterator]() {
|
||||
return { /* stuff */ }
|
||||
},
|
||||
[3]() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
concise_methods_and_mangle_props: {
|
||||
mangle_props = {
|
||||
regex: /_/
|
||||
};
|
||||
input: {
|
||||
function x() {
|
||||
obj = {
|
||||
_foo() { return 1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function x() {
|
||||
obj = {
|
||||
a() { return 1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
concise_generators: {
|
||||
input: {
|
||||
x = {
|
||||
*foo(a, b) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
y = {
|
||||
*foo([{a}]) {
|
||||
yield a;
|
||||
},
|
||||
bar(){}
|
||||
}
|
||||
}
|
||||
expect_exact: "x={*foo(a,b){return x}};y={*foo([{a}]){yield a},bar(){}};"
|
||||
}
|
||||
|
||||
concise_methods_and_keyword_names: {
|
||||
input: {
|
||||
x = {
|
||||
catch() {},
|
||||
throw() {}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
x={catch(){},throw(){}};
|
||||
}
|
||||
}
|
||||
|
||||
classes: {
|
||||
input: {
|
||||
class SomeClass {
|
||||
constructor() {
|
||||
};
|
||||
foo() {};
|
||||
};
|
||||
class NoSemi {
|
||||
constructor(...args) {
|
||||
}
|
||||
foo() {}
|
||||
};
|
||||
class ChildClass extends SomeClass {};
|
||||
var asExpression = class AsExpression {};
|
||||
var nameless = class {};
|
||||
}
|
||||
expect_exact: "class SomeClass{constructor(){}foo(){}}class NoSemi{constructor(...args){}foo(){}}class ChildClass extends SomeClass{}var asExpression=class AsExpression{};var nameless=class{};"
|
||||
}
|
||||
|
||||
getter_setter_with_computed_value: {
|
||||
input: {
|
||||
class C {
|
||||
get ['a']() {
|
||||
return 'A';
|
||||
}
|
||||
set ['a'](value) {
|
||||
do_something(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
class C {
|
||||
get ['a']() {
|
||||
return 'A';
|
||||
}
|
||||
set ['a'](value) {
|
||||
do_something(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class_statics: {
|
||||
input: {
|
||||
x = class {
|
||||
static staticMethod() {}
|
||||
static get foo() {}
|
||||
static set bar() {}
|
||||
static() { /* "static" can be a method name! */ }
|
||||
get() { /* "get" can be a method name! */ }
|
||||
set() { /* "set" can be a method name! */ }
|
||||
}
|
||||
}
|
||||
expect_exact: "x=class{static staticMethod(){}static get foo(){}static set bar(){}static(){}get(){}set(){}};"
|
||||
}
|
||||
|
||||
class_name_can_be_mangled: {
|
||||
mangle = { };
|
||||
input: {
|
||||
function x() {
|
||||
class Foo {
|
||||
}
|
||||
var class1 = Foo
|
||||
var class2 = class Bar {}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function x() {
|
||||
class a { }
|
||||
var n = a
|
||||
var r = class a {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class_name_can_be_preserved: {
|
||||
mangle = {
|
||||
keep_classnames: true
|
||||
}
|
||||
input: {
|
||||
function x() {
|
||||
(class Baz { });
|
||||
class Foo {};
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function x() {
|
||||
(class Baz { });
|
||||
class Foo {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
classes_can_have_generators: {
|
||||
input: {
|
||||
class Foo {
|
||||
*bar() {}
|
||||
static *baz() {}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
class Foo {
|
||||
*bar() {}
|
||||
static *baz() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
classes_can_have_computed_generators: {
|
||||
input: {
|
||||
class C4 {
|
||||
*['constructor']() {}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
class C4 {
|
||||
*['constructor']() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
classes_can_have_computed_static: {
|
||||
input: {
|
||||
class C4 {
|
||||
static ['constructor']() {}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
class C4 {
|
||||
static ['constructor']() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_target: {
|
||||
input: {
|
||||
new.target;
|
||||
new.target.name;
|
||||
}
|
||||
expect_exact: "new.target;new.target.name;"
|
||||
}
|
||||
|
||||
number_literals: {
|
||||
input: {
|
||||
0b1001;
|
||||
0B1001;
|
||||
0o11;
|
||||
0O11;
|
||||
}
|
||||
|
||||
expect: {
|
||||
9;
|
||||
9;
|
||||
9;
|
||||
9;
|
||||
}
|
||||
}
|
||||
|
||||
import_statement: {
|
||||
input: {
|
||||
import "mod-name";
|
||||
import Foo from "bar";
|
||||
import { Bar, Baz } from 'lel';
|
||||
import Bar, { Foo } from 'lel';
|
||||
import { Bar as kex, Baz as food } from 'lel';
|
||||
}
|
||||
expect_exact: "import\"mod-name\";import Foo from\"bar\";import{Bar,Baz}from\"lel\";import Bar,{Foo}from\"lel\";import{Bar as kex,Baz as food}from\"lel\";"
|
||||
}
|
||||
|
||||
export_statement: {
|
||||
input: {
|
||||
export default 1;
|
||||
export var foo = 4;
|
||||
export let foo = 6;
|
||||
export const foo = 6;
|
||||
export function foo() {};
|
||||
export class foo { };
|
||||
}
|
||||
expect_exact: "export default 1;export var foo=4;export let foo=6;export const foo=6;export function foo(){};export class foo{};"
|
||||
}
|
||||
|
||||
import_statement_mangling: {
|
||||
mangle = { };
|
||||
input: {
|
||||
import Foo from "foo";
|
||||
import Bar, {Food} from "lel";
|
||||
import {What as Whatever} from "lel";
|
||||
Foo();
|
||||
Bar();
|
||||
Food();
|
||||
Whatever();
|
||||
}
|
||||
expect: {
|
||||
import l from "foo";
|
||||
import e, {Food as o} from "lel";
|
||||
import {What as f} from "lel";
|
||||
l();
|
||||
e();
|
||||
o();
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
export_statement_mangling: {
|
||||
mangle = { };
|
||||
input: {
|
||||
export var foo = 6;
|
||||
export function bar() { }
|
||||
export class Baz { }
|
||||
bar(foo, Baz)
|
||||
}
|
||||
expect: {
|
||||
export var foo = 6;
|
||||
export function bar() { }
|
||||
export class Baz { }
|
||||
bar(foo, Baz)
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/mishoo/UglifyJS2/issues/1021
|
||||
regression_for_of_const: {
|
||||
input: {
|
||||
for (const x of y) {}
|
||||
for (const x in y) {}
|
||||
}
|
||||
expect: {
|
||||
for (const x of y);for (const x in y);
|
||||
}
|
||||
}
|
||||
|
||||
// Fabio: My patches accidentally caused a crash whenever
|
||||
// there's an extraneous set of parens around an object.
|
||||
regression_cannot_destructure: {
|
||||
input: {
|
||||
var x = ({ x : 3 });
|
||||
x(({ x: 3 }));
|
||||
}
|
||||
expect_exact: "var x={x:3};x({x:3});";
|
||||
}
|
||||
|
||||
regression_cannot_use_of: {
|
||||
input: {
|
||||
function of() {
|
||||
}
|
||||
var of = "is a valid variable name";
|
||||
of = { of: "is ok" };
|
||||
x.of;
|
||||
of: foo()
|
||||
}
|
||||
expect: {
|
||||
function of(){}
|
||||
var of="is a valid variable name";
|
||||
of={of:"is ok"};
|
||||
x.of;
|
||||
foo(); /* Label statement missing? No prob. */
|
||||
}
|
||||
}
|
||||
85
test/compress/hoist.js
Normal file
85
test/compress/hoist.js
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
|
||||
hoist_vars: {
|
||||
options = {
|
||||
hoist_vars: true
|
||||
}
|
||||
input: {
|
||||
function a() {
|
||||
bar();
|
||||
var var1;
|
||||
var var2;
|
||||
}
|
||||
function b(anArg) {
|
||||
bar();
|
||||
var var1;
|
||||
var anArg;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function a() {
|
||||
var var1, var2; // Vars go up and are joined
|
||||
bar();
|
||||
}
|
||||
function b(anArg) {
|
||||
var var1;
|
||||
bar();
|
||||
// But vars named like arguments go away!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hoist_funs: {
|
||||
options = {
|
||||
hoist_funs: true
|
||||
}
|
||||
input: {
|
||||
function a() {
|
||||
bar();
|
||||
function foo() {}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function a() {
|
||||
function foo() {} // Funs go up
|
||||
bar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hoist_no_destructurings: {
|
||||
options = {
|
||||
hoist_vars: true,
|
||||
hoist_funs: true
|
||||
}
|
||||
input: {
|
||||
function a([anArg]) {
|
||||
bar();
|
||||
var var1;
|
||||
var anArg; // Because anArg is already declared, this goes away!
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function a([anArg]) {
|
||||
var var1;
|
||||
bar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dont_hoist_var_destructurings: {
|
||||
options = {
|
||||
hoist_vars: true,
|
||||
hoist_funs: true
|
||||
}
|
||||
input: {
|
||||
function x() {
|
||||
// If foo is null or undefined, this should be an exception
|
||||
var {x,y} = foo;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function x() {
|
||||
var {x,y} = foo;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
test/compress/issue-1001.js
Normal file
8
test/compress/issue-1001.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
parenthesis_strings_in_parenthesis: {
|
||||
input: {
|
||||
var foo = ('(');
|
||||
a(')');
|
||||
|
||||
}
|
||||
expect_exact: 'var foo="(";a(")");'
|
||||
}
|
||||
30
test/compress/issue-1043.js
Normal file
30
test/compress/issue-1043.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
issue_1043: {
|
||||
options = {
|
||||
side_effects: true
|
||||
};
|
||||
|
||||
input: {
|
||||
function* range(start = 0, end = null, step = 1) {
|
||||
if (end == null) {
|
||||
end = start;
|
||||
start = 0;
|
||||
}
|
||||
|
||||
for (let i = start; i < end; i += step) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect: {
|
||||
function* range(start = 0, end = null, step = 1) {
|
||||
if (null == end) {
|
||||
end = start;
|
||||
start = 0;
|
||||
}
|
||||
|
||||
for (let i = start; i < end; i += step)
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
test/compress/issue-1044.js
Normal file
9
test/compress/issue-1044.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
issue_1044: {
|
||||
options = { evaluate: true, conditionals: true };
|
||||
input: {
|
||||
const mixed = Base ? class extends Base {} : class {}
|
||||
}
|
||||
expect: {
|
||||
const mixed = Base ? class extends Base {} : class {}
|
||||
}
|
||||
}
|
||||
30
test/compress/issue-203.js
Normal file
30
test/compress/issue-203.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
compress_new_function: {
|
||||
options = {
|
||||
unsafe: true
|
||||
}
|
||||
input: {
|
||||
new Function("aa, bb", 'return aa;');
|
||||
}
|
||||
expect: {
|
||||
Function("a", "b", "return a");
|
||||
}
|
||||
}
|
||||
|
||||
compress_new_function_with_destruct: {
|
||||
options = {
|
||||
unsafe: true
|
||||
}
|
||||
input: {
|
||||
new Function("aa, [bb]", 'return aa;');
|
||||
new Function("aa, {bb}", 'return aa;');
|
||||
new Function("[[aa]], [{bb}]", 'return aa;');
|
||||
}
|
||||
expect: {
|
||||
Function("a", "[b]", "return a");
|
||||
Function("a", "{bb}", "return a");
|
||||
Function("[[a]]", "[{bb}]", 'return a');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
9
test/compress/issue-926.js
Normal file
9
test/compress/issue-926.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
template_strings: {
|
||||
input: {
|
||||
foo(
|
||||
`<span>${contents}</span>`,
|
||||
`<a href="${url}">${text}</a>`
|
||||
);
|
||||
}
|
||||
expect_exact: "foo(`<span>${contents}</span>`,`<a href=\"${url}\">${text}</a>`);"
|
||||
}
|
||||
40
test/compress/join-vars.js
Normal file
40
test/compress/join-vars.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
only_vars: {
|
||||
options = { join_vars: true };
|
||||
input: {
|
||||
let netmaskBinary = '';
|
||||
for (let i = 0; i < netmaskBits; ++i) {
|
||||
netmaskBinary += '1';
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
let netmaskBinary = '';
|
||||
for (let i = 0; i < netmaskBits; ++i) netmaskBinary += '1';
|
||||
}
|
||||
}
|
||||
|
||||
issue_1079_with_vars: {
|
||||
options = { join_vars: true };
|
||||
input: {
|
||||
var netmaskBinary = '';
|
||||
for (var i = 0; i < netmaskBits; ++i) {
|
||||
netmaskBinary += '1';
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
for (var netmaskBinary = '', i = 0; i < netmaskBits; ++i) netmaskBinary += '1';
|
||||
}
|
||||
}
|
||||
|
||||
issue_1079_with_mixed: {
|
||||
options = { join_vars: true };
|
||||
input: {
|
||||
var netmaskBinary = '';
|
||||
for (let i = 0; i < netmaskBits; ++i) {
|
||||
netmaskBinary += '1';
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var netmaskBinary = ''
|
||||
for (let i = 0; i < netmaskBits; ++i) netmaskBinary += '1';
|
||||
}
|
||||
}
|
||||
9
test/compress/super.js
Normal file
9
test/compress/super.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
super_can_be_parsed: {
|
||||
input: {
|
||||
super(1,2);
|
||||
super.meth();
|
||||
}
|
||||
expect_exact: "super(1,2);super.meth();"
|
||||
}
|
||||
|
||||
|
|
@ -15,3 +15,88 @@ unicode_parse_variables: {
|
|||
var l০ = 3;
|
||||
}
|
||||
}
|
||||
|
||||
unicode_escaped_identifier: {
|
||||
beautify = {ecma: 6}
|
||||
input: {
|
||||
var \u{61} = "foo";
|
||||
var \u{10000} = "bar";
|
||||
}
|
||||
expect_exact: 'var a="foo";var \u{10000}="bar";';
|
||||
}
|
||||
|
||||
unicode_identifier_ascii_only: {
|
||||
beautify = {ascii_only: true, ecma: 6}
|
||||
input: {
|
||||
var \u{0061} = "hi";
|
||||
var bar = "h\u{0065}llo";
|
||||
var \u{10000} = "testing \u{101111}";
|
||||
}
|
||||
expect_exact: 'var a="hi";var bar="hello";var \\u{10000}="testing \\u{101111}";'
|
||||
}
|
||||
|
||||
unicode_string_literals: {
|
||||
beautify = {ascii_only: true, ecma: 6}
|
||||
input: {
|
||||
var a = "6 length unicode character: \u{101111}";
|
||||
}
|
||||
expect_exact: 'var a="6 length unicode character: \\u{101111}";'
|
||||
}
|
||||
|
||||
unicode_output_es5_surrogates: {
|
||||
beautify = {ascii_only: true, ecma: 5}
|
||||
input: {
|
||||
var \u{10000} = "6 length unicode character: \u{10FFFF}";
|
||||
}
|
||||
expect_exact: 'var \\ud800\\udc00="6 length unicode character: \\udbff\\udfff";'
|
||||
}
|
||||
|
||||
check_escape_style: {
|
||||
beautify = {ascii_only: true, ecma: 6}
|
||||
input: {
|
||||
var a = "\x01";
|
||||
var \ua0081 = "\x10"; // \u0081 only in ID_Continue
|
||||
var \u0100 = "\u0100";
|
||||
var \u1000 = "\u1000";
|
||||
var \u{10000} = "\u{10000}";
|
||||
var \u{2f800} = "\u{100000}";
|
||||
}
|
||||
expect_exact: 'var a="\\x01";var \\ua0081="\\x10";var \\u0100="\\u0100";var \\u1000="\\u1000";var \\u{10000}="\\u{10000}";var \\u{2f800}="\\u{100000}";'
|
||||
}
|
||||
|
||||
check_escape_style_es5: {
|
||||
beautify = {ascii_only: true, ecma: 5}
|
||||
input: {
|
||||
var a = "\x01";
|
||||
var \ua0081 = "\x10"; // \u0081 only in ID_Continue
|
||||
var \u0100 = "\u0100";
|
||||
var \u1000 = "\u1000";
|
||||
var \u{10000} = "\u{10000}";
|
||||
var \u{2f800} = "\u{100000}";
|
||||
}
|
||||
expect_exact: 'var a="\\x01";var \\ua0081="\\x10";var \\u0100="\\u0100";var \\u1000="\\u1000";var \\ud800\\udc00="\\ud800\\udc00";var \\ud87e\\udc00="\\udbc0\\udc00";'
|
||||
}
|
||||
|
||||
ID_continue_with_surrogate_pair: {
|
||||
beautify = {ascii_only: true, ecma: 6}
|
||||
input: {
|
||||
var \u{2f800}\u{2f800}\u{2f800}\u{2f800} = "\u{100000}\u{100000}\u{100000}\u{100000}\u{100000}";
|
||||
}
|
||||
expect_exact: 'var \\u{2f800}\\u{2f800}\\u{2f800}\\u{2f800}="\\u{100000}\\u{100000}\\u{100000}\\u{100000}\\u{100000}";'
|
||||
}
|
||||
|
||||
escape_non_escaped_identifier: {
|
||||
beautify = {ascii_only: true, ecma: 6}
|
||||
input: {
|
||||
var µþ = "µþ";
|
||||
}
|
||||
expect_exact: 'var \\u00b5\\u00fe="\\xb5\\xfe";'
|
||||
}
|
||||
|
||||
non_escape_2_non_escape: {
|
||||
beautify = {ascii_only: false, ecma: 6}
|
||||
input: {
|
||||
var µþ = "µþ";
|
||||
}
|
||||
expect_exact: 'var µþ="µþ";'
|
||||
}
|
||||
142
test/compress/yield.js
Normal file
142
test/compress/yield.js
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
generators: {
|
||||
input: {
|
||||
function* fn() {};
|
||||
}
|
||||
expect_exact: "function*fn(){}"
|
||||
}
|
||||
|
||||
generators_yield: {
|
||||
input: {
|
||||
function* fn() {
|
||||
yield remote();
|
||||
}
|
||||
}
|
||||
expect_exact: "function*fn(){yield remote()}"
|
||||
}
|
||||
|
||||
generators_yield_assign: {
|
||||
input: {
|
||||
function* fn() {
|
||||
var x = {};
|
||||
x.prop = yield 5;
|
||||
}
|
||||
}
|
||||
expect_exact: "function*fn(){var x={};x.prop=yield 5}"
|
||||
}
|
||||
|
||||
generator_yield_undefined: {
|
||||
input: {
|
||||
function* fn() {
|
||||
yield;
|
||||
}
|
||||
}
|
||||
expect_exact: "function*fn(){yield}"
|
||||
}
|
||||
|
||||
yield_optimize_expression: {
|
||||
options = {
|
||||
}
|
||||
input: {
|
||||
function* f1() { yield; }
|
||||
function* f2() { yield undefined; }
|
||||
function* f3() { yield null; }
|
||||
function* f4() { yield* undefined; }
|
||||
}
|
||||
expect: {
|
||||
function* f1() { yield }
|
||||
function* f2() { yield; }
|
||||
function* f3() { yield null; }
|
||||
function* f4() { yield* void 0; }
|
||||
}
|
||||
}
|
||||
|
||||
yield_statements: {
|
||||
input: {
|
||||
function* fn() {
|
||||
var a = (yield 1) + (yield 2);
|
||||
var b = (yield 3) === (yield 4);
|
||||
var c = (yield 5) << (yield 6);
|
||||
var d = yield 7;
|
||||
var e = (yield 8) ? yield 9 : yield 10;
|
||||
var f = -(yield 11);
|
||||
}
|
||||
}
|
||||
expect_exact: "function*fn(){var a=(yield 1)+(yield 2);var b=(yield 3)===(yield 4);var c=(yield 5)<<(yield 6);var d=yield 7;var e=(yield 8)?yield 9:yield 10;var f=-(yield 11)}"
|
||||
}
|
||||
|
||||
yield_as_identifier_in_function_in_generator: {
|
||||
input: {
|
||||
var g = function*() {
|
||||
function h() {
|
||||
yield = 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
expect: {
|
||||
var g = function*() {
|
||||
function h() {
|
||||
yield = 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
yield_before_punctuators: {
|
||||
input: {
|
||||
iter = (function*() {
|
||||
assignmentResult = [ x = yield ] = value;
|
||||
})();
|
||||
function* g1() { (yield) }
|
||||
function* g2() { [yield] }
|
||||
function* g3() { return {yield} } // Added return to avoid {} drop
|
||||
function* g4() { yield, yield; }
|
||||
function* g5() { (yield) ? yield : yield; }
|
||||
}
|
||||
expect: {
|
||||
iter = (function*() {
|
||||
assignmentResult = [ x = yield ] = value;
|
||||
})();
|
||||
function* g1() { (yield) }
|
||||
function* g2() { [yield] }
|
||||
function* g3() { return {yield} }
|
||||
function* g4() { yield, yield; }
|
||||
function* g5() { (yield) ? yield : yield; }
|
||||
}
|
||||
}
|
||||
|
||||
yield_as_identifier_outside_strict_mode: {
|
||||
input: {
|
||||
import yield from "bar";
|
||||
yield = 123;
|
||||
while (true) {
|
||||
yield:
|
||||
for(;;) break yield;
|
||||
|
||||
foo();
|
||||
}
|
||||
while (true)
|
||||
yield: for(;;) continue yield;
|
||||
function yield(){}
|
||||
function foo(...yield){}
|
||||
try { new Error("") } catch (yield) {}
|
||||
var yield = "foo";
|
||||
class yield {}
|
||||
}
|
||||
expect: {
|
||||
import yield from "bar";
|
||||
yield = 123;
|
||||
while (true) {
|
||||
yield:
|
||||
for(;;) break yield;
|
||||
|
||||
foo();
|
||||
}
|
||||
while (true)
|
||||
yield: for(;;) continue yield;
|
||||
function yield(){}
|
||||
function foo(...yield){}
|
||||
try { new Error("") } catch (yield) {}
|
||||
var yield = "foo";
|
||||
class yield {}
|
||||
}
|
||||
}
|
||||
25
test/mocha/arrow.js
Normal file
25
test/mocha/arrow.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
var assert = require("assert");
|
||||
var uglify = require("../../");
|
||||
|
||||
describe("Arrow functions", function() {
|
||||
it("Should not accept spread tokens on non-last parameters or without arguments parentheses", function() {
|
||||
var tests = [
|
||||
"var a = ...a => {return a.join()}",
|
||||
"var b = (a, ...b, c) => { return a + b.join() + c}",
|
||||
"var c = (...a, b) => a.join()"
|
||||
];
|
||||
var test = function(code) {
|
||||
return function() {
|
||||
uglify.parse(code, {fromString: true});
|
||||
}
|
||||
}
|
||||
var error = function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error &&
|
||||
e.message === "SyntaxError: Unexpected token: expand (...)";
|
||||
}
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(test(tests[i]), error);
|
||||
}
|
||||
});
|
||||
});
|
||||
26
test/mocha/builtins.js
Normal file
26
test/mocha/builtins.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
var UglifyJS = require("../../");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("builtins", function() {
|
||||
it ("Should not mangle builtins", function() {
|
||||
var test = "function foo(something){\n" +
|
||||
" return [Object,Array,Function,Number,String,Boolean,Error,Math,Date,RegExp,Symbol,something];\n" +
|
||||
"};";
|
||||
|
||||
var result = UglifyJS.minify(test, {fromString: true, parse: {bare_returns: true}}).code;
|
||||
|
||||
assert.strictEqual(result.indexOf("something"), -1);
|
||||
|
||||
assert.notEqual(result.indexOf("Object"), -1);
|
||||
assert.notEqual(result.indexOf("Array"), -1);
|
||||
assert.notEqual(result.indexOf("Function"), -1);
|
||||
assert.notEqual(result.indexOf("Number"), -1);
|
||||
assert.notEqual(result.indexOf("String"), -1);
|
||||
assert.notEqual(result.indexOf("Boolean"), -1);
|
||||
assert.notEqual(result.indexOf("Error"), -1);
|
||||
assert.notEqual(result.indexOf("Math"), -1);
|
||||
assert.notEqual(result.indexOf("Date"), -1);
|
||||
assert.notEqual(result.indexOf("RegExp"), -1);
|
||||
assert.notEqual(result.indexOf("Symbol"), -1);
|
||||
});
|
||||
});
|
||||
26
test/mocha/class.js
Normal file
26
test/mocha/class.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
var assert = require("assert");
|
||||
var uglify = require("../../");
|
||||
|
||||
describe("Class", function() {
|
||||
it("Should not accept spread on non-last parameters in methods", function() {
|
||||
var tests = [
|
||||
"class foo { bar(...a, b) { return a.join(b) } }",
|
||||
"class foo { bar(a, b, ...c, d) { return c.join(a + b) + d } }",
|
||||
"class foo { *bar(...a, b) { return a.join(b) } }",
|
||||
"class foo { *bar(a, b, ...c, d) { return c.join(a + b) + d } }"
|
||||
];
|
||||
var test = function(code) {
|
||||
return function() {
|
||||
uglify.parse(code, {fromString: true});
|
||||
}
|
||||
}
|
||||
var error = function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error &&
|
||||
e.message === "SyntaxError: Unexpected token: expand (...)";
|
||||
}
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(test(tests[i]), error);
|
||||
}
|
||||
});
|
||||
});
|
||||
32
test/mocha/expression.js
Normal file
32
test/mocha/expression.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
var assert = require("assert");
|
||||
var uglify = require("../../");
|
||||
|
||||
describe("Expression", function() {
|
||||
it("Should not allow the first exponentiation operator to be prefixed with an unary operator", function() {
|
||||
var tests = [
|
||||
"+5 ** 3",
|
||||
"-5 ** 3",
|
||||
"~5 ** 3",
|
||||
"!5 ** 3",
|
||||
"void 5 ** 3",
|
||||
"typeof 5 ** 3",
|
||||
"delete 5 ** 3",
|
||||
"var a = -(5) ** 3;"
|
||||
];
|
||||
|
||||
var fail = function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error &&
|
||||
/^SyntaxError: Unexpected token: operator \((?:[!+~-]|void|typeof|delete)\)/.test(e.message);
|
||||
}
|
||||
|
||||
var exec = function(test) {
|
||||
return function() {
|
||||
uglify.parse(test);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(exec(tests[i]), fail, tests[i]);
|
||||
}
|
||||
});
|
||||
});
|
||||
30
test/mocha/function.js
Normal file
30
test/mocha/function.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
var assert = require("assert");
|
||||
var uglify = require("../../");
|
||||
|
||||
describe("Function", function() {
|
||||
it("Should not accept spread on non-last parameters", function() {
|
||||
var tests = [
|
||||
"var a = function(...a, b) { return a.join(b) }",
|
||||
"var b = function(a, b, ...c, d) { return c.join(a + b) + d }",
|
||||
"function foo(...a, b) { return a.join(b) }",
|
||||
"function bar(a, b, ...c, d) { return c.join(a + b) + d }",
|
||||
"var a = function*(...a, b) { return a.join(b) }",
|
||||
"var b = function*(a, b, ...c, d) { return c.join(a + b) + d }",
|
||||
"function* foo(...a, b) { return a.join(b) }",
|
||||
"function* bar(a, b, ...c, d) { return c.join(a + b) + d }"
|
||||
];
|
||||
var test = function(code) {
|
||||
return function() {
|
||||
uglify.parse(code, {fromString: true});
|
||||
}
|
||||
}
|
||||
var error = function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error &&
|
||||
e.message === "SyntaxError: Unexpected token: expand (...)";
|
||||
}
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(test(tests[i]), error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -85,4 +85,11 @@ describe("New", function() {
|
|||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should check target in new.target", function() {
|
||||
assert.throws(function() {uglify.parse("new.blah")}, function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error
|
||||
&& e.message === "SyntaxError: Unexpected token name «blah», expected name «target»";
|
||||
});
|
||||
});
|
||||
});
|
||||
20
test/mocha/object.js
Normal file
20
test/mocha/object.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
var Uglify = require("../../");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("Object", function() {
|
||||
it ("Should allow objects to have a methodDefinition as property", function() {
|
||||
var code = "var a = {test() {return true;}}";
|
||||
assert.equal(Uglify.minify(code, {fromString: true}).code, "var a={test(){return!0}};");
|
||||
});
|
||||
|
||||
it ("Should not allow objects to use static keywords like in classes", function() {
|
||||
var code = "{static test() {}}";
|
||||
var parse = function() {
|
||||
Uglify.parse(code);
|
||||
}
|
||||
var expect = function(e) {
|
||||
return e instanceof Uglify.JS_Parse_Error;
|
||||
}
|
||||
assert.throws(parse, expect);
|
||||
});
|
||||
});
|
||||
134
test/mocha/unicode.js
Normal file
134
test/mocha/unicode.js
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
var assert = require("assert");
|
||||
var uglify = require("../../");
|
||||
|
||||
describe("Unicode", function() {
|
||||
it("Should not accept invalid code ranges in unicode escape", function() {
|
||||
var tests = [
|
||||
"\\u{110000}", // A bit over the unicode range
|
||||
"\\u{100000061} = 'foo'", // 32-bit overflow resulting in "a"
|
||||
"\\u{fffffffffff}", // A bit too much over the unicode range
|
||||
];
|
||||
|
||||
var exec = function(test) {
|
||||
return function() {
|
||||
uglify.parse(test);
|
||||
}
|
||||
}
|
||||
|
||||
var fail = function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error
|
||||
&& e.message === "SyntaxError: Unicode reference out of bounce";
|
||||
}
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(exec(tests[i]), fail);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should not accept invalid unicode sequences", function() {
|
||||
var tests = [
|
||||
"var foo = '\\u-111'",
|
||||
"var bar = '\\u{-1}'",
|
||||
"var baz = '\\ugggg'"
|
||||
];
|
||||
|
||||
var exec = function(test) {
|
||||
return function() {
|
||||
uglify.parse(test);
|
||||
}
|
||||
}
|
||||
|
||||
var fail = function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error
|
||||
&& e.message === "SyntaxError: Invalid hex-character pattern in string";
|
||||
}
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(exec(tests[i]), fail);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should throw error if escaped first identifier char is not part of ID_start", function() {
|
||||
var tests = [
|
||||
'var \\u{0} = "foo";',
|
||||
'var \\u{10ffff} = "bar";',
|
||||
'var \\u000a = "what\'s up";'
|
||||
];
|
||||
|
||||
var exec = function(test) {
|
||||
return function() {
|
||||
uglify.parse(test);
|
||||
}
|
||||
}
|
||||
|
||||
var fail = function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error
|
||||
&& e.message === "SyntaxError: First identifier char is an invalid identifier char";
|
||||
}
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(exec(tests[i]), fail);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should throw error if escaped non-first identifier char is not part of ID_start", function() {
|
||||
var tests = [
|
||||
'var a\\u{0} = "foo";',
|
||||
'var a\\u{10ffff} = "bar";',
|
||||
'var z\\u000a = "what\'s up";'
|
||||
];
|
||||
|
||||
var exec = function(test) {
|
||||
return function() {
|
||||
uglify.parse(test);
|
||||
}
|
||||
}
|
||||
|
||||
var fail = function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error
|
||||
&& e.message === "SyntaxError: Invalid escaped identifier char";
|
||||
}
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(exec(tests[i]), fail);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should throw error if identifier is a keyword with a escape sequences", function() {
|
||||
var tests = [
|
||||
'var \\u0069\\u006e = "foo"', // in
|
||||
'var \\u0076\\u0061\\u0072 = "bar"', // var
|
||||
'var \\u{66}\\u{6f}\\u{72} = "baz"', // for
|
||||
'var \\u0069\\u{66} = "foobar"', // if
|
||||
'var \\u{73}uper' // super
|
||||
];
|
||||
|
||||
var exec = function(test) {
|
||||
return function() {
|
||||
uglify.parse(test);
|
||||
}
|
||||
}
|
||||
|
||||
var fail = function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error
|
||||
&& e.message === "SyntaxError: Escaped characters are not allowed in keywords";
|
||||
}
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(exec(tests[i]), fail);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should read strings containing surigates correctly", function() {
|
||||
var tests = [
|
||||
['var a = "\ud800\udc00";', 'var a="\\u{10000}";'],
|
||||
['var b = "\udbff\udfff";', 'var b="\\u{10ffff}";']
|
||||
];
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.strictEqual(uglify.minify(tests[i][0], {
|
||||
fromString: true, output: { ascii_only: true, ecma: 6}
|
||||
}).code, tests[i][1]);
|
||||
}
|
||||
});
|
||||
});
|
||||
106
test/mocha/yield.js
Normal file
106
test/mocha/yield.js
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
var UglifyJS = require("../../");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("Yield", function() {
|
||||
it("Should not delete statements after yield", function() {
|
||||
var js = 'function *foo(bar) { yield 1; yield 2; return 3; }';
|
||||
var result = UglifyJS.minify(js, {fromString: true});
|
||||
assert.strictEqual(result.code, 'function*foo(e){return yield 1,yield 2,3}');
|
||||
});
|
||||
|
||||
it("Should not allow yield as labelIdentifier within generators", function() {
|
||||
var js = "function* g() {yield: 1}"
|
||||
var test = function() {
|
||||
UglifyJS.parse(js);
|
||||
}
|
||||
var expect = function(e) {
|
||||
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||
e.message === "SyntaxError: Yield cannot be used as label inside generators";
|
||||
}
|
||||
assert.throws(test, expect);
|
||||
});
|
||||
|
||||
it("Should not allow yield* followed by a semicolon in generators", function() {
|
||||
var js = "function* test() {yield*\n;}";
|
||||
var test = function() {
|
||||
UglifyJS.parse(js);
|
||||
}
|
||||
var expect = function(e) {
|
||||
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||
e.message === "SyntaxError: Unexpected token: punc (;)";
|
||||
}
|
||||
assert.throws(test, expect);
|
||||
});
|
||||
|
||||
it("Should not allow yield with next token star on next line", function() {
|
||||
var js = "function* test() {yield\n*123;}";
|
||||
var test = function() {
|
||||
UglifyJS.parse(js);
|
||||
}
|
||||
var expect = function(e) {
|
||||
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||
e.message === "SyntaxError: Unexpected token: operator (*)";
|
||||
}
|
||||
assert.throws(test, expect);
|
||||
});
|
||||
|
||||
it("Should be able to compress its expression", function() {
|
||||
assert.strictEqual(
|
||||
UglifyJS.minify("function *f() { yield 3-4; }", {fromString: true, compress: true}).code,
|
||||
"function*f(){yield-1}"
|
||||
);
|
||||
});
|
||||
|
||||
it("Should keep undefined after yield without compression if found in ast", function() {
|
||||
assert.strictEqual(
|
||||
UglifyJS.minify("function *f() { yield undefined; yield; yield* undefined; yield void 0}", {fromString: true, compress: false}).code,
|
||||
"function*f(){yield undefined;yield;yield*undefined;yield void 0}"
|
||||
);
|
||||
});
|
||||
|
||||
it("Should be able to drop undefined after yield if necessary with compression", function() {
|
||||
assert.strictEqual(
|
||||
UglifyJS.minify("function *f() { yield undefined; yield; yield* undefined; yield void 0}", {fromString: true, compress: true}).code,
|
||||
"function*f(){yield,yield,yield*void 0,yield}"
|
||||
);
|
||||
});
|
||||
|
||||
it("Should not allow yield to be used as symbol, identifier or property outside generators in strict mode", function() {
|
||||
var tests = [
|
||||
// Fail as as_symbol
|
||||
'"use strict"; import yield from "bar";',
|
||||
'"use strict"; yield = 123;',
|
||||
'"use strict"; yield: "123";',
|
||||
'"use strict"; for(;;){break yield;}',
|
||||
'"use strict"; for(;;){continue yield;}',
|
||||
'"use strict"; function yield(){}',
|
||||
'"use strict"; function foo(...yield){}',
|
||||
'"use strict"; try { new Error("")} catch (yield) {}',
|
||||
'"use strict"; var yield = "foo";',
|
||||
'"use strict"; class yield {}',
|
||||
|
||||
// Fail as maybe_assign
|
||||
'"use strict"; var foo = yield;',
|
||||
'"use strict"; var foo = bar = yield',
|
||||
|
||||
// Fail as as_property_name
|
||||
'"use strict"; var foo = {yield};',
|
||||
'"use strict"; var bar = {yield: "foo"};'
|
||||
];
|
||||
|
||||
var fail = function(e) {
|
||||
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||
e.message === "SyntaxError: Unexpected yield identifier inside strict mode";
|
||||
}
|
||||
|
||||
var test = function(input) {
|
||||
return function() {
|
||||
UglifyJS.parse(input);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(test(tests[i]), fail, tests[i]);
|
||||
}
|
||||
});
|
||||
});
|
||||
138
test/parser.js
Normal file
138
test/parser.js
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
|
||||
var UglifyJS = require("..");
|
||||
var ok = require('assert');
|
||||
|
||||
module.exports = function () {
|
||||
console.log("--- Parser tests");
|
||||
|
||||
// Destructuring arguments
|
||||
|
||||
// Function argument nodes are correct
|
||||
function get_args(args) {
|
||||
return args.map(function (arg) {
|
||||
return [arg.TYPE, arg.name];
|
||||
});
|
||||
}
|
||||
|
||||
// Destructurings as arguments
|
||||
var destr_fun1 = UglifyJS.parse('(function ({a, b}) {})').body[0].body;
|
||||
var destr_fun2 = UglifyJS.parse('(function ([a, [b]]) {})').body[0].body;
|
||||
|
||||
ok.equal(destr_fun1.argnames.length, 1);
|
||||
ok.equal(destr_fun2.argnames.length, 1);
|
||||
|
||||
var destr_fun1 = UglifyJS.parse('({a, b}) => null').body[0].body;
|
||||
var destr_fun2 = UglifyJS.parse('([a, [b]]) => null').body[0].body;
|
||||
|
||||
ok.equal(destr_fun1.argnames.length, 1);
|
||||
ok.equal(destr_fun2.argnames.length, 1);
|
||||
|
||||
var destruct1 = destr_fun1.argnames[0];
|
||||
var destruct2 = destr_fun2.argnames[0];
|
||||
|
||||
ok(destruct1 instanceof UglifyJS.AST_Destructuring);
|
||||
ok(destruct2 instanceof UglifyJS.AST_Destructuring);
|
||||
ok(destruct2.names[1] instanceof UglifyJS.AST_Destructuring);
|
||||
|
||||
ok.equal(destruct1.start.value, '{');
|
||||
ok.equal(destruct1.end.value, '}');
|
||||
ok.equal(destruct2.start.value, '[');
|
||||
ok.equal(destruct2.end.value, ']');
|
||||
|
||||
ok.equal(destruct1.is_array, false);
|
||||
ok.equal(destruct2.is_array, true);
|
||||
|
||||
var aAndB = [
|
||||
['SymbolFunarg', 'a'],
|
||||
['SymbolFunarg', 'b']
|
||||
];
|
||||
|
||||
ok.deepEqual(
|
||||
[
|
||||
destruct1.names[0].TYPE,
|
||||
destruct1.names[0].name],
|
||||
aAndB[0]);
|
||||
|
||||
ok.deepEqual(
|
||||
[
|
||||
destruct2.names[1].names[0].TYPE,
|
||||
destruct2.names[1].names[0].name
|
||||
],
|
||||
aAndB[1]);
|
||||
|
||||
ok.deepEqual(
|
||||
get_args(destr_fun1.args_as_names()),
|
||||
aAndB)
|
||||
ok.deepEqual(
|
||||
get_args(destr_fun2.args_as_names()),
|
||||
aAndB)
|
||||
|
||||
// Making sure we don't accidentally accept things which
|
||||
// Aren't argument destructurings
|
||||
|
||||
ok.throws(function () {
|
||||
UglifyJS.parse('(function ([]) {})');
|
||||
}, /Invalid destructuring function parameter/);
|
||||
|
||||
ok.throws(function () {
|
||||
UglifyJS.parse('(function ( { a, [ b ] } ) { })')
|
||||
});
|
||||
|
||||
ok.throws(function () {
|
||||
UglifyJS.parse('(function (1) { })');
|
||||
}, /Invalid function parameter/);
|
||||
|
||||
ok.throws(function () {
|
||||
UglifyJS.parse('(function (this) { })');
|
||||
});
|
||||
|
||||
ok.throws(function () {
|
||||
UglifyJS.parse('(function ([1]) { })');
|
||||
}, /Invalid function parameter/);
|
||||
|
||||
ok.throws(function () {
|
||||
UglifyJS.parse('(function [a] { })');
|
||||
});
|
||||
|
||||
// Destructuring variable declaration
|
||||
|
||||
var decls = UglifyJS.parse('var {a,b} = foo, { c, d } = bar');
|
||||
|
||||
ok.equal(decls.body[0].TYPE, 'Var');
|
||||
ok.equal(decls.body[0].definitions.length, 2);
|
||||
ok.equal(decls.body[0].definitions[0].name.TYPE, 'Destructuring');
|
||||
ok.equal(decls.body[0].definitions[0].value.TYPE, 'SymbolRef');
|
||||
|
||||
var nested_def = UglifyJS.parse('var [{x}] = foo').body[0].definitions[0];
|
||||
|
||||
ok.equal(nested_def.name.names[0].names[0].TYPE, 'SymbolVar');
|
||||
ok.equal(nested_def.name.names[0].names[0].name, 'x');
|
||||
|
||||
var holey_def = UglifyJS.parse('const [,,third] = [1,2,3]').body[0].definitions[0];
|
||||
|
||||
ok.equal(holey_def.name.names[0].TYPE, 'Hole');
|
||||
ok.equal(holey_def.name.names[2].TYPE, 'SymbolConst');
|
||||
|
||||
var expanding_def = UglifyJS.parse('var [first, ...rest] = [1,2,3]').body[0].definitions[0];
|
||||
|
||||
ok.equal(expanding_def.name.names[0].TYPE, 'SymbolVar');
|
||||
ok.equal(expanding_def.name.names[1].TYPE, 'Expansion');
|
||||
ok.equal(expanding_def.name.names[1].expression.TYPE, 'SymbolVar');
|
||||
|
||||
// generators
|
||||
var generators_def = UglifyJS.parse('function* fn() {}').body[0];
|
||||
ok.equal(generators_def.is_generator, true);
|
||||
|
||||
ok.throws(function () {
|
||||
UglifyJS.parse('function* (){ }');
|
||||
});
|
||||
|
||||
var generators_yield_def = UglifyJS.parse('function* fn() {\nyield remote();\}').body[0].body[0];
|
||||
ok.strictEqual(generators_yield_def.body.is_star, false);
|
||||
}
|
||||
|
||||
// Run standalone
|
||||
if (module.parent === null) {
|
||||
module.exports();
|
||||
}
|
||||
|
||||
|
|
@ -30,6 +30,10 @@ run_ast_conversion_tests({
|
|||
iterations: 1000
|
||||
});
|
||||
|
||||
var run_parser_tests = require('./parser.js');
|
||||
|
||||
run_parser_tests();
|
||||
|
||||
/* -----[ utils ]----- */
|
||||
|
||||
function tmpl() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user