Stage 3 Draft / January 22, 2023

Import Attributes

Import Attributes are an extension to the import syntax that allows specifying additional information to affect how the module is imported. This proposal, other than defining such syntax, also defines the first import attribtue: type, which is passed to the host to specify the expected type of the loaded module. See the explainer for more information.

Possible future extensions include:

Not every import attribute needs to interact with host semantics, for example module and defer could be defined completely within ECMA-262.

1 Syntax

ImportDeclaration:importImportClauseFromClause; importModuleSpecifier; importImportClauseFromClause[no LineTerminator here]AttributesClause; importModuleSpecifier[no LineTerminator here]AttributesClause; ExportDeclaration:exportExportFromClauseFromClause; exportExportFromClauseFromClause[no LineTerminator here]AttributesClause; exportNamedExports; exportVariableStatement[~Yield, ~Await] exportDeclaration[~Yield, ~Await] exportdefaultHoistableDeclaration[~Yield, ~Await, +Default] exportdefaultClassDeclaration[~Yield, ~Await, +Default] exportdefault[lookahead ∉ { function, async [no LineTerminator here] function, class }]AssignmentExpression[+In, ~Yield, ~Await]; AttributesClause:with{} with{AttributeEntries,opt} AttributeEntries:AttributeEntry AttributeEntries,AttributeEntry AttributeEntry:AttributeKey:StringLiteral AttributeKey:IdentifierName StringLiteral ImportCall[Yield, Await]:import(AssignmentExpression[+In, ?Yield, ?Await],opt) import(AssignmentExpression[+In, ?Yield, ?Await],AssignmentExpression[+In, ?Yield, ?Await],opt)

2 Semantics

Editor's Note

Many productions operating on grammar are the same whether or not an AttributesClause/second ImportCall parameter is included; the new parameter is ignored. In this section, only the semantically significant changes are included, and the PR to merge into the main specification would fill in the straightforward details.

2.1 Import Calls

2.1.1 Runtime Semantics: Evaluation

2.1.1.1 EvaluateImportCall ( specifierExpression [ , optionsExpression ] )

  1. Let referencingScriptOrModule be ! GetActiveScriptOrModule().
  2. Let specifierRef be the result of evaluating specifierExpression.
  3. Let specifier be ? GetValue(specifierRef).
  4. If optionsExpression is present, then
    1. Let optionsRef be the result of evaluating optionsExpression.
    2. Let options be ? GetValue(optionsRef).
  5. Else,
    1. Let options be undefined.
  6. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  7. Let specifierString be ToString(specifier).
  8. IfAbruptRejectPromise(specifierString, promiseCapability).
  9. Let moduleRequest be a new ModuleRequest Record { [[Specifier]]: specifierString, [[Type]]: empty }.
  10. If options is not undefined, then
    1. If Type(options) is not Object,
      1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
      2. Return promiseCapability.[[Promise]].
    2. Let attributesObj be Get(options, "with").
    3. IfAbruptRejectPromise(attributesObj, promiseCapability).
    4. If attributesObj is not undefined,
      1. If Type(attributesObj) is not Object,
        1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
        2. Return promiseCapability.[[Promise]].
      2. Let keys be EnumerableOwnPropertyNames(attributesObj, key).
      3. IfAbruptRejectPromise(keys, promiseCapability).
      4. For each String key of keys,
        1. Let value be Get(attributesObj, key).
        2. IfAbruptRejectPromise(value, promiseCapability).
        3. If Type(value) is not String, then
          1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
          2. Return promiseCapability.[[Promise]].
        4. If key is "type", then
          1. Assert: moduleRequest.[[Type]] is empty.
          2. Set moduleRequest.[[Type]] to value.
        5. TODO: Throw on unknown keys?
  11. Perform HostLoadImportedModule(referrer, moduleRequest, empty, promiseCapability).
  12. Return promiseCapability.[[Promise]].
ImportCall:import(AssignmentExpression,opt)
  1. Return ? EvaluateImportCall(AssignmentExpression).
ImportCall:import(AssignmentExpression,AssignmentExpression,opt)
  1. Return ? EvaluateImportCall(the first AssignmentExpression, the second AssignmentExpression).

2.2 HostLoadImportedModule ( referrer, specifiermoduleRequest, hostDefined, payload )

The host-defined abstract operation HostLoadImportedModule takes arguments referrer (a Script Record, a Cyclic Module Record, or a Realm Record), specifier (a String)moduleRequest (a ModuleRequest Record), hostDefined (anything), and payload (a GraphLoadingState Record or a PromiseCapability Record) and returns unused.

Note

An example of when referrer can be a Realm Record 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 HostLoadImportedModule must conform to the following requirements:

  • The host environment must perform FinishLoadingImportedModule(referrer, specifiermoduleRequest, payload, result), where result is either a normal completion containing the loaded Module Record or a throw completion, either synchronously or asynchronously.
  • If this operation is called multiple times with the same (referrer, specifier) pair(referrer, moduleRequest.[[Specifier]], moduleRequest.[[Type]]) triple and it performs FinishLoadingImportedModule(referrer, specifiermoduleRequest, payload, result) where result is a normal completion, then it must perform FinishLoadingImportedModule(referrer, specifiermoduleRequest, payload, result) with the same result each time.

  • The operation must treat payload as an opaque value to be passed through to FinishLoadingImportedModule.

2.3 FinishLoadingImportedModule ( referrer, specifiermoduleRequest, payload, result )

