Proposal: https://github.com/tc39/proposal-deferred-reexports
Spec text: https://tc39.es/proposal-deferred-reexports/
Feature flag: deferred-reexports
Stage: 2
Champions: @nicolo-ribaudo, @caiolima
Syntax
Valid syntax:
Early errors (SyntaxError):
Load and Evaluation
Namespace Object
Triggering evaluation via import * as ns:
Property enumeration:
Interaction with import defer * as ns:
Dynamic import
Evaluation Order
Basic ordering (non-deferred before deferred, deferred at end):
Error Propagation
Syntax/resolution errors in deferred source modules:
Runtime errors on source of deferred re-export:
Circular references:
Interaction with Other Features
With top-level await (async modules):
Proposal: https://github.com/tc39/proposal-deferred-reexports
Spec text: https://tc39.es/proposal-deferred-reexports/
Feature flag:
deferred-reexportsStage: 2
Champions: @nicolo-ribaudo, @caiolima
Syntax
Valid syntax:
export defer { x } from "mod"-- single named deferred re-exportexport defer { x, y } from "mod"-- multiple named deferred re-exportsexport defer { x as y } from "mod"-- renamed deferred re-exportexport defer * as ns from "mod"-- deferred namespace re-exportexport defer { x } from "mod" with { type: "json" }-- with import attributesEarly errors (SyntaxError):
export defer * from "mod"-- bare star withoutasis aSyntaxErrorexport defer { x }-- missingfromclause (must be a re-export)export defer function f() {}-- cannot defer local declarationsexport defer default x-- cannot defer default exportexport defer const x = 1-- cannot defer variable declarationsexport deferused outside of a module (script context)Load and Evaluation
export defer { x } from "./dep.js"where no consumer importsx--dep.jsis never loaded and evaluatedexport defer { x } from "./dep.js"where a consumer importsx--dep.jsis loaded and evaluatedexport defer: A hasexport defer { x } from "./B.js", B hasexport defer { x } from "./C.js"-- C is only evaluated whenxis actually imported/accessed by the consumer of Aexport defer { x } from "./dep.js"where a consumer re-exportsxwithout deferexport {x} from ...and there IS a consumer ofxon entrypoint. It should trigger load and evaluation ofdep.jsexport defer { x } from "./dep.js"where a consumer re-exportsxwithout deferexport {x} from ...and there IS NO consumer ofxon entrypoint. It should trigger load and evaluation ofdep.jsNamespace Object
Triggering evaluation via
import * as ns:import * as ns from "./reexport.js"where reexport hasexport defer { x } from "./dep.js". Ir eargerly loadsdep.js, and accessingns.xtriggers evaluation ofdep.jsand returns the correct valueimport * as ns from "./reexport.js"where reexport hasexport defer { x } from "./middle.js"and middle hasexport defer { x } from "./deep.js". Accessingns.xtriggers evaluation through the chainTypeErrorimport * as ns from "./reexport.js", where reexport hasexport defer { x } from "./dep.js``, and dep has syntax error, it eagerly throwsSyntaxError, sincedep.js` will be loadedimport * as ns from "./reexport.js", where reexport has transitive export defer chain and source has syntax error, it eagerly throwsSyntaxError, sincesource.jswill be loadedProperty enumeration:
Reflect.ownKeys(ns)includes names fromexport deferdeclarations if it comes fromimport * as ns ...Reflect.ownKeys(ns)includes names fromexport deferdeclarations if it comes fromimport defer * as ns ...Reflect.ownKeys(ns)includes names fromexport deferdeclarations if it comes fromimport("mod")Reflect.ownKeys(ns)includes names fromexport deferdeclarations if it comes fromimport.defer("mod")Interaction with
import defer * as ns:import defer * as ns from "./reexport.js"where reexport usesexport defer { x } from "./dep.js"-- accessingns.xtriggers evaluation of both the reexport and the deferred dependency, returns the correct valueimport defer * as ns from "./reexport.js"-- accessing a non-deferred binding onnstriggers evaluation of the reexport but NOT evaluation of deferred dependenciesimport defer * as ns from "./reexport.js"where reexport hasexport defer--Reflect.ownKeys(ns)triggers reexport evaluation but does NOT trigger evaluation of deferred modulesLoad test variants that we have on
import * as ns ...Dynamic import
import("./reexport.js")where reexport usesexport deferdoesn't trigger evaluation of deferred exports (it's similar toimport * as ns from "reexport.js"). The evaluation happens once the deferred re-export is acessed.import("./reexport.js")where reexport usesexport defer {x} from "dep.js", and dep hasSyntaxError, it will result inSyntaxError, sincedep.jswill be loadedimport.defer("./reexport.js")where reexport usesexport deferdoesn't trigger evaluation of deferred exports (it's similar toimport defer * as ns from "reexport.js"). The evaluation happens once the deferred re-export is acessed, and both the deferred module and the chain from deferred re-exports gets evaluatedimport.defer("./reexport.js")where reexport usesexport defer {x} from "dep.js", and dep hasSyntaxError, it will result inSyntaxError, sincedep.jswill be loadedexport defer * as ns from "./dep.js"-- deferred namespace re-export works correctlyEvaluation Order
Basic ordering (non-deferred before deferred, deferred at end):
export { b } from "./b.js"; export defer { a } from "./a.js";and a entrypoint that importsaandb, the order should be:b.js-> barrel ->a.js-> entrypointexport defer { x } from "./async-dep.js"whereasync-dep.jsuses top-level awaitexport defer { x } from "./dep.js"combined withexport { y } from "./dep.js"for the same module but different bindingsError Propagation
Syntax/resolution errors in deferred source modules:
SyntaxError, but its reexport is never used, the program executes properly, because it will never be loadedSyntaxError, and its reexported is used, the program throwsSyntaxErrorSyntaxError, and its reexport is imported via a namespace object, the program throwsSyntaxErrorbecause source will be loadedRuntime errors on source of deferred re-export:
x, and they are imported as 2 different namespace objectsimport * ns1 from "reexport1"; import * ns2 from "reexport2";and there's an exception thrown on evaluation, both accessns1.xandns2.xshould throw the same exception.Circular references:
export deferforming a cycle with its consumer throws aSyntaxErrorSyntaxErrorif it's importedSyntaxErrorif it IS NOT imported, since it's never loaded.Interaction with Other Features
With top-level await (async modules):