16.2 Modules

Syntax

Module : ModuleBodyopt ModuleBody : ModuleItemList ModuleItemList : ModuleItem ModuleItemList ModuleItem ModuleItem : ImportDeclaration ExportDeclaration StatementListItem[~Yield, +Await, ~Return]

16.2.1 Module Semantics

16.2.1.1 Static Semantics: Early Errors

ModuleBody : ModuleItemList Note

The duplicate ExportedNames rule implies that multiple export default ExportDeclaration items within a ModuleBody is a Syntax Error. Additional error conditions relating to conflicting or duplicate declarations are checked during module linking prior to evaluation of a Module. If any such errors are detected the Module is not evaluated.

16.2.1.2 Static Semantics: ImportedLocalNames ( importEntries )

The abstract operation ImportedLocalNames takes argument importEntries (a List of ImportEntry Records (see Table 59)). It creates a List of all of the local name bindings defined by importEntries. It performs the following steps when called:

  1. Let localNames be a new empty List.
  2. For each ImportEntry Record i of importEntries, do
    1. Append i.[[LocalName]] to localNames.
  3. Return localNames.

16.2.1.3 Static Semantics: ModuleRequests

The syntax-directed operation ModuleRequests takes no arguments. It is defined piecewise over the following productions:

Module : [empty]
  1. Return a new empty List.
ModuleItemList : ModuleItem
  1. Return ModuleRequests of ModuleItem.
ModuleItemList : ModuleItemList ModuleItem
  1. Let moduleNames be ModuleRequests of ModuleItemList.
  2. Let additionalNames be ModuleRequests of ModuleItem.
  3. Append to moduleNames each element of additionalNames that is not already an element of moduleNames.
  4. Return moduleNames.
ModuleItem : StatementListItem
  1. Return a new empty List.
ImportDeclaration : import ImportClause FromClause ;
  1. Return ModuleRequests of FromClause.
ModuleSpecifier : StringLiteral
  1. Return a List whose sole element is the SV of StringLiteral.
ExportDeclaration : export ExportFromClause FromClause ;
  1. Return the ModuleRequests of FromClause.
ExportDeclaration : export NamedExports ; export VariableStatement export Declaration export default HoistableDeclaration export default ClassDeclaration export default AssignmentExpression ;
  1. Return a new empty List.

16.2.1.4 Abstract Module Records

A Module Record encapsulates structural information about the imports and exports of a single module. This information is used to link the imports and exports of sets of connected modules. A Module Record includes four fields that are only used when evaluating a module.

For specification purposes Module Record values are values of the Record specification type and can be thought of as existing in a simple object-oriented hierarchy where Module Record is an abstract class with both abstract and concrete subclasses. This specification defines the abstract subclass named Cyclic Module Record and its concrete subclass named Source Text Module Record. Other specifications and implementations may define additional Module Record subclasses corresponding to alternative module definition facilities that they defined.

Module Record defines the fields listed in Table 45. All Module Definition subclasses include at least those fields. Module Record also defines the abstract method list in Table 46. All Module definition subclasses must provide concrete implementations of these abstract methods.

Table 45: Module Record Fields
Field Name Value Type Meaning
[[Realm]] Realm Record The Realm within which this module was created.
[[Environment]] module Environment Record | empty The Environment Record containing the top level bindings for this module. This field is set when the module is linked.
[[Namespace]] Object | empty The Module Namespace Object (28.3) if one has been created for this module.
[[HostDefined]] Any, default value is undefined. Field reserved for use by host environments that need to associate additional information with a module.
Table 46: Abstract Methods of Module Records
Method Purpose
GetExportedNames([exportStarSet]) Return a list of all names that are either directly or indirectly exported from this module.
ResolveExport(exportName [, resolveSet])

Return the binding of a name exported by this module. Bindings are represented by a ResolvedBinding Record, of the form { [[Module]]: Module Record, [[BindingName]]: String }. If the export is a Module Namespace Object without a direct binding in any module, [[BindingName]] will be set to "*namespace*". Return null if the name cannot be resolved, or "ambiguous" if multiple bindings were found.

Each time this operation is called with a specific exportName, resolveSet pair as arguments it must return the same result if it completes normally.

Link()

Prepare the module for evaluation by transitively resolving all module dependencies and creating a module Environment Record.

Evaluate()

Returns a promise for the evaluation of this module and its dependencies, resolving on successful evaluation or if it has already been evaluated successfully, and rejecting for an evaluation error or if it has already been evaluated unsuccessfully. If the promise is rejected, hosts are expected to handle the promise rejection and rethrow the evaluation error.

Link must have completed successfully prior to invoking this method.

16.2.1.5 Cyclic Module Records

A Cyclic Module Record is used to represent information about a module that can participate in dependency cycles with other modules that are subclasses of the Cyclic Module Record type. Module Records that are not subclasses of the Cyclic Module Record type must not participate in dependency cycles with Source Text Module Records.

In addition to the fields defined in Table 45 Cyclic Module Records have the additional fields listed in Table 47

