5 thoughts on “[Awaiting Stage 4] Grammar: Dynamic imports

  1. Dynamic imports are only Stage 3 syntax at the moment, though Chrome and Safari have started supporting the syntax. It is generally our policy to wait for a syntax to reach Stage 4, to avoid the syntax changing on us after we’ve implemented it, unless someone wants to implement it now with transpilation (like how we currently support object spread).

    Unfortunately there doesn’t appear to be a possible transpilation for this. Babel basically just passes it through, and if the runtime doesn’t support it, too bad. I suppose we could do the same, but the point of having our policy on experimental features is so that we don’t have to put a warning at the top of the docs like “but if you use feature X, make sure your runtime supports it.” We already have that warning for JSX, which will never be standardized, and we warn about the fact that Node still doesn’t support import/export despite those being standardized; but the more exceptions we make to this the harder it is to use CoffeeScript.

    Furthermore, the Chrome announcement has this warning:

    Note: Although import() looks like a function call, it is specified as syntax that just happens to use parentheses (similar to super()).

    This means it’s potentially not as simple as just allowing import to be used as a function call, similar to how require can be today. We might need to validate that import gets only one argument, or that the argument be a variable name and not a string, or who knows what the final syntax will be. This is the danger if implementing it ahead of standardization. Any thoughts @jashkenas or @lydell?

    @jtenner I know it’s ugly, but in the meantime you can use backticks for this:

    loadStuff = ->
      await Promise.all [
        `import('./assets')`
        `import('./stuff')`
      ]
  2. I haven’t been following this thread too closely, but — looking at the grammar — it seems to me that we can disambiguate clearly and cleanly:

    • Static imports in JS are statements. You cannot, and must not, use them as values, assigning them to a variable, or chaining them as a promise.

    • Dynamic imports in JS are expressions. They’re mostly useless unless you await them, assign their return promise to a variable, or chain their return promise with .then().

    CoffeeScript’s rule could simply be that statement-style unused imports are static, and expression-style imports that are part of a larger piece of the grammar are dynamic, like so:

    import "module" # static
    
    if loaded
      import "module" # static, illegal
    
    await import "module" # dynamic
    
    if loaded
      import("module").then -> # dynamic
        ...
    
    libraryPromise = import "module" # dynamic

    (And I know this disallows bare dynamic imports that depend on asynchronous, un-awaited global variable manipulation — but I think that’s a fine limitation to have. You can always assign it to a variable.) But I’m probably missing something?

  3. It is great that this is being worked on. “Required parentheses” (i.e. CS import(...) to force ES import(...)) is the correct approach in my opinion; that is clear to understand and it will always work if you need it.

    For example, it is mandatory for something like () => import(...) to work, because that is the common use case with Webpack (and frameworks such as Vue.js that make heavy use of Webpack).

    The current “backtick” workaround is acceptable, but I can’t wait for this to ship in Coffeescript proper.