1 of 16

Deferred re-exports

update since last plenary

export defer … from

Nicolò Ribaudo (Igalia, in partnership with Bloomberg)

2 of 16

Last plenary summary

In November the proposal did not advance to Stage 2.7 over concerns with:

  • Interaction with namespace imports, required for import defer
  • Spec complexity of having two separate evaluation paths for static graphs vs deferred execution on module namespaces

We are working on this.

2

3 of 16

The "performance cliff"

3

4 of 16

The "performance cliff"

4

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library/_�_util-1_

_library/_�_util-2_

Loading

Execution

Initial

// library

export { something } from "./util-1";

export { other } from "./util-2";

export const mainThing = /* ... */

// app.js

import { mainThing } from 'library';

$button.onclick = () => {

use(mainThing);

};

5 of 16

The "performance cliff"

5

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library/_�_util-1_

_library/_�_util-2_

Loading

Execution

Happening later

Initial

import defer

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library/_�_util-1_

_library/_�_util-2_

// library

export { something } from "./util-1";

export { other } from "./util-2";

export const mainThing = /* ... */

// app.js

import defer * as lib from 'library';

$button.onclick = () => {

use(lib.mainThing);

};

6 of 16

The "performance cliff"

6

// library

export defer { something } from "./util-1";

export defer { other } from "./util-2";

export const mainThing = /* ... */

// app.js

import { mainThing } from 'library';

$button.onclick = () => {

use(mainThing);

};

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library/_�_util-1_

_library/_�_util-2_

Loading

Execution

Happening later

Initial

export defer

_library_

_library_

7 of 16

The "performance cliff"

7

// library

export defer { something } from "./util-1";

export defer { other } from "./util-2";

export const mainThing = /* ... */

// app.js

import defer * as lib from 'library';

$button.onclick = () => {

use(lib.mainThing);

};

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library/_�_util-1_

_library/_�_util-2_

Loading

Execution

Happening later

Initial

import defer export defer

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

8 of 16

The "performance cliff"

8

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library/_�_util-1_

_library/_�_util-2_

Initial

export defer

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

Loading

Execution

Happening later

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library_

import defer *�export defer

import defer *

9 of 16

Some draft ideas for solutions

9

They all need more concrete details: please join the Module Harmony calls if you are interested in discussing them

10 of 16

Option 1: export defer is just about execution

export defer was born as the export version of import defer: should we go back to its origin?

Like import defer, it would defer execution when used with module namespaces, but it would not affect loading.

10

11 of 16

Option 1

11

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library/_�_util-1_

_library/_�_util-2_

Initial

import defer *

export defer�(current)

export defer�(new)

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

Loading

Execution

Happening later

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library_

import defer *�export defer

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

12 of 16

Option 2: allow listing names with "namespace" imports

The reason import defer uses * as ns is not because it needs to import everything, but it's because it needs an object to anchor execution to.

Can we get an object while still having a list of imported names?

import { mainThing, other } as lib from 'library';

Bonus: export defer right now cannot skip loading with dynamic imports, and this would allow it:

const lib = await import('library', { select: ['mainThing', 'other'] });

12

13 of 16

Option 2

13

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library/_�_util-1_

_library/_�_util-2_

Initial

import defer *

export defer

Loading

Execution

Happening later

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library/_�_util-1_

_library/_�_util-2_

import defer { mainThing } as …�export defer

_library_

_library_

import { mainThing }

export defer

_library_

_library_

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

import defer *

14 of 16

Option 3: namespace imports do not load defer re-exports

Deferred re-exports would keep following the rule that they are only loaded if they are explicitly named: * is not explicitly naming anything, so it does not load them. Same applies to dynamic import().

lib.other is available, but lib.something would be in TDZ until when something else causes it to be loaded.

Maybe some way (an attribute?) to allow explicitly listing some names.

14

// library

export defer { something } from "./util-1";

export { other } from "./util-2";

// app.js

import * as lib from 'library';

15 of 16

Option 3

15

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library/_�_util-1_

_library/_�_util-2_

Initial

import *

export defer

Loading

Execution

Happening later

_library_

_library/_�_util-1_

_library/_�_util-2_

_library_

_library/_�_util-1_

_library/_�_util-2_

import defer *

export defer

_library_

_library_

import { mainThing }

export defer

_library_

_library_

import defer *

_library_

_library_

16 of 16

Feedback?

16

+ please join the Module Harmony calls if you are interested in discussing more