tc39 / proposal-import-attributes

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

Allow (statically) importing source code (from GLSL files etc) with an appropriate type (binaries??). #120

Closed 7ombie closed 2 years ago

7ombie commented 2 years ago

I'd like to import a WebGL frag shader into an ES6 module, and (because it's far easier to maintain) I'd also like to keep the shader source in its own GLSL file. There's currently no way to do this.

The supported alternatives are not very workable: Storing source code in a JSON string requires a lot of escaping, making the code effectively uneditable. Storing code in a multiline JS string, and exporting it from an ES6 module requires less escaping, but it's still pretty clunky.

Storing code in a proper source file with its conventional extension has a bunch of benefits that I don't need to list here, but there's no way to access those files (statically). Something like this would be useful:

import shaderSource from "frag.glsl" assert {type: "text"};

While GLSL is a good usecase for a feature request, as it's a web standard, there are lots of less obvious usecases for importing source code and the bodies of other kinds of text file. None are very common, but there would be all kinds of useful corner-cases that this feature would address.


It would also be nice if we could import binaries into an ArrayBuffer too. Something like this:

import arrayBuffer from "mario.rom" assert {type: "data"};

The same data could be stored in a JSON array, but it's a lot less efficient, and there are obvious advantages to storing binary data in its proper file format.

7ombie commented 2 years ago

Maybe moving towards an API that's closer to the Response.Body that fetch resolves to would make sense:

import game from "mario.rom" assert {type: "arrayBuffer"};
ljharb commented 2 years ago

Import assertions do not change what’s imported - they merely cause importing a module to throw, or not.

7ombie commented 2 years ago

Import assertions do not change what’s imported - they merely cause importing a module to throw, or not.

@ljharb - Import assertions change how the code is imported. Without the assertion, the browser will try to execute the JSON text (as JavaScript code). This will be extended when Wasm support is implemented.

How would you import the source code from a GLSL file within the current spec?

ljharb commented 2 years ago

@7ombie that's not correct. the browser doesn't evaluate the module at all when it's JSON and there's no assertion, but whenever it is evaluated, it is evaluated the same. (it may help to know that import assertions for JSON modules are also not required by the spec; the intention was always for non-browsers to not require the assertion when importing JSON)

GeoffreyBooth commented 2 years ago

@7ombie In your examples, what is the value of the imported thing? Like in import game from "mario.rom" assert {type: "arrayBuffer"}, I assume game would be an ArrayBuffer?

In that sense I think this is more like CSS modules, which are part of the HTML spec. If you look at https://web.dev/css-module-scripts/, code like import sheet from './styles.css' assert { type: 'css' } creates a sheet variable of type CSSStyleSheet. But it’s not the assertion that causes the browser to treat the import as CSS; it’s the MIME type of the network request, which in this case needs to be text/css. The request fails unless both the MIME type and assertion are present and correct; the assertion is just a check that the MIME type of the resource is what the importing script expects.

TC39 has chosen to stay out of the business of defining available module types and their accompanying import assertions. You can see in https://html.spec.whatwg.org/multipage/webappapis.html#creating-scripts that the HTML spec has defined types for JavaScript (the default), JSON and CSS (see https://html.spec.whatwg.org/multipage/webappapis.html#creating-a-css-module-script). The types defined here are the standard that browsers follow, and have become the de facto standard that Node and Deno have also chosen to follow.

You can open an issue or PR at https://github.com/whatwg/html to suggest a new type. I would suggest you propose a “data” type with an assertion of "type": "data" and a MIME type of application/octet-stream. I would guess that this would be more likely to be accepted than anything particular to WebGL shaders.

7ombie commented 2 years ago

@GeoffreyBooth - Thank you for the detailed reply. Much appreciated.

It's become pretty confusing what I'm even asking for, and my original post wasn't especially clear, so I wanted to explain in terms of how things currently work, as I understand it, and you can tell me what I'm getting wrong...

When a type assertion is added to an import, the string that defines the type is not specifying a MIME type. It's specifying an abstract import type.

Each import type has one or more associated MIME types, but also specifies other things, like if and how the file should be parsed and executed, which data types will be imported into the module, and under what circumstances et cetera.

The scope is strictly limited to specific import types that will likely expand to eventually include HTML, JavaScript, CSS, JSON and Wasm. I'm saying that we should also support more generic import types that would work with text files and binary data.

The more generic import types could just be text and data, with the data types String and ArrayBuffer, but that may be a little simplistic. Having import types that imply a text encoding or different data type (like Blob) may be required/desired.

I admit I haven't given this much thought. I had a problem with importing some GLSL source (and didn't want my library to become asynchronous just so it can access it own source code), so ended up here. Just leaving a complaint seemed unhelpful, so went with suggesting something as a starting point. Ultimately, I just want to statically import text and data from files.

GeoffreyBooth commented 2 years ago

I wanted to explain in terms of how things currently work, as I understand it, and you can tell me what I’m getting wrong…

Import assertions are only a validation step. They don’t tell the browser how to interpret an import. The MIME type as listed in the Content-Type header defines what a module’s type is. The assertion duplicates this information for the purposes of security. The idea is that an import of JSON, say, shouldn’t turn into JavaScript—and be executed—when the importing script was expecting JSON data. The assertion type isn’t the same as the MIME type; json is for application/json and css is for text/css, for example.

This proposal, and the TC39 spec, define import assertions in the abstract—the assert syntax itself, not which types can be asserted. The types are host-dependent, and therefore are defined in the HTML spec for browsers. (There’s no separate spec covering server-side runtimes like Node and Deno, so they follow the HTML spec for this.) Since WebGL is obviously browser-specific, I think the HTML spec is the place to open an issue. They can guide you through whether the best approach is data or ArrayBuffer or blob or text or whatever.

7ombie commented 2 years ago

Thank you, @GeoffreyBooth. You're a legend. Thanks for the work you do on CoffeeScript too (I used to hang out there, as @carlsmith).

I get it now (finally). I'd conflated various standards processes, and just assumed this repo aught to get to the right folks. Sorry. That was ignorant of me. Thank you for your patience. Much appreciated.

GeoffreyBooth commented 2 years ago

Great, happy to help!