tc39 / proposal-import-attributes

Proposal for syntax to import ES modules with assertions
https://tc39.es/proposal-import-attributes/
Apache License 2.0
599 stars 26 forks source link

Use general key-value syntax, or a single string? #12

Closed littledan closed 4 years ago

littledan commented 5 years ago

In the original thread at https://github.com/w3c/webcomponents/issues/839, there are several suggestions of syntax which includes a single string, rather than key-value syntax. Let's discuss the advantages and disadvantages of this option in this thread.

For example, the syntax for an import statement could be:

import json from "./foo.json" as "json";

Advantages of a single string

Advantages of key-value syntax

My opinion is that the advantages of a general key-value syntax outweigh the advantages for a single string. It's circumstantial evidence (since this proposal doesn't suggest acting on any of them yet), but the relatively high number of other possible use cases points to a k/v syntax to me. This would be analogous to the decision to make import.meta an object, rather than just adding a syntax for import.meta.url.

chicoxyzzy commented 5 years ago

Key-value syntax is definitely look like a more future proof solution to me. Also this will make static imports and dynamic imports look more consistent, or are there any other suggestions than import('module', { type: 'json' }) which look more like a single string solution for static imports?

bahrus commented 5 years ago

Could the type be treated as the default, but if more parameters need to be defined, switch to k/v syntax? I.e a hybrid approach? The analogy of the default export, which doesn't use braces when importing comes to mind.

Janpot commented 5 years ago

Can't there still be key/value syntax with a single string, if encoded as a querystring?

import json from "./foo.json" as "type=json&anotherOption=42";

// and default behavior
import json from "./foo.json" as "json";
// would be equivalent to
import json from "./foo.json" as "type=json";
littledan commented 5 years ago

@Janpot We could do that. My feeling is that more sub-syntaxes will lead to more problems, from a developer experience perspective, from possible diversity/bugs in parsers, etc.

Janpot commented 5 years ago

My feeling is that it would leave more freedom to hosts/compilers on how to parse that string. My understanding is that the spec doesn't dictate how the module identifier is interpreted, I feel like in the same way it could leave freedom on how the module attributes are interpreted.

littledan commented 5 years ago

Well, this comes a bit in contrast with the goals articulated in #10

Janpot commented 5 years ago

I see. fwiw, I didn't necessarily mean "freedom" as in "no consensus". I meant it more like "allow enough escape hatch for people to come up with ideas that we didn't think of today"

bmeck commented 5 years ago

I'd like to call out editor UX here. Key-Value will lead to better completions if we ever have multiple attributes (which I would consider a mandate for this feature personally). Even today I am given sub-par UX with import statements due to their design and would not like to have another UX pitfall here for import.

Janpot commented 5 years ago

@bmeck Are you talking about it being hard to autocomplete the import since you type the module specifier last? As in, it would be more ergonomic to do:

from 'lodash' import { groupBy };
bmeck commented 5 years ago

@Janpot that syntax allows tooling to better complete the code as you type, which is my concern yes.

littledan commented 5 years ago

@bmeck I am with you on completions. I think you should usually not have to type the type at all if using editor completions.

thw0rted commented 5 years ago

Has anybody made a strong argument against supporting both?

import j1 from "./foo.json" as "json";
import j2 from "./foo.json" as {type: "json", bar: "baz"};
const j3 = import("./foo.json", "json");
const j4 = import("./foo.json", {type: "json", bar: "baz"});

The thing I keep coming back to is that nobody can give me a concrete example of what the bar attribute should be. Of course it makes sense to save those 6 characters if the proposal never grows beyond type:, but if another attribute comes along and is not only useful but more commonly specified, having a "shorthand version" that's always interpreted as being the type will seem short-sighted in retrospect. At this point, to me, the opposite seems more likely: that we're currently over-engineering a solution for the general case (bag of key-value attributes) when the immediate need is for a simple case (exactly one attribute).

littledan commented 5 years ago

Well, the basic concern would be that this goes in contrast with goals of minimalism. In the OP, I had some concerns with using as as a keyword here as well.

xtuc commented 5 years ago

