tc39 / proposal-dynamic-import

import() proposal for JavaScript

Home Page:https://tc39.github.io/proposal-dynamic-import/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Error Asymmetry with declaration form

Jamesernator opened this issue · comments

Something that the declarative import form is able to do but the import() form is not able to do is throw errors on missing or ambiguous imports, e.g. these can throw errors with the declaration form:

import foo from "./foo.js"
// SyntaxError: The requested module does not provide an export named 'foo'
import { foo } from "./exportStars.js"
// SyntaxError: The requested module does not provide an export named 'foo'
// SyntaxError: The requested module contains conflicting star exports for name 'foo'

Now you definitely can create functions that do these behaviours e.g.:

async function importDefault(module) {
    const ns = await import(module)
    if (!Reflect.has(ns, 'default')) {
        // It might be useful to actually have the host error message  instead so that it can be known if ambiguous or missing
        throw new SyntaxError(`Some error message`)
    }
    return ns.default
}

async function importNames(names) {
    // Same thing as default but for many
}

However this suffers the same issue as #37 (comment) in that you can't share it around different modules so you wind up having to reimplement it everywhere.

I'm not sure what the solution would be, it might even be a part of the loader API instead but I could imagine something like on the loader issue of having import.names where you can specify names you want.

e.g.

async function validateStudentCode() {
    const { parseJS } = await import.names('/validators.js', ['parseJS'])
}

An alternative could be that the import() form doesn't return a normal Module object but rather another object that has someModule.someNonexistentName be a getter that throws the error that would've happened e.g. (await import('./foo.js')).nonExistentName would throw the same error as import { nonExistentName } from "./foo.js".

This isn't an asymmetry, so much as a deliberate omission. This imperative form only corresponds to namespace imports (i.e. import * as foo from "./foo.js"). On top of that, you can build wrappers or libraries that do any kind of error-checking you want, but that's the lower-level primitive we're interested in providing, since it's the most fundamental. E.g. consider

const { parseJS } = await validate(import("./validators.js"), "parseJS");

(where validate is a function you write yourself, which applies to any object.)

So this is out of scope of this proposal, and IMO not something we need to add to the language. Going to close as this isn't going to lead to actionable change to this proposal, but happy to continue discussing in the closed thread.

Fair enough, I was mainly interested because there's no way to get what the host's error message would've been e.g. Chrome does: // SyntaxError: The requested module contains conflicting star exports for name 'foo' so that errors are easier to track.

Something I wish was the case but is probably too late to change now is that Module Namespace objects had getters that throw errors for those weird "ambiguous" exports instead of silently vanishing them. At current I feel like the vanishing of ambiguous imports is just going to be a weird source of bugs, especially for people dealing with dynamic import because you can only get the Module Namespace object.

Do you have any idea what the original reasoning behind vanishing "ambiguous" imports was? Because I haven't managed to find anything on the matter.

I don't really know, sorry. Maybe someone on the es-discuss mailing list does.