Stage 1 Draft / July 28, 2022

Module & ModuleSource API

1 Well-known intrinsic objects

Table 1: Well-known Intrinsic Objects
Intrinsic Name Global Name ECMAScript Language Association
%ModuleSource% ModuleSource The ModuleSource constructor (5.2)
%Module% Module The Module constructor (6.2)

2 Source Text Module Records

In addition to the fields defined in Table 46, Source Text Module Records have the additional fields listed in Table 2. Each of these fields is initially set in ParseModule.

Table 2: Additional Fields of Source Text Module Records
Field Name Value Type Meaning
...
[[ModuleInstance]] An instance of a %Module% constructor or undefined. A Module Instance or undefined if no Module Instance is associated to this Record.

3 Module Source Records

A Module Source Record is used to represent information about a module source that was defined from ECMAScript source text (11) that was parsed using the goal symbol Module. Its fields contain digested information about the names that are imported and exported by the module.

Module Source Records have the fields listed in Table 3. Each of these fields is initially set in ParseModuleSource.

Table 3: Fields of Module Source Records
Field Name Value Type Meaning
[[ECMAScriptCode]] a Parse Node The result of parsing the source text of this module using Module as the goal symbol.
[[ImportEntries]] a List of ImportEntry Records A List of ImportEntry records derived from the code of this module source.
[[LocalExportEntries]] a List of ExportEntry Records A List of ExportEntry records derived from the code of this module that correspond to declarations that occur within the module source.
[[IndirectExportEntries]] a List of ExportEntry Records A List of ExportEntry records derived from the code of this module source that correspond to reexported imports that occur within the module or exports from export * as namespace declarations.
[[StarExportEntries]] a List of ExportEntry Records A List of ExportEntry records derived from the code of this module source that correspond to export * declarations that occur within the module, not including export * as namespace declarations.
[[HasTLA]] a Boolean Whether this module source is individually asynchronous. Having an asynchronous dependency does not mean this field is true.
[[RequestedModules]] a List of Strings A List of all the ModuleSpecifier strings used by the module source represented by this record to request the importation of a module. The List is source text occurrence ordered.
[[HostDefined]] anything (default value is undefined) Field reserved for use by host environments that need to associate additional information with a module source.
Editor's Note
A Module Source Record is the static portion of a Module Record. A refactor of the spec can make this separation more formal instead of just copying internal slot from one record to another.

4 Import Calls

4.1 Runtime Semantics: Evaluation

ImportCall : import ( AssignmentExpression )
  1. Let referencingScriptOrModule be GetActiveScriptOrModule().
  2. Let argRef be the result of evaluating AssignmentExpression.
  3. Let specifier be ? GetValue(argRef).
  4. Let specifierOrModule be ? GetValue(argRef).
  5. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  6. If Type(specifierOrModule) is Object that has a [[ModuleSourceInstance]] internal slot, then
    1. Let moduleRecord be specifierOrModule.[[Module]].
    2. Perform HostImportModuleRecordDynamically(moduleRecord, promiseCapability).
  7. Else,
    1. Let specifierString be Completion(ToString(specifierOrModule)).
    2. IfAbruptRejectPromise(specifierString, promiseCapability).
    3. Perform HostImportModuleDynamically(referencingScriptOrModule, specifierString, promiseCapability).
  8. Return promiseCapability.[[Promise]].

5 ModuleSource Objects

5.1 ModuleSource Abstract Operations

5.1.1 ParseModuleSource ( sourceText )

The abstract operation ParseModuleSource takes argument sourceText (ECMAScript source text) and returns a Module Source Record or throw a SyntaxError object. It creates a Module Source Record based upon the result of parsing sourceText as a Module. It performs the following steps when called:

  1. Let body be ParseText(sourceText, Module).
  2. If body is a List of errors, throw a SyntaxError exception.
  3. Let requestedModules be the ModuleRequests of body.
  4. Let importEntries be ImportEntries of body.
  5. Let importedBoundNames be ImportedLocalNames(importEntries).
  6. Let indirectExportEntries be a new empty List.
  7. Let localExportEntries be a new empty List.
  8. Let starExportEntries be a new empty List.
  9. Let exportEntries be ExportEntries of body.
  10. For each ExportEntry Record ee of exportEntries, do
    1. If ee.[[ModuleRequest]] is null, then
      1. If ee.[[LocalName]] is not an element of importedBoundNames, then
        1. Append ee to localExportEntries.
      2. Else,
        1. Let ie be the element of importEntries whose [[LocalName]] is the same as ee.[[LocalName]].
        2. If ie.[[ImportName]] is namespace-object, then
          1. NOTE: This is a re-export of an imported module namespace object.
          2. Append ee to localExportEntries.
        3. Else,
          1. NOTE: This is a re-export of a single name.
          2. Append the ExportEntry Record { [[ModuleRequest]]: ie.[[ModuleRequest]], [[ImportName]]: ie.[[ImportName]], [[LocalName]]: null, [[ExportName]]: ee.[[ExportName]] } to indirectExportEntries.
    2. Else if ee.[[ImportName]] is all-but-default, then
      1. Assert: ee.[[ExportName]] is null.
      2. Append ee to starExportEntries.
    3. Else,
      1. Append ee to indirectExportEntries.
  11. Let async be body Contains await.
  12. NOTE: How should hostDefined be populated if this operation is triggered by the user and whether or not hostDefined should be used for sources created in user land.
  13. Let hostDefined be undefined.
  14. Return Module Source Record { [[HasTLA]]: async, [[ECMAScriptCode]]: body, [[RequestedModules]]: requestedModules, [[ImportEntries]]: importEntries, [[LocalExportEntries]]: localExportEntries, [[IndirectExportEntries]]: indirectExportEntries, [[StarExportEntries]]: starExportEntries, [[HostDefined]]: hostDefined }.
