proposal-source-phase-imports icon indicating copy to clipboard operation
proposal-source-phase-imports copied to clipboard

ambiguity with module type

Open trusktr opened this issue 1 year ago • 11 comments

import source mod from './foo.dope'

if (mod instanceof WebAssembly.Module) console.log('a')
else if (mod instanceof ModuleSource) console.log('b')

Does this log "a" or "b"?

It seems like import attributes are required, otherwise if they are not, then that begs the question as to why import attributes are even required in the first place for importing anything other than JavaScript.

This seems to make sense:

import source mod from './foo.dope'

if (mod instanceof WebAssembly.Module) console.log('a')
else if (mod instanceof ModuleSource) console.log('b')  // THIS ONE
import source mod from './foo.dope' with { type: "wasm" }

if (mod instanceof WebAssembly.Module) console.log('a') // THIS ONE
else if (mod instanceof ModuleSource) console.log('b')

Otherwise without import attributes, we are saying that it is ok to determine the type of a module based on the file name, which we've already determined we do not want to do in the import attributes spec and that's why with { type: "whatever" } is required.

Seems there is a contradiction.

trusktr avatar Feb 12 '25 07:02 trusktr

import attributes are often/expected-to-be required (but it's up to the host) when you want to ensure that the thing you're importing can't execute code (like json, css, html, etc).

JS and WASM both don't/won't require an attribute because they both execute code.

As such, it could log a or b depending on how the engine interprets the specifier './foo.dope', and what format it loads. It is 100% always OK for a host to determine the type of a module based on the specifier, and always has been.

ljharb avatar Feb 12 '25 07:02 ljharb

Does a browser then need to look at content-type headers?

And if so, then what's the point of import attributes if the browser can look at content-type?

trusktr avatar Feb 12 '25 07:02 trusktr

Yes, absolutely a browser always does, with or without import attributes.

The point is so that the source code can embed a constraint that the content-type must match.

ljharb avatar Feb 12 '25 07:02 ljharb

It seems then that the import attribute should just be the ultimate source of truth, so that from './foo.dope' with { type: "wasm" } will fail if a JS content-type is returned.

Just thinking as an end user, it seems arbitrary to not enforce the type for some types of files (except for, say, JS files, as that is the host language).

trusktr avatar Feb 12 '25 07:02 trusktr

If that was a valid type, then yes, it would. But you shouldn’t be caring about the module format - only whether it executes code or not.

ljharb avatar Feb 12 '25 07:02 ljharb

I hear what you are saying, but people will intuit the following:

  • import foo from 'foo' - "treat it as a JS file (error at me if it is not)"
  • import foo from 'foo' with { type: 'json' } - "treat it as a JSON file (error at me if it is not)"

and then by intuitive extension, one would think of this:

  • import foo from 'foo' with { type: 'wasm' } - "treat it as a Wasm file (error at me if it isn't)"

See what I'm saying?

As the "end user", I don't think about if it executes code or not, but that I get the expected result based on what I want, and being able to be explicit about what I want in a consistent way would be welcome and intuitive.

trusktr avatar Feb 12 '25 07:02 trusktr

Perhaps, but since wasm isn’t a valid type value, you’ll learn the proper mental model quickly.

ljharb avatar Feb 12 '25 07:02 ljharb

@trusktr When this was discussed in the wasm proposal to allow importing wasm, the conclusion in that venue is that wasm imports do not require the attribute because they are free to import JS modules anyway, so the attribute would give no guarantee about what is running.

import "./mod.wasm";
(module
  (import "./foo.js" "myFunc" (func $log (param i32))))

Not having the attribute also allows libraries to swap a JS implementation with a potentially faster wasm one.

Imports are already not limited to JavaScript, think for example of builtin modules or .node files.

nicolo-ribaudo avatar Feb 12 '25 08:02 nicolo-ribaudo

Perhaps, but since wasm isn’t a valid type value, you’ll learn the proper mental model quickly.

yes, and it also won't make sense. Sure, I'll learn what to do, but not really why.

trusktr avatar Feb 24 '25 19:02 trusktr

Not having the attribute also allows libraries to swap a JS implementation with a potentially faster wasm one.

I get what you're saying. By this same token, this seems fine too then:

import whatever from 'foo'
console.log(whatever)

where foo can be a JS file or a JSON file. Either type of file can export the same thing (for example an object).

trusktr avatar Feb 24 '25 19:02 trusktr

Not having the attribute also allows libraries to swap a JS implementation with a potentially faster wasm one.

@nicolo-ribaudo I can imagine wanting to switch from a JSON file to a JS file to import a dynamic vs a static config. I don't want the import attribute for JSON.

Or maybe I want to replace a foo.css module with a JS module for something more dynamic while importing the same API, but attributes prevent that.

Why not an optional {execute: false} attribute for both JSON and CSS modules (and future new types of modules) for people who want to forbid userland code execution regardless of the mime type?

Basically this is confusing, not only to users, but to runtime authors: there is seemingly no clear definition of what import attributes are, and how they are supposed to be used when a runtime defines new types of modules.

It also isn't clear why a standard set of attributes isn't defined for certain types of modules (or all types of modules), for example

import foo from 'foo.wasm' with { phase: 'source' }

Why a whole new syntax???

I get that the new proposed syntax is shorter. But fundamentally it is still ambiguous with import attributes.


When browsers come out with HTML modules, should they have import attributes? Or not?

import MyElement from `./my-element.html`

customElements.define('my-el', MyElement)

When should import attributes be used and for what, when defining new types of modules, besides assertion of file type?

Is it arbitrary, as in a runtime can decide what to do with the attributes, and for example the decision for type:'json' to be a mime type assertion happens to be arbitrary for that specific case and happens to be standardized?

Can you give examples of hypothetical new types of modules with and without attributes, and why each one would have or not have attributes?

trusktr avatar Mar 18 '25 18:03 trusktr