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, then 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%").
- 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%.
Besides the internal slots and the length property (whose value is 0), the Loader constructor has the following properties:
3.2.1. Loader.prototype
The value of Loader.prototype is %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 LoadModule(entry, "ready").
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 "ready". - 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. When called as a constructor it creates and initializes a new Registry object. When Registry is called as a function rather than as a constructor, it throws 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%.
Besides the internal slots and the length property (whose value is 0), the Registry constructor has the following properties:
4.3.1. Registry.prototype
The 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
Registry.prototype.constructor is not intended to be called as a function or as a constructor and will always throw an exception.
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", "instantiate", "satisfy", "link" or "ready", 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.
- 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 fulfillment handler that, when called with argument entry, runs the following steps:
- If entry.[[Module]] is a Function object, return entry.[[Module]].
- Return
undefined .
- Return the result of transforming RequestInstantiate(entry) with a fulfillment handler that, when called with argument entry, runs the following steps:
- If stage is "satisfy", then:
- Return the result of transforming RequestSatisfy(entry) with a fulfillment handler that, when called with argument entry, runs the following steps:
- If entry.[[Module]] is a Function object, return entry.[[Module]].
- Return
undefined .
- Return the result of transforming RequestSatisfy(entry) with a fulfillment handler that, when called with argument entry, runs the following steps:
- If stage is "link", then:
- Return the result of transforming RequestLink(entry) with a fulfillment handler that returns
undefined .
- Return the result of transforming RequestLink(entry) with a fulfillment handler that returns
- If stage is "ready" or
undefined , then:- Return the result of transforming RequestReady(entry) with a fulfillment handler that, when called with argument entry, runs the following steps:
- Return GetModuleNamespace(entry.[[Module]]).
- Return the result of transforming RequestReady(entry) with a fulfillment handler that, when called with argument entry, runs the following steps:
- Return a promise rejected with a new
RangeError exception.
5.1.5. UpgradeToStage(entry, stage)
- 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", "satisfy", "link", "ready"
| 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 |
"satisfy"
| satisfying the module’s dependency graph by instantiating all of its (direct or indirect) dependencies |
"link"
| linking all imports and exports of the module’s dependency graph |
"ready"
| fully loaded and initialized |
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[, module])
When ModuleStatus is called with arguments loader, key and module, 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).
- If Type(module) is not Object, throw a
TypeError exception. - If module does not have all of the internal slots of a Module Instance (8.5), throw a
TypeError exception. - Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%ModuleStatusPrototype%", «[[Loader]], [[Pipeline]], [[Key]], [[Module]], [[Metadata]], [[Dependencies]], [[Error]]» ).
- Let pipeline be a new List.
- If module exists, then
- Let result be a promise resolved with module.
- Add new stage entry record { [[Stage]]: "ready", [[Result]]: result } as a new element of the list pipeline.
- Else,
- 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. - Add new stage entry record { [[Stage]]: "satisfy", [[Result]]:
undefined } as a new element of the list pipeline. - Add new stage entry record { [[Stage]]: "link", [[Result]]:
undefined } as a new element of the list pipeline. - Add new stage entry record { [[Stage]]: "ready", [[Result]]:
undefined } as a new element of the list pipeline.
- Add new stage entry record { [[Stage]]: "fetch", [[Result]]:
- 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
undefined . - Set O’s [[Error]] internal slot to
nothing . - 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%.
Besides the internal slots and the length property (whose value is 0), the ModuleStatus constructor has the following properties:
5.3.1. ModuleStatus.prototype
The value of ModuleStatus.prototype is %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.module
ModuleStatus.prototype.module is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:
5.4.4. 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.5. 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 }. - Let requestNameStatus be ? DefinePropertyOrThrow(O, "requestName", requestNameDesc).
- Let keyDesc be the PropertyDescriptor{[[Value]]: pair.[[Key]], [[Writable]]:
false , [[Enumerable]]:true , [[Configurable]]:false }. - Let keyStatus be ? DefinePropertyOrThrow(O, "key", keyDesc).
- Let moduleStatusDesc be the PropertyDescriptor{[[Value]]: pair.[[ModuleStatus]], [[Writable]]:
false , [[Enumerable]]:true , [[Configurable]]:false }. - Let moduleStatus be ? DefinePropertyOrThrow(O, "entry", moduleStatusDesc).
- Let status be ? CreateDataProperty(array, ? ToString(n), O).
- Increment n by 1.
- Return array.
5.4.6. 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 then let stageValue be "fetch". - Else 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.7. 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).
- RejectIfAbrupt(stageEntry).
- 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.8. 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. - If Type(entry) is not Object, throw a new
TypeError . - Let stageValue be ToString(stage).
- RejectIfAbrupt(stageValue).
- If IsValidStageValue(stageValue) is
false , return a promise rejected with a newRangeError exception. - 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:
- Let stageEntry be GetStage(entry, stageValue).
- If stageEntry is
undefined , throw a newTypeError . - If stageEntry.[[Result]] is
undefined , then- Set stageEntry.[[Result]] to a promise resolved with value.
- Else,
- Fulfill stageEntry.[[Result]] with value.
- UpgradeToStage(entry, stageValue).
- Return value.
- Return p0.
5.4.9. 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 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 . - If stageEntry.[[Result]] is
undefined , then- Set stageEntry.[[Result]] to a promise rejected with value.
- Else,
- Reject stageEntry.[[Result]] with value.
- UpgradeToStage(entry, stageValue).
- Return value.
- Return p0.
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, [[Key]]: 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 if the entry is ready, or a thunk if the entry is delayed; otherwise undefined.
|
| [[Error]] | Any or nothing | An error that was encountered during one of the phases of the loading pipeline; nothing if no error has been encountered. |
6. Loading Semantics
6.1. Auxiliary Operations
6.1.1. EnsureRegistered(loader, key)
- 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)
6.1.3. ExtractDependencies(entry, optionalInstance, source)
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Let instance be ? Instantiation(entry.[[Loader]], optionalInstance, source).
- Let deps be a new empty List.
- If instance is a Module Record, then:
- Assert: instance is a Source Text Module Record.
- Set instance.[[ModuleStatus]] to entry.
- For each dep in instance.[[RequestedModules]], do:
- Append the record { [[RequestName]]: dep, [[Key]]:
undefined , [[ModuleStatus]]:undefined } to deps.
- Append the record { [[RequestName]]: dep, [[Key]]:
- Set entry.[[Dependencies]] to deps.
- Set entry.[[Module]] to instance.
6.1.4. Instantiation(loader, result, source)
- Assert: loader must have all of the internal slots of a Loader Instance (3.5).
- If result is
undefined , then return ParseModule(source). - If IsCallable(result) is
false then throw a newTypeError . - Set result.[[Realm]] to loader.[[Realm]].
- Return result.
6.2. Loading Operations
6.2.1. RequestFetch(entry)
- 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 p0 be the result of promise-calling hook(entry, entry.[[Key]]).
- Let p1 be the result of transforming p0 with a new pass-through promise.
- Let p2 be the result of transforming p1 with a fulfillment handler that, when called with argument payload, runs the following steps:
- UpgradeToStage(entry, "translate").
- Set fetchStageEntry.[[Result]] to p1.
- Return p1.
6.2.2. RequestTranslate(entry)
- 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 hook be GetMethod(entry.[[Loader]], @@translate).
- Let p be the result of transforming RequestFetch(entry) with a fulfillment handler that, when called with argument payload, runs the following steps:
- Let p0 be the result of promise-calling hook(entry, payload).
- Let p1 be the result of transforming p0 with a new pass-through promise.
- Let p2 be the result of transforming p1 with a fulfillment handler that, when called with argument source, runs the following steps:
- UpgradeToStage(entry, "instantiate").
- Return p1.
- Set translateStageEntry.[[Result]] to p.
- Return p.
6.2.3. RequestInstantiate(entry)
- 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 hook be GetMethod(entry.[[Loader]], @@instantiate).
- Let p be the result of transforming RequestTranslate(entry) with a fulfillment handler that, when called with argument source, runs the following steps:
- Let p0 be the result of promise-calling hook(entry, source).
- Let p1 be the result of transforming p0 with a new pass-through promise.
- Let p2 be the result of transforming p1 with a fulfillment handler that, when called with argument optionalInstance, runs the following steps:
- Let status be ? ExtractDependencies(entry, optionalInstance, source).
- UpgradeToStage(entry, "satisfy").
- Return p1.
- Set instantiateStageEntry.[[Result]] to p.
- Return p.
6.2.4. RequestSatisfy(entry)
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Let satisfyStageEntry be GetStage(entry.[[Loader]], "satisfy").
- If satisfyStageEntry is
undefined , return a promise resolved withundefined . - If satisfyStageEntry.[[Result]] is not
undefined , return satisfyStageEntry.[[Result]]. - Let p be the result of transforming RequestInstantiate(entry) with a fulfillment handler that, when called with argument entry, runs the following steps:
- Let depLoads be a new empty List.
- For each pair in entry.[[Dependencies]], do:
- Let p be the result of transforming Resolve(loader, pair.[[Key]], key) with a fulfillment handler that, when called with value depKey, runs the following steps:
- Let depEntry be EnsureRegistered(entry.[[Loader]], depKey).
- Let pair.[[Key]] to depKey.
- Let pair.[[ModuleStatus]] to depEntry.
- Let currentStageEntry be GetCurrentStage(entry).
- If currentStageEntry.[[Stage]] is "ready", then:
- Return depEntry.[[Module]].
- Return the result of transforming RequestSatisfy(depEntry) with a fulfillment handler that, when called with value depEntry, runs the following steps:
- Return depEntry.[[Module]].
- Append p to depLoads.
- Let p be the result of transforming Resolve(loader, pair.[[Key]], key) with a fulfillment handler that, when called with value depKey, runs the following steps:
- Let p be the result of waiting for all depLoads.
- Return the result of transforming p with a fulfillment handler that, when called, runs the following steps:
- UpgradeToStage(entry, "link").
- Return entry.
- Set satisfyStageEntry.[[Result]] to p.
- Return p.
6.2.5. RequestLink(entry)
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Let linkStageEntry be GetStage(entry, "link").
- If linkStageEntry is
undefined , return a promise resolved withundefined . - If linkStageEntry.[[Result]] is not
undefined , return linkStageEntry.[[Result]]. - Return the result of transforming RequestSatisfy(entry) with a fulfillment handler that, when called with argument entry, runs the following steps:
- Assert: entry’s whole dependency graph is in "link" or "ready" stage.
- Let status be ? Link(entry).
- Assert: entry’s whole dependency graph is in "ready" stage.
- Return entry.
6.2.6. RequestReady(entry)
- Assert: entry must have all of the internal slots of a ModuleStatus Instance (5.5).
- Let currentStageEntry be GetCurrentStage(entry).
- If currentStageEntry.[[Stage]] is equal "ready", return currentStageEntry.[[Result]].
- Return the result of transforming RequestLink(entry) with a fulfillment handler that, when called with argument entry, runs the following steps:
- Let module be entry.[[Module]].
- Let status be ? module.ModuleEvaluation().
- Return module.
7. Linking Semantics
7.1. Resolving Dependencies
7.1.1. HostResolveImportedModule(module, requestName)
The modules spec should only invoke this operation from methods of Source Text Module Records, and this spec does not invoke the operation at all.
- 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 in "link" or "ready" stage.
- 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 equal "link" or "ready" stage.
- Return depEntry.[[Module]].
7.2. Linking
7.2.1. Link(root)
- Assert: root must have all of the internal slots of a ModuleStatus Instance (5.5).
- Let deps be DependencyGraph(root).
- For each dep in deps, do:
- Let depStageEntry be GetCurrentStage(dep).
- If depStageEntry.[[Stage]] is "link" and dep.[[Module]] is a Function object, then:
- Let f be dep.[[Module]].
- Let m be ? f().
- Set dep.[[Module]] to m.
- UpgradeToStage(dep, "ready").
- Assert: the following sequence is guaranteed not to run any user code.
- For each dep in deps, do:
- Let depStageEntry be GetCurrentStage(dep).
- If depStageEntry.[[Stage]] is "link", then:
- Let module be dep.[[Module]].
- Assert: module is a Module Record.
- Let status be ? module.ModuleDeclarationInstantiation().
- UpgradeToStage(dep, "ready").
- Return
undefined .
7.2.2. DependencyGraph(root)
- 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.3. ComputeDependencyGraph(entry, result)
- 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, then return
undefined . - Append entry to result.
- For each pair in entry.[[Dependencies]], do:
- Assert: pair.[[ModuleStatus]] is defined.
- Call ComputeDependencyGraph(pair.[[ModuleStatus]], result).
- Return
undefined .
8. Module Objects
8.1. Reflective Module Records
A reflective module record is a kind of module record. It extends
| 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)
TODO: parse as in these examples
- uninitialized, mutable:
{ } - uninitialized, immutable:
{ const: true } - initialized, mutable:
{ value: 42 } - initialized, immutable:
{ value: 42, const: true } - re-export (immutable):
{ module: m, import: "foo" }
- // TODO: spec me
8.2.2. CreateModuleMutator(module)
- // TODO: spec me
8.2.3. GetExportNames(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)
- 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 exports be module.[[LocalExports]].
- Let pair be the pair in exports such that pair.[[Key]] is equal to exportName.
- If pair is defined, then:
- Return the Record { [[module]]: module, [[bindingName]]: exportName }.
- Let exports be module.[[IndirectExports]].
- Let pair be the pair in exports such that pair.[[Key]] is equal to exportName.
- If pair is defined, then return pair.[[Value]].
- Return
null .
8.2.5. ModuleDeclarationInstantiation()
Reflective modules are always already instantiated.
- Return
undefined .
8.2.6. ModuleEvaluation()
- Let module be this Reflective Module Record.
- Let evaluate be module.[[Evaluate]].
- Set module.[[Evaluate]] to
undefined . - Return evaluate().
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.
- Let env be NewModuleEnvironment(realm.[[globalEnv]]).
- 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:
- Let status be envRec.CreateImmutableBinding(exportName,
true ). - Assert: status is not an abrupt completion.
- Let status be envRec.CreateImmutableBinding(exportName,
- Else,
- Assert: desc is a Mutable Export Descriptor.
- Let status be envRec.CreateMutableBinding(exportName,
false ). - Assert: status is not an abrupt completion.
- If desc.[[Initialized]] is
true , then:- Call envRec.InitializeBinding(exportName, desc.[[Value]]).
- If evaluate is
undefined , then let evaluated betrue . Otherwise let evaluated befalse . - Let mod be a new Reflective Module Record {[[Realm]]: realm, [[Environment]]: env, [[Namespace]]:
undefined , [[Evaluated]]: evaluated, [[LocalExports]]: localExports, [[IndirectExports]]: indirectExports, [[Evaluate]]: evaluate}. - Let ns be ModuleNamespaceCreate(mod, realm, exportNames).
- Set mod.[[Namespace]] to ns.
- If executor is not
undefined , then- Let mutator be CreateModuleMutator(mod).
- Let status be ? executor(mutator, ns).
- 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%.
Besides the internal slots and the length property (whose value is 0), the Loader constructor has the following properties:
8.4.1. Module.evaluate(m)
TODO: way to force evaluation of a module namespace exotic object (Reflect.Module.evaluate(m)? m[Reflect.Module.evaluate]()?)
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%.
Besides the internal slots and the length property (whose value is 0), the BrowserLoader constructor has the following properties:
11.2.1. BrowserLoader.prototype
The value of BrowserLoader.prototype is %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.2) |
| %LoaderPrototype% | The initial value of the *prototype* data property of %Loader% (3.3.1) |
| %BrowserLoader% | The browser loader constructor (10.1) |
| %BrowserLoaderPrototype% | The initial value of the *prototype* data property of %BrowserLoader% (10.2.1) |
| %Registry% | The registry constructor (4.1) |
| %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) |