Status
This document is a work in progress and dreams of becoming a living standard.
1. Module Loading
This section is non-normative.
1.1. Introduction
Throughout their development, JavaScript modules have been divided into two general areas:
- The authoring format, which defines the importing and exporting syntax, as well as the semantics for variable bindings and cycles.
- The JavaScript Loader, which provides a pipeline for on-demand, asynchronous loading of JavaScript modules.
The authoring format was carefully designed to support pre-compilation (like Browserify) and on-demand asynchronous loading (like AMD). It defines the minimal syntax necessary to allow people to write portable modules that can work across different platforms, most notably Node.js and web browsers.
The JavaScript Loader allows host environments, like Node.js and browsers, to fetch and load modules on demand. It provides a hookable pipeline, to allow front-end packaging solutions like Browserify, WebPack and jspm to hook into the loading process.
This division provides a single format that developers can use in all JavaScript environments, and a separate loading mechanism for each environment. For example, a Node Loader would load its modules from the file system, using its own module lookup algorithm, while a Browser Loader would fetch modules and use browser-supplied packaging formats.
JavaScript itself, in ECMAScript 2015, defines the module syntax and the "linking semantics" between modules. When a module is requested, it delegates responsibility for loading the module to the host environment. The Loader defines how host environments can allow JavaScript code to configure that process.
The primary goal is to make as much of this process as possible consistent between Node and Browser environments. For example, if a JavaScript program wants to translate .coffee
files to JavaScript on the fly, the Loader defines a "translate" hook that can be used. This allows programs to participate in the loading process, even though some details (specifically, the process of getting a particular module from its host-defined storage) will be different between environments.
1.2. Loader Pipeline
TODO: include pipeline diagram
2. Conventions
2.1. Well-Known Symbols
Well-known symbols are built-in Symbol values that are explicitly referenced by algorithms of this specification. They are typically used as the keys of properties whose values serve as extension points of a specification algorithm.
Within this specification a well-known symbol is referred to by using a notation of the form @@name, where "name" is one of the values listed in table below:
Specification Name | [[Description]] | Value and Purpose |
---|---|---|
@@resolve | "Reflect.Loader.resolve" | A function valued property that is the resolve hook function of loader’s instances. |
@@fetch | "Reflect.Loader.fetch" | A function valued property that is the fetch hook function of loader’s instances. |
@@translate | "Reflect.Loader.translate" | A function valued property that is the translate hook function of loader’s instances. |
@@instantiate | "Reflect.Loader.instantiate" | A function valued property that is the instantiate hook function of loader’s instances. |
2.2. Well-Known Intrinsic Objects
Well-known intrinsics are built-in objects that are explicitly referenced by the algorithms of this specification and which usually have realm-specific identities. Unless otherwise specified each intrinsic object actually corresponds to a set of similar objects, one per realm.
Within this specification a reference such as %name% means the intrinsic object, associated with the current realm, corresponding to the name. Determination of the current realm and its intrinsics is described in ES2015, 8.3.
2.3. Promises
This spec makes heavy use of promises, and adopts the notational conventions established in the promises guide.
2.3.1. Reacting to Promises
Transforming p with a new pass-through promise is a shorthand for wrapping the promise to avoid exposing the original promise. It represents the following step:
- Transforming p with a fulfillment handler that, when called with argument value, returns value.
2.4. Shorthand Phrases
2.4.1. RejectIfAbrupt(x)
Algorithm steps that say
- RejectIfAbrupt(x).
mean the same thing as:
- If x is an abrupt completion, return a promise rejected with x.[[Value]].
- Else if x is a Completion Record, then let x be x.[[Value]].
2.5. Common Operations
2.5.1. CreateObject()
- Let obj be ObjectCreate(%ObjectPrototype%).
- Return obj.
2.5.2. SimpleDefine(obj, name, value)
- Let desc be a new PropertyDescriptor record {[[Value]]: value, [[Writable]]:
true , [[Enumerable]]:true , [[Configurable]]:true }. - Return ? OrdinaryDefineOwnProperty(obj, name, desc).
2.6. Built-in Function Objects
We follow the ECMA-262 convention for built-in function objects in which the value of NewTarget in each function argument is **undefined** for [[Call]] and the newTarget parameter for [[Construct]].
3. Loader Objects
3.1. The Loader Constructor
The Loader constructor is the %Loader% intrinsic object and the initial value of the Loader property of the the Reflect object. When called as a constructor it creates and initializes a new Loader object. When Loader is called as a function rather than as a constructor, it throws an exception.
The Loader constructor is designed to be subclassable. It may be used as the value of an extends clause of a class definition. Subclass constructors that intend to inherit the specified Loader behaviour must include a super call to the Loader constructor to create and initialize the subclass instance with the corresponding internal slots.
3.1.1. Loader()
When Loader is called with no arguments, the following steps are taken:
- If NewTarget is
undefined , then throw aTypeError exception. - Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%LoaderPrototype%", «[[Realm]], [[Registry]]»).
- Set O’s [[Realm]] internal slot to current Realm Record.
- Set O’s [[Registry]] internal slot to CreateRegistry().
- Return O.
3.2. Properties of the Loader Constructor
The value of the [[Prototype]] internal slot of the Loader constructor is the intrinsic object %FunctionPrototype%.
The Loader constructor has the following properties:
3.2.1. Loader.prototype
The initial value of Loader.prototype is the intrinsic object %LoaderPrototype%.
This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.
3.3. Properties of the Loader Prototype Object
3.3.1. Loader.prototype.constructor
The initial value of Loader.prototype.constructor is the intrinsic object %Loader%.
3.3.2. Loader.prototype.import(name[, referrer])
The following steps are taken:
- Let loader be
this value. - If Type(loader) is not Object, throw a
TypeError exception. - If loader does not have all of the internal slots of a Loader Instance (3.5), throw a
TypeError exception. - Return the result of transforming Resolve(loader, name, referrer) with a fulfillment handler that, when called with argument key, runs the following steps:
- Let entry be EnsureRegistered(loader, key).
- Return the result of transforming LoadModule(entry, "instantiate") with a fulfillment handler that, when called, runs the following steps:
- Return EnsureEvaluated(entry).
3.3.3. Loader.prototype.resolve(name[, referrer])
The following steps are taken:
3.3.4. Loader.prototype.load(name[, referrer[, stage]])
The following steps are taken:
- Let loader be
this value. - If Type(loader) is not Object, throw a
TypeError exception. - If loader does not have all of the internal slots of a Loader Instance (3.5), throw a
TypeError exception. - If stage is
undefined then let stageValue be "instantiate". - Else let stageValue be ToString(stage).
- RejectIfAbrupt(stageValue).
- If IsValidStageValue(stageValue) is
false , return a promise rejected with a newRangeError exception. - Return the result of transforming Resolve(loader, name, referrer) with a fulfillment handler that, when called with argument key, runs the following steps:
- Let entry be EnsureRegistered(loader, key).
- Return LoadModule(entry, stageValue).
3.3.5. get Loader.prototype.registry
Loader.prototype.registry
is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:
3.3.6. Loader.prototype [ @@toStringTag ]
The initial value of the @@toStringTag property is the String value "Object".
This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
3.4. Properties of Loader Instances
Loader instances are ordinary objects that inherit properties from the *Loader.prototype*.
Loader instances are initially created with the internal slots described in the following table:
Internal Slot | Value Type (non-normative) | Description (non-normative) |
---|---|---|
[[Realm]] | Realm Record | The realm this loader belongs to. |
[[Registry]] | An object | An instance of Registry. |
4. Registry Objects
4.1. Abstract Operations for Registry Objects
4.1.1. CreateRegistry()
The abstract operation CreateRegistry with no arguments performs the following steps:
- Let O be ? OrdinaryCreateFromConstructor(Registry, "%RegistryPrototype%", «[[RegistryMap]]» ).
- Let M be ObjectCreate(%MapIteratorPrototype%, «[[Map]], [[MapNextIndex]], [[MapIterationKind]]»).
- Set O’s [[RegistryMap]] internal slot to M.
- Return O.
4.2. The Registry Constructor
The Registry constructor is the %Registry% intrinsic object. It is not intended to be called as a function or as a constructor and will always throw an exception.
4.3. Properties of the Registry Constructor
The value of the [[Prototype]] internal slot of the Registry constructor is the intrinsic object %FunctionPrototype%.
The Registry constructor has the following properties:
4.3.1. Registry.prototype
The initial value of Registry.prototype is %RegistryPrototype%.
This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.
4.4. Properties of the Registry Prototype Object
4.4.1. Registry.prototype.constructor
The initial value of the constructor property of the %RegistryPrototype% object is the %Registry% object.
4.4.2. Registry.prototype[ @@iterator ]()
The initial value of the @@iterator property is the same function object as the initial value of the entries property.
The value of the name property of this function is "[Symbol.iterator]".
4.4.3. Registry.prototype.entries()
The following steps are taken:
4.4.4. Registry.prototype.keys()
The following steps are taken:
4.4.5. Registry.prototype.values()
The following steps are taken:
4.4.6. Registry.prototype.get(key)
The following steps are taken:
- Let registry be
this value. - If Type(registry) is not Object, throw a
TypeError exception. - If registry does not have all of the internal slots of a Registry Instance (4.4), throw a
TypeError exception. - Let M be registry.[[RegistryMap]].
- Let entries be the List that is the value of M’s [[MapData]] internal slot.
- Repeat for each Record {[[key]], [[value]]} p that is an element of entries,
- If p.[[key]] is not empty and SameValueZero(p.[[key]], key) is
true , return p.[[value]].
- If p.[[key]] is not empty and SameValueZero(p.[[key]], key) is
- Return
undefined .
4.4.7. Registry.prototype.set(key, entry)
The following steps are taken:
- Let registry be
this value. - If Type(registry) is not Object, throw a
TypeError exception. - If registry does not have all of the internal slots of a Registry Instance (4.4), throw a
TypeError exception. - If Type(entry) is not Object, throw a
TypeError exception. - If entry does not have all of the internal slots of a ModuleStatus Instance (5.5), throw a
TypeError exception. - Let M be registry.[[RegistryMap]].
- Let entries be the List that is the value of M’s [[MapData]] internal slot.
- Repeat for each Record {[[key]], [[value]]} p that is an element of entries,
- If p.[[key]] is not empty and SameValueZero(p.[[key]], key) is
true , then- Set p.[[value]] to entry.
- Return registry.
- If p.[[key]] is not empty and SameValueZero(p.[[key]], key) is
- Let p be the Record {[[key]]: key, [[value]]: entry}.
- Append p as the last element of entries.
- Return registry.
4.4.8. Registry.prototype.has(key)
The following steps are taken:
- Let registry be
this value. - If Type(registry) is not Object, throw a
TypeError exception. - If registry does not have all of the internal slots of a Registry Instance (4.4), throw a
TypeError exception. - Let M be registry.[[RegistryMap]].
- Let entries be the List that is the value of M’s [[MapData]] internal slot.
- Repeat for each Record {[[key]], [[value]]} p that is an element of entries,
- If p.[[key]] is not empty and SameValueZero(p.[[key]], key) is
true , then, returntrue .
- If p.[[key]] is not empty and SameValueZero(p.[[key]], key) is
- Return
false .
4.4.9. Registry.prototype.delete(key)
The following steps are taken:
- Let registry be
this value. - If Type(registry) is not Object, throw a
TypeError exception. - If registry does not have all of the internal slots of a Registry Instance (4.4), throw a
TypeError exception. - Let M be registry.[[RegistryMap]].
- Let entries be the List that is the value of M’s [[MapData]] internal slot.
- Repeat for each Record {[[key]], [[value]]} p that is an element of entries,
- If p.[[key]] is not empty and SameValueZero(p.[[key]], key) is
true , then- Set p.[[key]] to empty.
- Set p.[[value]] to empty.
- Return
true .
- If p.[[key]] is not empty and SameValueZero(p.[[key]], key) is
- Return
false .
4.5. Properties of Registry Instances
Registry instances are ordinary objects that inherit properties from the %RegistryPrototype%.
Registry instances are initially created with the internal slots described in the following table:
Internal Slot | Value Type (non-normative) | Description (non-normative) |
---|---|---|
[[RegistryMap]] | The Map object of pairs of String and module status. | The registry of installed modules. |
5. ModuleStatus Objects
5.1. Abstract Operations for ModuleStatus Objects
5.1.1. GetCurrentStage(entry)
The abstract operation GetCurrentStage with argument entry performs the following steps:
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Let stages be entry.[[Pipeline]].
- Return the first element of stages.
5.1.2. IsValidStageValue(stage)
The abstract operation IsValidStageValue with argument stage performs the following steps:
- Assert: Type(stage) is String.
- If stage is "fetch", "translate" or "instantiate", return
true . - Else return
false .
5.1.3. GetStage(entry, stage)
The abstract operation GetStage with arguments entry and stage performs the following steps:
5.1.4. LoadModule(entry, stage)
The abstract operation LoadModule with arguments entry and stage performs the following steps:
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Assert: Type(stage) is String.
- Assert: stage is a valid stage value.
- If stage is "fetch", then:
- Return the result of transforming RequestFetch(entry) with a new pass-through promise.
- If stage is "translate", then:
- Return the result of transforming RequestTranslate(entry) with a new pass-through promise.
- If stage is "instantiate", then:
- Return the result of transforming RequestInstantiate(entry) with a new pass-through promise.
- Return a promise rejected with a new
RangeError exception.
5.1.5. UpgradeToStage(entry, stage)
The abstract operation UpgradeToStage with arguments entry and stage performs the following steps:
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Assert: Type(stage) is String.
- Let pipeline be entry.[[Pipeline]].
- Let stageEntry be GetStage(entry, stage).
- If stageEntry is not
undefined , then- Repeat while the first element of pipeline is not equal to stageEntry
- Remove first element from pipeline.
- Repeat while the first element of pipeline is not equal to stageEntry
An stage is a record with the following fields:
Internal Slot | Value Type (non-normative) | Description (non-normative) |
---|---|---|
[[Stage]] | "fetch" , "translate" , "instantiate"
| A constant value to indicating which phase the entry is at. |
[[Result]] | Promise or undefined
| A promise for the stage entry. |
Each [[Stage]] value indicates the currently pending operation. If the [[Result]] field is *undefined*, the operation has not been initiated; if the [[Result]] field is a promise, the operation has been initiated but not completed. Once a stage completes, its Stage Entry is removed from the pipeline. The following table describes the intended purpose of each stage of the pipeline:
Value | Description (non-normative) |
---|---|
"fetch"
| fetching the requested module (e.g. from a filesystem or network) |
"translate"
| translating the fetched source (as via a preprocessor or compiler) |
"instantiate"
| instantiating the translated source as a Module Record |
5.2. The ModuleStatus Constructor
The ModuleStatus constructor is the %ModuleStatus% intrinsic object and the initial value of the Status property of the Reflect.Module object. When called as a constructor it creates and initializes a new ModuleStatus object. When ModuleStatus is called as a function rather than as a constructor, it throws an exception.
The ModuleStatus constructor is designed to be subclassable. It may be used as the value of an extends clause of a class definition. Subclass constructors that intend to inherit the specified ModuleStatus behaviour must include a super call to the ModuleStatus constructor to create and initialize the subclass instance with the corresponding internal slots.
5.2.1. ModuleStatus(loader, key, ns)
When ModuleStatus is called with arguments loader, key and ns, the following steps are taken:
- If NewTarget is
undefined , then throw aTypeError exception. - If Type(loader) is not Object, throw a
TypeError exception. - If loader does not have all of the internal slots of a Loader Instance (3.5), throw a
TypeError exception. - Let keyString be ? ToString(key).
- Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%ModuleStatusPrototype%", «[[Loader]], [[Pipeline]], [[Key]], [[Module]], [[Metadata]], [[Dependencies]], [[Error]]» ).
- Let pipeline be a new List.
- If ns is
undefined , then:- Let module be
undefined . - Let deps be
undefined . - Add new stage entry record { [[Stage]]: "fetch", [[Result]]:
undefined } as a new element of the list pipeline. - Add new stage entry record { [[Stage]]: "translate", [[Result]]:
undefined } as a new element of the list pipeline. - Add new stage entry record { [[Stage]]: "instantiate", [[Result]]:
undefined } as a new element of the list pipeline.
- Let module be
- Else,
- If ns is not a module namespace exotic object, throw a
TypeError exception. - Let module be ns.[[Module]].
- Let deps be a new empty List.
- Assert: module is a Module Record.
- Assert: All [[RequestedModule]] of module are safistied.
- Let result be a promise resolved with ns.
- Add new stage entry record { [[Stage]]: "instantiate", [[Result]]: result } as a new element of the list pipeline.
- If ns is not a module namespace exotic object, throw a
- Set O’s [[Loader]] internal slot to loader.
- Set O’s [[Pipeline]] internal slot to pipeline.
- Set O’s [[Key]] internal slot to keyString.
- Set O’s [[Module]] internal slot to module.
- Set O’s [[Metadata]] internal slot to
undefined . - Set O’s [[Dependencies]] internal slot to deps.
- Set O’s [[Error]] internal slot to
false . - Return O.
5.3. Properties of the ModuleStatus Constructor
The value of the [[Prototype]] internal slot of the Registry constructor is the intrinsic object %FunctionPrototype%.
The ModuleStatus constructor has the following properties:
5.3.1. ModuleStatus.prototype
The initial value of ModuleStatus.prototype is the intrinsic object %ModuleStatusPrototype%.
This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.
5.4. Properties of the ModuleStatus Prototype Object
5.4.1. ModuleStatus.prototype.constructor
The initial value of ModuleStatus.prototype.constructor is the intrinsic object %ModuleStatus%.
5.4.2. get ModuleStatus.prototype.stage
ModuleStatus.prototype.stage
is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:
- Let entry be
this value. - If Type(entry) is not Object, throw a
TypeError exception. - If entry does not have all of the internal slots of a ModuleStatus Instance (5.5), throw a
TypeError exception. - Let stageEntry be GetCurrentStage(entry).
- Return stageEntry.[[Stage]].
5.4.3. get ModuleStatus.prototype.originalKey
ModuleStatus.prototype.originalKey
is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:
5.4.4. get ModuleStatus.prototype.module
ModuleStatus.prototype.module
is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:
- Let entry be
this value. - If Type(entry) is not Object, throw a
TypeError exception. - If entry does not have all of the internal slots of a ModuleStatus Instance (5.5), throw a
TypeError exception. - Let module be entry.[[Module]].
- If module is a Module Record, return GetModuleNamespace(module).
- Return
undefined .
5.4.5. get ModuleStatus.prototype.error
ModuleStatus.prototype.error
is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:
5.4.6. get ModuleStatus.prototype.dependencies
ModuleStatus.prototype.dependencies
is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:
- Let entry be
this value. - If Type(entry) is not Object, throw a
TypeError exception. - If entry does not have all of the internal slots of a ModuleStatus Instance (5.5), throw a
TypeError exception. - Let array be ArrayCreate(0).
- Let n be 0.
- For each pair in entry.[[Dependencies]], do:
- Let O be ObjectCreate(%ObjectPrototype%).
- Let requestNameDesc be the PropertyDescriptor{[[Value]]: pair.[[RequestName]], [[Writable]]:
false , [[Enumerable]]:true , [[Configurable]]:false }. - Perform ? DefinePropertyOrThrow(O, "requestName", requestNameDesc).
- Let moduleStatusDesc be the PropertyDescriptor{[[Value]]: pair.[[ModuleStatus]], [[Writable]]:
false , [[Enumerable]]:true , [[Configurable]]:false }. - Perform ? DefinePropertyOrThrow(O, "entry", moduleStatusDesc).
- Perform ? CreateDataProperty(array, ? ToString(n), O).
- Increment n by 1.
- Return array.
5.4.7. ModuleStatus.prototype.load(stage)
The following steps are taken:
- Let entry be
this value. - If Type(entry) is not Object, return a promise rejected with a new
TypeError exception. - If entry does not have all of the internal slots of a ModuleStatus Instance (5.5), return a promise rejected with a new
TypeError exception. - If stage is
undefined , let stageValue be "fetch"; otherwise, let stageValue be ToString(stage). - RejectIfAbrupt(stageValue).
- If IsValidStageValue(stageValue) is
false , return a promise rejected with a newRangeError exception. - Return LoadModule(entry, stageValue).
5.4.8. ModuleStatus.prototype.result(stage)
The following steps are taken:
- Let entry be
this value. - If Type(entry) is not Object, return a promise rejected with a new
TypeError exception. - If entry does not have all of the internal slots of a ModuleStatus Instance (5.5), return a promise rejected with a new
TypeError exception. - Let stageValue be ToString(stage).
- RejectIfAbrupt(stageValue).
- If IsValidStageValue(stageValue) is
false , return a promise rejected with a newRangeError exception. - Let stageEntry be GetStage(entry, stageValue).
- If stageEntry is
undefined , return a promise resolved withundefined . - If stageEntry.[[Result]] is
undefined , return a promise resolved withundefined . - Return the result of transforming stageEntry.[[Result]] with a new pass-through promise.
5.4.9. ModuleStatus.prototype.resolve(stage, result)
The following steps are taken:
- Let entry be
this value. - If Type(entry) is not Object, return a promise rejected with a new
TypeError exception. - If entry does not have all of the internal slots of a ModuleStatus Instance (5.5), return a promise rejected with a new
TypeError exception. - Let stageValue be ToString(stage).
- RejectIfAbrupt(stageValue).
- If IsValidStageValue(stageValue) is
false , return a promise rejected with a newRangeError exception. - Let stageEntry be GetStage(entry, stageValue).
- If stageEntry is
undefined , return a promise rejected with a newTypeError exception. - Perform UpgradeToStage(entry, stageValue).
- Let p0 be the result of transforming result with a new pass-through promise.
- Let p1 be the result of transforming p0 with a fulfillment handler that, when called with argument value, runs the following steps:
- If stageValue is "instantiate", then:
- Return the result of transforming SatisfyInstance(entry, value,
undefined ,undefined ) with a fulfillment handler that, when called with value instance, runs the following steps:- Set entry.[[Module]] to instance.
- Let stageEntry be GetStage(entry, stageValue).
- Assert: stageEntry is not
undefined . - Fulfill stageEntry.[[Result]] with value.
- Return the result of transforming SatisfyInstance(entry, value,
- Else,
- Let stageEntry be GetStage(entry, stageValue).
- If stageEntry is
undefined , throw a newTypeError . - Fulfill stageEntry.[[Result]] with value.
- If stageValue is "instantiate", then:
- Let pCatch be the result of transforming p1 with a rejection handler that, when called, runs the following steps:
- Set entry.[[Error]] to
true .
- Set entry.[[Error]] to
- If stageEntry.[[Result]] is
undefined , set stageEntry.[[Result]] to p1. - Return p1.
5.4.10. ModuleStatus.prototype.reject(stage, error)
The following steps are taken:
- Let entry be
this value. - If Type(entry) is not Object, return a promise rejected with a new
TypeError exception. - If entry does not have all of the internal slots of a ModuleStatus Instance (5.5), return a promise rejected with a new
TypeError exception. - Let stageValue be ToString(stage).
- RejectIfAbrupt(stageValue).
- If IsValidStageValue(stageValue) is
false , return a promise rejected with a newRangeError exception. - Let stageEntry be GetStage(entry, stageValue).
- If stageEntry is
undefined , return a promise rejected with a newTypeError exception. - Perform UpgradeToStage(entry, stageValue).
- Let p0 be the result of transforming error with a new pass-through promise.
- Let p1 be the result of transforming p0 with a fulfillment handler that, when called with argument value, runs the following steps:
- Let stageEntry be GetStage(entry, stageValue).
- If stageEntry is
undefined , throw a newTypeError . - Reject stageEntry.[[Result]] with value.
- Let pCatch be the result of transforming p1 with a rejection handler that, when called, runs the following steps:
- Set entry.[[Error]] to
true .
- Set entry.[[Error]] to
- If stageEntry.[[Result]] is
undefined , set stageEntry.[[Result]] to p1. - Return p1.
5.5. Properties of ModuleStatus Instances
ModuleStatus instances are ordinary objects that inherit properties from the %ModuleStatusPrototype%.
ModuleStatus instances are initially created with the internal slots described in the following table:
Internal Slot | Value Type (non-normative) | Description (non-normative) |
---|---|---|
[[Loader]] | An object | The loader this module status belongs to. |
[[Pipeline]] | A List | A list whose elements are stage records. |
[[Key]] | A String | A value that identifies what key was used to create the module status instance. |
[[Metadata]] | Object or undefined
| The metadata object passed through the pipeline. |
[[Dependencies]] | List of Records of the form {[[RequestName]]: String, [[ModuleStatus]]: Module Status} . | Table mapping unresolved names to their resolved key and module status entries. |
[[Module]] | Module Record or Function object or undefined
| The Module Record or a thunk after the module has been satisfied; otherwise undefined .
|
[[Error]] | A Boolean | A boolean valued that if true indicates that an error that was encountered during one of the phases of the loading pipeline; false if no error has been encountered. |
6. Loading Semantics
6.1. Auxiliary Operations
6.1.1. EnsureRegistered(loader, key)
When the abstract operation EnsureRegistered is called with arguments loader and key, the following steps are taken:
- Assert: loader must have all of the internal slots of a Loader Instance (3.5).
- Assert: Type(key) is String.
- Let registry be loader.[[Registry]].
- Let pair be the entry in registry.[[RegistryMap]] such that pair.[[Key]] is equal to key.
- If pair exists, then:
- Let entry be pair.[[Value]].
- Else,
- Let entry be a new ModuleStatus(loader, key).
- Return entry.
6.1.2. Resolve(loader, name, referrer)
When the abstract operation Resolve is called with arguments loader, name and referrer, the following steps are taken:
6.1.3. ExtractDependencies(entry, instance)
When the abstract operation ExtractDependencies is called with arguments entry and instance, the following steps are taken:
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Let deps be a new empty List.
- If instance is a Source Text Module Record, then:
- For each dep in instance.[[RequestedModules]], do:
- Append the record { [[RequestName]]: dep, [[ModuleStatus]]:
undefined } to deps.
- Append the record { [[RequestName]]: dep, [[ModuleStatus]]:
- For each dep in instance.[[RequestedModules]], do:
- Set entry.[[Dependencies]] to deps.
6.1.4. Instantiation(loader, optionalInstance, source)
When the abstract operation Instantiation is called with arguments loader, optionalInstance and source, the following steps are taken:
- Assert: loader must have all of the internal slots of a Loader Instance (3.5).
- If optionalInstance is
undefined , then:- If source is not a ECMAScript source text, throw new
TypeError . - Let realm be loader.[[Realm]].
- Return ? ParseModule(source, realm,
undefined ).
- If source is not a ECMAScript source text, throw new
- If optionalInstance is a namespace exotic object, return optionalInstance.[[Module]].
- If IsCallable(optionalInstance) is
false then throw a newTypeError . - Return optionalInstance.
6.2. Loading Operations
6.2.1. RequestFetch(entry)
When the abstract operation RequestFetch is called with argument entry, the following steps are taken:
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Let fetchStageEntry be GetStage(entry, "fetch").
- If fetchStageEntry is
undefined , return a promise resolved withundefined . - If fetchStageEntry.[[Result]] is not
undefined , return fetchStageEntry.[[Result]]. - Let hook be GetMethod(entry.[[Loader]], @@fetch).
- Let hookResult be the result of promise-calling hook(entry, entry.[[Key]]).
- Let p be the result of transforming hookResult with a fulfillment handler that, when called with argument payload, runs the following steps:
- Perform UpgradeToStage(entry, "translate").
- Return payload.
- Let pCatch be the result of transforming p with a rejection handler that, when called, runs the following steps:
- Set entry.[[Error]] to
true .
- Set entry.[[Error]] to
- Set fetchStageEntry.[[Result]] to p.
- Return p.
6.2.2. RequestTranslate(entry)
When the abstract operation RequestTranslate is called with argument entry, the following steps are taken:
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Let translateStageEntry be GetStage(entry, "translate").
- If translateStageEntry is
undefined , return a promise resolved withundefined . - If translateStageEntry.[[Result]] is not
undefined , return translateStageEntry.[[Result]]. - Let p be the result of transforming RequestFetch(entry) with a fulfillment handler that, when called with argument payload, runs the following steps:
- Let hook be GetMethod(entry.[[Loader]], @@translate).
- Let hookResult be the result of promise-calling hook(entry, payload).
- Return the result of transforming hookResult with a fulfillment handler that, when called with argument source, runs the following steps:
- Perform UpgradeToStage(entry, "instantiate").
- Return source.
- Let pCatch be the result of transforming p with a rejection handler that, when called, runs the following steps:
- Set entry.[[Error]] to
true .
- Set entry.[[Error]] to
- Set translateStageEntry.[[Result]] to p.
- Return p.
6.2.3. RequestInstantiate(entry, instantiateSet)
When the abstract operation RequestInstantiate is called with arguments entry and instantiateSet, the following steps are taken:
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Let instantiateStageEntry be GetStage(entry, "instantiate").
- If instantiateStageEntry is
undefined , return a promise resolved withundefined . - If instantiateStageEntry.[[Result]] is not
undefined , return instantiateStageEntry.[[Result]]. - Let p be the result of transforming RequestTranslate(entry) with a fulfillment handler that, when called with argument source, runs the following steps:
- Let hook be GetMethod(entry.[[Loader]], @@instantiate).
- Let hookResult be the result of promise-calling hook(entry, source).
- Return the result of transforming hookResult with a fulfillment handler that, when called with argument optionalInstance, runs the following steps:
- Return the result of transforming SatisfyInstance(entry, optionalInstance, source, instantiateSet) with a fulfillment handler that, when called with value instance, runs the following steps:
- Set entry.[[Module]] to instance.
- Return optionalInstance.
- Return the result of transforming SatisfyInstance(entry, optionalInstance, source, instantiateSet) with a fulfillment handler that, when called with value instance, runs the following steps:
- Let pCatch be the result of transforming p with a rejection handler that, when called, runs the following steps:
- Set entry.[[Error]] to
true .
- Set entry.[[Error]] to
- Set instantiateStageEntry.[[Result]] to p.
- Return p.
6.2.4. SatisfyInstance(entry, optionalInstance, source, instantiateSet)
When the abstract operation SatisfyInstance is called with arguments entry, optionalInstance, source and instantiateSet, the following steps are taken:
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- If instantiateSet is
undefined , Set instantiateSet to a new empty List. - If entry is already in instantiateSet, return
undefined . - Append entry to instantiateSet.
- Let loader be entry.[[Loader]].
- Let instance be ? Instantiation(loader, optionalInstance, source).
- If instance is a Module Record, then:
- Set instance.[[ModuleStatus]] to entry.
- Perform ? ExtractDependencies(entry, instance).
- Let list be a new empty List.
- For each pair in entry.[[Dependencies]], do:
- Let p be the result of transforming Resolve(loader, pair.[[RequestName]], entry.[[Key]]) with a fulfillment handler that, when called with value depKey, runs the following steps:
- Let depEntry be EnsureRegistered(loader, depKey).
- If depEntry is already in instantiateSet, return
undefined . - Set pair.[[ModuleStatus]] to depEntry.
- Return RequestInstantiate(depEntry, instantiateSet).
- Append p to list.
- Let p be the result of transforming Resolve(loader, pair.[[RequestName]], entry.[[Key]]) with a fulfillment handler that, when called with value depKey, runs the following steps:
- Return the result of waiting for all list with a fulfillment handler that, when called, return instance.
7. Linking Semantics
7.1. Resolving Dependencies
7.1.1. HostResolveImportedModule(module, requestName)
The modules spec should only invoke this abstract operation from methods of Source Text Module Records, and this spec does not invoke the operation at all.
When the abstract operation HostResolveImportedModule is called with arguments module and requestName, the following steps are taken:
- Assert: module is a Source Text Module Record.
- Assert: Type(requestName) is String.
- Let entry be module.[[ModuleStatus]].
- Let currentStageEntry be GetCurrentStage(entry).
- Assert: currentStageEntry.[[Stage]] is "instantiate" and currentStageEntry.[[Result]] is a resolved promise.
- Let pair be the pair in entry.[[Dependencies]] such that pair.[[RequestName]] is equal to requestName.
- Assert: pair is defined.
- Let depEntry be pair.[[ModuleStatus]].
- Let depStageEntry be GetCurrentStage(depEntry).
- Assert: depStageEntry.[[Stage]] is "instantiate" and depStageEntry.[[Result]] is a resolved promise.
- Return depEntry.[[Module]].
7.2. Linking
7.2.1. DependencyGraph(root)
When the abstract operation DependencyGraph is called with argument root the following steps are taken:
- Assert: root must have all of the internal slots of a ModuleStatus Instance (5.5).
- Let result be a new empty List.
- Call ComputeDependencyGraph(root, result).
- Return result.
7.2.2. ComputeDependencyGraph(entry, result)
When the abstract operation ComputeDependencyGraph is called with arguments entry and result, the following steps are taken:
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Assert: result must be a List.
- If entry is already in result, return
undefined . - Insert entry as the first element of result.
- For each pair in entry.[[Dependencies]], do:
- Assert: pair.[[ModuleStatus]] is defined.
- Call ComputeDependencyGraph(pair.[[ModuleStatus]], result).
- Return
undefined .
7.2.3. EnsureLinked(entry)
When the abstract operation EnsureLinked is called with argument entry the following steps are taken:
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Let deps be DependencyGraph(entry).
- For each dep in deps, do:
- Let depStageEntry be GetCurrentStage(dep).
- Assert: depStageEntry.[[Stage]] is "instantiate" and depStageEntry.[[Result]] is a resolved promise.
- If dep.[[Module]] is a Function object, then:
- Let func be dep.[[Module]].
- Let argList be a new empty List.
- Let ns be ? Call(func,
undefined , argList). - If ns is not a module namespace exotic object, throw a
TypeError exception. - Set dep.[[Module]] to ns.[[Module]].
- Assert: the following sequence is guaranteed not to run any user code.
- For each dep in deps, do:
- Let module be dep.[[Module]].
- Assert: module is a Module Record.
- Perform ? module.ModuleDeclarationInstantiation().
7.2.4. EnsureEvaluated(entry)
When the abstract operation EnsureEvaluated is called with argument entry, the following steps are taken:
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Let stageEntry be GetCurrentStage(entry).
- Assert: stageEntry.[[Stage]] is "instantiate" and stageEntry.[[Result]] is a resolved promise.
- Let module be entry.[[Module]].
- If module.[[Evaluated]] is
false , then:- Perform ? EnsureLinked(entry).
- Perform ? module.ModuleEvaluation().
- Return ? GetModuleNamespace(module).
8. Module Objects
8.1. Reflective Module Records
A reflective module record is a kind of module record. In addition to the fields, defined in ES2016 Table 37, Reflective Module Records have the additional fields listed below. Each of these fields initially has the value undefined.
Internal Slot | Value Type (non-normative) | Description (non-normative) |
---|---|---|
[[LocalExports]] | A List of Strings | The set of exported names stored in this module’s environment. |
[[IndirectExports]] | A List of pairs of String and {[[module]]: Module Record, [[bindingName]]: String}. | The set of re-exported bindings. This ensures that ResolveExport can fully resolve re-exports. |
[[Evaluate]] | A function object or undefined
| A thunk to call when the the module is evaluated, or undefined if the module is already evaluated.
|
-
8.2. Abstract Operations for Module Objects
8.2.1. ParseExportsDescriptors(obj)
When the abstract operation ParseExportsDescriptors is called with argument obj, the following steps are taken:
- Assert: Type(obj) is an Object.
- Let props be ? ToObject(Obj).
- Let keys be ? props.[[OwnPropertyKeys]]().
- Let descriptors be an empty List.
- Repeat for each element nextKey of keys in List order,
- Let propDesc be ? props.[[GetOwnProperty]](nextKey).
- If propDesc is not
undefined and propDesc.[[Enumerable]] istrue , then- Let descObj be ? Get(props, nextKey).
- Let hasModule be ? HasProperty(descObj, "module").
- If hasModule is
true , then - Else,
- Let hasValue be ? HasProperty(descObj, "value").
- If hasValue is
true , let value be ? Get(descObj, "value"). - Let hasConst be ? HasProperty(descObj, "const").
- If hasConst is
true , let isConst be ToBoolean(? Get(descObj, "const")). - If isConst is
true , then- If hasValue is
true , then- Let desc be a new Immutable Export Descriptor Record {[[Name]]: nextKey, [[Value]]: value, [[Initialized]]:
true }.
- Let desc be a new Immutable Export Descriptor Record {[[Name]]: nextKey, [[Value]]: value, [[Initialized]]:
- Else,
- Let desc be a new Immutable Export Descriptor Record {[[Name]]: nextKey, [[Value]]:
undefined , [[Initialized]]:false }.
- Let desc be a new Immutable Export Descriptor Record {[[Name]]: nextKey, [[Value]]:
- If hasValue is
- Else,
- If hasValue is
true , then- Let desc be a new Mutable Export Descriptor Record {[[Name]]: nextKey, [[Value]]: value, [[Initialized]]:
true }.
- Let desc be a new Mutable Export Descriptor Record {[[Name]]: nextKey, [[Value]]: value, [[Initialized]]:
- Else,
- Let desc be a new Mutable Export Descriptor Record {[[Name]]: nextKey, [[Value]]:
undefined , [[Initialized]]:false }.
- Let desc be a new Mutable Export Descriptor Record {[[Name]]: nextKey, [[Value]]:
- If hasValue is
- Append desc to the end of descriptors.
- Return descriptors.
- uninitialized, mutable:
{ }
- uninitialized, immutable:
{ const: true }
- initialized, mutable:
{ value: 42 }
- initialized, immutable:
{ value: 42, const: true }
- re-export (immutable):
{ module: m, import: "foo" }
8.2.2. CreateModuleMutator(module)
When the abstract operation CreateModuleMutator is called with argument module, the following steps are taken:
- Assert: module is a Reflective Module Records.
- Let mutator be ObjectCreate(%ObjectPrototype%).
- Let env be module.[[Environment]].
- Let envRec be env’s environment record.
- For each name in module.[[LocalExports]], do:
- Assert: mutator does not already have a binding for name.
- Let p be MakeArgSetter(name, envRec).
- Let localExportDesc be the PropertyDescriptor{[[Get]]: %ThrowTypeError%, [[Set]]: p, [[Enumerable]]: true, [[Configurable]]: false}.
- Perform ? DefinePropertyOrThrow(mutator, name, localExportDesc).
- Return mutator.
8.2.3. GetExportNames(exportStarStack)
When the abstract operation GetExportNames is called with argument exportStarStack, the following steps are taken:
- Let module be this Reflective Module Record.
- Let exports be a new empty List.
- For each name in module.[[LocalExports]], do:
- Append name to exports.
- For each pair in module.[[IndirectExports]], do:
- Append pair.[[Key]] to exports.
- Return exports.
8.2.4. ResolveExport(exportName, resolveStack, exportStarStack)
When the abstract operation ResolveExport is called with arguments exportName, resolveStack and exportStarStack, the following steps are taken:
- Let module be this Reflective Module Record.
- If resolveStack contains a record r such that r.[[module]] is equal to module and r.[[exportName]] is equal to exportName, then
- Assert: this is a circular import request.
- Throw a
SyntaxError exception.
- Append the record {[[module]]: module, [[exportName]]: exportName} to resolveStack.
- Let localExports be module.[[LocalExports]].
- If exportName is in localExports, then:
- Return the Record { [[module]]: module, [[bindingName]]: exportName }.
- Let indirectExports be module.[[IndirectExports]].
- Let pair be the pair in indirectExports such that pair.[[Key]] is equal to exportName.
- If pair is defined, return pair.[[Value]].
- Return
null .
8.2.5. ModuleDeclarationInstantiation()
When the abstract operation ModuleDeclarationInstantiation is called with no arguments, the following steps are taken:
- Return
undefined .
8.2.6. ModuleEvaluation()
When the abstract operation ModuleEvaluation is called with no arguments, the following steps are taken:
- Let module be this Reflective Module Record.
- If module.[[Evaluated]] is
true , returnundefined . - Let func be module.[[Evaluate]].
- Set module.[[Evaluated]] to
true . - Set module.[[Evaluate]] to
undefined . - For each pair in module.[[IndirectExports]], do:
- Let requiredModule be pair.[[Value]].[[module]].
- Assert: requiredModule is a Module Record.
- Perform ? requiredModule.ModuleEvaluation().
- If IsCallable(func) is
true , then:- Let argList be a new empty List.
- Perform ? Call(func,
undefined , argList).
- Return
undefined .
8.3. The Module Constructor
The Module constructor is the initial value of the Module property of the the Reflect object. When called as a constructor it creates and initializes a new Module object. Module is not intended to be called as a function and will throw an exception when called in that manner.
The Module constructor is designed to be subclassable. It may be used as the value in an extends clause of a class definition. Subclass constructors that intend to inherit the specified Module behaviour must include a super call to the Module constructor to create and initialize the subclass instance with the internal stage necessary to integrated with loaders.
8.3.1. Module(descriptors[, executor[, evaluate]])
When Module is called with arguments descriptors, executor, and evaluate, the following steps are taken:
- Let realm be the current Realm Record.
- Let env be NewModuleEnvironment(realm.[[globalEnv]]).
- If Type(descriptors) is not Object, throw a
TypeError exception. - Let exportDescriptors be ParseExportsDescriptors(descriptors). // TODO: interleave the subsequent loop with parsing?
- Let localExports be a new empty List.
- Let indirectExports be a new empty List.
- Let exportNames be a new empty List.
- Let envRec be env’s environment record.
- For each desc in exportDescriptors, do:
- Let exportName be desc.[[Name]].
- Append exportName to exportNames.
- If desc is an Indirect Export Descriptor, then:
- Let otherMod be desc.[[Module]].
- Let resolution be ? otherMod.ResolveExport(desc.[[Import]], « »).
- If resolution is
null , then throw aSyntaxError exception. - Append the record {[[Key]]: exportName, [[Value]]: resolution} to indirectExports.
- Else,
- Append exportName to localExports.
- If desc is an Immutable Export Descriptor, then:
- Perform ? envRec.CreateImmutableBinding(exportName,
true ).
- Perform ? envRec.CreateImmutableBinding(exportName,
- Else,
- Assert: desc is a Mutable Export Descriptor.
- Perform ? envRec.CreateMutableBinding(exportName,
false ).
- If desc.[[Initialized]] is
true , then:- Call envRec.InitializeBinding(exportName, desc.[[Value]]).
- If evaluate is not
undefined , then- If IsCallable(evaluate) is
false , throw a newTypeError exception.
- If IsCallable(evaluate) is
- Let mod be a new Reflective Module Record {[[Realm]]: realm, [[Environment]]: env, [[Namespace]]:
undefined , [[LocalExports]]: localExports, [[IndirectExports]]: indirectExports, [[Evaluated]]:false , [[Evaluate]]: evaluate, [[HostDefined]]:undefined }. - Let ns be ModuleNamespaceCreate(mod, realm, exportNames).
- Set mod.[[Namespace]] to ns.
- If executor is not
undefined , then- If IsCallable(executor) is
false , throw a newTypeError exception. - Let mutator be CreateModuleMutator(mod).
- Perform ? executor(mutator, ns).
- If IsCallable(executor) is
- Return ns.
8.4. Properties of the Module Constructor
The value of the [[Prototype]] internal slot of the Module constructor is the intrinsic object %FunctionPrototype%.
The Loader constructor has the following properties:
8.4.1. Module.evaluate(ns)
When Module.evaluate is called with argument ns, the following steps are taken:
- If ns is not a module namespace exotic object, throw a
TypeError exception. - Let module be ns.[[Module]].
- Let entry be module.[[Entry]].
- Let status be EnsureEvaluated(entry).
- RejectIfAbrupt(status);
- Return a promise resolved with
undefined .
8.4.2. Module.prototype
The value of Module.prototype is an ordinary object with a null [[Prototype]].
This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.
8.5. Properties of Module Instances
Module instances are module namespace exotic objects.
9. Local Loading
TODO:
- syntax for accessing module local information:
import local from this;
- dynamic import:
local.import()
- extending the hooks to handle
this
- debugging info
- room for host environment-specific data
10. Browser Loader
Every host environment must implement a default loader object as the initial value of the loader property of the System object.
10.1. System.loader Object
The Default Browser Loader Object is an %BrowserLoader% instance, whose internal slots are set as if it had been constructed by the expression Construct(%BrowserLoader%).
11. BrowserLoader Objects
11.1. The BrowserLoader Constructor
The BrowserLoader constructor is the %BrowserLoader% intrinsic object. When called as a constructor it creates and initializes a new BrowserLoader object. When BrowserLoader is called as a function rather than as a constructor, it throws an exception.
11.2. Properties of the BrowserLoader Constructor
The value of the [[Prototype]] internal slot of the BrowserLoader constructor is the intrinsic object %FunctionPrototype%.
The BrowserLoader constructor has the following properties:
11.2.1. BrowserLoader.prototype
The initial value of BrowserLoader.prototype is the intrinsic object %BrowserLoaderPrototype%.
This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.
11.3. Properties of the BrowserLoader Prototype
11.3.1. BrowserLoader.prototype[ @@resolve ](name, referrer)
When the @@resolve method is called, the following steps are taken:
TODO: name resolution policy
- relative and site-relative URLs:
"./utils.js"
,"/scripts/utils.js"
- JS standard modules:
"std/math"
,"std/json"
,"std/reflect"
- Web standard modules:
"web/worker"
,"web/audio"
- absolute URLs:
"https://cdn.example.com/jquery/v/2.0"
The value of the name property of this function is "[Reflect.Loader.resolve]".
11.3.2. BrowserLoader.prototype[ @@fetch ](entry, key)
When the @@fetch method is called, the following steps are taken:
TODO:
- reference fetch standard
- cross-origin produces an opaque object as in ServiceWorker
- CORS, CSP
- other kinds of web assets
The value of the name property of this function is "[Reflect.Loader.fetch]".
11.3.3. BrowserLoader.prototype[ @@translate ](entry, payload)
When the @@translate method is called, the following steps are taken:
TODO: no-op.
The value of the name property of this function is "[Reflect.Loader.translate]".
11.3.4. BrowserLoader.prototype[ @@instantiate ](entry, source)
When the @@instantiate method is called, the following steps are taken:
TODO:
- basically a no-op.
- but also needs to re-absorb opaque responses.
The value of the name property of this function is "[Reflect.Loader.instantiate]".
Annexes
List of Well-Known Intrinsic Objects
The intrinsics are listed in table below:
Intrinsic Name | Description (non-normative) |
---|---|
%Loader% | The loader constructor (3.1) |
%LoaderPrototype% | The initial value of the *prototype* data property of %Loader% (3.2.1) |
%BrowserLoader% | The browser loader constructor (11.1) |
%BrowserLoaderPrototype% | The initial value of the *prototype* data property of %BrowserLoader% (11.2.1) |
%Registry% | The registry constructor (4.2) |
%RegistryPrototype% | The initial value of the *prototype* data property of %Registry% (4.3.1) |
%ModuleStatus% | The module status constructor (5.2) |
%ModuleStatusPrototype% | The initial value of the *prototype* data property of %ModuleStatus% (5.3.1) |