1 of 24

Problems with import assertions for module types …

… and a solution!

+ downgrade to Stage 2

Nicolò Ribaudo

January 2023

2 of 24

Outline

  • Current import assertions semantics
  • Unsolved web platform needs
  • A possible solution
  • Request a downgrade to Stage 2

2

3 of 24

Recap: what are import assertions?

import styles from "./main.css" assert { type: "css" };

let stylesP = import("./main.css", { assert: { type: "css" } });

The assertion can check that the imported module respects some conditions, and throw an error (preventing its evaluation) if it doesn't.

3

4 of 24

Host invariants on import assertions handling

  • Import assertions can only influence if a module is loaded or evaluated, but not how.

4

5 of 24

Host invariants on import assertions handling

  • Import assertions can only influence if a module is loaded or evaluated, but not how.
  • Import assertions should not be used as part of the modules cache key.

5

6 of 24

Host invariants on import assertions handling

  • Import assertions can only influence if a module is loaded or evaluated, but not how.
  • Import assertions should not be used as part of the modules cache key.

If all imports succeed, A === B and B === C should be true.

6

const A = await import("./x");

const B = await import("./x", { assert: { type: "json" } });

const C = await import("./x", { assert: { type: "yaml" } });

7 of 24

History!

  • The proposal originally included support for multiple attributes, even if the only motivating use case was type for JSON/CSS/HTML modules. f01b3ef8.

import json from "./foo.json" with type: "json";

  • It has then been updated to a single attribute, included in the cache key. 47bfc181.

import json from "./foo.json" as "json";

  • The proposal was approved for Stage 2 in June 2020 with the original extensible syntax, but with attributes bag out of the cache key.
  • The keyword has then been replaced with if, and then with assert. 51278ed5.

import json from "./foo.json" assert { type: "json" };

  • The proposal was approved for Stage 3 in September 2020, with relaxed host constraints to allow then using assertions in the cache key. 2ea51b67.

7

8 of 24

Resource types on the web

8

9 of 24

Resource types on the web

Resources on the web are loaded differently depending on their usage goal, and they are differentiated before knowing what the server response contains.

  • Different CSP policies (script-src, style-src, …)
  • Different Fetch destinations and accepted response types
    • Sec-Fetch-Dest and Accept HTTP headers
    • request.destination

9

10 of 24

Resource types on the web

<link rel="stylesheet" href="/file">

GET /file HTTP/1.1

...

Accept: text/css,*/*;q=0.1

Sec-Fetch-Dest: style

...

<script type="module" src="/file">

</script>

GET /file HTTP/1.1

...

Accept: */*

Sec-Fetch-Dest: script

...

10

11 of 24

Resource types on the web

import "/file" assert { type:"css" }

GET /file HTTP/1.1

...

Accept: text/css,*/*;q=0.1

Sec-Fetch-Dest: style

...

import "/file";

GET /file HTTP/1.1

...

Accept: */*

Sec-Fetch-Dest: script

...

11

Can the browser fetch these two modules as if they were loaded using the corresponding <link> and <script> tags?

12 of 24

Developers' mental model of import assertions

12

13 of 24

What causes a module do be interpreted in a certain way?

There is a strong developer intuition that the type assertion guides the engine to interpret the module according to the specified type.

In current implementations, type 1-1 matches the MIME type or file extension of the imported module, and it's either required (e.g. JSON modules) or disallowed (JS modules). It's not observably false that it doesn't affect how the module is interpreted.

13

import data from "/data.json" assert { type:"json" }

14 of 24

Real-world attempts to overload assertions semantics

14

15 of 24

A possible solution

15

16 of 24

A possible solution

  1. Explicitly allow type to affect how a module is loaded or evaluated.
  2. Update the syntax to clarify that it's not only an assertion:

import styles from "./styles.css" with { type: "css" };

* the syntax is a strawperson heavily based on the existing assert-only�syntax, and with was one of the alternative keywords originally�considered for this feature before settling on assert-only semantics.

16

17 of 24

Possible further benefits

There are currently other proposals planning to extend the import syntax:

  • Import reflectionimport module x from "x";
  • Deferred evaluationimport defer * as x from "x";

With a generic "attributes" syntax, we can simply define new attributes instead of inventing new syntax every time:

  • Import reflectionimport x from "x" with { reflect: "module" }
  • Deferred evaluation import * as x from "x" with { defer: true }

17

18 of 24

Limiting ecosystem divergence

Previously, significant concerns were raised about import attributes being too flexible, limiting portability and intelligibility of code.

Possible compromise

  • ECMA-262 can specify the list of valid attributes and their semantics.
  • This proposal only specifies type, which is passed as-is to the host loader.
  • Future proposals can introduce new valid attributes.

18

19 of 24

Expressiveness

A previous alternative that only extended the import syntax with a simple string was deemed as being too restrictive and preventing future extensions:

import styles from "./style.css" as "css";

This proposal currently defines a single type: string attribute, but it has a clear extension path:

  • we can define new attributes
  • we can extend the range of allowed attribute values

import "./style.css" with { optional: true, allow: ["fs-read"] }

19

20 of 24

Web compatibility & migration path

20

21 of 24

Web compatibility & migration path

Import assertions have not been enabled everywhere yet! However, they are already enabled in some browsers, server-side runtimes and tools.

Now No immediate action to take. It's fine to not unship the current implementation.

In background Collect statistics on how frequently import assertions are used.

In a few months If we get consensus on new semantics, implementations can ship them and use the same code path for the assert syntax.

Mid/long-term Analyze the impact of removing assert from existing implementation. Hopefully it can be done in one year or two.

  • Worst possible case: assert goes into AnnexB as "deprecated".

21

22 of 24

Next steps

22

23 of 24

Next steps

  • Please don't ship anything yet, if you haven't already done so!
  • The syntax in these slides is a strawperson.

import x from "y" with { type: "css" };

import x from "y" with type: "css";

import x from "y" as "css";

import x from "y" as <any primitive literal>;

  • We will iterate in the Module Harmony calls, and propose consensus on something soon at a future meeting.
  • Draft PR reflecting this slides deck: tc39/proposal-import-assertions#127

23

24 of 24

Downgrade to Stage 2?

24