Table 47: Additional Fields of Cyclic Module Records
Field Name Value Type Meaning
[[Status]] unlinked | linking | linked | evaluating | evaluating-async | evaluated Initially unlinked. Transitions to linking, linked, evaluating, possibly evaluating-async, evaluated (in that order) as the module progresses throughout its lifecycle. evaluating-async indicates this module is queued to execute on completion of its asynchronous dependencies or it is a module whose [[HasTLA]] field is true that has been executed and is pending top-level completion.
[[EvaluationError]] An abrupt completion | empty A completion of type throw representing the exception that occurred during evaluation. undefined if no exception occurred or if [[Status]] is not evaluated.
[[DFSIndex]] Integer | empty Auxiliary field used during Link and Evaluate only. If [[Status]] is linking or evaluating, this non-negative number records the point at which the module was first visited during the depth-first traversal of the dependency graph.
[[DFSAncestorIndex]] Integer | empty Auxiliary field used during Link and Evaluate only. If [[Status]] is linking or evaluating, this is either the module's own [[DFSIndex]] or that of an "earlier" module in the same strongly connected component.
[[RequestedModules]] List of String A List of all the ModuleSpecifier strings used by the module represented by this record to request the importation of a module. The List is source code occurrence ordered.
[[CycleRoot]] Cyclic Module Record | empty The first visited module of the cycle, the root DFS ancestor of the strongly connected component. For a module not in a cycle this would be the module itself. Once Evaluate has completed, a module's [[DFSAncestorIndex]] is equal to the [[DFSIndex]] of its [[CycleRoot]].
[[HasTLA]] Boolean Whether this module is individually asynchronous (for example, if it's a Source Text Module Record containing a top-level await). Having an asynchronous dependency does not mean this field is true. This field must not change after the module is parsed.
[[AsyncEvaluation]] Boolean Whether this module is either itself asynchronous or has an asynchronous dependency. Note: The order in which this field is set is used to order queued executions, see 16.2.1.5.2.4.
[[TopLevelCapability]] PromiseCapability Record | empty If this module is the [[CycleRoot]] of some cycle, and Evaluate() was called on some module in that cycle, this field contains the PromiseCapability Record for that entire evaluation. It is used to settle the Promise object that is returned from the Evaluate() abstract method. This field will be empty for any dependencies of that module, unless a top-level Evaluate() has been initiated for some of those dependencies.
[[AsyncParentModules]] List of Cyclic Module Records If this module or a dependency has [[HasTLA]] true, and execution is in progress, this tracks the parent importers of this module for the top-level execution job. These parent modules will not start executing before this module has successfully completed execution.
[[PendingAsyncDependencies]] Integer | empty If this module has any asynchronous dependencies, this tracks the number of asynchronous dependency modules remaining to execute for this module. A module with asynchronous dependencies will be executed when this field reaches 0 and there are no execution errors.

In addition to the methods defined in Table 46 Cyclic Module Records have the additional methods listed in Table 48

Table 48: Additional Abstract Methods of Cyclic Module Records
Method Purpose
InitializeEnvironment() Initialize the Environment Record of the module, including resolving all imported bindings, and create the module's execution context.
ExecuteModule( [ promiseCapability ] ) Evaluate the module's code within its execution context. If this module has true in [[HasTLA]], then a PromiseCapability Record is passed as an argument, and the method is expected to resolve or reject the given capability. In this case, the method must not throw an exception, but instead reject the PromiseCapability Record if necessary.

16.2.1.5.1 Link ( )

The Link concrete method of a Cyclic Module Record module takes no arguments. On success, Link transitions this module's [[Status]] from unlinked to linked. On failure, an exception is thrown and this module's [[Status]] remains unlinked. (Most of the work is done by the auxiliary function InnerModuleLinking.) It performs the following steps when called:

  1. Assert: module.[[Status]] is not linking or evaluating.
  2. Let stack be a new empty List.
  3. Let result be InnerModuleLinking(module, stack, 0).
  4. If result is an abrupt completion, then
    1. For each Cyclic Module Record m of stack, do
      1. Assert: m.[[Status]] is linking.
      2. Set m.[[Status]] to unlinked.
    2. Assert: module.[[Status]] is unlinked.
    3. Return result.
  5. Assert: module.[[Status]] is linked, evaluating-async, or evaluated.
  6. Assert: stack is empty.
  7. Return undefined.

16.2.1.5.1.1 InnerModuleLinking ( module, stack, index )

The abstract operation InnerModuleLinking takes arguments module (a Cyclic Module Record), stack, and index (a non-negative integer). It is used by Link to perform the actual linking process for module, as well as recursively on all other modules in the dependency graph. The stack and index parameters, as well as a module's [[DFSIndex]] and [[DFSAncestorIndex]] fields, keep track of the depth-first search (DFS) traversal. In particular, [[DFSAncestorIndex]] is used to discover strongly connected components (SCCs), such that all modules in an SCC transition to linked together. It performs the following steps when called:

  1. If module is not a Cyclic Module Record, then
    1. Perform ? module.Link().
    2. Return index.
  2. If module.[[Status]] is linking, linked, evaluating-async, or evaluated, then
    1. Return index.
  3. Assert: module.[[Status]] is unlinked.
  4. Set module.[[Status]] to linking.
  5. Set module.[[DFSIndex]] to index.
  6. Set module.[[DFSAncestorIndex]] to index.
  7. Set index to index + 1.
  8. Append module to stack.
  9. For each String required of module.[[RequestedModules]], do
    1. Let requiredModule be ? HostResolveImportedModule(module, required).
    2. Set index to ? InnerModuleLinking(requiredModule, stack, index).
    3. If requiredModule is a Cyclic Module Record, then
      1. Assert: requiredModule.[[Status]] is either linking, linked, evaluating-async, or evaluated.
      2. Assert: requiredModule.[[Status]] is linking if and only if requiredModule is in stack.
      3. If requiredModule.[[Status]] is linking, then
        1. Set module.[[DFSAncestorIndex]] to min(module.[[DFSAncestorIndex]], requiredModule.[[DFSAncestorIndex]]).
  10. Perform ? module.InitializeEnvironment().
  11. Assert: module occurs exactly once in stack.
  12. Assert: module.[[DFSAncestorIndex]] ≤ module.[[DFSIndex]].
  13. If module.[[DFSAncestorIndex]] = module.[[DFSIndex]], then
    1. Let done be false.
    2. Repeat, while done is false,
      1. Let requiredModule be the last element in stack.
      2. Remove the last element of stack.
      3. Assert: requiredModule is a Cyclic Module Record.
      4. Set requiredModule.[[Status]] to linked.
      5. If requiredModule and module are the same Module Record, set done to true.
  14. Return index.

16.2.1.5.2 Evaluate ( )

The Evaluate concrete method of a Cyclic Module Record module takes no arguments. Evaluate transitions this module's [[Status]] from linked to either evaluating-async or evaluated. The first time it is called on a module in a given strongly connected component, Evaluate creates and returns a Promise which resolves when the module has finished evaluating. This Promise is stored in the [[TopLevelCapability]] field of the [[CycleRoot]] for the component. Future invocations of Evaluate on any module in the component return the same Promise. (Most of the work is done by the auxiliary function InnerModuleEvaluation.) It performs the following steps when called:

  1. Assert: This call to Evaluate is not happening at the same time as another call to Evaluate within the surrounding agent.
  2. Assert: module.[[Status]] is linked, evaluating-async, or evaluated.
  3. If module.[[Status]] is evaluating-async or evaluated, set module to module.[[CycleRoot]].
  4. If module.[[TopLevelCapability]] is not empty, then
    1. Return module.[[TopLevelCapability]].[[Promise]].
  5. Let stack be a new empty List.
  6. Let capability be ! NewPromiseCapability(%Promise%).
  7. Set module.[[TopLevelCapability]] to capability.
  8. Let result be InnerModuleEvaluation(module, stack, 0).
  9. If result is an abrupt completion, then
    1. For each Cyclic Module Record m of stack, do
      1. Assert: m.[[Status]] is evaluating.
      2. Set m.[[Status]] to evaluated.
      3. Set m.[[EvaluationError]] to result.
    2. Assert: module.[[Status]] is evaluated.
    3. Assert: module.[[EvaluationError]] is result.
    4. Perform ! Call(capability.[[Reject]], undefined, « result.[[Value]] »).
  10. Else,
    1. Assert: module.[[Status]] is evaluating-async or evaluated.
    2. Assert: module.[[EvaluationError]] is empty.
    3. If module.[[AsyncEvaluation]] is false, then
      1. Assert: module.[[Status]] is evaluated.
      2. Perform ! Call(capability.[[Resolve]], undefined, « undefined »).
    4. Assert: stack is empty.
  11. Return capability.[[Promise]].

16.2.1.5.2.1 InnerModuleEvaluation ( module, stack, index )

The abstract operation InnerModuleEvaluation takes arguments module (a Module Record), stack, and index (a non-negative integer). It is used by Evaluate to perform the actual evaluation process for module, as well as recursively on all other modules in the dependency graph. The stack and index parameters, as well as module's [[DFSIndex]] and [[DFSAncestorIndex]] fields, are used the same way as in InnerModuleLinking. It performs the following steps when called:

  1. If module is not a Cyclic Module Record, then
    1. Let promise be ! module.Evaluate().
    2. Assert: promise.[[PromiseState]] is not pending.
    3. If promise.[[PromiseState]] is rejected, then
      1. Return ThrowCompletion(promise.[[PromiseResult]]).
    4. Return index.
  2. If module.[[Status]] is evaluating-async or evaluated, then
    1. If module.[[EvaluationError]] is empty, return index.
    2. Otherwise, return module.[[EvaluationError]].
  3. If module.[[Status]] is evaluating, return index.
  4. Assert: module.[[Status]] is linked.
  5. Set module.[[Status]] to evaluating.
  6. Set module.[[DFSIndex]] to index.
  7. Set module.[[DFSAncestorIndex]] to index.
  8. Set module.[[PendingAsyncDependencies]] to 0.
  9. Set index to index + 1.
  10. Append module to stack.
  11. For each String required of module.[[RequestedModules]], do
    1. Let requiredModule be ! HostResolveImportedModule(module, required).
    2. NOTE: Link must be completed successfully prior to invoking this method, so every requested module is guaranteed to resolve successfully.
    3. Set index to ? InnerModuleEvaluation(requiredModule, stack, index).
    4. If requiredModule is a Cyclic Module Record, then
      1. Assert: requiredModule.[[Status]] is either evaluating, evaluating-async, or evaluated.
      2. Assert: requiredModule.[[Status]] is evaluating if and only if requiredModule is in stack.
      3. If requiredModule.[[Status]] is evaluating, then
        1. Set module.[[DFSAncestorIndex]] to min(module.[[DFSAncestorIndex]], requiredModule.[[DFSAncestorIndex]]).
      4. Else,
        1. Set requiredModule to requiredModule.[[CycleRoot]].
        2. Assert: requiredModule.[[Status]] is evaluating-async or evaluated.
        3. If requiredModule.[[EvaluationError]] is not empty, return requiredModule.[[EvaluationError]].
      5. If requiredModule.[[AsyncEvaluation]] is true, then
        1. Set module.[[PendingAsyncDependencies]] to module.[[PendingAsyncDependencies]] + 1.
        2. Append module to requiredModule.[[AsyncParentModules]].
  12. If module.[[PendingAsyncDependencies]] > 0 or module.[[HasTLA]] is true, then
    1. Assert: module.[[AsyncEvaluation]] is false and was never previously set to true.
    2. Set module.[[AsyncEvaluation]] to true.
    3. NOTE: The order in which module records have their [[AsyncEvaluation]] fields transition to true is significant. (See 16.2.1.5.2.4.)
    4. If module.[[PendingAsyncDependencies]] is 0, perform ! ExecuteAsyncModule(module).
  13. Otherwise, perform ? module.ExecuteModule().
  14. Assert: module occurs exactly once in stack.
  15. Assert: module.[[DFSAncestorIndex]] ≤ module.[[DFSIndex]].
  16. If module.[[DFSAncestorIndex]] = module.[[DFSIndex]], then
    1. Let done be false.
    2. Repeat, while done is false,
      1. Let requiredModule be the last element in stack.
      2. Remove the last element of stack.
      3. Assert: requiredModule is a Cyclic Module Record.
      4. If requiredModule.[[AsyncEvaluation]] is false, set requiredModule.[[Status]] to evaluated.
      5. Otherwise, set requiredModule.[[Status]] to evaluating-async.
      6. If requiredModule and module are the same Module Record, set done to true.
      7. Set requiredModule.[[CycleRoot]] to module.
  17. Return index.
Note 1

A module is evaluating while it is being traversed by InnerModuleEvaluation. A module is evaluated on execution completion or evaluating-async during execution if its [[HasTLA]] field is true or if it has asynchronous dependencies.

Note 2

Any modules depending on a module of an asynchronous cycle when that cycle is not evaluating will instead depend on the execution of the root of the cycle via [[CycleRoot]]. This ensures that the cycle state can be treated as a single strongly connected component through its root module state.

16.2.1.5.2.2 ExecuteAsyncModule ( module )

The abstract operation ExecuteAsyncModule takes argument module (a Cyclic Module Record). It performs the following steps when called:

  1. Assert: module.[[Status]] is evaluating or evaluating-async.
  2. Assert: module.[[HasTLA]] is true.
  3. Let capability be ! NewPromiseCapability(%Promise%).
  4. Let fulfilledClosure be a new Abstract Closure with no parameters that captures module and performs the following steps when called:
    1. Perform ! AsyncModuleExecutionFulfilled(module).
    2. Return undefined.
  5. Let onFulfilled be ! CreateBuiltinFunction(fulfilledClosure, 0, "", « »).
  6. Let rejectedClosure be a new Abstract Closure with parameters (error) that captures module and performs the following steps when called:
    1. Perform ! AsyncModuleExecutionRejected(module, error).
    2. Return undefined.
  7. Let onRejected be ! CreateBuiltinFunction(rejectedClosure, 0, "", « »).
  8. Perform ! PerformPromiseThen(capability.[[Promise]], onFulfilled, onRejected).
  9. Perform ! module.ExecuteModule(capability).

16.2.1.5.2.3 GatherAvailableAncestors ( module, execList )

The abstract operation GatherAvailableAncestors takes arguments module (a Cyclic Module Record) and execList (a List of Cyclic Module Records). It performs the following steps when called:

  1. For each Cyclic Module Record m of module.[[AsyncParentModules]], do
    1. If execList does not contain m and m.[[CycleRoot]].[[EvaluationError]] is empty, then
      1. Assert: m.[[Status]] is evaluating-async.
      2. Assert: m.[[EvaluationError]] is empty.
      3. Assert: m.[[AsyncEvaluation]] is true.
      4. Assert: m.[[PendingAsyncDependencies]] > 0.
      5. Set m.[[PendingAsyncDependencies]] to m.[[PendingAsyncDependencies]] - 1.
      6. If m.[[PendingAsyncDependencies]] = 0, then
        1. Append m to execList.
        2. If m.[[HasTLA]] is false, perform ! GatherAvailableAncestors(m, execList).
Note

When an asynchronous execution for a root module is fulfilled, this function determines the list of modules which are able to synchronously execute together on this completion, populating them in execList.

16.2.1.5.2.4 AsyncModuleExecutionFulfilled ( module )

The abstract operation AsyncModuleExecutionFulfilled takes argument module (a Cyclic Module Record). It performs the following steps when called:

  1. If module.[[Status]] is evaluated, then
    1. Assert: module.[[EvaluationError]] is not empty.
    2. Return.
  2. Assert: module.[[Status]] is evaluating-async.
  3. Assert: module.[[AsyncEvaluation]] is true.
  4. Assert: module.[[EvaluationError]] is empty.
  5. Set module.[[AsyncEvaluation]] to false.
  6. Set module.[[Status]] to evaluated.
  7. If module.[[TopLevelCapability]] is not empty, then
    1. Assert: module.[[CycleRoot]] is module.
    2. Perform ! Call(module.[[TopLevelCapability]].[[Resolve]], undefined, « undefined »).
  8. Let execList be a new empty List.
  9. Perform ! GatherAvailableAncestors(module, execList).
  10. Let sortedExecList be a List whose elements are the elements of execList, in the order in which they had their [[AsyncEvaluation]] fields set to true in InnerModuleEvaluation.
  11. Assert: All elements of sortedExecList have their [[AsyncEvaluation]] field set to true, [[PendingAsyncDependencies]] field set to 0, and [[EvaluationError]] field set to empty.
  12. For each Cyclic Module Record m of sortedExecList, do
    1. If m.[[Status]] is evaluated, then
      1. Assert: m.[[EvaluationError]] is not empty.
    2. Else if m.[[HasTLA]] is true, then
      1. Perform ! ExecuteAsyncModule(m).
    3. Else,
      1. Let result be m.ExecuteModule().
      2. If result is an abrupt completion, then
        1. Perform ! AsyncModuleExecutionRejected(m, result.[[Value]]).
      3. Else,
        1. Set m.[[Status]] to evaluated.
        2. If m.[[TopLevelCapability]] is not empty, then
          1. Assert: m.[[CycleRoot]] is m.
          2. Perform ! Call(m.[[TopLevelCapability]].[[Resolve]], undefined, « undefined »).

16.2.1.5.2.5 AsyncModuleExecutionRejected ( module, error )

The abstract operation AsyncModuleExecutionRejected takes arguments module (a Cyclic Module Record) and error (an ECMAScript language value). It performs the following steps when called:

  1. If module.[[Status]] is evaluated, then
    1. Assert: module.[[EvaluationError]] is not empty.
    2. Return.
  2. Assert: module.[[Status]] is evaluating-async.
  3. Assert: module.[[AsyncEvaluation]] is true.
  4. Assert: module.[[EvaluationError]] is empty.
  5. Set module.[[EvaluationError]] to ThrowCompletion(error).
  6. Set module.[[Status]] to evaluated.
  7. For each Cyclic Module Record m of module.[[AsyncParentModules]], do
    1. Perform ! AsyncModuleExecutionRejected(m, error).
  8. If module.[[TopLevelCapability]] is not empty, then
    1. Assert: module.[[CycleRoot]] is module.
    2. Perform ! Call(module.[[TopLevelCapability]].[[Reject]], undefined, « error »).

16.2.1.5.3 Example Cyclic Module Record Graphs

This non-normative section gives a series of examples of the linking and evaluation of a few common module graphs, with a specific focus on how errors can occur.

First consider the following simple module graph:

Figure 2: A simple module graph
A module graph in which module A depends on module B, and module B depends on module C

Let's first assume that there are no error conditions. When a host first calls A.Link(), this will complete successfully by assumption, and recursively link modules B and C as well, such that A.[[Status]] = B.[[Status]] = C.[[Status]] = linked. This preparatory step can be performed at any time. Later, when the host is ready to incur any possible side effects of the modules, it can call A.Evaluate(), which will complete successfully, returning a Promise resolving to undefined (again by assumption), recursively having evaluated first C and then B. Each module's [[Status]] at this point will be evaluated.

Consider then cases involving linking errors. If InnerModuleLinking of C succeeds but, thereafter, fails for B, for example because it imports something that C does not provide, then the original A.Link() will fail, and both A and B's [[Status]] remain unlinked. C's [[Status]] has become linked, though.

Finally, consider a case involving evaluation errors. If InnerModuleEvaluation of C succeeds but, thereafter, fails for B, for example because B contains code that throws an exception, then the original A.Evaluate() will fail, returning a rejected Promise. The resulting exception will be recorded in both A and B's [[EvaluationError]] fields, and their [[Status]] will become evaluated. C will also become evaluated but, in contrast to A and B, will remain without an [[EvaluationError]], as it successfully completed evaluation. Storing the exception ensures that any time a host tries to reuse A or B by calling their Evaluate() method, it will encounter the same exception. (Hosts are not required to reuse Cyclic Module Records; similarly, hosts are not required to expose the exception objects thrown by these methods. However, the specification enables such uses.)

The difference here between linking and evaluation errors is due to how evaluation must be only performed once, as it can cause side effects; it is thus important to remember whether evaluation has already been performed, even if unsuccessfully. (In the error case, it makes sense to also remember the exception because otherwise subsequent Evaluate() calls would have to synthesize a new one.) Linking, on the other hand, is side-effect-free, and thus even if it fails, it can be retried at a later time with no issues.

Now consider a different type of error condition:

Figure 3: A module graph with an unresolvable module
A module graph in which module A depends on a missing (unresolvable) module, represented by ???

In this scenario, module A declares a dependency on some other module, but no Module Record exists for that module, i.e. HostResolveImportedModule throws an exception when asked for it. This could occur for a variety of reasons, such as the corresponding resource not existing, or the resource existing but ParseModule throwing an exception when trying to parse the resulting source text. Hosts can choose to expose the cause of failure via the exception they throw from HostResolveImportedModule. In any case, this exception causes a linking failure, which as before results in A's [[Status]] remaining unlinked.

Now, consider a module graph with a cycle:

Figure 4: A cyclic module graph
A module graph in which module A depends on module B and C, but module B also depends on module A

Here we assume that the entry point is module A, so that the host proceeds by calling A.Link(), which performs InnerModuleLinking on A. This in turn calls InnerModuleLinking on B. Because of the cycle, this again triggers InnerModuleLinking on A, but at this point it is a no-op since A.[[Status]] is already linking. B.[[Status]] itself remains linking when control gets back to A and InnerModuleLinking is triggered on C. After this returns with C.[[Status]] being linked, both A and B transition from linking to linked together; this is by design, since they form a strongly connected component.

An analogous story occurs for the evaluation phase of a cyclic module graph, in the success case.

Now consider a case where A has an linking error; for example, it tries to import a binding from C that does not exist. In that case, the above steps still occur, including the early return from the second call to InnerModuleLinking on A. However, once we unwind back to the original InnerModuleLinking on A, it fails during InitializeEnvironment, namely right after C.ResolveExport(). The thrown SyntaxError exception propagates up to A.Link, which resets all modules that are currently on its stack (these are always exactly the modules that are still linking). Hence both A and B become unlinked. Note that C is left as linked.

Alternatively, consider a case where A has an evaluation error; for example, its source code throws an exception. In that case, the evaluation-time analog of the above steps still occurs, including the early return from the second call to InnerModuleEvaluation on A. However, once we unwind back to the original InnerModuleEvaluation on A, it fails by assumption. The exception thrown propagates up to A.Evaluate(), which records the error in all modules that are currently on its stack (i.e., the modules that are still evaluating) as well as via [[AsyncParentModules]], which form a chain for modules which contain or depend on top-level await through the whole dependency graph through the AsyncModuleExecutionRejected algorithm. Hence both A and B become evaluated and the exception is recorded in both A and B's [[EvaluationError]] fields, while C is left as evaluated with no [[EvaluationError]].

Lastly, consider a module graph with a cycle, where all modules complete asynchronously:

Figure 5: An asynchronous cyclic module graph
A module graph in which module A depends on module B and C, module B depends on module D, module C depends on module D and E, and module D depends on module A

Linking happens as before, and all modules end up with [[Status]] set to linked.

Calling A.Evaluate() calls InnerModuleEvaluation on A, B, and D, which all transition to evaluating. Then InnerModuleEvaluation is called on A again, which is a no-op because it is already evaluating. At this point, D.[[PendingAsyncDependencies]] is 0, so ExecuteAsyncModule(D) is called and we call D.ExecuteModule with a new PromiseCapability tracking the asynchronous execution of D. We unwind back to the InnerModuleEvaluation on B, setting B.[[PendingAsyncDependencies]] to 1 and B.[[AsyncEvaluation]] to true. We unwind back to the original InnerModuleEvaluation on A, setting A.[[PendingAsyncDependencies]] to 1. In the next iteration of the loop over A's dependencies, we call InnerModuleEvaluation on C and thus on D (again a no-op) and E. As E has no dependencies and is not part of a cycle, we call ExecuteAsyncModule(E) in the same manner as D and E is immediately removed from the stack. We unwind once more to the original InnerModuleEvaluation on A, setting C.[[AsyncEvaluation]] to true. Now we finish the loop over A's dependencies, set A.[[AsyncEvaluation]] to true, and remove the entire strongly connected component from the stack, transitioning all of the modules to evaluating-async at once. At this point, the fields of the modules are as given in Table 49.

Table 49: Module fields after the initial Evaluate() call
Module [[DFSIndex]] [[DFSAncestorIndex]] [[Status]] [[AsyncEvaluation]] [[AsyncParentModules]] [[PendingAsyncDependencies]]
A 0 0 evaluating-async true « » 2 (B and C)
B 1 0 evaluating-async true « A » 1 (D)
C 2 0 evaluating-async true « A » 2 (D and E)
D 3 0 evaluating-async true « B, C » 0
E 4 4 evaluating-async true « C » 0

Let us assume that E finishes executing first. When that happens, AsyncModuleExecutionFulfilled is called, E.[[Status]] is set to evaluated and C.[[PendingAsyncDependencies]] is decremented to become 1. The fields of the updated modules are as given in Table 50.

Table 50: Module fields after module E finishes executing
Module [[DFSIndex]] [[DFSAncestorIndex]] [[Status]] [[AsyncEvaluation]] [[AsyncParentModules]] [[PendingAsyncDependencies]]
C 2 0 evaluating-async true « A » 1 (D)
E 4 4 evaluated true « C » 0

D is next to finish (as it was the only module that was still executing). When that happens, AsyncModuleExecutionFulfilled is called again and D.[[Status]] is set to evaluated. Then B.[[PendingAsyncDependencies]] is decremented to become 0, ExecuteAsyncModule is called on B, and it starts executing. C.[[PendingAsyncDependencies]] is also decremented to become 0, and C starts executing (potentially in parallel to B if B contains an await). The fields of the updated modules are as given in Table 51.

Table 51: Module fields after module D finishes executing
Module [[DFSIndex]] [[DFSAncestorIndex]] [[Status]] [[AsyncEvaluation]] [[AsyncParentModules]] [[PendingAsyncDependencies]]
B 1 0 evaluating-async true « A » 0
C 2 0 evaluating-async true « A » 0
D 3 0 evaluated true « B, C » 0

Let us assume that C finishes executing next. When that happens, AsyncModuleExecutionFulfilled is called again, C.[[Status]] is set to evaluated and A.[[PendingAsyncDependencies]] is decremented to become 1. The fields of the updated modules are as given in Table 52.

Table 52: Module fields after module C finishes executing
Module [[DFSIndex]] [[DFSAncestorIndex]] [[Status]] [[AsyncEvaluation]] [[AsyncParentModules]] [[PendingAsyncDependencies]]
A 0 0 evaluating-async true « » 1 (B)
C 2 0 evaluated true « A » 0

Then, B finishes executing. When that happens, AsyncModuleExecutionFulfilled is called again and B.[[Status]] is set to evaluated. A.[[PendingAsyncDependencies]] is decremented to become 0, so ExecuteAsyncModule is called and it starts executing. The fields of the updated modules are as given in Table 53.

Table 53: Module fields after module B finishes executing
Module [[DFSIndex]] [[DFSAncestorIndex]] [[Status]] [[AsyncEvaluation]] [[AsyncParentModules]] [[PendingAsyncDependencies]]
A 0 0 evaluating-async true « » 0
B 1 0 evaluated true « A » 0

Finally, A finishes executing. When that happens, AsyncModuleExecutionFulfilled is called again and A.[[Status]] is set to evaluated. At this point, the Promise in A.[[TopLevelCapability]] (which was returned from A.Evaluate()) is resolved, and this concludes the handling of this module graph. The fields of the updated module are as given in Table 54.

Table 54: Module fields after module A finishes executing
Module [[DFSIndex]] [[DFSAncestorIndex]] [[Status]] [[AsyncEvaluation]] [[AsyncParentModules]] [[PendingAsyncDependencies]]
A 0 0 evaluated true « » 0

Alternatively, consider a failure case where C fails execution and returns an error before B has finished executing. When that happens, AsyncModuleExecutionRejected is called, which sets C.[[Status]] to evaluated and C.[[EvaluationError]] to the error. It then propagates this error to all of the AsyncParentModules by performing AsyncModuleExecutionRejected on each of them. The fields of the updated modules are as given in Table 55.

Table 55: Module fields after module C finishes with an error
Module [[DFSIndex]] [[DFSAncestorIndex]] [[Status]] [[AsyncEvaluation]] [[AsyncParentModules]] [[PendingAsyncDependencies]] [[EvaluationError]]
A 0 0 evaluated true « » 1 (B) empty
C 2 1 evaluated true « A » 0 C's evaluation error

A will be rejected with the same error as C since C will call AsyncModuleExecutionRejected on A with C's error. A.[[Status]] is set to evaluated. At this point the Promise in A.[[TopLevelCapability]] (which was returned from A.Evaluate()) is rejected. The fields of the updated module are as given in Table 56.

Table 56: Module fields after module A is rejected
Module [[DFSIndex]] [[DFSAncestorIndex]] [[Status]] [[AsyncEvaluation]] [[AsyncParentModules]] [[PendingAsyncDependencies]] [[EvaluationError]]
A 0 0 evaluated true « » 0 C's Evaluation Error

Then, B finishes executing without an error. When that happens, AsyncModuleExecutionFulfilled is called again and B.[[Status]] is set to evaluated. GatherAvailableAncestors is called on B. However, A.[[CycleRoot]] is A which has an evaluation error, so it will not be added to the returned sortedExecList and AsyncModuleExecutionFulfilled will return without further processing. Any future importer of B will resolve the rejection of B.[[CycleRoot]].[[EvaluationError]] from the evaluation error from C that was set on the cycle root A. The fields of the updated modules are as given in Table 57.

Table 57: Module fields after module B finishes executing in an erroring graph
Module [[DFSIndex]] [[DFSAncestorIndex]] [[Status]] [[AsyncEvaluation]] [[AsyncParentModules]] [[PendingAsyncDependencies]] [[EvaluationError]]
A 0 0 evaluated true « » 0 C's Evaluation Error
B 1 0 evaluated true « A » 0 empty

16.2.1.6 Source Text Module Records

A Source Text Module Record is used to represent information about a module 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 by the module and its concrete methods use this digest to link, link, and evaluate the module.

A Source Text Module Record can exist in a module graph with other subclasses of the abstract Module Record type, and can participate in cycles with other subclasses of the Cyclic Module Record type.

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

Table 58: Additional Fields of Source Text Module 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.
[[Context]] An ECMAScript execution context. The execution context associated with this module.
[[ImportMeta]] Object An object exposed through the import.meta meta property. It is empty until it is accessed by ECMAScript code.
[[ImportEntries]] List of ImportEntry Records A List of ImportEntry records derived from the code of this module.
[[LocalExportEntries]] 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.
[[IndirectExportEntries]] List of ExportEntry Records A List of ExportEntry records derived from the code of this module that correspond to reexported imports that occur within the module or exports from export * as namespace declarations.
[[StarExportEntries]] List of ExportEntry Records A List of ExportEntry records derived from the code of this module that correspond to export * declarations that occur within the module, not including export * as namespace declarations.

An ImportEntry Record is a Record that digests information about a single declarative import. Each ImportEntry Record has the fields defined in Table 59:

Table 59: ImportEntry Record Fields
Field Name Value Type Meaning
[[ModuleRequest]] String String value of the ModuleSpecifier of the ImportDeclaration.
[[ImportName]] String The name under which the desired binding is exported by the module identified by [[ModuleRequest]]. The value "*" indicates that the import request is for the target module's namespace object.
[[LocalName]] String The name that is used to locally access the imported value from within the importing module.
Note 1

Table 60 gives examples of ImportEntry records fields used to represent the syntactic import forms:

Table 60 (Informative): Import Forms Mappings to ImportEntry Records
Import Statement Form [[ModuleRequest]] [[ImportName]] [[LocalName]]
import v from "mod"; "mod" "default" "v"
import * as ns from "mod"; "mod" "*" "ns"
import {x} from "mod"; "mod" "x" "x"
import {x as v} from "mod"; "mod" "x" "v"
import "mod"; An ImportEntry Record is not created.

An ExportEntry Record is a Record that digests information about a single declarative export. Each ExportEntry Record has the fields defined in Table 61:

Table 61: ExportEntry Record Fields
Field Name Value Type Meaning
[[ExportName]] String | null The name used to export this binding by this module.
[[ModuleRequest]] String | null The String value of the ModuleSpecifier of the ExportDeclaration. null if the ExportDeclaration does not have a ModuleSpecifier.
[[ImportName]] String | null The name under which the desired binding is exported by the module identified by [[ModuleRequest]]. null if the ExportDeclaration does not have a ModuleSpecifier. "*" indicates that the export request is for all exported bindings.
[[LocalName]] String | null The name that is used to locally access the exported value from within the importing module. null if the exported value is not locally accessible from within the module.
Note 2

Table 62 gives examples of the ExportEntry record fields used to represent the syntactic export forms:

Table 62 (Informative): Export Forms Mappings to ExportEntry Records
Export Statement Form [[ExportName]] [[ModuleRequest]] [[ImportName]] [[LocalName]]
export var v; "v" null null "v"
export default function f() {} "default" null null "f"
export default function () {} "default" null null "*default*"
export default 42; "default" null null "*default*"
export {x}; "x" null null "x"
export {v as x}; "x" null null "v"
export {x} from "mod"; "x" "mod" "x" null
export {v as x} from "mod"; "x" "mod" "v" null
export * from "mod"; null "mod" "*" null
export * as ns from "mod"; "ns" "mod" "*" null

The following definitions specify the required concrete methods and other abstract operations for Source Text Module Records

16.2.1.6.1 ParseModule ( sourceText, realm, hostDefined )

The abstract operation ParseModule takes arguments sourceText (ECMAScript source text), realm, and hostDefined. It creates a Source Text Module 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, return body.
  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 "*", 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 "*" and ee.[[ExportName]] is null, then
      1. Append ee to starExportEntries.
    3. Else,
      1. Append ee to indirectExportEntries.
  11. Let async be body Contains await.
  12. 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 }.
Note

An implementation may parse module source text and analyse it for Early Error conditions prior to the evaluation of ParseModule for that module source text. However, the reporting of any errors must be deferred until the point where this specification actually performs ParseModule upon that source text.

16.2.1.6.2 GetExportedNames ( [ exportStarSet ] )

The GetExportedNames concrete method of a Source Text Module Record module takes optional argument exportStarSet (a List of Source Text Module Records). It performs the following steps when called:

  1. If exportStarSet is not present, set exportStarSet to a new empty List.
  2. If exportStarSet contains module, then
    1. Assert: We've reached the starting point of an export * circularity.
    2. Return a new empty List.
  3. Append module to exportStarSet.
  4. Let exportedNames be a new empty List.
  5. For each ExportEntry Record e of module.[[LocalExportEntries]], do
    1. Assert: module provides the direct binding for this export.
    2. Append e.[[ExportName]] to exportedNames.
  6. For each ExportEntry Record e of module.[[IndirectExportEntries]], do
    1. Assert: module imports a specific binding for this export.
    2. Append e.[[ExportName]] to exportedNames.
  7. For each ExportEntry Record e of module.[[StarExportEntries]], do
    1. Let requestedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]).
    2. Let starNames be ? requestedModule.GetExportedNames(exportStarSet).
    3. For each element n of starNames, do
      1. If SameValue(n, "default") is false, then
        1. If n is not an element of exportedNames, then
          1. Append n to exportedNames.
  8. Return exportedNames.
Note

GetExportedNames does not filter out or throw an exception for names that have ambiguous star export bindings.

16.2.1.6.3 ResolveExport ( exportName [ , resolveSet ] )

The ResolveExport concrete method of a Source Text Module Record module takes argument exportName (a String) and optional argument resolveSet (a List of Records that have [[Module]] and [[ExportName]] fields).

ResolveExport attempts to resolve an imported binding to the actual defining module and local binding name. The defining module may be the module represented by the Module Record this method was invoked on or some other module that is imported by that module. The parameter resolveSet is used to detect unresolved circular import/export paths. If a pair consisting of specific Module Record and exportName is reached that is already in resolveSet, an import circularity has been encountered. Before recursively calling ResolveExport, a pair consisting of module and exportName is added to resolveSet.

If a defining module is found, a ResolvedBinding Record { [[Module]], [[BindingName]] } is returned. This record identifies the resolved binding of the originally requested export, unless this is the export of a namespace with no local binding. In this case, [[BindingName]] will be set to "*namespace*". If no definition was found or the request is found to be circular, null is returned. If the request is found to be ambiguous, the string "ambiguous" is returned.

It performs the following steps when called:

  1. If resolveSet is not present, set resolveSet to a new empty List.
  2. For each Record { [[Module]], [[ExportName]] } r of resolveSet, do
    1. If module and r.[[Module]] are the same Module Record and SameValue(exportName, r.[[ExportName]]) is true, then
      1. Assert: This is a circular import request.
      2. Return null.
  3. Append the Record { [[Module]]: module, [[ExportName]]: exportName } to resolveSet.
  4. For each ExportEntry Record e of module.[[LocalExportEntries]], do
    1. If SameValue(exportName, e.[[ExportName]]) is true, then
      1. Assert: module provides the direct binding for this export.
      2. Return ResolvedBinding Record { [[Module]]: module, [[BindingName]]: e.[[LocalName]] }.
  5. For each ExportEntry Record e of module.[[IndirectExportEntries]], do
    1. If SameValue(exportName, e.[[ExportName]]) is true, then
      1. Let importedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]).
      2. If e.[[ImportName]] is "*", then
        1. Assert: module does not provide the direct binding for this export.
        2. Return ResolvedBinding Record { [[Module]]: importedModule, [[BindingName]]: "*namespace*" }.
      3. Else,
        1. Assert: module imports a specific binding for this export.
        2. Return importedModule.ResolveExport(e.[[ImportName]], resolveSet).
  6. If SameValue(exportName, "default") is true, then
    1. Assert: A default export was not explicitly defined by this module.
    2. Return null.
    3. NOTE: A default export cannot be provided by an export * or export * from "mod" declaration.
  7. Let starResolution be null.
  8. For each ExportEntry Record e of module.[[StarExportEntries]], do
    1. Let importedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]).
    2. Let resolution be ? importedModule.ResolveExport(exportName, resolveSet).
    3. If resolution is "ambiguous", return "ambiguous".
    4. If resolution is not null, then
      1. Assert: resolution is a ResolvedBinding Record.
      2. If starResolution is null, set starResolution to resolution.
      3. Else,
        1. Assert: There is more than one * import that includes the requested name.
        2. If resolution.[[Module]] and starResolution.[[Module]] are not the same Module Record or SameValue(resolution.[[BindingName]], starResolution.[[BindingName]]) is false, return "ambiguous".
  9. Return starResolution.

16.2.1.6.4 InitializeEnvironment ( )

The InitializeEnvironment concrete method of a Source Text Module Record module takes no arguments. It performs the following steps when called:

  1. For each ExportEntry Record e of module.[[IndirectExportEntries]], do
    1. Let resolution be ? module.ResolveExport(e.[[ExportName]]).
    2. If resolution is null or "ambiguous", throw a SyntaxError exception.
    3. Assert: resolution is a ResolvedBinding Record.
  2. Assert: All named exports from module are resolvable.
  3. Let realm be module.[[Realm]].
  4. Assert: realm is not undefined.
  5. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]).
  6. Set module.[[Environment]] to env.
  7. For each ImportEntry Record in of module.[[ImportEntries]], do
    1. Let importedModule be ! HostResolveImportedModule(module, in.[[ModuleRequest]]).
    2. NOTE: The above call cannot fail because imported module requests are a subset of module.[[RequestedModules]], and these have been resolved earlier in this algorithm.
    3. If in.[[ImportName]] is "*", then
      1. Let namespace be ? GetModuleNamespace(importedModule).
      2. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true).
      3. Call env.InitializeBinding(in.[[LocalName]], namespace).
    4. Else,
      1. Let resolution be ? importedModule.ResolveExport(in.[[ImportName]]).
      2. If resolution is null or "ambiguous", throw a SyntaxError exception.
      3. If resolution.[[BindingName]] is "*namespace*", then
        1. Let namespace be ? GetModuleNamespace(resolution.[[Module]]).
        2. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true).
        3. Call env.InitializeBinding(in.[[LocalName]], namespace).
      4. Else,
        1. Call env.CreateImportBinding(in.[[LocalName]], resolution.[[Module]], resolution.[[BindingName]]).
  8. Let moduleContext be a new ECMAScript code execution context.
  9. Set the Function of moduleContext to null.
  10. Assert: module.[[Realm]] is not undefined.
  11. Set the Realm of moduleContext to module.[[Realm]].
  12. Set the ScriptOrModule of moduleContext to module.
  13. Set the VariableEnvironment of moduleContext to module.[[Environment]].
  14. Set the LexicalEnvironment of moduleContext to module.[[Environment]].
  15. Set the PrivateEnvironment of moduleContext to null.
  16. Set module.[[Context]] to moduleContext.
  17. Push moduleContext onto the execution context stack; moduleContext is now the running execution context.
  18. Let code be module.[[ECMAScriptCode]].
  19. Let varDeclarations be the VarScopedDeclarations of code.
  20. Let declaredVarNames be a new empty List.
  21. For each element d of varDeclarations, do
    1. For each element dn of the BoundNames of d, do
      1. If dn is not an element of declaredVarNames, then
        1. Perform ! env.CreateMutableBinding(dn, false).
        2. Call env.InitializeBinding(dn, undefined).
        3. Append dn to declaredVarNames.
  22. Let lexDeclarations be the LexicallyScopedDeclarations of code.
  23. Let privateEnv be null.
  24. For each element d of lexDeclarations, do
    1. For each element dn of the BoundNames of d, do
      1. If IsConstantDeclaration of d is true, then
        1. Perform ! env.CreateImmutableBinding(dn, true).
      2. Else,
        1. Perform ! env.CreateMutableBinding(dn, false).
      3. If d is a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration, then
        1. Let fo be InstantiateFunctionObject of d with arguments env and privateEnv.
        2. Call env.InitializeBinding(dn, fo).
  25. Remove moduleContext from the execution context stack.
  26. Return NormalCompletion(empty).

16.2.1.6.5 ExecuteModule ( [ capability ] )

The ExecuteModule concrete method of a Source Text Module Record module takes optional argument capability. It performs the following steps when called:

  1. Let moduleContext be a new ECMAScript code execution context.
  2. Set the Function of moduleContext to null.
  3. Set the Realm of moduleContext to module.[[Realm]].
  4. Set the ScriptOrModule of moduleContext to module.
  5. Assert: module has been linked and declarations in its module environment have been instantiated.
  6. Set the VariableEnvironment of moduleContext to module.[[Environment]].
  7. Set the LexicalEnvironment of moduleContext to module.[[Environment]].
  8. Suspend the currently running execution context.
  9. If module.[[HasTLA]] is false, then
    1. Assert: capability is not present.
    2. Push moduleContext onto the execution context stack; moduleContext is now the running execution context.
    3. Let result be the result of evaluating module.[[ECMAScriptCode]].
    4. Suspend moduleContext and remove it from the execution context stack.
    5. Resume the context that is now on the top of the execution context stack as the running execution context.
    6. Return Completion(result).
  10. Else,
    1. Assert: capability is a PromiseCapability Record.
    2. Perform ! AsyncBlockStart(capability, module.[[ECMAScriptCode]], moduleContext).
    3. Return NormalCompletion(empty).

16.2.1.7 HostResolveImportedModule ( referencingScriptOrModule, specifier )

The host-defined abstract operation HostResolveImportedModule takes arguments referencingScriptOrModule (a Script Record or Module Record or null) and specifier (a ModuleSpecifier String). It provides the concrete Module Record subclass instance that corresponds to specifier occurring within the context of the script or module represented by referencingScriptOrModule. referencingScriptOrModule may be null if the resolution is being performed in the context of an import() expression and there is no active script or module at that time.

Note

An example of when referencingScriptOrModule can be null is in a web browser host. There, if a user clicks on a control given by

<button type="button" onclick="import('./foo.mjs')">Click me</button>

there will be no active script or module at the time the import() expression runs. More generally, this can happen in any situation where the host pushes execution contexts with null ScriptOrModule components onto the execution context stack.

An implementation of HostResolveImportedModule must conform to the following requirements:

  • If it completes normally, the [[Value]] slot of the completion must contain an instance of a concrete subclass of Module Record.
  • If a Module Record corresponding to the pair referencingScriptOrModule, specifier does not exist or cannot be created, an exception must be thrown.
  • Each time this operation is called with a specific referencingScriptOrModule, specifier pair as arguments it must return the same Module Record instance if it completes normally.

Multiple different referencingScriptOrModule, specifier pairs may map to the same Module Record instance. The actual mapping semantic is host-defined but typically a normalization process is applied to specifier as part of the mapping process. A typical normalization process would include actions such as alphabetic case folding and expansion of relative and abbreviated path specifiers.

16.2.1.8 HostImportModuleDynamically ( referencingScriptOrModule, specifier, promiseCapability )

The host-defined abstract operation HostImportModuleDynamically takes arguments referencingScriptOrModule (a Script Record or Module Record or null), specifier (a ModuleSpecifier String), and promiseCapability (a PromiseCapability Record). It performs any necessary setup work in order to make available the module corresponding to specifier occurring within the context of the script or module represented by referencingScriptOrModule. referencingScriptOrModule may be null if there is no active script or module when the import() expression occurs. It then performs FinishDynamicImport to finish the dynamic import process.

An implementation of HostImportModuleDynamically must conform to the following requirements:

  • It must return NormalCompletion(undefined). 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 FinishDynamicImport(referencingScriptOrModule, specifier, 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 referencingScriptOrModule, specifier pair, 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 FinishDynamicImport.

The actual process performed is host-defined, but typically consists of performing whatever I/O operations are necessary to allow HostResolveImportedModule to synchronously retrieve the appropriate Module Record, and then calling its Evaluate concrete method. This might require performing similar normalization as HostResolveImportedModule does.

16.2.1.9 FinishDynamicImport ( referencingScriptOrModule, specifier, promiseCapability, innerPromise )

The abstract operation FinishDynamicImport takes arguments referencingScriptOrModule, specifier, promiseCapability (a PromiseCapability Record), and innerPromise. FinishDynamicImport completes the process of a dynamic import originally started by an import() call, 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 HostImportModuleDynamically. It performs the following steps when called:

  1. Let fulfilledClosure be a new Abstract Closure with parameters (result) that captures referencingScriptOrModule, specifier, and promiseCapability and performs the following steps when called:
    1. Assert: result is undefined.
    2. Let moduleRecord be ! HostResolveImportedModule(referencingScriptOrModule, specifier).
    3. Assert: Evaluate has already been invoked on moduleRecord and successfully completed.
    4. Let namespace be GetModuleNamespace(moduleRecord).
    5. If namespace is an abrupt completion, then
      1. Perform ! Call(promiseCapability.[[Reject]], undefined, « namespace.[[Value]] »).
    6. Else,
      1. Perform ! Call(promiseCapability.[[Resolve]], undefined, « namespace.[[Value]] »).
    7. Return undefined.
  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 undefined.
  4. Let onRejected be ! CreateBuiltinFunction(rejectedClosure, 0, "", « »).
  5. Perform ! PerformPromiseThen(innerPromise, onFulfilled, onRejected).

16.2.1.10 GetModuleNamespace ( module )

The abstract operation GetModuleNamespace takes argument module (an instance of a concrete subclass of Module Record). It retrieves the Module Namespace Object representing module's exports, lazily creating it the first time it was requested, and storing it in module.[[Namespace]] for future retrieval. It performs the following steps when called:

  1. Assert: If module is a Cyclic Module Record, then module.[[Status]] is not unlinked.
  2. Let namespace be module.[[Namespace]].
  3. If namespace is empty, then
    1. Let exportedNames be ? module.GetExportedNames().
    2. Let unambiguousNames be a new empty List.
    3. For each element name of exportedNames, do
      1. Let resolution be ? module.ResolveExport(name).
      2. If resolution is a ResolvedBinding Record, append name to unambiguousNames.
    4. Set namespace to ModuleNamespaceCreate(module, unambiguousNames).
  4. Return namespace.
Note

The only way GetModuleNamespace can throw is via one of the triggered HostResolveImportedModule calls. Unresolvable names are simply excluded from the namespace at this point. They will lead to a real linking error later unless they are all ambiguous star exports that are not explicitly requested anywhere.

16.2.1.11 Runtime Semantics: Evaluation

Module : [empty]
  1. Return NormalCompletion(undefined).
ModuleBody : ModuleItemList
  1. Let result be the result of evaluating ModuleItemList.
  2. If result.[[Type]] is normal and result.[[Value]] is empty, then
    1. Return NormalCompletion(undefined).
  3. Return Completion(result).
ModuleItemList : ModuleItemList ModuleItem
  1. Let sl be the result of evaluating ModuleItemList.
  2. ReturnIfAbrupt(sl).
  3. Let s be the result of evaluating ModuleItem.
  4. Return Completion(UpdateEmpty(s, sl)).
Note

The value of a ModuleItemList is the value of the last value-producing item in the ModuleItemList.

ModuleItem : ImportDeclaration
  1. Return NormalCompletion(empty).

16.2.2 Imports

Syntax

ImportDeclaration : import ImportClause FromClause ; import ModuleSpecifier ; ImportClause : ImportedDefaultBinding NameSpaceImport NamedImports ImportedDefaultBinding , NameSpaceImport ImportedDefaultBinding , NamedImports ImportedDefaultBinding : ImportedBinding NameSpaceImport : * as ImportedBinding NamedImports : { } { ImportsList } { ImportsList , } FromClause : from ModuleSpecifier ImportsList : ImportSpecifier ImportsList , ImportSpecifier ImportSpecifier : ImportedBinding IdentifierName as ImportedBinding ModuleSpecifier : StringLiteral ImportedBinding : BindingIdentifier[~Yield, +Await]

16.2.2.1 Static Semantics: Early Errors

ModuleItem : ImportDeclaration

16.2.2.2 Static Semantics: ImportEntries

The syntax-directed operation ImportEntries takes no arguments. It is defined piecewise over the following productions:

Module : [empty]
  1. Return a new empty List.
ModuleItemList : ModuleItemList ModuleItem
  1. Let entries1 be ImportEntries of ModuleItemList.
  2. Let entries2 be ImportEntries of ModuleItem.
  3. Return the list-concatenation of entries1 and entries2.
ModuleItem : ExportDeclaration StatementListItem
  1. Return a new empty List.
ImportDeclaration : import ImportClause FromClause ;
  1. Let module be the sole element of ModuleRequests of FromClause.
  2. Return ImportEntriesForModule of ImportClause with argument module.
ImportDeclaration : import ModuleSpecifier ;
  1. Return a new empty List.

16.2.2.3 Static Semantics: ImportEntriesForModule

The syntax-directed operation ImportEntriesForModule takes argument module. It is defined piecewise over the following productions:

ImportClause : ImportedDefaultBinding , NameSpaceImport
  1. Let entries1 be ImportEntriesForModule of ImportedDefaultBinding with argument module.
  2. Let entries2 be ImportEntriesForModule of NameSpaceImport with argument module.
  3. Return the list-concatenation of entries1 and entries2.
ImportClause : ImportedDefaultBinding , NamedImports
  1. Let entries1 be ImportEntriesForModule of ImportedDefaultBinding with argument module.
  2. Let entries2 be ImportEntriesForModule of NamedImports with argument module.
  3. Return the list-concatenation of entries1 and entries2.
ImportedDefaultBinding : ImportedBinding
  1. Let localName be the sole element of BoundNames of ImportedBinding.
  2. Let defaultEntry be the ImportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: "default", [[LocalName]]: localName }.
  3. Return a List whose sole element is defaultEntry.
NameSpaceImport : * as ImportedBinding
  1. Let localName be the StringValue of ImportedBinding.
  2. Let entry be the ImportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: "*", [[LocalName]]: localName }.
  3. Return a List whose sole element is entry.
NamedImports : { }
  1. Return a new empty List.
ImportsList : ImportsList , ImportSpecifier
  1. Let specs1 be the ImportEntriesForModule of ImportsList with argument module.
  2. Let specs2 be the ImportEntriesForModule of ImportSpecifier with argument module.
  3. Return the list-concatenation of specs1 and specs2.
ImportSpecifier : ImportedBinding
  1. Let localName be the sole element of BoundNames of ImportedBinding.
  2. Let entry be the ImportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: localName, [[LocalName]]: localName }.
  3. Return a List whose sole element is entry.
ImportSpecifier : IdentifierName as ImportedBinding
  1. Let importName be the StringValue of IdentifierName.
  2. Let localName be the StringValue of ImportedBinding.
  3. Let entry be the ImportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: importName, [[LocalName]]: localName }.
  4. Return a List whose sole element is entry.

16.2.3 Exports

Syntax

ExportDeclaration : export ExportFromClause FromClause ; export NamedExports ; export VariableStatement[~Yield, +Await] export Declaration[~Yield, +Await] export default HoistableDeclaration[~Yield, +Await, +Default] export default ClassDeclaration[~Yield, +Await, +Default] export default [lookahead ∉ { function, async [no LineTerminator here] function, class }] AssignmentExpression[+In, ~Yield, +Await] ; ExportFromClause : * * as IdentifierName NamedExports NamedExports : { } { ExportsList } { ExportsList , } ExportsList : ExportSpecifier ExportsList , ExportSpecifier ExportSpecifier : IdentifierName IdentifierName as IdentifierName

16.2.3.1 Static Semantics: Early Errors

ExportDeclaration : export NamedExports ; Note

The above rule means that each ReferencedBindings of NamedExports is treated as an IdentifierReference.

16.2.3.2 Static Semantics: ExportedBindings

The syntax-directed operation ExportedBindings takes no arguments.

Note

ExportedBindings are the locally bound names that are explicitly associated with a Module's ExportedNames.

It is defined piecewise over the following productions:

ModuleItemList : ModuleItemList ModuleItem
  1. Let names1 be ExportedBindings of ModuleItemList.
  2. Let names2 be ExportedBindings of ModuleItem.
  3. Return the list-concatenation of names1 and names2.
ModuleItem : ImportDeclaration StatementListItem
  1. Return a new empty List.
ExportDeclaration : export ExportFromClause FromClause ;
  1. Return a new empty List.
ExportDeclaration : export NamedExports ;
  1. Return the ExportedBindings of NamedExports.
ExportDeclaration : export VariableStatement
  1. Return the BoundNames of VariableStatement.
ExportDeclaration : export Declaration
  1. Return the BoundNames of Declaration.
ExportDeclaration : export default HoistableDeclaration export default ClassDeclaration export default AssignmentExpression ;
  1. Return the BoundNames of this ExportDeclaration.