@thw0rted I think there are many use case for more than one argument: #22 or https://github.com/littledan/proposal-module-attributes/issues/16#issuecomment-552421527 are one of them in this repo.

bmeck commented 5 years ago

I'd like to second the contrast of minimalism.

If you look at things like how default exports work in JS, they actually add quite a bit of complexity for a programmer to learn even if they are a common usage when importing modules. I find the usefulness of having 2 forms of imports to be plausible, but do not see such currently with module attributes as not all environments are even seeking to use the same attributes for the same purposes/reasoning currently.

ljharb commented 5 years ago

I also would prefer minimalism (to me, default as a name is the confusion, not the existence of defaults, but my conclusion here is the same)

littledan commented 4 years ago

After thinking about this a bit more, and chatting with @xtuc @dandclark and @MylesBorins offline, I'm thinking that it might make sense to start with the simple as "string" form, with a potential follow-on proposal to permit more complex attributes, like as {type: "json", key2: "value2}.

Advantages:

Disadvantages:

@xtuc and @dandclark suggested that for might be an alternative for as which makes sense with complex things on either side, but @MylesBorins pointed out that, even reusing as, the syntax is far from ambiguous: It switches based on whether as comes before or after the module specifier.

Thoughts?

jridgewell commented 4 years ago

I thought the committee specifically didn't want to tie this to import types, and wanted to explore generic metadata attached to imports? https://github.com/tc39/notes/blob/master/meetings/2019-12/december-5.md#conclusions

I'm kinda disappointed to see us taking this route, since metadata on types would have been useful. Eg, TypeScript and Flow marked type-only imports, Babel could mark lazy imports, etc.

littledan commented 4 years ago

@jridgewell The proposal leaves non-string metadata open for expansion into objects. In my mind, this is a key requirement, that we're not locking ourselves into just a string. But the idea is to start with a string, and make this further expansion when we are very solid on the use cases for other things (even though we have plenty of ideas).

bmeck commented 4 years ago

I've stated in other issues that only tailoring to the web specific type attribute is not appealing. If the intent is not to include the ability to have multiple attributes in this proposal but instead in a follow on proposal I would not be comfortable moving forward.

littledan commented 4 years ago

OK, given the aim of avoiding making the import types special, I'd like to conclude on the general key/value syntax, as is currently in master in this repository. I see this as one of the core questions that we'd be affirming consensus on for Stage 2.

xtuc commented 4 years ago

We decided to go with the key-value syntax and reached consensus for stage 2.

jerrygreen commented 4 years ago

Weird it reached consensus.

You have this in readme:

We propose to omit this generalization in the initial proposal, as a key/value list of strings already affords significant flexibility to start, but we're open to a follow-on proposal providing this kind of generalization

So why complicating the syntax, if you're already committed to "omit generalization"? What if it will come out that the type is sufficient, and there wouldn't be follow-on proposal? We will simply end up with weird syntax, having some object-like literal with one usable key: type.

Imo this code is enough for this particular proposal:

import json from "./foo" as "json";

And if another proposal will come, with support of some useful keys, they will have to bring some "assertions 2.0" with this object-like notation along with it, making key-value notation also available.

I personally think that there's too small chance for more keys to be real useful for assertions, too small to bringing this generalization straight away.

Sorry for notifying a closed thread but I had to say it.

Jack-Works commented 4 years ago

If the JSON module proposal is requiring the host must interpret "type: json" as JSON, it means "type" is special. Maybe we can make as "string literal" as a shorthand of "type: 'string literal'"

ljharb commented 4 years ago

That specialness is a quality of the specification though, not necessarily the runtime - a host might have many assertions available that operate the same way as “type”, and to a user on that host, “type” wouldn’t be special at all.

kwebble commented 2 years ago

As a developer, not someone who creates a language, When I see a literal string value I expect it can to be using an expression, is dynamic at runtime or can be taken from a variable. Please do not mix user code with language definition.

ljharb commented 2 years ago

That expectation already doesn’t apply to the specifier (the part after the “from”).

kwebble commented 2 years ago

True, and I have the same objections about that. Whitespace as separator seems good enough for me.