Editor's Note
A Module Source Record is the static portion of a Module Record. A refactor of the spec can make this separation more formal instead of just having both of them sharing the same set of Internal Slots.

5.2 The ModuleSource Constructor

The ModuleSource constructor:

  • is the intrinsic object %ModuleSource%.
  • is the initial value of the "ModuleSource" property of the global object.
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • creates and initializes a new ModuleSource object when called as a constructor.

5.2.1 ModuleSource ( sourceText )

When the ModuleSource function is called with argument sourceText, the following steps are taken:

  1. If NewTarget is undefined, throw a TypeError exception.
  2. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%ModuleSource.prototype%", « [[ModuleSource]] »).
  3. Set O.[[ModuleSource]] to ? ParseModuleSource(sourceText).
  4. Return O.

5.3 Properties of the ModuleSource Constructor

The ModuleSource constructor:

  • has a [[Prototype]] internal slot whose value is %Function.prototype%.
  • has the following properties:

5.3.1 ModuleSource.prototype

The initial value of ModuleSource.prototype is %ModuleSource.prototype%.

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.

5.4 Properties of the ModuleSource Prototype Object

The ModuleSource prototype object:

5.4.1 ModuleSource.prototype.constructor

The initial value of ModuleSource.prototype.constructor is %ModuleSource%.

5.4.2 ModuleSource.prototype [ @@toStringTag ]

The initial value of the @@toStringTag property is the String value "ModuleSource".

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.

5.5 Properties of ModuleSource Instances

ModuleSource instances are ordinary objects that inherit properties from the ModuleSource prototype object (the intrinsic, %ModuleSource.prototype%). ModuleSource instances are initially created with the internal slots described in Table 4.

Table 4: Internal Slots of ModuleSource Instances
Internal Slot Type Description
[[ModuleSource]] Module Source Record The Module Source Record for the provided source text.

6 Module Objects

6.1 Module Abstract Operations

6.1.1 CreateModuleRecord ( moduleSource )

The abstract operation CreateModuleRecord takes argument moduleSource (a Module Source Record) and returns a Source Text Module Record. It creates a Source Text Module Record based upon the result of a previously parsed sourceText as a Module associated to the provided Module Source Record. It performs the following steps when called:

  1. Assert: moduleSource is a Module Source Record.
  2. Let async be sourceModule.[[HasTLA]].
  3. Let body be sourceModule.[[ECMAScriptCode]].
  4. Let requestedModules be sourceModule.[[RequestedModules]].
  5. Let importEntries be sourceModule.[[ImportEntries]].
  6. Let localExportEntries be sourceModule.[[LocalExportEntries]].
  7. Let indirectExportEntries be sourceModule.[[IndirectExportEntries]].
  8. Let starExportEntries be sourceModule.[[StarExportEntries]].
  9. Let hostDefined be sourceModule.[[HostDefined]].
  10. Let realm be the current Realm Record.
  11. Return Source Text Module Record { [[Realm]]: realm, [[Environment]]: empty, [[Namespace]]: empty, [[CycleRoot]]: empty, [[HasTLA]]: async, [[AsyncEvaluation]]: false, [[TopLevelCapability]]: empty, [[AsyncParentModules]]: « », [[PendingAsyncDependencies]]: empty, [[Status]]: unlinked, [[EvaluationError]]: empty, [[HostDefined]]: hostDefined, [[ECMAScriptCode]]: body, [[Context]]: empty, [[ImportMeta]]: empty, [[RequestedModules]]: requestedModules, [[ImportEntries]]: importEntries, [[LocalExportEntries]]: localExportEntries, [[IndirectExportEntries]]: indirectExportEntries, [[StarExportEntries]]: starExportEntries, [[DFSIndex]]: empty, [[DFSAncestorIndex]]: empty, [[ModuleInstance]]: undefined }.

