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

import default operator

Constellation opened this issue · comments

It may be beyond the current proposal, but I've just received some feedback. (BG: dynamic-import is now implemented in WebKit nightly).

In the static import syntax. we have 3 forms: import * as namespace, import { named }, and import deafult.

On the other hand, the current dynamic-import proposal always resolves the result promise with the namespace object.
By using this object and existing ES syntax, we can write the code that is similar to the second named form.

const { named } = await import("...");

However, for the third case, we need to write a bit storage code like this.

const { default:map } = await import("../map.js");

or

const map = (await import("../map.js")).default;

In the static import syntax, we have the import default syntax separately while we can write the named import syntax like the following.

import {default as map} from "../map.js"

This default export/import mechanism encouraged modules to export the one representative default export and helped the developer to just use the default exported one instead of specifying the exported binding names.

So here, I would like to discuss about introducing the similar thing to the dynamic import. Like,

import default ("../map.js")

This was briefly discussed in committee and the feeling was that the additional complexity of having these compound contextual keywords was not worth the few characters saved.

@domenic

Personally I do not want to introduce it immediately in this proposal. (It means that I would like to introduce another draft for that if we think we should introduce it) So it is not in a hurry. :)

However, the above reason is not convincing to me. I do not think that the purpose of this import default is just saving the few characters,
It is essentially the same problem to import {default as map} from "../map.js". And is it (static import default syntax) introduced to just save the few characters?

In my opinion, yes, both are about saving characters. The difference is in the complexity tradeoff in exchange for saving those characters. When you already have forms of the form import _____ from "specifier", adding a new value for ___ (i.e. default) is not a big deal. But there is no empty ____ in the import() form which we can fill in with various things, default being only one of them. Furthermore, there is no precedent for a function or function-like form that has a space in its name (like import default(x)).

My thinking is that import() is a low-level function for exposing the low-level primitive, the module namespace object. It isn't meant to be super-ergonomic; for that you want to just use normal import statements, which are designed for ergonomics.

Thanks so much!!

In my opinion, yes, both are about saving characters. The difference is in the complexity. When you already have forms of the form import _____ from "specifier", adding a new value for ___ (i.e. default) is not a big deal. But there is no empty ____ in the import() form which we can fill in with various things, default being only one of them.

Personally, I do not agree with this. ES provides the one special representative binding named default.
Right, it saves the few characters. But rather than that, it provides the "standard" way to expose the module: like, typically, we expose the XXX class with the default for the XXX.js file.
It sounds to me like the difference between constructor and function. We can write the function
that returns an object instead of using ES constructor form. But ES provides the one representative way to do that, constructors and new XXX form.
This "standard" way introduces a convention to the libraries. And we can construct an object in the library by using this standard way instead of bothering how to create the object in this library: some library may provide XXX.create method, and some library may provide XXX.new method.

Furthermore, there is no precedent for a function or function-like form that has a space in its name (like import default(x)).

Right. But fortunately, the both import and default are keywords. So I think it is much easier than the spaced contextual keywords.

My thinking is that import() is a low-level function for exposing the low-level primitive, the module namespace object. It isn't meant to be super-ergonomic; for that you want to just use normal import statements, which are designed for ergonomics.

But it is reasonable to me. If we think import() is a low-level function (like fetch in whatwg), I think the current one is reasonable. It exposes the namespace object, which contains all the bindings.

However it also raises the different question. Can we use this import()as a building block?
It is context sensitive operator. Thus we cannot wrap this import with the function. For example, we cannot create the function importDefault based on the current import() operator due to the referencingScriptOrModule.
Do we have a plan extending this import to take some referencingScriptOrModule information? (maybe, as a second argument)
And do we have a plan to expose the current running referencingScriptOrModule information to the JS? And even in that case, still we need to write the code like the following since referencingScriptOrModule is context sensitive.

await importDefault("../map.js", referencingScriptOrModule())

edit: Oops. I forgot to mention to static import.

It isn't meant to be super-ergonomic; for that you want to just use normal import statements, which are designed for ergonomics.

Yeah, but it lacks the dynamic features...

Plz do not laugh. There is a keyword which already semantically assumes we are creating the object: "new"
In the context of synchronous call and parsing it should be about similar to "await"
const { default:map } = new import("../map.js");

To make it even more attractive, it is shimmable in case of the need to have runtime distinction between new import and import()

However it also raises the different question. Can we use this import()as a building block?
It is context sensitive operator. Thus we cannot wrap this import with the function. For example, we cannot create the function importDefault based on the current import() operator due to the referencingScriptOrModule.

That's a great question. Note that you can still wrap it if you want, but not in a shared library. So e.g. at the top of your module you could do

function importDefault(x) {
  return import(x).then(ns => ns.default);
}

Do we have a plan extending this import to take some referencingScriptOrModule information? (maybe, as a second argument)

That would be an interesting idea worth exploring, IMO. Fortunately we've reserved the second argument so we can do that.

And do we have a plan to expose the current running referencingScriptOrModule information to the JS?

Yes, that's largely tracked as whatwg/html#1013.

@sashafirsov I can't tell how what you're proposing is related to this thread.

That's a great question. Note that you can still wrap it if you want, but not in a shared library. So e.g. at the top of your module you could do

Yeah, but it's a bit unfortunate... but,

That would be an interesting idea worth exploring, IMO. Fortunately we've reserved the second argument so we can do that.

If we can do that thing, we can alleviate the situation. Like,

function importDefault(x) {
  return import(modifyBasedOnSomeScript(x), someScript).then(ns => ns.default);
}

Yes, that's largely tracked as whatwg/html#1013.

Great! I've just commented on that issue about the script in worker module script :)

Another issue is with tree shaking: contrary to import {onlyThis} from 'module, import(module) provides no information to optimizers about what part of the namespace is used.

Would it not be possible to add a second argument, like import(module, ['onlyThis']) -> {onlyThis: fn...} and maybe even import(module, 'onlyThis') -> fn...?

@wmertens no. See #19.

I'll close this issue as we don't plan on modifying this proposal in this way.