The abstract operation FinishLoadingImportedModule takes arguments referrer (a Script Record, a Cyclic Module Record, or a Realm Record), specifier (a String)moduleRequest (a ModuleRequest Record), payload (a GraphLoadingState Record or a PromiseCapability Record), and result (either a normal completion containing a Module Record or a throw completion) and returns unused.

  1. If result is a normal completion, then
    1. If referrer.[[LoadedModules]] contains a Record record such that record.[[Specifier]] is moduleRequest.[[Specifier]] and record.[[Type]] is moduleRequest.[[Type]], then
      1. Assert: record.[[Module]] is result.[[Value]].
    2. Else, add Record { [[Specifier]]: moduleRequest.[[Specifier]], [[Type]]: moduleRequest.[[Type]], [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]].
  2. If payload is a GraphLoadingState Record, then
    1. Perform ContinueModuleLoading(payload, result).
  3. Else,
    1. Perform ContinueDynamicImport(payload, result).
  4. Return unused.
Editor's Note

The description of the [[LoadedModules]] field of Realm Record, Script Record, and Cyclic Module Record should be updated to include the [[Type]] field.

2.4 Static Semantics: CollectImportAttributes ( moduleRequest )

AttributeEntries:AttributeEntries,AttributeEntry
  1. Perform CollectImportAttributes of AttributeEntries with parameters « moduleRequest ».
  2. Perform CollectImportAttributes of AttributeEntry with parameters « moduleRequest ».
AttributeEntry:AttributeKey:StringLiteral
  1. Let key be StringValue of AttributeKey.
  2. If key is "type", then
    1. If moduleRequest.[[Type]] is not empty, throw a SyntaxError exception.
    2. Set moduleRequest.[[Type]] to the StringValue of StringLiteral.
  3. TODO: Throw on unknown keys?

2.5 ModuleRequest Records

A ModuleRequest Record represents the request to import a module with given import attributes. It consists of the following fields:

Table 1: ModuleRequest Record fields
Field Name Value Type Meaning
[[Specifier]] String The module specifier
[[Type]] a String, or empty The type attribute of this import
Editor's Note
In general, this proposal replaces places where module specifiers are passed around with ModuleRequest Records. For example, several syntax-directed operations, such as ModuleRequests produce Lists of ModuleRequest Records rather than Lists of Strings which are interpreted as module specifiers. Some algorithms like ImportEntries and ImportEntriesForModule pass around ModuleRequest Records rather than Strings, in a way which doesn't require any particular textual change. Additionally, record fields in Cyclic Module Records and Source Text Module Records which contained Lists of Strings are replaced by Lists of ModuleRequest Records, as indicated above.
Table 2: Additional Fields of Cyclic Module Records
Field Name Value Type Meaning
[[Status]] unlinked | linking | linked | evaluating | evaluated Initially unlinked. Transitions to linking, linked, evaluating, evaluated (in that order) as the module progresses throughout its lifecycle.
[[EvaluationError]] An abrupt completion | undefined 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 | undefined Auxiliary field used during Link and Evaluate only. If [[Status]] is linking or evaluating, this nonnegative number records the point at which the module was first visited during the ongoing depth-first traversal of the dependency graph.
[[DFSAncestorIndex]] Integer | undefined 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 StringModuleRequest Record A List of all the ModuleSpecifier strings with the corresponding type import attribute used by the module represented by this record to request the importation of a module. The List is source code occurrence ordered.

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

Table 3: ImportEntry Record Fields
Field Name Value Type Meaning
[[ModuleRequest]] String ModuleRequest Record String value of the ModuleSpecifier of the ImportDeclaration. ModuleRequest Record representing the ModuleSpecifier and the type import attribute 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.

2.6 Static Semantics: ModuleRequests

ImportDeclaration:importImportClauseFromClause;
  1. Return ModuleRequests of FromClause.
  2. Let specifier be StringValue of FromClause.
  3. Return a ModuleRequest Record { [[Specifer]]: specifier, [[Type]]: empty }.
ImportDeclaration:importImportClauseFromClauseAttributesClause;
  1. Let specifier be StringValue of FromClause.
  2. Let request be the ModuleRequest Record { [[Specifer]]: specifier, [[Type]]: empty }.
  3. Perform CollectImportAttributes of AttributesClause with argument request.
  4. Return request.
Module:[empty]
  1. Return a new empty List.
ModuleItemList:ModuleItem
  1. Return ModuleRequests of ModuleItem.
ModuleItemList:ModuleItemListModuleItem
  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.
ExportDeclaration:exportExportFromClauseFromClause;
  1. Return ModuleRequests of FromClause.
  2. Let specifier be StringValue of FromClause.
  3. Return the ModuleRequest Record { [[Specifer]]: specifier, [[Type]]: empty }.
ExportDeclaration:exportExportFromClauseFromClauseAttributesClause;
  1. Let specifier be StringValue of the StringLiteral contained in FromClause.
  2. Let request be the ModuleRequest Record { [[Specifer]]: specifier, [[Type]]: empty }.
  3. Perform CollectImportAttributes of AttributesClause with argument request.
  4. Return request.
ExportDeclaration:exportNamedExports; exportVariableStatement exportDeclaration exportdefaultHoistableDeclaration exportdefaultClassDeclaration exportdefaultAssignmentExpression;
  1. Return a new empty List.

A Sample host integration: The Web embedding

The import attributes proposal is intended to give key information about how modules are interpreted to hosts. For the Web embedding and environments which aim to be similar to it, the string is interpreted as the "module type". This is not the primary way the module type is determined (which, on the Web, would be the MIME type, and in other environments may be the file extension), but rather a secondary check which is required to pass for the module graph to load.

In the Web embedding, the following changes would be made to the HTML specification for import attributes:

The module map is keyed by the absolute URL and the type. Initially no other import attributes are supported, so they are not present.