diff options
author | Alex Lam S.L <alexlamsl@gmail.com> | 2021-02-13 20:26:43 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-14 04:26:43 +0800 |
commit | b7219ac489e47091f17091a08d7ef50980d68972 (patch) | |
tree | c46da154cf918ec3573ddb9b4dfd9df244919556 /lib | |
parent | a6bb66931bfdd04d6ce05b640d89a8deff7ca3de (diff) | |
download | tracifyjs-b7219ac489e47091f17091a08d7ef50980d68972.tar.gz tracifyjs-b7219ac489e47091f17091a08d7ef50980d68972.zip |
support `import` statements (#4646)
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ast.js | 62 | ||||
-rw-r--r-- | lib/compress.js | 5 | ||||
-rw-r--r-- | lib/output.js | 47 | ||||
-rw-r--r-- | lib/parse.js | 50 | ||||
-rw-r--r-- | lib/transform.js | 5 |
5 files changed, 159 insertions, 10 deletions
@@ -198,13 +198,16 @@ var AST_Debugger = DEFNODE("Debugger", null, { $documentation: "Represents a debugger statement", }, AST_Statement); -var AST_Directive = DEFNODE("Directive", "value quote", { +var AST_Directive = DEFNODE("Directive", "quote value", { $documentation: "Represents a directive, like \"use strict\";", $propdoc: { + quote: "[string?] the original quote character", value: "[string] The value of this directive as a plain string (it's not an AST_String!)", - quote: "[string] the original quote character" }, _validate: function() { + if (this.quote != null) { + if (typeof this.quote != "string") throw new Error("quote must be string"); + } if (typeof this.value != "string") throw new Error("value must be string"); }, }, AST_Statement); @@ -1035,6 +1038,44 @@ var AST_VarDef = DEFNODE("VarDef", "name value", { /* -----[ OTHER ]----- */ +var AST_Import = DEFNODE("Import", "all default path properties quote", { + $documentation: "An `import` statement", + $propdoc: { + all: "[AST_SymbolImport?] the imported namespace, or null if not specified", + default: "[AST_SymbolImport?] the alias for default `export`, or null if not specified", + path: "[string] the path to import module", + properties: "[(AST_SymbolImport*)?] array of aliases, or null if not specified", + quote: "[string?] the original quote character", + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + if (node.all) node.all.walk(visitor); + if (node.default) node.default.walk(visitor); + if (node.properties) node.properties.forEach(function(prop) { + prop.walk(visitor); + }); + }); + }, + _validate: function() { + if (this.all != null) { + if (!(this.all instanceof AST_SymbolImport)) throw new Error("all must be AST_SymbolImport"); + if (this.properties != null) throw new Error("cannot import both * and {} in the same statement"); + } + if (this.default != null) { + if (!(this.default instanceof AST_SymbolImport)) throw new Error("default must be AST_SymbolImport"); + if (this.default.key !== "") throw new Error("invalid default key: " + this.default.key); + } + if (typeof this.path != "string") throw new Error("path must be string"); + if (this.properties != null) this.properties.forEach(function(node) { + if (!(node instanceof AST_SymbolImport)) throw new Error("properties must contain AST_SymbolImport"); + }); + if (this.quote != null) { + if (typeof this.quote != "string") throw new Error("quote must be string"); + } + }, +}, AST_Statement); + var AST_DefaultValue = DEFNODE("DefaultValue", "name value", { $documentation: "A default value declaration", $propdoc: { @@ -1494,6 +1535,16 @@ var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, { $documentation: "Symbol naming a function argument", }, AST_SymbolVar); +var AST_SymbolImport = DEFNODE("SymbolImport", "key", { + $documentation: "Symbol defined by an `import` statement", + $propdoc: { + key: "[string] the original `export` name", + }, + _validate: function() { + if (typeof this.key != "string") throw new Error("key must be string"); + }, +}, AST_SymbolVar); + var AST_SymbolDefun = DEFNODE("SymbolDefun", null, { $documentation: "Symbol defining a function", }, AST_SymbolDeclaration); @@ -1567,13 +1618,16 @@ var AST_Constant = DEFNODE("Constant", null, { }, }); -var AST_String = DEFNODE("String", "value quote", { +var AST_String = DEFNODE("String", "quote value", { $documentation: "A string literal", $propdoc: { + quote: "[string?] the original quote character", value: "[string] the contents of this string", - quote: "[string] the original quote character" }, _validate: function() { + if (this.quote != null) { + if (typeof this.quote != "string") throw new Error("quote must be string"); + } if (typeof this.value != "string") throw new Error("value must be string"); }, }, AST_Constant); diff --git a/lib/compress.js b/lib/compress.js index 7e113ecc..f7151441 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -70,6 +70,7 @@ function Compressor(options, false_by_default) { hoist_vars : false, ie8 : false, if_return : !false_by_default, + imports : !false_by_default, inline : !false_by_default, join_vars : !false_by_default, keep_fargs : false_by_default, @@ -6081,6 +6082,10 @@ merge(Compressor.prototype, { scope = save_scope; return node; } + if (node instanceof AST_SymbolImport) { + if (!compressor.option("imports") || node.definition().id in in_use_ids) return node; + return in_list ? List.skip : null; + } }, function(node, in_list) { if (node instanceof AST_BlockStatement) { return trim_block(node, in_list); diff --git a/lib/output.js b/lib/output.js index 3406daa1..150f270a 100644 --- a/lib/output.js +++ b/lib/output.js @@ -1011,6 +1011,27 @@ function OutputStream(options) { output.space(); force_statement(self.body, output); }); + DEFPRINT(AST_Import, function(output) { + var self = this; + output.print("import"); + output.space(); + if (self.default) self.default.print(output); + if (self.all) { + if (self.default) output.comma(); + self.all.print(output); + } + if (self.properties) { + if (self.default) output.comma(); + print_properties(self, output); + } + if (self.all || self.default || self.properties) { + output.space(); + output.print("from"); + output.space(); + } + output.print_string(self.path, self.quote); + output.semicolon(); + }); /* -----[ functions ]----- */ function print_funargs(self, output) { @@ -1454,8 +1475,8 @@ function OutputStream(options) { }); else print_braced_empty(this, output); }); - DEFPRINT(AST_Object, function(output) { - var props = this.properties; + function print_properties(self, output) { + var props = self.properties; if (props.length > 0) output.with_block(function() { props.forEach(function(prop, i) { if (i) { @@ -1467,7 +1488,10 @@ function OutputStream(options) { }); output.newline(); }); - else print_braced_empty(this, output); + else print_braced_empty(self, output); + } + DEFPRINT(AST_Object, function(output) { + print_properties(this, output); }); function print_property_key(self, output) { @@ -1512,9 +1536,22 @@ function OutputStream(options) { } DEFPRINT(AST_ObjectGetter, print_accessor("get")); DEFPRINT(AST_ObjectSetter, print_accessor("set")); + function print_symbol(self, output) { + var def = self.definition(); + output.print_name(def && def.mangled_name || self.name); + } DEFPRINT(AST_Symbol, function(output) { - var def = this.definition(); - output.print_name(def && def.mangled_name || this.name); + print_symbol(this, output); + }); + DEFPRINT(AST_SymbolImport, function(output) { + var self = this; + if (self.key) { + output.print_name(self.key); + output.space(); + output.print("as"); + output.space(); + } + print_symbol(self, output); }); DEFPRINT(AST_Hole, noop); DEFPRINT(AST_This, function(output) { diff --git a/lib/parse.js b/lib/parse.js index 27803f2e..e287c40e 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -47,7 +47,7 @@ var KEYWORDS = "break case catch const continue debugger default delete do else finally for function if in instanceof let new return switch throw try typeof var void while with"; var KEYWORDS_ATOM = "false null true"; var RESERVED_WORDS = [ - "await abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield", + "abstract async await boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield", KEYWORDS_ATOM, KEYWORDS, ].join(" "); @@ -844,6 +844,9 @@ function parse($TEXT, options) { case "await": if (S.in_async) return simple_statement(); break; + case "import": + next(); + return import_(); case "yield": if (S.in_generator) return simple_statement(); break; @@ -1272,6 +1275,51 @@ function parse($TEXT, options) { }); } + function import_() { + var all = null; + var def = as_symbol(AST_SymbolImport, true); + var props = null; + if (def ? (def.key = "", is("punc", ",") && next()) : !is("string")) { + if (is("operator", "*")) { + next(); + expect_token("name", "as"); + all = as_symbol(AST_SymbolImport); + all.key = "*"; + } else { + expect("{"); + props = []; + while (is("name") || is_identifier_string(S.token.value)) { + var alias; + if (is_token(peek(), "name", "as")) { + var key = S.token.value; + next(); + next(); + alias = as_symbol(AST_SymbolImport); + alias.key = key; + } else { + alias = as_symbol(AST_SymbolImport); + alias.key = alias.name; + } + props.push(alias); + if (!is("punc", "}")) expect(","); + } + expect("}"); + } + } + if (all || def || props) expect_token("name", "from"); + if (!is("string")) unexpected(); + var path = S.token; + next(); + semicolon(); + return new AST_Import({ + all: all, + default: def, + path: path.value, + properties: props, + quote: path.quote, + }); + } + function block_() { expect("{"); var a = []; diff --git a/lib/transform.js b/lib/transform.js index 716fb148..b6a0295a 100644 --- a/lib/transform.js +++ b/lib/transform.js @@ -204,6 +204,11 @@ TreeTransformer.prototype = new TreeWalker; if (self.key instanceof AST_Node) self.key = self.key.transform(tw); self.value = self.value.transform(tw); }); + DEF(AST_Import, function(self, tw) { + if (self.all) self.all = self.all.transform(tw); + if (self.default) self.default = self.default.transform(tw); + if (self.properties) self.properties = do_list(self.properties, tw); + }); DEF(AST_Template, function(self, tw) { if (self.tag) self.tag = self.tag.transform(tw); self.expressions = do_list(self.expressions, tw); |