6.1.2 ResolveModuleRecordDependency ( moduleRecord, specifier, promiseCapability )

The abstract operation ResolveModuleRecordDependency takes arguments moduleRecord (a Module Record), specifier (a ModuleSpecifier String), and promiseCapability (a PromiseCapability Record) and returns unused. It provides the concrete Module Record subclass instance that corresponds to specifier occurring within the module represented by moduleRecord. It performs the following steps when called:

  1. Assert: moduleRecord is a Module Record.
  2. Assert: moduleRecord.[[ModuleInstance]] is an Object.
  3. Assert: ResolveModuleRecordDependency has not been invoked with moduleRecord and specifier pair.
  4. Let moduleInstance be moduleRecord.[[ModuleInstance]].
  5. Let importHook be moduleInstance.[[ImportHook]].
  6. Let completion be Completion(Call(importHook, undefined, « specifier »).
  7. IfAbruptRejectPromise(completion, promiseCapability).
  8. Let importHookPromise be ? PromiseResolve(%Promise%, completion.[[Value]]).
  9. Let fulfilledClosure be a new Abstract Closure with parameters (result) that captures moduleRecord and promiseCapability and performs the following steps when called
    1. If result is an Object that has a [[ImportHook]] internal slot, then
      1. Let moduleRecord be result.[[Module]].
      2. Perform ! Call(promiseCapability.[[Resolve]], undefined, « moduleRecord »).
    2. Else,
      1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
    3. Return unused.
  10. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 0, "", « »).
  11. Let rejectedClosure be a new Abstract Closure with parameters (error) that captures promiseCapability and performs the following steps when called:
    1. Perform ! Call(promiseCapability.[[Reject]], undefined, « error »).
    2. Return unused.
  12. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »).
  13. Perform PerformPromiseThen(importHookPromise, onFulfilled, onRejected).
  14. Return unused.
Note
ResolveModuleRecordDependency Abstract Operation can not be called more than once for arguments moduleRecord and specifier pair.
Editor's Note
Import Reflection Proposal can provide the expansion mechanism to delegate to the host the resolution of a particular dependency, allowing the [[ImportHook]] to return a Module Instance rather than having to open up the ImportHook to support module namespace exotic objects.

6.1.3 HostImportModuleRecordDynamically ( moduleRecord, specifier, promiseCapability )

The host-defined abstract operation HostImportModuleRecordDynamically takes arguments moduleRecord (a Module Record), specifier (a ModuleSpecifier String), and promiseCapability (a PromiseCapability Record) and returns unused. It performs any necessary setup work in order to make available the module corresponding to module represented by moduleRecord. It then performs FinishModuleDynamicImport to finish the dynamic import process.

An implementation of HostImportModuleRecordDynamically must conform to the following requirements:

  • It must call ResolveModuleRecordDependency for every (moduleRecord, specifierString) pair, where specifierString are the entries of moduleRecord.[[RequestedModules]] for each recursively resolved Cyclic Module Record moduleRecord. ResolveModuleRecordDependency must not be called twice with the same (moduleRecord, specifierString) pair: instead, the result of the first call should be cached and reused as the result of successive resolutions.
  • It must return unused. Success or failure must instead be signaled as discussed below.
  • The host environment must conform to one of the two following sets of requirements:
    Success path
    Failure path
    • At some future time, the host environment must perform FinishModuleDynamicImport(moduleRecord, promiseCapability, promise), where promise is a Promise rejected with an error representing the cause of failure.
  • If the host environment takes the success path once for a given moduleRecord, it must always do so for subsequent calls.
  • The operation must not call promiseCapability.[[Resolve]] or promiseCapability.[[Reject]], but instead must treat promiseCapability as an opaque identifying value to be passed through to FinishModuleDynamicImport.
Editor's Note
This host operation is hand-waving, similar to FinishDynamicImport. This will be addressed on a separate refactor that is orthogonal to this proposal.

6.1.4 FinishModuleDynamicImport ( moduleRecord, promiseCapability, innerPromise )

