mooz / js2-mode

Improved JavaScript editing mode for GNU Emacs
GNU General Public License v3.0
1.33k stars 186 forks source link

support for webpack imports? #547

Closed ArneBab closed 3 years ago

ArneBab commented 4 years ago

Webpack uses function-scope lazy imports in the style of

import(/* webpackChunkName: "some-file" */'path/to/some-file')
    .then( { foo } => foo())
    .catch(error => console.error("could not import some file"));

Is there a way to get js2-mode to not display this as error?

I need an error-free buffer for js2-refactor variable renaming.

dgutov commented 4 years ago

Do you know if it's part of some ECMAScript? Or is it just an ad-hoc language extension?

To be sure, this looks like just a function call, but according to our grammar (as of now) it is interpreted as an import statement.

ArneBab commented 4 years ago

This is not part of ECMAScript, but rather syntax used by webpack via babel to transpile the code into legacy-browser-compatible javascript, see https://webpack.js.org/guides/code-splitting/#dynamic-imports

I’m not looking for a way to put this into js2-mode, but rather to enable myself to work more efficiently with our existing codebase.

dgutov commented 4 years ago

Okay, but is this code compatible with recent ECMAScript versions (going by the spec)?

It could be, and that would mean a bug in our parser.

UwUnyaa commented 4 years ago

The proposal for async import() is currently in stage 4: https://github.com/tc39/proposal-dynamic-import

dgutov commented 4 years ago

Thank you.

In that case, patches welcome. Since we don't have any runtime part, the change will probably require no more than 5-15 lines of code.

sandinmyjoints commented 3 years ago

Just noting another (related) use case for dynamic import is loadable components.

Would an approach for this be something like, modify js2-parse-import to bail if the import usage looks like a function call, ie, import is immediately followed by a left paren? If js2-parse-import bails, then would js2-parse-function-stmt simply parse it as a function call?

dgutov commented 3 years ago

Would an approach for this be something like, modify js2-parse-import to bail if the import usage looks like a function call, ie, import is immediately followed by a left paren?

Yep.

Here's a patch to try:

diff --git a/js2-mode.el b/js2-mode.el
index 8327ca3..abc1bfa 100644
--- a/js2-mode.el
+++ b/js2-mode.el
@@ -8612,7 +8612,7 @@ node are given relative start positions and correct lengths."
     (aset parsers js2-FOR       #'js2-parse-for)
     (aset parsers js2-FUNCTION  #'js2-parse-function-stmt)
     (aset parsers js2-IF        #'js2-parse-if)
-    (aset parsers js2-IMPORT    #'js2-parse-import)
+    (aset parsers js2-IMPORT    #'js2-parse-import-maybe)
     (aset parsers js2-LC        #'js2-parse-block)
     (aset parsers js2-LET       #'js2-parse-let-stmt)
     (aset parsers js2-NAME      #'js2-parse-name-or-label)
@@ -8742,6 +8742,13 @@ Return value is a list (EXPR LP RP), with absolute paren positions."
     (js2-node-add-children pn (car cond) if-true if-false)
     pn))

+(defun js2-parse-import-maybe ()
+  (if (eq (js2-peek-token) js2-LP)
+      (let ((name (js2-parse-name js2-IMPORT)))
+        (js2-unget-token)
+        (js2-parse-function-call name))
+    (js2-parse-import)))
+
 (defun js2-parse-import ()
   "Parse import statement. The current token must be js2-IMPORT."
   (unless (js2-ast-root-p js2-current-scope)
ArneBab commented 3 years ago

from the code this looks as if it should match, and it catches imports on top-level like this:

import(/* webpackChunkName: "some-dialog" */ 'path/to/some-dialog');

What can I do to make it work as preparation for a return-value, too?

export function someFun () {
  return import(/* webpackChunkName: "some-dialog" */ 'path/to/some-dialog').then(({ SomeDialog }) => {
    return new SomeDialog().show();
  }).catch(error => console.log(error));
}
dgutov commented 3 years ago

OK, I see the problem. Let me try something else.

sandinmyjoints commented 3 years ago

Thanks for tackling this!

dgutov commented 3 years ago

This should do it. Let me know if it's still not working right.

sandinmyjoints commented 3 years ago

Working for me!

ArneBab commented 3 years ago

Very cool! Thank you!