Support dynamic import (take 2) (#395)

* feat: support dynamic import

* Add a couple of tests

* Align with estree/estree#198

* Update escodegen.js

Co-Authored-By: Michael Ficarra <github@michael.ficarra.me>

* Remove outdated "State 3" from comment

* Add test of a SequenceExpression as a dynamic import source

https://github.com/estools/escodegen/pull/395#discussion_r313073705

* Properly parenthesize when used with a higher precedence operator

https://github.com/estools/escodegen/pull/395#discussion_r313074448
diff --git a/escodegen.js b/escodegen.js
index a8d0170..41b3850 100644
--- a/escodegen.js
+++ b/escodegen.js
@@ -2434,7 +2434,15 @@
 
         ModuleSpecifier: function (expr, precedence, flags) {
             return this.Literal(expr, precedence, flags);
-        }
+        },
+
+        ImportExpression: function(expr, precedence, flag) {
+            return parenthesize([
+                'import(',
+                this.generateExpression(expr.source, Precedence.Assignment, E_TTT),
+                ')'
+            ], Precedence.Call, precedence);
+        },
 
     };
 
diff --git a/test/harmony.js b/test/harmony.js
index 2dfeaf9..249bf34 100644
--- a/test/harmony.js
+++ b/test/harmony.js
@@ -6416,8 +6416,111 @@
                 }
             }
         }
-    }
+    },
 
+    // https://github.com/tc39/proposal-dynamic-import/#import
+    'dynamic import': {
+        "import('foo').then(quux);": {
+            generateFrom: {
+                "type": "ExpressionStatement",
+                "expression": {
+                    "type": "CallExpression",
+                    "callee": {
+                        "type": "MemberExpression",
+                        "object": {
+                            "type": "ImportExpression",
+                            "source": {
+                                "type": "Literal",
+                                "value": "foo"
+                            }
+                        },
+                        "property": {
+                            "type": "Identifier",
+                            "name": "then"
+                        },
+                        "computed": false
+                    },
+                    "arguments": [
+                        {
+                            "type": "Identifier",
+                            "name": "quux"
+                        }
+                    ]
+                }
+            }
+        },
+
+        "import(('a', 'b'))": {
+            generateFrom: {
+                "type": "ImportExpression",
+                "source": {
+                    "type": "SequenceExpression",
+                    "expressions": [
+                        {
+                            "type": "Literal",
+                            "value": "a"
+                        },
+                        {
+                            "type": "Literal",
+                            "value": "b"
+                        }
+                    ]
+                }
+            }
+        },
+
+        "new (import('foo'))()": {
+            generateFrom: {
+                "type": "NewExpression",
+                "callee": {
+                    "type": "ImportExpression",
+                    "source": {
+                        "type": "Literal",
+                        "value": "foo"
+                    }
+                },
+                "arguments": []
+            }
+        },
+
+        "import('foo' + bar).then(quux);": {
+            generateFrom: {
+                "type": "ExpressionStatement",
+                "expression": {
+                    "type": "CallExpression",
+                    "callee": {
+                        "type": "MemberExpression",
+                        "object": {
+                            "type": "ImportExpression",
+                            "source": {
+                                "type": "BinaryExpression",
+                                "left": {
+                                    "type": "Literal",
+                                    "value": "foo"
+                                },
+                                "operator": "+",
+                                "right": {
+                                    "type": "Identifier",
+                                    "name": "bar"
+                                }
+                            }
+                        },
+                        "property": {
+                            "type": "Identifier",
+                            "name": "then"
+                        },
+                        "computed": false
+                    },
+                    "arguments": [
+                        {
+                            "type": "Identifier",
+                            "name": "quux"
+                        }
+                    ]
+                }
+            }
+        }
+    }
 };
 
 function updateDeeply(target, override) {