Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Could the static and dynamic import syntaxes be more symmetric? #99

Open
devongovett opened this issue Sep 22, 2020 · 14 comments
Open

Could the static and dynamic import syntaxes be more symmetric? #99

devongovett opened this issue Sep 22, 2020 · 14 comments

Comments

@devongovett
Copy link

Reading through the readme, I'm confused about the syntax, as it seems inconsistent between static and dynamic imports.

import json from "./foo.json" assert { type: "json" };
import("foo.json", { assert: { type: "json" } })

Then, there's also this mentioned, but it appears to be a separate proposal (?):

import json from "./foo.json" assert { type: "json" } with { transformA: "value" };

There is no equivalent dynamic import shown, so it's unclear which of the following it would be:

import("foo.json", { assert: { type: "json" }, with: { transformA: "value" } })
import("foo.json", { assert: { type: "json" }, transformA: "value" })

My questions based on this:

  1. Why are assert and with separate keywords in the static import case? Could assert be an option within the with options object? This seems like it would be more symmetric with the dynamic import syntax:

    import json from "./foo.json" with { assert: { type: "json" }}
    import("foo.json", { assert: { type: "json" } })
  2. In the dynamic import syntax, is assert the only supported key like it appears to be for static imports? Will engines throw if other keys are added?

We're interested in using custom attributes in Parcel to indicate bundle preload/prefetch hints: parcel-bundler/parcel#5158. Webpack currently does this via their magic comments, but we'd like a less hacky syntax and this proposal looks very promising for that.

My questions above are based on this use case. If assert is the only supported attribute at the parser level, then we likely won't be able to use it. I'm mainly wondering why it needs to be limited in this way (if it is), and why a more general proposal was rejected. Please feel free to point me to other threads if this was already discussed! 😄

@ljharb
Copy link
Member

ljharb commented Sep 22, 2020

What you're talking about are evaluator attributes, that can impact how a module is loaded - assertions, by spec, must not alter how a module is loaded/interpreted.

@littledan
Copy link
Member

My thought was that, if we add this separate proposal for transformation, it would support dynamic import in the way you suggest, with a second option for the transformations. This all hasn't been written up yet. Would you be interested in working together on this proposal?

@devongovett
Copy link
Author

I guess I'm wondering why they are separate syntactically? Why is assert a keyword rather than just a property?

As I mentioned in the issue, this seems much more symmetric and extensible.

import json from "./foo.json" with { assert: { type: "json" }}
import("foo.json", { assert: { type: "json" } })

@ljharb
Copy link
Member

ljharb commented Sep 23, 2020

That's not entirely symmetric in that there's no "with" appearing in the dynamic form. Your first example in the OP seems the most symmetric and consistent to me, since the only difference is some curly braces and whatnot.

@devongovett
Copy link
Author

Sure ok, we can bikeshed all day but honestly I don't care too much about the actual syntax. I am really looking for a reason why this is two proposals rather than one. It seems to me that adding two separate more specific keywords to the language is more work/less elegant than adding a single one that's general purpose.

The point I am making is that the dynamic import syntax is extensible: you can add additional options without going through the spec process to add it to the syntax. The static import syntax is less flexible in that way, and I'm wondering whether it can be made equivalently extensible without changing the syntax each time a new option is added.

@littledan
Copy link
Member

Is there anything more that you can say about the extensions you're interested in? I thought that, between assertions and with, the space would be covered. We are starting with just assertions since they are more regular and easier to understand, and we have concrete use cases for them. Additional use cases would help drive further development.

@devongovett
Copy link
Author

I think the preload usecase above is interesting. I guess it could fall into the with category if you think there is really a need to categorize these things syntactically (that's what I'm asking). All the examples I've seen of with made it seem like it was meant for transforms or something, and preloading/prefetching hints don't really affect that. Another example is webpack's chunk name magic comment that can be used to influence the output filename for a bundler at a dynamic import callsite. I imagine there could be more examples as well, so I was wondering whether categorizing these options separately made sense.

@ljharb
Copy link
Member

ljharb commented Sep 23, 2020

Does preloading affect module evaluation order?

@devongovett
Copy link
Author

No. It essentially injects a <link rel="preload"> or <link rel="prefetch"> element, which fetches the script but does not evaluate it. There's also <link rel="modulepreload"> which also parses the script in advance but still does not execute it until it is actually imported. We do this when the script containing the dynamic import with this attribute loads, therefore preloading these scripts before the dynamic import is actually called. It's very similar to what webpack does but with a different syntax.

@ljharb
Copy link
Member

ljharb commented Sep 23, 2020

Preloading, then, seems like neither an assertion nor an evaluator, but an annotation - which could be done by a comment, but could also be done by a no-op assertion. Would either of those be satisfactory?

@littledan
Copy link
Member

It sounds like these preload options are neither assert nor with logically, and they only are needed for dynamic import. Is that accurate?

@devongovett
Copy link
Author

Correct, it doesn't seem to fit either category. With dynamic import it seems like maybe we can do this as long as extra top-level options passed to the second argument are ignored, but it's not clear from my reading whether this is the case. Given that only assert and with are allowed in the static import case, I wasn't sure whether this was also the case for dynamic imports.

@josephrocca
Copy link

josephrocca commented Sep 30, 2020

I've only just come across this proposal, so I might not be qualified to make a comment here, but while going through the readme I immediately had the same thought as @devongovett. As Devon said, the dynamic import's option object is easily extendable with other properties (e.g. for sub-resource integrity, if that ever gets an in-band version):

import "./foo.mjs" with { assert: { type: "json" }, integrity: "...", referrerPolicy: "..." };
import("./foo.mjs", { assert: { type: "json" }, integrity: "...", referrerPolicy: "..." });

Developers are accustomed to a JSON-like format, and will already be using it for the dynamic import, so this seems like the most intuitive and future-proof syntax.

@littledan
Copy link
Member

I think integrity fits into assertions, and preload fits into transformations. We already have an extensible key/value format; it is just sorted into whether we are talking about assertions or transformations. So I am not convinced that more generalization is needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants