tc39 / proposal-source-phase-imports

Proposal to enable importing modules at the source phase
https://tc39.es/proposal-source-phase-imports/
MIT License
129 stars 9 forks source link

Use extensible object syntax? #18

Closed jonathantneal closed 1 year ago

jonathantneal commented 2 years ago

Would you be open to using an extensible key/value format, to future-proof reflection in case it's needed for other use cases, even though none are implemented now?

import JoyStyle from "./joy.css" as { type: "css", media: "(width > 640px)" }
Jack-Works commented 2 years ago

We already have infinitely extensible syntax with no runtime semantics - comments. Adding syntax that does nothing at runtime is adding a lot of cost for decidedly non universal value.

Out of Topic: That's how I think of the Type Comment proposal

jridgewell commented 2 years ago

We already have infinitely extensible syntax with no runtime semantics - comments. Adding syntax that does nothing at runtime is adding a lot of cost for decidedly non universal value.

Out of Topic: That's how I think of the Type Comment proposal

By this logic, we can call JS a complete language. Who needs pipelines, pattern matching, or import reflection? We can just add comments to the appropriate places and let the build steps magic everything together.

ljharb commented 2 years ago

@devongovett yes, that is the exact idea. Adding something to the language is a permanent decision that can never be unmade; “time to add” is, in the long run, a negligible cost compared to permanent mistakes. Moving too fast breaks things, which slows down everything that comes after it. That’s the job of JS language stewards imo - to minimize mistakes, not to accelerate experimentation (which requires no standards process anyways)

ljharb commented 2 years ago

@jridgewell i don’t think your slippery slope there is accurate. What may be an accurate logical extension is, that anything without runtime semantics can and perhaps should be achieved without any alterations to the language. Anything with runtime semantics, however, is unrelated to that logic.

devongovett commented 2 years ago

Not everything can or should be standardized by TC39. What if browsers want a feature that doesn't make sense in other runtimes? Or the reverse? Is JS going to standardize a feature of a single runtime? How about features of a bundler? I don't think the JS language should be playing gatekeeper.

ggoodman commented 2 years ago

We already have infinitely extensible syntax with no runtime semantics - comments. Adding syntax that does nothing at runtime is adding a lot of cost for decidedly non universal value.

Adding something to the language is a permanent decision that can never be unmade; “time to add” is, in the long run, a negligible cost compared to permanent mistakes. Moving too fast breaks things, which slows down everything that comes after it. That’s the job of JS language stewards imo - to minimize mistakes, not to accelerate experimentation (which requires no standards process anyways)

This comes across as dismissing community feedback because we're perceived not to fully appreciate the problem or problem space.

Is this a forum for discussion that is legitimately open to non-TC39 members of the community?

That first comment is such a facile argument; it doesn't contribute to the discussion and is unnecessarily dismissive. There is a major undertaking right now for exactly the sort of non-semantic syntax 'that can be accomplished in comments' before this very same group and with significant momentum.

I will bow out of this discussion.

devongovett commented 2 years ago

As requested in the tools meeting today, here are some potential use cases for import attributes. Tried to group them into some kind of semantic categories.

// this proposal
import mod from './foo.wasm' with { type: 'module' };

// more reflections
import img from './foo.png' with { type: 'url' };
import img from './foo.png' with { type: 'arraybuffer' };
import img from './foo.png' with { type: 'dataURL' };
import img from './foo.png' with { type: 'arraybuffer' };
import worker from './worker.js' with { type: 'worker' };
import sw from './worker.js' with { type: 'service-worker' };
import svg from './foo.svg' with { type: 'dom' };
import svg from './foo.svg' with { type: 'image' };

// tc39/proposal-defer-import-eval
import {x} from "y" with { lazyInit: true };

// attributes to apply to a constructed object
import stylesheet from './foo.css' with { layer: 'utilities' }; // css cascade layers
import stylesheet from './foo.css' with { media: 'print' };
import audio from './file.mp3' with { loop: true };

// preloading (possibly related to lazyInit?)
import x from './foo.js' with { preload: true };
let mod = await x.load();

// more assertions
import x from 'y' with { assert: { integrity: '...' }};
import x from 'y' with { assert: { referrerPolicy: '...' }};

// transforms that bundlers/tools could implement
import img from './foo.png' with { width: 500 };
import img from './foo.png' with { convertTo: 'jpeg' };