NamedExports : { }
  1. Return a new empty List.
ExportsList : ExportsList , ExportSpecifier
  1. Let names1 be the ExportedBindings of ExportsList.
  2. Let names2 be the ExportedBindings of ExportSpecifier.
  3. Return the list-concatenation of names1 and names2.
ExportSpecifier : IdentifierName
  1. Return a List whose sole element is the StringValue of IdentifierName.
ExportSpecifier : IdentifierName as IdentifierName
  1. Return a List whose sole element is the StringValue of the first IdentifierName.

16.2.3.3 Static Semantics: ExportedNames

The syntax-directed operation ExportedNames takes no arguments.

Note

ExportedNames are the externally visible names that a Module explicitly maps to one of its local name bindings.

It is defined piecewise over the following productions:

ModuleItemList : ModuleItemList ModuleItem
  1. Let names1 be ExportedNames of ModuleItemList.
  2. Let names2 be ExportedNames of ModuleItem.
  3. Return the list-concatenation of names1 and names2.
ModuleItem : ExportDeclaration
  1. Return the ExportedNames of ExportDeclaration.
ModuleItem : ImportDeclaration StatementListItem
  1. Return a new empty List.
ExportDeclaration : export ExportFromClause FromClause ;
  1. Return the ExportedNames of ExportFromClause.
ExportFromClause : *
  1. Return a new empty List.
ExportFromClause : * as IdentifierName
  1. Return a List whose sole element is the StringValue of IdentifierName.
ExportFromClause : NamedExports
  1. Return the ExportedNames of NamedExports.
ExportDeclaration : export VariableStatement
  1. Return the BoundNames of VariableStatement.
ExportDeclaration : export Declaration
  1. Return the BoundNames of Declaration.
ExportDeclaration : export default HoistableDeclaration export default ClassDeclaration export default AssignmentExpression ;
  1. Return « "default" ».
NamedExports : { }
  1. Return a new empty List.
ExportsList : ExportsList , ExportSpecifier
  1. Let names1 be the ExportedNames of ExportsList.
  2. Let names2 be the ExportedNames of ExportSpecifier.
  3. Return the list-concatenation of names1 and names2.
ExportSpecifier : IdentifierName
  1. Return a List whose sole element is the StringValue of IdentifierName.
ExportSpecifier : IdentifierName as IdentifierName
  1. Return a List whose sole element is the StringValue of the second IdentifierName.

16.2.3.4 Static Semantics: ExportEntries

The syntax-directed operation ExportEntries takes no arguments. It is defined piecewise over the following productions:

Module : [empty]
  1. Return a new empty List.
ModuleItemList : ModuleItemList ModuleItem
  1. Let entries1 be ExportEntries of ModuleItemList.
  2. Let entries2 be ExportEntries of ModuleItem.
  3. Return the list-concatenation of entries1 and entries2.
ModuleItem : ImportDeclaration StatementListItem
  1. Return a new empty List.
ExportDeclaration : export ExportFromClause FromClause ;
  1. Let module be the sole element of ModuleRequests of FromClause.
  2. Return ExportEntriesForModule of ExportFromClause with argument module.
ExportDeclaration : export NamedExports ;
  1. Return ExportEntriesForModule of NamedExports with argument null.
ExportDeclaration : export VariableStatement
  1. Let entries be a new empty List.
  2. Let names be the BoundNames of VariableStatement.
  3. For each element name of names, do
    1. Append the ExportEntry Record { [[ModuleRequest]]: null, [[ImportName]]: null, [[LocalName]]: name, [[ExportName]]: name } to entries.
  4. Return entries.
ExportDeclaration : export Declaration
  1. Let entries be a new empty List.
  2. Let names be the BoundNames of Declaration.
  3. For each element name of names, do
    1. Append the ExportEntry Record { [[ModuleRequest]]: null, [[ImportName]]: null, [[LocalName]]: name, [[ExportName]]: name } to entries.
  4. Return entries.
ExportDeclaration : export default HoistableDeclaration
  1. Let names be BoundNames of HoistableDeclaration.
  2. Let localName be the sole element of names.
  3. Return a List whose sole element is the ExportEntry Record { [[ModuleRequest]]: null, [[ImportName]]: null, [[LocalName]]: localName, [[ExportName]]: "default" }.
ExportDeclaration : export default ClassDeclaration
  1. Let names be BoundNames of ClassDeclaration.
  2. Let localName be the sole element of names.
  3. Return a List whose sole element is the ExportEntry Record { [[ModuleRequest]]: null, [[ImportName]]: null, [[LocalName]]: localName, [[ExportName]]: "default" }.
ExportDeclaration : export default AssignmentExpression ;
  1. Let entry be the ExportEntry Record { [[ModuleRequest]]: null, [[ImportName]]: null, [[LocalName]]: "*default*", [[ExportName]]: "default" }.
  2. Return a List whose sole element is entry.
Note

"*default*" is used within this specification as a synthetic name for anonymous default export values.

16.2.3.5 Static Semantics: ExportEntriesForModule

The syntax-directed operation ExportEntriesForModule takes argument module. It is defined piecewise over the following productions:

ExportFromClause : *
  1. Let entry be the ExportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: "*", [[LocalName]]: null, [[ExportName]]: null }.
  2. Return a List whose sole element is entry.
ExportFromClause : * as IdentifierName
  1. Let exportName be the StringValue of IdentifierName.
  2. Let entry be the ExportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: "*", [[LocalName]]: null, [[ExportName]]: exportName }.
  3. Return a List whose sole element is entry.
NamedExports : { }
  1. Return a new empty List.
ExportsList : ExportsList , ExportSpecifier
  1. Let specs1 be the ExportEntriesForModule of ExportsList with argument module.
  2. Let specs2 be the ExportEntriesForModule of ExportSpecifier with argument module.
  3. Return the list-concatenation of specs1 and specs2.
ExportSpecifier : IdentifierName
  1. Let sourceName be the StringValue of IdentifierName.
  2. If module is null, then
    1. Let localName be sourceName.
    2. Let importName be null.
  3. Else,
    1. Let localName be null.
    2. Let importName be sourceName.
  4. Return a List whose sole element is the ExportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: importName, [[LocalName]]: localName, [[ExportName]]: sourceName }.
ExportSpecifier : IdentifierName as IdentifierName
  1. Let sourceName be the StringValue of the first IdentifierName.
  2. Let exportName be the StringValue of the second IdentifierName.
  3. If module is null, then
    1. Let localName be sourceName.
    2. Let importName be null.
  4. Else,
    1. Let localName be null.
    2. Let importName be sourceName.
  5. Return a List whose sole element is the ExportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: importName, [[LocalName]]: localName, [[ExportName]]: exportName }.

16.2.3.6 Static Semantics: ReferencedBindings

The syntax-directed operation ReferencedBindings takes no arguments. It is defined piecewise over the following productions:

NamedExports : { }
  1. Return a new empty List.
ExportsList : ExportsList , ExportSpecifier
  1. Let names1 be the ReferencedBindings of ExportsList.
  2. Let names2 be the ReferencedBindings of ExportSpecifier.
  3. Return the list-concatenation of names1 and names2.
ExportSpecifier : IdentifierName
  1. Return a List whose sole element is the IdentifierName.
ExportSpecifier : IdentifierName as IdentifierName
  1. Return a List whose sole element is the first IdentifierName.

16.2.3.7 Runtime Semantics: Evaluation

ExportDeclaration : export ExportFromClause FromClause ; export NamedExports ;
  1. Return NormalCompletion(empty).
ExportDeclaration : export VariableStatement
  1. Return the result of evaluating VariableStatement.
ExportDeclaration : export Declaration
  1. Return the result of evaluating Declaration.
ExportDeclaration : export default HoistableDeclaration
  1. Return the result of evaluating HoistableDeclaration.
ExportDeclaration : export default ClassDeclaration
  1. Let value be ? BindingClassDeclarationEvaluation of ClassDeclaration.
  2. Let className be the sole element of BoundNames of ClassDeclaration.
  3. If className is "*default*", then
    1. Let env be the running execution context's LexicalEnvironment.
    2. Perform ? InitializeBoundName("*default*", value, env).
  4. Return NormalCompletion(empty).
ExportDeclaration : export default AssignmentExpression ;
  1. If IsAnonymousFunctionDefinition(AssignmentExpression) is true, then
    1. Let value be ? NamedEvaluation of AssignmentExpression with argument "default".
  2. Else,
    1. Let rhs be the result of evaluating AssignmentExpression.
    2. Let value be ? GetValue(rhs).
  3. Let env be the running execution context's LexicalEnvironment.
  4. Perform ? InitializeBoundName("*default*", value, env).
  5. Return NormalCompletion(empty).