diff --git a/lib/ast.js b/lib/ast.js index 8700b4ba..da654606 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -445,7 +445,7 @@ var AST_PrefixedTemplateString = DEFNODE("PrefixedTemplateString", "template_str this.prefix._walk(visitor); this.template_string._walk(visitor); } -}) +}); var AST_TemplateString = DEFNODE("TemplateString", "segments", { $documentation: "A template string literal", @@ -643,7 +643,7 @@ var AST_NameMapping = DEFNODE("NameMapping", "foreign_name name", { this.name._walk(visitor); }); } -}) +}); var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", { $documentation: "An `import` statement", diff --git a/lib/mozilla-ast.js b/lib/mozilla-ast.js index 41ba04f1..1676ee37 100644 --- a/lib/mozilla-ast.js +++ b/lib/mozilla-ast.js @@ -71,12 +71,88 @@ body: normalize_directives(M.body.map(from_moz)) }); }, + ArrayPattern: function(M) { + return new AST_Destructuring({ + start: my_start_token(M), + end: my_end_token(M), + names: M.elements.map(function(elm) { + if (elm === null) { + return new AST_Hole(); + } + return from_moz(elm); + }), + is_array: true + }); + }, + ObjectPattern: function(M) { + return new AST_Destructuring({ + start: my_start_token(M), + end: my_end_token(M), + names: M.properties.map(from_moz), + is_array: false + }); + }, + AssignmentPattern: function(M) { + return new AST_Binary({ + start: my_start_token(M), + end: my_end_token(M), + left: from_moz(M.left), + operator: "=", + right: from_moz(M.right) + }); + }, + SpreadElement: function(M) { + return new AST_Expansion({ + start: my_start_token(M), + end: my_end_token(M), + expression: from_moz(M.argument) + }); + }, + RestElement: function(M) { + return new AST_Expansion({ + start: my_start_token(M), + end: my_end_token(M), + expression: from_moz(M.argument) + }); + }, + TemplateElement: function(M) { + return new AST_TemplateSegment({ + start: my_start_token(M), + end: my_end_token(M), + value: M.value.cooked, + raw: M.value.raw + }); + }, + TemplateLiteral: function(M) { + var segments = []; + for (var i = 0; i < M.quasis.length; i++) { + segments.push(from_moz(M.quasis[i])); + if (M.expressions[i]) { + segments.push(from_moz(M.expressions[i])); + } + } + return new AST_TemplateString({ + start: my_start_token(M), + end: my_end_token(M), + segments: segments + }); + }, + TaggedTemplateExpression: function(M) { + return new AST_PrefixedTemplateString({ + start: my_start_token(M), + end: my_end_token(M), + template_string: from_moz(M.quasi), + prefix: from_moz(M.tag) + }); + }, FunctionDeclaration: function(M) { return new AST_Defun({ start: my_start_token(M), end: my_end_token(M), name: from_moz(M.id), argnames: M.params.map(from_moz), + is_generator: M.generator, + async: M.async, body: normalize_directives(from_moz(M.body).body) }); }, @@ -86,9 +162,20 @@ end: my_end_token(M), name: from_moz(M.id), argnames: M.params.map(from_moz), + is_generator: M.generator, + async: M.async, body: normalize_directives(from_moz(M.body).body) }); }, + ArrowFunctionExpression: function(M) { + return new AST_Arrow({ + start: my_start_token(M), + end: my_end_token(M), + argnames: M.params.map(from_moz), + body: from_moz(M.body), + async: M.async, + }); + }, ExpressionStatement: function(M) { return new AST_SimpleStatement({ start: my_start_token(M), @@ -112,18 +199,62 @@ Property: function(M) { var key = M.key; var args = { - start : my_start_token(key), + start : my_start_token(key || M.value), end : my_end_token(M.value), key : key.type == "Identifier" ? key.name : key.value, value : from_moz(M.value) }; - if (M.kind == "init") return new AST_ObjectKeyVal(args); - args.key = new AST_SymbolMethod({ - name: args.key - }); + if (M.computed) { + args.key = from_moz(M.key); + } + if (M.method) { + args.is_generator = M.value.generator; + args.async = M.value.async; + if (!M.computed) { + args.key = new AST_SymbolMethod({ name: args.key }); + } else { + args.key = from_moz(M.key); + } + return new AST_ConciseMethod(args); + } + if (M.kind == "init") { + if (key.type != "Identifier" && key.type != "Literal") { + args.key = from_moz(key); + } + return new AST_ObjectKeyVal(args); + } + if (typeof args.key === "string" || typeof args.key === "number") { + args.key = new AST_SymbolMethod({ + name: args.key + }); + } args.value = new AST_Accessor(args.value); if (M.kind == "get") return new AST_ObjectGetter(args); if (M.kind == "set") return new AST_ObjectSetter(args); + if (M.kind == "method") { + args.async = M.value.async; + args.is_generator = M.value.generator; + args.quote = M.computed ? "\"" : null; + return new AST_ConciseMethod(args); + } + }, + MethodDefinition: function(M) { + var args = { + start : my_start_token(M), + end : my_end_token(M), + key : M.computed ? from_moz(M.key) : new AST_SymbolMethod({ name: M.key.name || M.key.value }), + value : from_moz(M.value), + static : M.static, + }; + if (M.kind == "get") { + return new AST_ObjectGetter(args); + } + if (M.kind == "set") { + return new AST_ObjectSetter(args); + } + args.is_generator = M.value.generator; + args.async = M.value.async; + return new AST_ConciseMethod(args); }, ArrayExpression: function(M) { return new AST_Array({ @@ -139,6 +270,9 @@ start : my_start_token(M), end : my_end_token(M), properties : M.properties.map(function(prop){ + if (prop.type === "SpreadElement") { + return from_moz(prop); + } prop.type = "Property"; return from_moz(prop) }) @@ -168,18 +302,98 @@ }); }, VariableDeclaration: function(M) { - return new (M.kind === "const" ? AST_Const : AST_Var)({ + return new (M.kind === "const" ? AST_Const : + M.kind === "let" ? AST_Let : AST_Var)({ start : my_start_token(M), end : my_end_token(M), definitions : M.declarations.map(from_moz) }); }, + + ImportDeclaration: function(M) { + var imported_name = null; + var imported_names = null; + M.specifiers.forEach(function (specifier) { + if (specifier.type === "ImportSpecifier") { + if (!imported_names) { imported_names = []; } + imported_names.push(new AST_NameMapping({ + start: my_start_token(specifier), + end: my_end_token(specifier), + foreign_name: from_moz(specifier.imported), + name: from_moz(specifier.local) + })); + } else if (specifier.type === "ImportDefaultSpecifier") { + imported_name = from_moz(specifier.local); + } else if (specifier.type === "ImportNamespaceSpecifier") { + if (!imported_names) { imported_names = []; } + imported_names.push(new AST_NameMapping({ + start: my_start_token(specifier), + end: my_end_token(specifier), + foreign_name: new AST_SymbolImportForeign({ name: "*" }), + name: from_moz(specifier.local) + })); + } + }); + return new AST_Import({ + start : my_start_token(M), + end : my_end_token(M), + imported_name: imported_name, + imported_names : imported_names, + module_name : from_moz(M.source) + }) + }, + ExportAllDeclaration: function(M) { + return new AST_Export({ + start: my_start_token(M), + end: my_end_token(M), + exported_names: [ + new AST_NameMapping({ + name: new AST_SymbolExportForeign({ name: "*" }), + foreign_name: new AST_SymbolExportForeign({ name: "*" }) + }) + ], + module_name: from_moz(M.source) + }); + }, + ExportNamedDeclaration: function(M) { + return new AST_Export({ + start: my_start_token(M), + end: my_end_token(M), + exported_definition: from_moz(M.declaration), + exported_names: M.specifiers && M.specifiers.length ? M.specifiers.map(function (specifier) { + return new AST_NameMapping({ + foreign_name: from_moz(specifier.exported), + name: from_moz(specifier.local) + }) + }) : null, + module_name: from_moz(M.source) + }); + }, + ExportDefaultDeclaration: function(M) { + return new AST_Export({ + start: my_start_token(M), + end: my_end_token(M), + exported_value: from_moz(M.declaration), + is_default: true + }); + }, Literal: function(M) { var val = M.value, args = { start : my_start_token(M), end : my_end_token(M) }; if (val === null) return new AST_Null(args); + var rx = M.regex; + if (rx && rx.pattern) { + // RegExpLiteral as per ESTree AST spec + args.value = new RegExp(rx.pattern, rx.flags); + args.value.raw_source = rx.pattern; + return new AST_RegExp(args); + } else if (rx) { + // support legacy RegExp + args.value = M.regex && M.raw ? M.raw : val; + return new AST_RegExp(args); + } switch (typeof val) { case "string": args.value = val; @@ -189,24 +403,29 @@ return new AST_Number(args); case "boolean": return new (val ? AST_True : AST_False)(args); - default: - var rx = M.regex; - if (rx && rx.pattern) { - // RegExpLiteral as per ESTree AST spec - args.value = new RegExp(rx.pattern, rx.flags).toString(); - } else { - // support legacy RegExp - args.value = M.regex && M.raw ? M.raw : val; - } - return new AST_RegExp(args); + } + }, + MetaProperty: function(M) { + if (M.meta.name === "new" && M.property.name === "target") { + return new AST_NewTarget({ + start: my_start_token(M), + end: my_end_token(M) + }); } }, Identifier: function(M) { var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2]; return new ( p.type == "LabeledStatement" ? AST_Label - : p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar) + : p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : p.kind == "let" ? AST_SymbolLet : AST_SymbolVar) + : /Import.*Specifier/.test(p.type) ? (p.local === M ? AST_SymbolImport : AST_SymbolImportForeign) + : p.type == "ExportSpecifier" ? (p.local === M ? AST_SymbolExport : AST_SymbolExportForeign) : p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg) : p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg) + : p.type == "ArrowFunctionExpression" ? (p.params.indexOf(M) !== -1) ? AST_SymbolFunarg : AST_SymbolRef + : p.type == "ClassExpression" ? (p.id === M ? AST_SymbolClass : AST_SymbolRef) + : p.type == "Property" ? (p.key === M && p.computed || p.value === M ? AST_SymbolRef : AST_SymbolMethod) + : p.type == "ClassDeclaration" ? (p.id === M ? AST_SymbolDefClass : AST_SymbolRef) + : p.type == "MethodDefinition" ? (p.computed ? AST_SymbolRef : AST_SymbolMethod) : p.type == "CatchClause" ? AST_SymbolCatch : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef : AST_SymbolRef)({ @@ -229,6 +448,17 @@ }); }; + MOZ_TO_ME.ClassDeclaration = + MOZ_TO_ME.ClassExpression = function From_Moz_Class(M) { + return new (M.type === "ClassDeclaration" ? AST_DefClass : AST_ClassExpression)({ + start : my_start_token(M), + end : my_end_token(M), + name : from_moz(M.id), + extends : from_moz(M.superClass), + properties: M.body.body.map(from_moz) + }); + }; + map("EmptyStatement", AST_EmptyStatement); map("BlockStatement", AST_BlockStatement, "body@body"); map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative"); @@ -243,11 +473,15 @@ map("DoWhileStatement", AST_Do, "test>condition, body>body"); map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body"); map("ForInStatement", AST_ForIn, "left>init, right>object, body>body"); + map("ForOfStatement", AST_ForOf, "left>init, right>object, body>body"); + map("AwaitExpression", AST_Await, "argument>expression"); + map("YieldExpression", AST_Yield, "argument>expression, delegate=is_star"); map("DebuggerStatement", AST_Debugger); map("VariableDeclarator", AST_VarDef, "id>name, init>value"); map("CatchClause", AST_Catch, "param>argname, body%body"); map("ThisExpression", AST_This); + map("SuperExpression", AST_Super); map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right"); map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right"); map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right"); @@ -259,24 +493,95 @@ return to_moz_scope("Program", M); }); + def_to_moz(AST_Expansion, function To_Moz_Spread(M, parent) { + return { + type: to_moz_in_destructuring() ? "RestElement" : "SpreadElement", + argument: to_moz(M.expression) + }; + }); + + def_to_moz(AST_PrefixedTemplateString, function To_Moz_TaggedTemplateExpression(M) { + return { + type: "TaggedTemplateExpression", + tag: to_moz(M.prefix), + quasi: to_moz(M.template_string) + }; + }); + + def_to_moz(AST_TemplateString, function To_Moz_TemplateLiteral(M) { + var quasis = []; + var expressions = []; + for (var i = 0; i < M.segments.length; i++) { + if (i % 2 !== 0) { + expressions.push(to_moz(M.segments[i])); + } else { + quasis.push({ + type: "TemplateElement", + value: { + raw: M.segments[i].raw, + cooked: M.segments[i].value + }, + tail: i === M.segments.length - 1 + }); + } + } + return { + type: "TemplateLiteral", + quasis: quasis, + expressions: expressions + }; + }); + def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) { return { type: "FunctionDeclaration", id: to_moz(M.name), params: M.argnames.map(to_moz), + generator: M.is_generator, + async: M.async, body: to_moz_scope("BlockStatement", M) } }); - def_to_moz(AST_Function, function To_Moz_FunctionExpression(M) { + def_to_moz(AST_Function, function To_Moz_FunctionExpression(M, parent) { + var is_generator = parent.is_generator !== undefined ? + parent.is_generator : M.is_generator return { type: "FunctionExpression", id: to_moz(M.name), params: M.argnames.map(to_moz), + generator: is_generator, + async: M.async, body: to_moz_scope("BlockStatement", M) } }); + def_to_moz(AST_Arrow, function To_Moz_ArrowFunctionExpression(M) { + var body = M.body instanceof Array ? { + type: "BlockStatement", + body: M.body.map(to_moz) + } : to_moz(M.body); + return { + type: "ArrowFunctionExpression", + params: M.argnames.map(to_moz), + async: M.async, + body: body + } + }); + + def_to_moz(AST_Destructuring, function To_Moz_ObjectPattern(M) { + if (M.is_array) { + return { + type: "ArrayPattern", + elements: M.names.map(to_moz) + } + } + return { + type: "ObjectPattern", + properties: M.names.map(to_moz) + }; + }); + def_to_moz(AST_Directive, function To_Moz_Directive(M) { return { type: "ExpressionStatement", @@ -324,11 +629,69 @@ def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) { return { type: "VariableDeclaration", - kind: M instanceof AST_Const ? "const" : "var", + kind: + M instanceof AST_Const ? "const" : + M instanceof AST_Let ? "let" : "var", declarations: M.definitions.map(to_moz) }; }); + def_to_moz(AST_Export, function To_Moz_ExportDeclaration(M) { + if (M.exported_names) { + if (M.exported_names[0].name.name === "*") { + return { + type: "ExportAllDeclaration", + source: to_moz(M.module_name) + }; + } + return { + type: "ExportNamedDeclaration", + specifiers: M.exported_names.map(function (name_mapping) { + return { + type: "ExportSpecifier", + exported: to_moz(name_mapping.foreign_name), + local: to_moz(name_mapping.name) + }; + }), + declaration: to_moz(M.exported_definition), + source: to_moz(M.module_name) + }; + } + return { + type: M.is_default ? "ExportDefaultDeclaration" : "ExportNamedDeclaration", + declaration: to_moz(M.exported_value || M.exported_definition) + }; + }); + + def_to_moz(AST_Import, function To_Moz_ImportDeclaration(M) { + var specifiers = []; + if (M.imported_name) { + specifiers.push({ + type: "ImportDefaultSpecifier", + local: to_moz(M.imported_name) + }); + } + if (M.imported_names && M.imported_names[0].foreign_name.name === '*') { + specifiers.push({ + type: "ImportNamespaceSpecifier", + local: to_moz(M.imported_names[0].name) + }); + } else if (M.imported_names) { + M.imported_names.forEach(function(name_mapping) { + specifiers.push({ + type: "ImportSpecifier", + local: to_moz(name_mapping.name), + imported: to_moz(name_mapping.foreign_name) + }); + }); + } + return { + type: "ImportDeclaration", + specifiers: specifiers, + source: to_moz(M.module_name) + } + }); + def_to_moz(AST_Sequence, function To_Moz_SequenceExpression(M) { return { type: "SequenceExpression", @@ -356,6 +719,13 @@ }); def_to_moz(AST_Binary, function To_Moz_BinaryExpression(M) { + if (M.operator == "=" && to_moz_in_destructuring()) { + return { + type: "AssignmentPattern", + left: to_moz(M.left), + right: to_moz(M.right) + }; + } return { type: M.operator == "&&" || M.operator == "||" ? "LogicalExpression" : "BinaryExpression", left: to_moz(M.left), @@ -378,14 +748,29 @@ }; }); - def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) { - var key = { - type: "Literal", - value: M.key instanceof AST_SymbolMethod ? M.key.name : M.key + def_to_moz(AST_ObjectProperty, function To_Moz_Property(M, parent) { + var key = M.key instanceof AST_Node ? to_moz(M.key) : { + type: "Identifier", + value: M.key }; + if (typeof M.key === "number") { + key = { + type: "Literal", + value: Number(M.key) + }; + } + if (typeof M.key === "string") { + key = { + type: "Identifier", + name: M.key + }; + } var kind; + var string_or_num = typeof M.key === "string" || typeof M.key === "number"; + var computed = string_or_num ? false : !(M.key instanceof AST_Symbol) || M.key instanceof AST_SymbolRef; if (M instanceof AST_ObjectKeyVal) { kind = "init"; + computed = !string_or_num; } else if (M instanceof AST_ObjectGetter) { kind = "get"; @@ -393,15 +778,81 @@ if (M instanceof AST_ObjectSetter) { kind = "set"; } + if (parent instanceof AST_Class) { + return { + type: "MethodDefinition", + computed: computed, + kind: kind, + static: M.static, + key: to_moz(M.key), + value: to_moz(M.value) + }; + } return { type: "Property", + computed: computed, kind: kind, key: key, value: to_moz(M.value) }; }); - def_to_moz(AST_Symbol, function To_Moz_Identifier(M) { + def_to_moz(AST_ConciseMethod, function To_Moz_MethodDefinition(M, parent) { + if (parent instanceof AST_Object) { + return { + type: "Property", + computed: !(M.key instanceof AST_Symbol) || M.key instanceof AST_SymbolRef, + kind: "init", + method: true, + shorthand: false, + key: to_moz(M.key), + value: to_moz(M.value) + }; + } + return { + type: "MethodDefinition", + computed: !(M.key instanceof AST_Symbol) || M.key instanceof AST_SymbolRef, + kind: M.key === "constructor" ? "constructor" : "method", + static: M.static, + key: to_moz(M.key), + value: to_moz(M.value) + }; + }); + + def_to_moz(AST_Class, function To_Moz_Class(M) { + var type = M instanceof AST_ClassExpression ? "ClassExpression" : "ClassDeclaration"; + return { + type: type, + superClass: to_moz(M.extends), + id: M.name ? to_moz(M.name) : null, + body: { + type: "ClassBody", + body: M.properties.map(to_moz) + } + } + }); + + def_to_moz(AST_NewTarget, function To_Moz_MetaProperty(M) { + return { + type: "MetaProperty", + meta: { + type: "Identifier", + name: "new" + }, + property: { + type: "Identifier", + name: "target" + } + }; + }); + + def_to_moz(AST_Symbol, function To_Moz_Identifier(M, parent) { + if (M instanceof AST_SymbolMethod && parent.quote) { + return { + type: "Literal", + value: M.name + }; + } var def = M.definition(); return { type: "Identifier", @@ -410,14 +861,15 @@ }); def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) { - var value = M.value; + var flags = M.value.toString().match(/[gimuy]*$/)[0]; + var value = "/" + M.value.raw_source + "/" + flags; return { type: "Literal", value: value, - raw: value.toString(), + raw: value, regex: { - pattern: value.source, - flags: value.toString().match(/[gimuy]*$/)[0] + pattern: M.value.raw_source, + flags: flags } }; }); @@ -586,13 +1038,30 @@ }; function def_to_moz(mytype, handler) { - mytype.DEFMETHOD("to_mozilla_ast", function() { - return set_moz_loc(this, handler(this)); + mytype.DEFMETHOD("to_mozilla_ast", function(parent) { + return set_moz_loc(this, handler(this, parent)); }); }; + var TO_MOZ_STACK = null; + function to_moz(node) { - return node != null ? node.to_mozilla_ast() : null; + if (TO_MOZ_STACK === null) { TO_MOZ_STACK = []; } + TO_MOZ_STACK.push(node); + var ast = node != null ? node.to_mozilla_ast(TO_MOZ_STACK[TO_MOZ_STACK.length - 2]) : null; + TO_MOZ_STACK.pop(); + if (TO_MOZ_STACK.length === 0) { TO_MOZ_STACK = null; } + return ast + }; + + function to_moz_in_destructuring() { + var i = TO_MOZ_STACK.length; + while (i--) { + if (TO_MOZ_STACK[i] instanceof AST_Destructuring) { + return true; + } + } + return false; }; function to_moz_block(node) { diff --git a/lib/output.js b/lib/output.js index e4987179..9093665e 100644 --- a/lib/output.js +++ b/lib/output.js @@ -1455,7 +1455,15 @@ function OutputStream(options) { output.space(); self.module_name.print(output); } - output.semicolon(); + if (self.exported_value + && !(self.exported_value instanceof AST_Defun || + self.exported_value instanceof AST_Function || + self.exported_value instanceof AST_Class) + || self.module_name + || self.exported_names + ) { + output.semicolon(); + } }); function parenthesize_for_noin(node, output, noin) { diff --git a/lib/parse.js b/lib/parse.js index d8cc9ac4..c44088c8 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -1159,7 +1159,9 @@ function parse($TEXT, options) { case "export": if (!is_token(peek(), "punc", "(")) { next(); - return export_(); + var node = export_(); + if (is("punc", ";")) semicolon(); + return node; } } } @@ -1294,9 +1296,14 @@ function parse($TEXT, options) { var body = _function_body(is("punc", "{"), false, is_async); + var end = + body instanceof Array && body.length ? body[body.length - 1].end : + body instanceof Array ? start : + body.end; + return new AST_Arrow({ start : start, - end : body.end, + end : end, async : is_async, argnames : argnames, body : body @@ -1709,6 +1716,8 @@ function parse($TEXT, options) { } // the await expression is parsed as a unary expression in Babel return new AST_Await({ + start: prev(), + end: S.token, expression : maybe_unary(true), }); } @@ -1719,6 +1728,7 @@ function parse($TEXT, options) { croak("Unexpected yield expression outside generator function", S.prev.line, S.prev.col, S.prev.pos); } + var start = S.token; var star = false; var has_expression = true; @@ -1742,8 +1752,10 @@ function parse($TEXT, options) { } return new AST_Yield({ + start : start, is_star : star, - expression : has_expression ? expression() : null + expression : has_expression ? expression() : null, + end : prev() }); } @@ -2478,7 +2490,7 @@ function parse($TEXT, options) { next(); if (is_import && is("name", "as")) { next(); // The "as" word - name = as_symbol(AST_SymbolImportForeign); + name = as_symbol(is_import ? AST_SymbolImportForeign : AST_SymbolExportForeign); } names = [map_nameAsterisk(is_import, name)]; } @@ -2686,7 +2698,8 @@ function parse($TEXT, options) { return subscripts(new AST_PrefixedTemplateString({ start: start, prefix: expr, - template_string: template_string() + template_string: template_string(), + end: prev() }), allow_calls); } return expr; @@ -2699,7 +2712,8 @@ function parse($TEXT, options) { next(); args.push(new AST_Expansion({ start: prev(), - expression: expression(false) + expression: expression(false), + end: prev() })); } else { args.push(expression(false)); diff --git a/package.json b/package.json index eb8e902d..c35fd034 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ }, "devDependencies": { "acorn": "~5.4.1", + "escodegen": "^1.9.1", "mocha": "~3.5.1", "semver": "~5.5.0" }, diff --git a/test/compress/export.js b/test/compress/export.js index 783fa80a..66eed356 100644 --- a/test/compress/export.js +++ b/test/compress/export.js @@ -277,7 +277,7 @@ export_default_anonymous_function: { foo(); } } - expect_exact: "export default function(){foo()};" + expect_exact: "export default function(){foo()}" } export_default_arrow: { @@ -309,7 +309,7 @@ export_default_anonymous_generator: { yield foo(); } } - expect_exact: "export default function*(){yield foo()};" + expect_exact: "export default function*(){yield foo()}" } export_default_anonymous_async_function: { @@ -326,7 +326,7 @@ export_default_anonymous_async_function: { return await foo(); } } - expect_exact: "export default async function(){return await foo()};" + expect_exact: "export default async function(){return await foo()}" } export_default_async_arrow_function: { @@ -358,7 +358,7 @@ export_default_named_generator: { yield foo(); } } - expect_exact: "export default function*gen(){yield foo()};" + expect_exact: "export default function*gen(){yield foo()}" } export_default_named_async_function: { @@ -375,7 +375,7 @@ export_default_named_async_function: { return await foo(); } } - expect_exact: "export default async function bar(){return await foo()};" + expect_exact: "export default async function bar(){return await foo()}" } export_default_anonymous_class: { @@ -394,7 +394,7 @@ export_default_anonymous_class: { } }; } - expect_exact: "export default class{constructor(){foo()}};" + expect_exact: "export default class{constructor(){foo()}}" } export_default_anonymous_function_not_call: { @@ -426,7 +426,7 @@ export_default_anonymous_generator_not_call: { export default function*(){}(foo); } // agrees with `acorn` and `babylon 7` - expect_exact: "export default function*(){};foo;" + expect_exact: "export default function*(){}foo;" } export_default_anonymous_async_function_not_call: { @@ -442,5 +442,5 @@ export_default_anonymous_async_function_not_call: { export default async function(){}(foo); } // agrees with `acorn` and `babylon 7` - expect_exact: "export default async function(){};foo;" + expect_exact: "export default async function(){}foo;" } diff --git a/test/compress/harmony.js b/test/compress/harmony.js index b85214ec..c7f105c0 100644 --- a/test/compress/harmony.js +++ b/test/compress/harmony.js @@ -218,7 +218,7 @@ export_statement: { export function d() {}; export class e {}; } - expect_exact: "export default 3;export var a=4;export let b=6;export const c=6;export function d(){};export class e{};" + expect_exact: "export default 3;export var a=4;export let b=6;export const c=6;export function d(){}export class e{}" } export_default_object_expression: { @@ -254,7 +254,7 @@ export_default_anon_function: { console.log(1 + 2); } } - expect_exact: "export default function(){console.log(3)};" + expect_exact: "export default function(){console.log(3)}" } export_default_anon_class: { @@ -266,7 +266,7 @@ export_default_anon_class: { foo() { console.log(1 + 2); } } } - expect_exact: "export default class{foo(){console.log(3)}};" + expect_exact: "export default class{foo(){console.log(3)}}" } export_module_statement: { @@ -680,7 +680,7 @@ export_default_function_decl: { export default function Foo() {}; export function Far() {}; } - expect_exact: "export default function Foo(){};export function Far(){};" + expect_exact: "export default function Foo(){}export function Far(){}" } export_default_class_decl: { @@ -694,7 +694,7 @@ export_default_class_decl: { export default class Car {}; export class Cab {}; } - expect_exact: "export default class Car{};export class Cab{};" + expect_exact: "export default class Car{}export class Cab{}" } object_rest_spread: { diff --git a/test/compress/issue-2001.js b/test/compress/issue-2001.js index f2c9504b..4196f6c3 100644 --- a/test/compress/issue-2001.js +++ b/test/compress/issue-2001.js @@ -5,9 +5,9 @@ export_func_1: { unused: true, } input: { - export function f(){}; + export function f(){} } - expect_exact: "export function f(){};" + expect_exact: "export function f(){}" } export_func_2: { @@ -20,7 +20,7 @@ export_func_2: { input: { export function f(){}(1); } - expect_exact: "export function f(){};1;" + expect_exact: "export function f(){}1;" } export_func_3: { @@ -33,7 +33,7 @@ export_func_3: { input: { export function f(){}(1); } - expect_exact: "export function f(){};" + expect_exact: "export function f(){}" } export_default_func_1: { @@ -43,9 +43,9 @@ export_default_func_1: { unused: true, } input: { - export default function f(){}; + export default function f(){} } - expect_exact: "export default function f(){};" + expect_exact: "export default function f(){}" } export_default_func_2: { @@ -58,7 +58,7 @@ export_default_func_2: { input: { export default function f(){}(1); } - expect_exact: "export default function f(){};1;" + expect_exact: "export default function f(){}1;" } export_default_func_3: { @@ -71,7 +71,7 @@ export_default_func_3: { input: { export default function f(){}(1); } - expect_exact: "export default function f(){};" + expect_exact: "export default function f(){}" } export_class_1: { @@ -83,7 +83,7 @@ export_class_1: { input: { export class C {}; } - expect_exact: "export class C{};" + expect_exact: "export class C{}" } export_class_2: { @@ -96,7 +96,7 @@ export_class_2: { input: { export class C {}(1); } - expect_exact: "export class C{};1;" + expect_exact: "export class C{}1;" } export_class_3: { @@ -109,7 +109,7 @@ export_class_3: { input: { export class C {}(1); } - expect_exact: "export class C{};" + expect_exact: "export class C{}" } export_default_class_1: { @@ -121,7 +121,7 @@ export_default_class_1: { input: { export default class C {}; } - expect_exact: "export default class C{};" + expect_exact: "export default class C{}" } export_default_class_2: { @@ -134,7 +134,7 @@ export_default_class_2: { input: { export default class C {}(1); } - expect_exact: "export default class C{};1;" + expect_exact: "export default class C{}1;" } export_default_class_3: { @@ -147,7 +147,7 @@ export_default_class_3: { input: { export default class C {}(1); } - expect_exact: "export default class C{};" + expect_exact: "export default class C{}" } export_mangle_1: { @@ -159,7 +159,7 @@ export_mangle_1: { return one - two; }; } - expect_exact: "export function foo(o,n){return o-n};" + expect_exact: "export function foo(o,n){return o-n}" } export_mangle_2: { @@ -171,7 +171,7 @@ export_mangle_2: { return one - two; }; } - expect_exact: "export default function foo(o,t){return o-t};" + expect_exact: "export default function foo(o,t){return o-t}" } export_mangle_3: { @@ -190,7 +190,7 @@ export_mangle_3: { } }; } - expect_exact: "export class C{go(r,e){return r-e+r}};" + expect_exact: "export class C{go(r,e){return r-e+r}}" } export_mangle_4: { @@ -209,7 +209,7 @@ export_mangle_4: { } }; } - expect_exact: "export default class C{go(e,r){return e-r+e}};" + expect_exact: "export default class C{go(e,r){return e-r+e}}" } export_mangle_5: { @@ -276,8 +276,8 @@ export_default_func_ref: { unused: true, } input: { - export default function f(){}; + export default function f(){} f(); } - expect_exact: "export default function f(){};f();" + expect_exact: "export default function f(){}f();" } diff --git a/test/input/spidermonkey/input.js b/test/input/spidermonkey/input.js new file mode 100644 index 00000000..e4de54ec --- /dev/null +++ b/test/input/spidermonkey/input.js @@ -0,0 +1,146 @@ +import "mod-name"; +import Foo from "bar"; +import * as Food from "food" +import { Bar, Baz } from "lel"; +import Bar1, { Foo2 } from "lel"; +import { Bar2 as kex, Baz as food } from "lel"; + +const x = 0b01; +let y = 6; + +export default x; +export const z = 4; +export function fun() {} +export * from "a.js"; +export {A} from "a.js"; +export {A1, B1} from "a.js"; +export {C}; + +(a, [b], {c:foo = 3}, ...d) => null; +() => {}; + +async function f() { } +function*gen() { + yield 1; + yield* 2; +} + +class Class extends Object { + constructor(...args) { + } + foo() {} +} + +x = class { + static staticMethod() {} + static get foo() {} + static set bar(value) {} + get x() {} + set x(value) {} + static() { + // "static" can be a method name! + } + get() { + // "get" can be a method name! + } + async set() { + // "set" can be a method name! + } + *bar() {} + static *baz() {} + *['constructor']() {} + static ['constructor']() {} + [a]() {} + "%"(){} +} + +y = { + get x() {}, + set x(value) {}, + bar() {}, + *bar() {}, + *['constructor']() {} +} +function f () { + console.log(new.target); +} +console.log([10, ...[], 20, ...[30, 40], 50]["length"]); +var { w: w1, ...V } = { w: 7, x: 1, y: 2 }; +for (const x of y) {} +async function f1() { await x; } + +``; +`x`; +`x${1}`; +String.raw`\n`; + +// arrow.js + +var foo = ([]) => "foo"; +var bar = ({}) => "bar"; +var with_default = (foo = "default") => foo; +var object_with_default = ({foo = "default", bar: baz = "default"}) => foo; +var array_after_spread = (...[foo]) => foo; +var array_after_spread = (...{foo}) => foo; +var computed = ({ [compute()]: x }) => {}; +var array_hole = ([, , ...x] = [1, 2]) => {}; +var object_trailing_elision = ({foo,}) => {}; +var spread_empty_array = (...[]) => "foo"; +var spread_empty_object = (...{}) => "foo"; + +// async.js + +async (x) => await x + +// destructuring.js + +var [aa, bb] = cc; +var [aa, [bb, cc]] = dd; +var [,[,,,,,],,,zz,] = xx; // Trailing comma +var [,,zzz,,] = xxx; // Trailing comma after hole + +var {aa, bb} = {aa:1, bb:2}; +var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}}; + +for (const [x,y] in pairs); +for (const [a] = 0;;); +for (const {c} of cees); + +// object.js + +var a = { + get, + set: "foo", + get bar() { + return this.get; + }, + get 5() { + return "five"; + }, + get 0xf55() { + return "f five five"; + }, + get "five"() { + return 5; + }, + set one(value) { + this._one = value; + }, + set 9(value) { + this._nine = value; + }, + set 0b1010(value) { + this._ten = value; + }, + set "eleven"(value) { + this._eleven = value; + }, + *"%"() { + return 2; + }, + *["%"]() { + return 2; + }, + [a]() {} +}; + diff --git a/test/mocha/spidermonkey.js b/test/mocha/spidermonkey.js index 709b89e5..d9d6f244 100644 --- a/test/mocha/spidermonkey.js +++ b/test/mocha/spidermonkey.js @@ -1,5 +1,8 @@ var assert = require("assert"); +var fs = require("fs"); var exec = require("child_process").exec; +var acorn = require("acorn"); +var escodegen = require("escodegen"); var uglify = require("../node"); describe("spidermonkey export/import sanity test", function() { @@ -112,4 +115,42 @@ describe("spidermonkey export/import sanity test", function() { assert.strictEqual(counter_strings, tests[i].strings, "String count mismatch for test " + tests[i].input); } }); + + it("should output and parse ES6 code correctly", function() { + var code = fs.readFileSync("test/input/spidermonkey/input.js", "utf-8"); + var uglify_ast = uglify.parse(code); + var moz_ast = uglify_ast.to_mozilla_ast(); + var from_moz_ast = uglify.AST_Node.from_mozilla_ast(moz_ast); + assert.strictEqual( + from_moz_ast.print_to_string(), + uglify_ast.print_to_string() + ); + }); + + it("should be capable of importing from acorn", function() { + var code = fs.readFileSync("test/input/spidermonkey/input.js", "utf-8"); + var uglify_ast = uglify.parse(code); + var moz_ast = acorn.parse(code, {sourceType: 'module', ecmaVersion: 9}); + var from_moz_ast = uglify.AST_Node.from_mozilla_ast(moz_ast); + assert.strictEqual( + from_moz_ast.print_to_string(), + uglify_ast.print_to_string() + ); + }); + + it("should produce an AST compatible with escodegen", function() { + var code = fs.readFileSync("test/input/spidermonkey/input.js", "utf-8"); + var uglify_ast = uglify.parse(code); + var moz_ast = uglify_ast.to_mozilla_ast(); + var generated = escodegen.generate(moz_ast) + .replace(/\[object Object\].\[object Object\]/g, "new.target"); // escodegen issue + var parsed = acorn.parse(generated, { + sourceType: "module", + ecmaVersion: 9 + }); + assert.strictEqual( + uglify.AST_Node.from_mozilla_ast(parsed).print_to_string(), + uglify_ast.print_to_string() + ); + }); }); diff --git a/test/run-tests.js b/test/run-tests.js index deb1f954..05994d99 100755 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -110,6 +110,16 @@ function run_compress_tests() { }); return false; } + var ast = input.to_mozilla_ast(); + var ast_as_string = U.AST_Node.from_mozilla_ast(ast).print_to_string(); + var input_string = input.print_to_string(); + if (input_string !== ast_as_string) { + log("!!! Mozilla AST I/O corrupted input\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n\n", { + input: input_string, + output: ast_as_string, + }); + return false; + } var options = U.defaults(test.options, { warnings: false });