tc39 / proposal-dynamic-import

import() proposal for JavaScript

Home Page:https://tc39.github.io/proposal-dynamic-import/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

"Tradeoffs" or "risks" section to the proposal?

si14 opened this issue · comments

Do you think it would be useful to add a "tradeoffs" or "risks" section to the proposal? At this moment it may seem as if it has no downsides to consider. However, some points were raised (and dismissed) in the issues that indicate potential problems arising from the proposal, for example:

  • breaking existing bundlers #28
  • breaking tree-shaking at least for same cases #19
  • making dead code elimination analysis harder

It seems like a common practice for language enhancement proposals, see for example http://openjdk.java.net/jeps/286 (or any other JEP).

Those are all variations of the same issue that involve some sort of optimization by turning modules into non-modules. This proposal doesn't break bundlers as bundlers already have methods for allowing dynamic looking (because in their case it's pre-determined) loading. The same work-arounds that are used today will be useable with import() (Webpack already supports import() and AFAIK it isn't broken because of it).

The need for such tools is, in my opinion, a bug in the web platform and should be fixed in the appropriate place, not by making the language less expressive.

of the same issue that involve some sort of optimization by turning modules into non-modules

Those were the examples present in the issue tracker. However, the underlying point is deeper and comes from the fact that ES modules are static, which allows to provide (unsurprisingly) better static analysis for any purpose, be it linting, drawing or analyzing module dependency graphs, IDE support or (as you correctly noted) optimization [1]. As evidenced by almost all programming languages out there (even highly dynamic ones like Python discourage import trickery), there are benefits for statically analyzable module dependencies. Even if they are not fully realized at this point in JS ecosystem (which can be expected given relative youth of ES modules and presence of alternatives), reasoning by analogy can give us a high degree of confidence in usefulness of static module system.

The proposal makes a currently static module system into a dynamic one seemingly without weighting potential downsides. Given the scope of impact of the decision (JS is one of the most important programming languages in the world right now, and the next ES spec will affect software for years to come) it seems prudent to list and weight potential downsides.

not by making the language less expressive

Expressiveness is a tradeoff, which can be seen in abundance of linters which constrain it in favor of readability, consistent style, and robustness. The upside of linter usage is that they allow different teams make different tradeoffs for themselves. However, this is not the case with the module system, which is a fundamental API for interacting with other's code. Granted, you can add a rule to your team's linter to prohibit things like import(getSomeRandomModuleNameFromUser()). Unfortunately, it will not help your linter/IDE/whatever to see your dependency's dependency, breaking your tools even if you are doing everything "right" (for your team). This is the fundamental difference between "local" features like splice operators or fat arrows and "global" ones like the module system.

So my main point is that it can be useful to enumerate and consider potential downsides of such a fundamental and far-reaching decision.

[1]: I should note that optimization like dead code elimination by itself has nothing to do with "turning modules into non-modules". It's possible to eliminate dead code inside modules, too, with varying degrees of sophistication ranging from "tree-shaking" to proper flow analysis. Either the "bug in the web platform" that you are mentioning is a limitedness of disk space, bandwidth, and CPU, or I fail to see how the existence of unused code or the need to remove it can be seen as a "bug in the platform".

As you point out, JavaScript is a dynamic language. It's hard to statically analyze many JavaScript features, this one doesn't make it any harder to do so. JavaScript is never going to be C++. ES modules weren't made static for the sake of tooling.

It's possible to eliminate dead code inside modules, too, with varying degrees of sophistication ranging from "tree-shaking" to proper flow analysis.

It's possible to dynamically load modules (at least on the web) with or without import(). Here's an example of where that is done using script tags: https://gist.github.com/domenic/fd84ee5f4e2dc0278ab1#file-import-module-current-js

So any tree-shaking is going to have to be enforced out-of-band.

And to clarify, I'm not involved in this proposal, I'm just an outside web developer who's interested in seeing it succeed. So my opinions are only my own here.

None of these are "tradeoffs" of this proposal - they're all things you can currently do in the language, and all the issues you referenced already are problems in the ecosystem. This simply allows you to do it syntactically, which makes it more possible to lint against them, and statically analyze them.

It's hard to statically analyze many JavaScript features

It's true.

this one doesn't make it any harder to do so

It does. As I said, it replaces static module system with a highly dynamic one. It's impossible to statically analyze a dependency like import(getMoonPhase()).

It's possible to dynamically load modules (at least on the web) with or without import()

I'm not sure how this is related to the part you've cited (about eliminating code inside modules). Moreover, I can't see how the fact that you can import stuff dynamically in host-specific and hacky manner implies that there are no tradeoffs in putting this into the language spec itself.


None of these are "tradeoffs" of this proposal

I would be really happy if you or author point at others. There are always tradeoffs, and I can't believe that such a fundamental change (static -> dynamic module system) in a huge ecosystem will not have any worth mentioning.

all the issues you referenced already are problems in the ecosystem

In my opinion, that's exactly right and the main reason why switching from a static module system back to a dynamic one may have downsides.

which makes it more possible to lint against them

I'm not sure if people lint second-order deps often. Right now putting stuff in the DOM to dynamically load another stuff is clearly a hack and is frowned upon; this proposal "legitimizes" the behavior and makes it more probable to encounter it in a random lib on NPM. Moreover, it can also make the ecosystem fragmentation even worse (one more module type to handle — ES6+include!). Are you really 100% sure that there will be not a single problem in the entire JS ecosystem arising from the switch to dynamic modules? If not, why not try to list them?

The module system was always intended to allow dynamic modules, from the very beginning - "only ever static modules" was never a goal nor a guarantee. This isn't a "switch" to dynamic modules, which both already exist and were always intended to exist - it's just a standard way to import them.

My interpretation is that static syntax is in support of required DEPENDENCIES for module execution where as dynamic importing is for potential RESOURCES possibly to be used within that execution. Are these not two completely different use cases?

So far the "tradeoffs" or "risks" been part of each conversations but have not shown as matrix with importance.
Few items on matrix I think of:

  • backward compatibility, i.e. availability of shim in old JS engines
  • extensibility, like wrapping the JS native import() function and doing custom parameters preprocessing and/or error handling with recover. Of course the proper extensibility API would be better, but so far there only objections to the idea :(

This is "cancellable Observables" proposal all over again:

  • a major change is shoved onto the language
  • it significantly alters behaviour, semantics and expectations
  • breaks existing tools and integrations
  • requires changes to browser and javascript runtimes

Not a single person pushing for this change is willing to admit these problems, or talk about these problems. The proposal is carried full speed ahead, and there is not a single public discussion or description of risks and tradeoffs.

Any question is dismissed with "you know nothing, John Snow" and "we know better" (e.g. #19 (comment)).

Reading this conversation makes me lose faith in humanity:

  • What are the risks?
  • You know nothing
  • What are tradeoffs?
  • This is a problem in the system
  • But even other dynamic languages...
  • Yes, we are dynamic, you know nothing, go away

Just like stated in README

The existing syntactic forms for importing modules are static declarations. They accept a string literal as the module specifier, and introduce bindings into the local scope via a pre-runtime "linking" process. This is a great design for the 90% case, and supports important use cases such as static analysis, bundling tools, and tree shaking.

Getting rid of great design for the 90% case really does look like some kind of a trade-off.
As for me import(intepretToPuppiesKillerModuleName()).then(killPuppies) looks like clearly worse than if (wantToKillPuppies) then {import('PuppiesKiller').then(killPuppies)}.
With this restriction we would know the whole set of importable modules before runtime at the cost of putting some conditional before import.

Sure, if I'd want to know the whole set I could use static import in my modules. But then I'd have to lint against my dependencies that they don't use dynamic imports. Which could bring more fragmentation to ES ecosystem: static modules world/dynamic modules world

Other major tradeoffs/risks:

The proposal overloads an existing symbol with new behaviour, new syntax, new semantics and makes the symbol context-sensitive:

  • "static import" is import X from Y
    • this is the only allowed form
    • can only exist at the beginning of the module <- context sensitive
    • is basically destructuring assignment of symbols imported from Y
  • "dynamic import" import(Y).then({ X })
    • this is the only allowed form
    • can exist anywhere <- context free
    • introduces "nesting" (a.k.a. scopes) in a separate proposal
      • which may or may not overlap this one

So, what is this new import?

A new keyword? No.

A builtin function on par with eval, parseInt et al? Why does it override the name of an existing keyword?

Wouldn't it be better to introduce module-related functionality into a new global Module object exposing

  • Module.import
  • Module.importAsync

etc.?

Oh, right. "You know nothing, John Snow" and "That's not an argument against progress."

Another drawback which is simply dismissed out of hand. #26

On second thought that has drawbacks that outweigh the considerations here. It should be up to the sandboxing environment to modify eval() to perform whatever restrictions it wants.

(emphasis mine)

Multiple incompatible security issues, anyone?

Multiple issues with proposed syntax and behaviour flatly ignored #23 (comment)

Reproducing some of them here:

  • Using a call-like syntax for dynamic import deviates from the pattern used for other keyword based expression forms including yield, await, typeof, void, delete.
    • We should not establish the precedent of context sensitive expression operations masquerading as function calls.
    • Conceptually JS devs should not think about import as a "function call" with hidden contextual arguments. It really is just an operator applied to a string producing a promise to a significant effect.
    • The super constructor call super() isn't a good precedent for this deviation, as that form actually performs a function call while dynamic import does not.

Also, import() is not a function, #23 (comment) but disguises itself as such:

Promise.all(['α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ'].map(import))
 .then(console.info.bind(console))
 .catch(console.error.bind(console));

You'll get syntax errors if you try to use dynamic import as a map function

Because? Reasons?

Oh. Because "TC39 conclusion: keep it as a function-like form" :-\

All this begs just one question: how did it ever get to Stage 3?

@si14

It does. As I said, it replaces static module system with a highly dynamic one

I think you misunderstood the purpose of this proposal, it doesn't replace anything it just makes loading easier at runtime, you can use only static imports if you want. It just makes convinient to use same loading mechanics and hooks. Better look first at https://github.com/whatwg/loader proposal

@dmitriid

A builtin function on par with eval, parseInt et al? Why does it override the name of an existing keyword?

eval and parseInt was in language from the begining but when you add new functionality you must be careful not to break existing code that may already use your naming, and import already reserved keyword from ECMAScript 1, so it will not break anything

import also not a function, it is special syntactic form:

const a = import; // will throw exception

Wouldn't it be better to introduce module-related functionality into a new global Module object exposing

dynamic import intended to be used not only in modules but also in usual scripts

@forceuser

import also not a function, it is special syntactic form:

Exactly. It looks like a function, it behaves like a function, but:

  • it overrides an existing keyword
  • it does not behave like a function in any other context where you would expect a function to work

So it introduces a new confusing construct which behaves like nothing else in the language (with the possible exception of eval). Why?

Better look first at https://github.com/whatwg/loader proposal

Looking at it. Unless I'm mistaken, it's clearly superior:

  • It doesn't override existing keywords
  • It doesn't provide "special forms" that have confusing semantics
  • It introduces functionality as global objects

dynamic import intended to be used not only in modules but also in usual scripts

There's a global Math object. Works perfectly in usual scripts. There's a global Array object. There's a global... You get the idea ;)

@dmitriid

There's a global Math object. Works perfectly in usual scripts. There's a global Array object. There's a global... You get the idea ;)

global object can't know about module context, so it can't load files relatively to the module path.
so import() is the solution for that issue
another possible solution to create object for each module context, like module in commonjs

So, there are other solutions ;)

And import() has multiple drawbacks as I listed them above, and exactly zero of them have been addressed. yet, somehow this is stage 3 already.

Let me re-iterate:

  • introduces a new special form in a form that doesn't exist in the language (it is a function call which is not a function, it is a keyword which is not a keyword)
  • the form overrides an existing keyword with new semantics and syntax
  • the form is context-sensitive in a language that is mostly context-free
  • the form overlaps Loaders proposal
  • the form overlaps nesting import proposal
  • the form's behaviour in eval is left to be implementation-specific (hence inherently incompatible between implementations)
  • the form introduces changes to runtime behaviour of multiple JavaScript VMs (also, making parsers slightly more complex — import is now entirely context-sensitive)

"Minor" issues

  • the form breaks or renders inefficient existing tools (dependency analysis, tree-shaking etc.)

None of these drawbacks and risks have been addressed in any shape or form, and any questions about these have been dismissed out of hand.

Why is this stage 3?

@domenic @probins @robpalme @jakearchibald ?

@dmitriid

  • introduces a new special form in a form that doesn't exist in the language (it is a function call which is not a function, it is a keyword which is not a keyword)

This is a non-problem, same as meta-properties are not properties. It is a keyword https://tc39.github.io/ecma262/#prod-Keyword . There are no known grammatical ambiguities.

  • the form overrides an existing keyword with new semantics and syntax

No grammatical collision, this is not true, same as new.target is not a problem.

  • the form is context-sensitive in a language that is mostly context-free

Modules are always this way. Consider import './foo';. To note, since this is syntax it cannot be passed between files, so the context is the file containing import(...). Same context sensitivity as import declarations.

  • the form overlaps Loaders proposal

whatwg-loader is not intended to be shipped currently to browser in favor of type=module and the node ep.

  • the form overlaps nesting import proposal
  1. This proposal is a higher stage
  2. This proposal is more flexible but still allows static analysis for dynamic specifiers in addition to DCE in await position.
  3. No grammatical ambiguity due to (.
  • the form's behaviour in eval is left to be implementation-specific (hence inherently incompatible between implementations)
  1. See plans for Realms
  2. Yes, same as import declaration inner workings which will not be 100% compatible between the web and node.
  • the form introduces changes to runtime behaviour of multiple JavaScript VMs (also, making parsers slightly more complex — import is now entirely context-sensitive)

False, engines already track host provided info such as location

  • the form breaks or renders inefficient existing tools (dependency analysis, tree-shaking etc.)
  1. Dep analysis is possible and used in webpack for static specifiers
  2. Tree shaking is possible and being worked on for await position

I would also like to note that ESM is only static for a single dep graph while most web pages for example have multiple entry points (hence multiple dep graphs):

<script type=module src=entry_1></script>
<script type=module src=entry_2></script>

Most of these comments ignore this fact and assume a single entry point.

In addition ESM needs to integrate against NCJS (Node CommonJS). ESM is not and was not intended to be an isolated system. Node will not ship ESM if it cannot interoperate with NCJS in some sane way.

Also note that some of the complaints here are based upon tradeoffs around the syntax of this. It was chosen to avoid being passed around to the wrong context like a first class function causing ambiguity. It was chosen since it is a keyword and is safe to extend grammatically even with ASI. It was chosen since it does not collide with import declarations which cannot have ( immediately after import. It was chosen because it needs special host environment information. All of these are required even if it only accepted static specifiers.

There are no known grammatical ambiguities.

  • import() is not a keyword: none of the existing keywords have function semantics (typeof is quite commonly used as typeof(X) but its usage is actually typeof X)
  • import() is not a function: it looks like a function, it behaves like a function, it cannot be used in contexts where a function is expected

Yes. There is grammatical ambiguity

No grammatical collision, this is not true, same as new.target is not a problem

  1. This is not new.target. import.async would not be a problem either
  2. It reuses existing keyword (import) and overloads this with entirely new semantics and syntax

Modules are always this way (i.e. context-sensitive)

I refer to the usage and syntax of the new ambiguous import

whatwg-loader is not intended to be shipped currently to browser in favor of type=module and the node ep.

Why, then, is it referred to in the proposal Readme and in this issue?

Yes, same as import declaration inner workings which will not be 100% compatible between the web and node.

There's not just node. "Implementation-specific" is one the worst possible errors to be left around in the specification

Also note that some of the complaints here are based upon tradeoffs around the syntax of this. It was chosen to avoid being passed around to the wrong context like a first class function causing ambiguity.

Hence, you selected a syntax which is ambiguous (looks like a function call, behaves like a function call, is not a function call)

import() is not a keyword: none of the existing keywords have function semantics (typeof is quite commonly used as typeof(X) but its usage is actually typeof X)

Unrelated?

import() is not a function: it looks like a function, it behaves like a function, it cannot be used in contexts where a function is expected

Same could be pointed out about meta-properties.

Yes. There is grammatical ambiguity

No, according to the specification. This is a statement that needs to be backed up by a grammar production.

This is not new.target. import.async would not be a problem either
It reuses existing keyword (import) and overloads this with entirely new semantics and syntax

import.async

Would also not be allowed to be a first class function due to contextual ambiguity when passed around.

It reuses existing keyword (import)

Valid, see new.target again.

new semantics and syntax

Yes, thats what syntax does.

There's not just node. "Implementation-specific" is one the worst possible errors to be left around in the specification

Modules are implementation specific. Different constraints in different environments. JS has one of the most diverse ecosystems as well that would not allow for 100% interop. Stating truth > trying to assert falsehood and not having any compatibility due to lack of implementation.

Hence, you selected a syntax which is ambiguous (looks like a function call, behaves like a function call, is not a function call)

This is a statement that needs to be backed up by a grammar production.

Please back up with specification/implementation problems and not opinions.

Case in point for ambiguity. Example provided by @domenic here: #17 (comment)

import((() => { throw new Error(); })()).then(() => { });

should behave the same as

const arg = (() => { throw new Error(); })();
import(arg).then(() => { });

... I can see how it's a bit different for a syntactic form, but I think we should treat it more like a function than not.

^ this is clearly a function call. Emphasis mine.

@bmeck Grammar will be read and used primarily by people, not by machines

No matter how many times you say that "grammar is unambiguous, because specification" it is ambiguous to the human. And this is the most important type of ambiguity.

As is overloading keywords with new syntax and semantics

this is clearly a function call.

No, it clearly uses parenthesis and produces a completion value. Similar to how super() is not a function even though it uses parenthesis.

This is clearly #23 and other issues all over again.

I give up. Do whatever you want.

@dmitriid disagree.

Grammar will be read and used primarily by people, not by machines

anecdotal / existential fallacy

No matter how many times you say that "grammar is unambiguous, because specification" it is ambiguous to the human. And this is the most important type of ambiguity.

I agree if there was ambiguity, this is just learning the grammar.

As is overloading keywords with new syntax and semantics

See meta-properties.

My 2 cents.

import(...) cannot be polyfilled, unless accessed each time via window.import(...) (or global?), so we are defining here yet another piece of the language that will make transpilers, and their bugs like we have already in class case for years now, mandatory, and for years to come.

Having a special module object though, exposed differently per module, with the ability to module.import(...).then(...), would be easier to provide for both NodeJS and browsers.

I've also already linked the working playground [1] that shows this scenario already happening, and with modules loaded via relative paths per each module, but I've been told this wasn't the right place for that so ... I hope I can at least provide what this implementation would look like in NodeJS:

// Module as module.constructor
Module.prototype.import = function (path) {
  return Promise.resolve().then(() => this.require(path));
};

That's it. Not only it's backward compatible with every module already available, all patterns desired in here would also work, like loading multiple modules:

Promise.all([
  './awe',
  './some'
].map(
  m => module.import(m)
)).then(modules => {
  const [awe, some] = modules;
  return {awe, some};
});

Strawberry on top, it will automatically make it possible to export asynchronous modules too.

module.exports =
  module.import('./crypto')
    .then((crypto) => {
      // return the module to export
      return {
        sha256:(str, secret = '') =>
          crypto.createHmac('sha256', secret)
              .update(str)
              .digest('hex')
      };
  });

For the WebComponents era to come, this will also play absolutely nice:

module.exports = customElements.whenDefined('comp-name').then(() => {
  const CompName = customElements.get('comp-name');
  class SubCompName extends CompName {}
  customElements.define('sub-comp-name', SubCompName);
  return SubCompName;
});

Same kind of export would work well in NodeJS for modules that perform DB connections and similar operations.

Today most modules return synchronously operations that will be possible to perform only asynchronously so that having module.import(...) on backend seems to be like a win for everyone.

On the front end, there won't be the need to even think about specifying a require like function, just module with an import method that does exactly what this proposal does, plus eventually the ability to define exports through the same object (but this is probably less important)

Best Regards

[1] #36

If you encounter some new syntax what's the first thing you do? For me, search new.target mdn or something similar. The same would be true for import(). I don't think I buy the ambiguity argument at all. For example I would expect a JS programmer to know what's going on here:

!function foo() {
... 
}();

But also that you cannot do this:

function foo() {
...
}();

I think it's fair to expect developers to understand (without great difficulty) that similar syntactic forms have different semantics depending on where they are in the code.

My opinion is "just leave import alone, you ppl".
Dynamic module loading should definitely be supported in the language but it should be an "advanced" feature, like ES6+, and nobody should be obliged to implement this (like there are "Tier 3" datacenters and there are "Tier 3+" datacenters and they have a clear certification process established for both).
It could be checked like if (Module.import) { ... }.

unfortunately that's not the most wise advice, if you consider everything I've written in the NodeJS proposal:
https://github.com/WebReflection/node-eps/blob/master/XXX-module-import.md

Specifically, you might be interested in the following paragraphs:

Why as core feature ? Why not transpilers ?

Why not as global function ?

Why polluting module and not require ?

@halt-hammerzeit "ES6+" includes ES2016, ES2017, and all future specs, which everyone is required to implement - there's no "advanced" or "optional" stuff besides Annex B, which exists solely to document optional and legacy behaviors for web compatibility.

Actually, on second thought, my idea is that the import word is not suitable here at all.
import is a symbol of the new (and very elegant) explicit static module interconnection.
It is what it means — "import", like:

import { printing_presses, hydraulic_pumps } from 'germany'
import { potatoes, crops } from 'ukraine'

I.e. its very essense is that something (a part) is imported from somewhere, like countries do in global economy.

And for the hypothetic import() stuff I'd rather suggest good old eval(), because it's semantically very different from what import is really meant to be: import is really meant to be a means of explicitly declaring dependencies, it's kind of a bureaucracy measure to fight all that require() chaos, and it turned out to be a very elegant and effective one (e.g. tree-shaking).
So the import() being proposed is whole another concept with whole another purpose and it should rather be something like Module.eval() or better Module.load(), i.e. no place for import word here at all.

"import" means "import". In no way does it mean "static" or "synchronous", and import('somewhere').then(something => { … }) is importing something from somewhere just the same conceptual way import something from 'somewhere'; is.

The fact that "methods of async loading" already exist on every platform means that tree-shaking already has to account for this and will have to do so forever - this proposal has zero impact on tree-shaking except perhaps by making it easier, by creating a syntactic way to identify and analyze dynamic imports.

@halt-hammerzeit the concept is the same because the module can be the same, either you use import * or import() to load the external module, it would still be the same.

Accordingly, there's no need to create confusion with the terminology because no matter if you import something synchronous or asynchronous, you are importing the exact same thing.

However, I kinda agree import(), in the apparently global scope (it's not!), and looking apparently like a function (it's not!), would confuse many developers while module, since the parallel proposal is to have <script type="module"> to load asynchronously on the Web, is a concept everyone can easily recognize as a variable that could be unique per module, just because that's what everyone know in the already popular NodeJS/CommonJS land: less to explain, and a method name that reflects what staic import declaration would similarly do: import the module.

As summary, in my opinion, module.import(path).then(...) is the best moving forward, backward compatible, future friendly way we have today to define such dynamic method.

@ljharb @WebReflection My point is that import means importing an explicitly defined part of something, not "just import everything".
You can't "import" ukraine from ukraine, you can't "import" germany from germany.
If it was "just import everything" then there would be no point in introducing import in the first place (at all) — good old require() would perfectly do.
The whole point of introducing import and dropping require() is to embrace developers put more thought into how exactly their code is interconnected, and to make them optimise their code better, to make them understand their own code better, to teach them writing higher quality code.
I find things like import * from 'module' and import module from 'module' very pointless and defeating the whole purpose of introducing this whole module importing system.
The only reason for import module from 'module' to exist is for tiny modules only, i.e. when modules are so tiny that they physically can't export more than one dependency, and not for the cases when developers abuse this feature and import huge pieces of code like they used to back in the old require() days.

@halt-hammerzeit you absolutely can import everything, that's what import * as everything from 'anywhere' is for. Your claims about "the whole point" aren't accurate.

@ljharb If you can do something it absolutely doesn't mean that it's meant (and embraced) to be done that way. It could as well mean "okay, we'll let you have this possibility for now as you transition your mind from your old mindset to the new one".

@dmitriid You seem to be arguing that the syntax is confusing to developers. This is a valid concern, but it's different from being ambiguous (i.e. allowing code that has multiple different correct parses). I think this distinction is causing some of the misunderstanding in this thread.

@not-an-aardvark Yes, confusing is a better term, thank you

The best part though? A horrible design decision is justified by examples of other horrible design decisions. To wit, new.target and !function{}()

@halt-hammerzeit beside what @ljharb said, the Web case and its runtime on-demand needs, cannot be ignored.

Luckily, you have all the syntax you want to explicit the part of a module you want to import

import * from './module';
import('module').then(module => {});

import bit, baz from './module';
import('module').then(({bit, baz}) => {});

Isn't arguments destructuring one of the best things we have these days?

Best Regards

Closing this since, as people have explained a few times, it's not related to the proposal, but to non-standard ways of turning modules into non-modules, which are outside the scope of the language.

To be honest I fail to see how an issue titled ""Tradeoffs" or "risks" section to the proposal?" can be "not related to the proposal". The list in the initial post is preceded by "for example" to indicate that it's just a list, not the list of downsides.

At this point the spec looks as if the decision is a no-brainer. Is it intended, @domenic? If not, do you think spelling out concerns (some of which were raised in this issue) and their analysis can be helpful to deflect further criticism of the spec?

How about "Benefits" and "pancakes"?

@jhabdas this is a very non-productive reply. "Risks", "tradeoffs" or "drawbacks" is a usual part of proposal in a lot of languages, see some examples from Rust ("drawbacks" and "alternatives"), Java ("risks and assumptions"), and Python ("pros and cons"). It's not like I'm pulling the issue out of thin air.

Thanks for the feedback @si14. I do believe this issue was closed already ;)

@jhabdas can I ask you to refrain from non-productive passive-agressive replies? You aren't helping no one with that.

You can ask. Or you could just open a new issue.

@si14 It's not relevant to the proposal because these criticisms exist with or without dynamic imports. As stated before, you can already dynamically import modules in browsers by inserting module scripts into the page. This proposal doesn't add new capabilities, so adding "Tradeoffs" section doesn't make sense. The tradeoffs you list are part of JavaScript itself.

@si14

  • "anecdotal evidence"
  • there are already horrible design decisions in the language already (target.new etc.), so we are justified
  • a clearly better proposal has not been pushed hard enough, so we are in a more advanced stage, so suck it

These are all the arguments you will get. Enjoy your JS monstrosity

suksma

Since this thread has pretty clearly degenerated, and no new information is being added, it seems best to lock it at this point.

@matthewp I believe there is a great deal of confusion between the original issue (there is no "official" consideration of downsides as in e.g. PEPs, JEPs and Rust's RFCs) and some concerns themselves. Given that this issue became an outlet for a great deal of frustration, it's very probable that a section to address potential downsides is apt.