Giter VIP home page Giter VIP logo

proposal-weak-imports's Introduction

ECMAScript proposal: Weak Imports

Champion: TBD

Status: Stage 0

Synopsis

Syntax for weakly importing module bindings without named exports validation errors.

Motivation

Module Upgrade Paths

Since imported bindings are always strict, modules with changing APIs through new exports added to their interfaces require detection using the import * as M syntax.

For example, consider a module implementing both oldFeature and newFeature:

import { oldFeature, newFeature } From 'module';

We would like the ability to use oldFeature, but only use newFeature if it is available, but if we use the old version of the module without the feature, an error will be thrown in the environment, meaning users instead must write:

import * as M from 'module';
if (M.newFeature) // ...

This forces users away from the more explicit syntax of ES modules into an object pattern to support these use cases.

Empty Modules

Consider the case where 'module' itself was not resolved at all in the target environment (eg a missing standard library).

In this case, the fallback mechanism in the environment might in some cases want to provide an empty module implementation to allow the import to resolve, but without any exports present.

The issue again, is that the empty module would have no named exports, so any package we might not resolve will require switching to the object import form.

Web Assembly

Web Assembly needs to support these same types of conditional upgrade paths with very similar reasons to the above (see discussion at WebAssembly/design#1281).

In the ES Module integration of Web Assembly, custom logic might be created to handle and populate weak imports.

It could be beneficial to work towards a linking-level primitive that sits at a higher level than both systems.

Solution: Weak Imports

Weak imports allow us to continue to rely on the strong semantics of named imports, while catering for upgrade paths and empty modules in certain cases:

import { feature, weak newFeature } from 'module';

When the import is found, it is resolved like any other binding. But if the binding was not resolved, instead of throwing an error we populate the binding as an undefined value that can be tested directly:

import { feature, weak newFeature } from 'module';

if (newFeature) newFeature();

// *undefined* in the weak unresolved case
typeof newFeature

FAQ

Shouldn't the weak import binding be uninitialized?

If the weak import binding were to be uninitialized, there would be no way to determine if it is defined or not, since typeof x for an unintialized binding will always throw.

Shouldn't the weak import binding be a special binding type?

We could define a new primitive value representing an empty weak import binding, and define its behaviours through the ECMAScript primitive handling, but adding a new primitive value to the language is not a process that should be taken lightly, and avoided if possible. If there were more general uses for the concept of weak bindings, this approach could certainly be explored further.

Will weak imports encourage looser use of ES Module semantics?

The goal of weak imports is exactly the converse - to ensure that the strong semantics of import bindings can be retained, despite variations of export shape and availability between environments.

Why not use dynamic import?

Using dynamic import as a way around this problem is the other natural solution for feature detections. Code like:

const { feature } = await import('maybe-module');
if (feature) feature();

The problem is that this is just as loose semantically as using import * as M for these use cases. In addition we're no longer able to treat the modules as direct static dependencies, and lose the live bindings properties as well. Furthermore, it either harms ergonomics by preventing top-level code from conditionally branching on feature detection, or it leads to unfortunate proliferation of top-level await.

Specification

References

proposal-weak-imports's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

robpalme

proposal-weak-imports's Issues

Alternative syntax proposal

In addition to what was said in #3, I think something like the following would be a lot simpler, at least for the main use-case laid out in the proposal

Edit: after further thought I don't think providing a fallback in the import statement is a good idea at all, but I stand by the ? syntax below.

import { newFeature or oldFeature } From 'module';

This could also avoid unnecessary imports.

However, in the case that this is unrealistic or unpopular, I think borrowing from the optional chaining syntax should be the way forward:

import { oldFeature, newFeature?, somethingElse } From 'module';

if (newFeature) {
  newFeature();
}

Will the caller be able to tell why a value is 'undefined'?

In the example from the explainer, is it possible for the caller to tell why a value is undefined?

import { feature, weak newFeature } from 'module';

if (newFeature) newFeature();

// *undefined* in the weak unresolved case
typeof newFeature

undefined may be a legitimate value for a successfully resolved module binding. This may even be a common case, since the binding is live.

name bikeshedding: overloaded meaning of 'weak'

'weak' is already used in JS to indicate the GC semantics:

import { weak WeakEventEmitter } from 'module';

const weakMap = new WeakMap();
const weakSet = new WeakSet();
const weakRef = new WeakRef();

Is there another keyword we can use, such as:

  1. 'optional'
  2. 'canFail'

in general, there's a risk of weird-looking stuff when the specifier has the same name as a binding:

import { optional optional } from 'module';

So perhaps special syntax that doesn't look like an identifier would be help. @HughCrail suggested that nullish-coalescing-style syntax might work. import { foo ?? "could not import" } from "foo";

related: #2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.