I'm sure there are more as well (please leave a comment!), but hopefully this demonstrates the need for an extensible syntax.

ljharb commented 2 years ago

@devongovett thanks! i'd love some more elaboration on what these do (in prose), as well as what they would be expected to do in browsers and node.

guybedford commented 2 years ago

@devongovett the examples really do help a lot to try to think more concretely about the requirements here.

We also went through a lot of these cases when exploring the possibilities for import reflection, so I can share some feedback based on what came out of that process (mixed in with my own opinions of course!), see comments below:

nayeemrmn commented 2 years ago

To me it would make a lot of sense if module references didn't have evaluator attributes but asset references did:

import module x from "./foo.js";
// x is a module-block-like
await import(x);
import asset y from "./bar.png" with { type: "image" };
hostUseImage(y);

There is a need to separate these two because tc39 could have a lot to say about x and would have very little to say about y.

i.e. I think module references are a special case where most of what they do can be understood at the vanilla level, they should preload deps as static imports would and be dynamically importable. Whereas arbitrary host-managed evaluator attributes and all of their use cases would be much better suited to asset references, which already export arbitrary host-defined values.

In other words, as soon as a module reference includes host-specified evaluator attributes to make it yield a host-specified value, it becomes an asset instead.

Rather than lamenting module vs asset not being selected by an extensible scheme, we should accept module as a necessary carve-out for importable module references, and let asset be the extensible category.

devongovett commented 2 years ago

Ok, I'll try to go through them with some more explanation and also try to answer @guybedford's questions along the way.

To be clear, my goal with these examples is to show that there are a wide range of potential use cases for import reflections and attributes. Some of these could eventually be standardized, either by TC39 or by other standards bodies (e.g. as a part of the CSS module script spec). Some of them could be implemented in tools and stripped before reaching runtimes. Making the syntax extensible now will avoid needing to specify new keywords or syntax later for each possible reflection or attribute. This is important not just for tools, but also for runtimes.

ljharb commented 2 years ago

What would importing an image do outside of a browser environment?

The language also needs to ensure it stays universal for environments beyond browsers and node, like IoT devices and other chipset environments, and the mental and implementation cost of having syntax (as opposed to API) that works in some environments but not others can be quite high.

devongovett commented 2 years ago

I think the host runtime should "register" the attributes that it supports with the JS engine, and unknown ones should be rejected. This would allow browsers to have different supported attributes from other environments, and would match the behavior of import assertions.

Pauan commented 2 years ago

@ljharb The language also needs to ensure it stays universal for environments beyond browsers and node, like IoT devices and other chipset environments, and the mental and implementation cost of having syntax (as opposed to API) that works in some environments but not others can be quite high.

ES6 module resolving and loading is already environment-specific. import.meta is already environment-specific. There's nothing wrong with having a feature which varies between different environments. And having extensible syntax makes things better for different environments, because each environment can define its own custom properties.

If extensible syntax doesn't exist, then every environment must either invent its own syntax for environment-specific things, or it must create a new TC39 proposal, which isn't always desirable or possible. And if the feature is environment-specific, then it's unlikely to be accepted as a TC39 proposal. Extensible syntax fixes all of those problems.

jridgewell commented 1 year ago

Rollup just landed https://github.com/rollup/rollup/pull/4646, which adds support for arbitrary assertions and custom processing of those assertions by plugins.

The bundler ecosystem continues to land on the same solution, and I think it's because it's extensible.

nayeemrmn commented 1 year ago

With import assertions having transitioned to import attributes (slides) and import reflections transitioning to import phases (decoupling from attributes in the process), I think this issue is addressed.

The use cases brought up are relevant to and satisfied by import attributes now. You can see them used with their extensible syntax in conjunction with phases in one of the slides: https://docs.google.com/presentation/d/1Abdr54Iflz_4sah2_yX2qS3K09qDJGV84qIZ6pHAqIk/edit#slide=id.g216c73c7b74_0_35

(Or at least unless anyone thinks the phase should use an extensible syntax?)

Jack-Works commented 1 year ago

at least allow booleans plz. only allowing string properties is annoying when I try to use them.

lucacasonato commented 1 year ago

This has been resolved, as this proposal is specifically dealing with only import phases.

As import attributes is now stage 3, please direct any feedback regarding the import attributes options bag to the TC39 Discourse.