The abstract operation FinishModuleDynamicImport takes arguments moduleRecord (a Module Record), promiseCapability (a PromiseCapability Record), and innerPromise and returns unused. FinishModuleDynamicImport completes the process of a dynamic import originally started by an import() call for a Module Instance, resolving or rejecting the promise returned by that call as appropriate according to innerPromise's resolution. It is performed by host environments as part of HostImportModuleRecordDynamically. It performs the following steps when called:

  1. Let fulfilledClosure be a new Abstract Closure with parameters (result) that captures moduleRecord and promiseCapability and performs the following steps when called:
    1. Assert: result is undefined.
    2. Assert: Evaluate has already been invoked on moduleRecord and successfully completed.
    3. Let namespace be Completion(GetModuleNamespace(moduleRecord)).
    4. If namespace is an abrupt completion, then
      1. Perform ! Call(promiseCapability.[[Reject]], undefined, « namespace.[[Value]] »).
    5. Else,
      1. Perform ! Call(promiseCapability.[[Resolve]], undefined, « namespace.[[Value]] »).
    6. Return unused.
  2. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 0, "", « »).
  3. Let rejectedClosure be a new Abstract Closure with parameters (error) that captures promiseCapability and performs the following steps when called:
    1. Perform ! Call(promiseCapability.[[Reject]], undefined, « error »).
    2. Return unused.
  4. Let onRejected be CreateBuiltinFunction(rejectedClosure, 0, "", « »).
  5. Perform PerformPromiseThen(innerPromise, onFulfilled, onRejected).
  6. Return unused.
Editor's Note
This Abstract Operation is analogous to FinishDynamicImport but for Module Records with an associated Module Instance.

6.2 The Module Constructor

The Module constructor:

  • is the intrinsic object %Module%.
  • is the initial value of the "Module" property of the global object.
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • creates and initializes a new Module object when called as a constructor.

6.2.1 Module ( moduleSource, referral, importHook [, importMeta ] )

When the Module function is called with the arguments moduleSource, referral, importHook and importMeta, the following steps are taken:

  1. If NewTarget is undefined, throw a TypeError exception.
  2. If Type(moduleSource) is not Object, throw a TypeError exception.
  3. If IsCallable(importHook) is false, throw a TypeError exception.
  4. If importMeta is undefined, then
    1. Set importMeta to null.
  5. Else, if Type(importMeta) is not Object, then
    1. Throw a TypeError exception.
  6. Perform ? RequireInternalSlot(moduleSource, [[ModuleSource]]).
  7. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Module.prototype%", « [[Module]], [[ModuleSourceInstance]], [[ImportHook]] »).
  8. Let moduleRecord to ! CreateModuleRecord(moduleSource.[[ModuleSource]]).
  9. Set moduleRecord.[[ModuleInstance]] to O.
  10. Set moduleRecord.[[ImportMeta]] to importMeta.
  11. Let importHookClosure be a new Abstract Closure with parameters (specifier) that captures importHook and referral and performs the following steps when called
    1. Assert: Type(specifier) is String.
    2. return ? Call(importHook, undefined, « specifier, referral »).
  12. Set O.[[Module]] to moduleRecord.
  13. Set O.[[ModuleSourceInstance]] to moduleSource.
  14. Set O.[[ImportHook]] be CreateBuiltinFunction(importHookClosure, 1, "", « »).
  15. Return O.

6.3 Properties of the Module Constructor

The Module constructor:

  • has a [[Prototype]] internal slot whose value is %Function.prototype%.
  • has the following properties:

6.3.1 Module.prototype

The initial value of Module.prototype is %Module.prototype%.

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.

6.4 Properties of the Module Prototype Object

The Module prototype object:

  • has a [[Prototype]] internal slot whose value is %Object.prototype%.
  • is %Module.prototype%.
  • is an ordinary object.
  • does not have a [[Module]] or any other of the internal slots that are specific to Realm instance objects.

6.4.1 Module.prototype.constructor

The initial value of Module.prototype.constructor is %Module%.

6.4.2 get Module.prototype.source

Module.prototype.source is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:

  1. Let M be the this value.
  2. Perform ? RequireInternalSlot(M, [[ModuleSourceInstance]]).
  3. Return M.[[ModuleSourceInstance]].

6.4.3 Module.prototype [ @@toStringTag ]

The initial value of the @@toStringTag property is the String value "Module".

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.

6.5 Properties of Module Instances

Module instances are ordinary objects that inherit properties from the Module prototype object (the intrinsic, %Module.prototype%). Module instances are initially created with the internal slots described in Table 5.

Table 5: Internal Slots of Module Instances
Internal Slot Type Description
[[Module]] Source Text Module Record The Source Text Module Record that represents this Module Instance.
[[ModuleSourceInstance]] ModuleSource Instance or null The Source Text Module Record that represents this Module Instance. null if this Module Instance has not a source associated to it.
[[ImportHook]] a callable Object The callable Object associated to the [[Module]] record to resolve module dependencies.
Editor's Note
A Module Instance without a [[ModuleSourceInstance]] value associated to it is a Module Instance created via the Import Reflection API where the host decides that the source is not available. E.g.: fs module or any built-in module in the future.