Skip to content

experimentalImportSupport: Place export-all assignments before explicit exports, fix precedence of export default > export * from#1773

Closed
robhogan wants to merge 1 commit into
mainfrom
export-D111013892
Closed

experimentalImportSupport: Place export-all assignments before explicit exports, fix precedence of export default > export * from#1773
robhogan wants to merge 1 commit into
mainfrom
export-D111013892

Conversation

@robhogan

Copy link
Copy Markdown
Contributor

Summary:
Fixes a bug in experimentalImportSupport where export * from 'foo' would incorrectly overwrite an export default, if 'foo' also has a default export.

Example

Input source

// foo.js  (the star source)
export default 'star default';
export const overridden = 'star named';
export const sourceOnly = 'source only';
// entry.js
export * from 'foo';
export const overridden = 'explicit named';
export default 'explicit default';

Per the ES spec, importing from entry.js should give overridden === 'explicit named', default === 'explicit default', and sourceOnly === 'source only'. Explicit exports take precedence over names supplied via export *, regardless of the position of export declarations.

Before

'use strict';

Object.defineProperty(exports, '__esModule', {value: true});

var _overridden = 'explicit named';
var _default = 'explicit default';

exports.overridden = _overridden;    // 'explicit named'
exports.default = _default;          // 'explicit default'

var _foo = require('foo');
for (var _key in _foo) {             // star copy runs LAST...
  exports[_key] = _foo[_key];        // ...clobbers the explicit exports
}

Runtime result (wrong):

exports.overridden // 'star named'     ❌ should be 'explicit named'
exports.default    // 'star default'   ❌ should be 'explicit default'
exports.sourceOnly // 'source only'    ✅

After

'use strict';

Object.defineProperty(exports, '__esModule', {value: true});

var _overridden = 'explicit named';
var _default = 'explicit default';

var _foo = require('foo');
for (var _key in _foo) {             // star copy runs FIRST
  exports[_key] = _foo[_key];
}

exports.overridden = _overridden;    // explicit exports now win
exports.default = _default;

Runtime result (spec-correct):

exports.overridden // 'explicit named'    ✅
exports.default    // 'explicit default'  ✅
exports.sourceOnly // 'source only'       ✅

Spec

ECMA-262 Source Text Module Record ResolveExport checks local ExportEntries and indirect ExportEntries before considering StarExportEntries, so explicit exports take precedence over names supplied through export *.

Provenance

This is part of the change made in Expo's fork in #38111 (though that's mainly concerned with introducing live bindings), so this is "in the wild" already for Expo users.

Implementation

This is just a straightforward ordering swap of the emitted assignments.

Changelog

 - **[Experimental]**: `experimentalImportSupport` - fix precedence of `export default` over `export * from`.

Reviewed By: huntie

Differential Revision: D111013892

…it exports, fix precedence of `export default` > `export * from`

Summary:
Fixes a bug in `experimentalImportSupport` where `export * from 'foo'` would incorrectly overwrite an `export default`, if `'foo'` also has a default export.

# Example 
## Input source

```js
// foo.js  (the star source)
export default 'star default';
export const overridden = 'star named';
export const sourceOnly = 'source only';
```

```js
// entry.js
export * from 'foo';
export const overridden = 'explicit named';
export default 'explicit default';
```

Per the ES spec, importing from `entry.js` should give `overridden === 'explicit named'`, `default === 'explicit default'`, and `sourceOnly === 'source only'`. Explicit exports take precedence over names supplied via `export *`, regardless of the position of export declarations.

## Before

```js
'use strict';

Object.defineProperty(exports, '__esModule', {value: true});

var _overridden = 'explicit named';
var _default = 'explicit default';

exports.overridden = _overridden;    // 'explicit named'
exports.default = _default;          // 'explicit default'

var _foo = require('foo');
for (var _key in _foo) {             // star copy runs LAST...
  exports[_key] = _foo[_key];        // ...clobbers the explicit exports
}
```

Runtime result (wrong):

```js
exports.overridden // 'star named'     ❌ should be 'explicit named'
exports.default    // 'star default'   ❌ should be 'explicit default'
exports.sourceOnly // 'source only'    ✅
```

## After

```js
'use strict';

Object.defineProperty(exports, '__esModule', {value: true});

var _overridden = 'explicit named';
var _default = 'explicit default';

var _foo = require('foo');
for (var _key in _foo) {             // star copy runs FIRST
  exports[_key] = _foo[_key];
}

exports.overridden = _overridden;    // explicit exports now win
exports.default = _default;
```

Runtime result (spec-correct):

```js
exports.overridden // 'explicit named'    ✅
exports.default    // 'explicit default'  ✅
exports.sourceOnly // 'source only'       ✅
```

# Spec
[ECMA-262 Source Text Module Record](https://tc39.es/ecma262/#sec-source-text-module-records-resolveexport-exportname-resolve-set) `ResolveExport` checks local `ExportEntries` and indirect `ExportEntries` before considering `StarExportEntries`, so explicit exports take precedence over names supplied through `export *`.

# Provenance
This is part of the change made in Expo's fork in [#38111](expo/expo#38111) (though that's mainly concerned with introducing live bindings), so this is "in the wild" already for Expo users.

# Implementation
This is just a straightforward ordering swap of the emitted assignments.

# Changelog
```
 - **[Experimental]**: `experimentalImportSupport` - fix precedence of `export default` over `export * from`.
```

Reviewed By: huntie

Differential Revision: D111013892
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 10, 2026
@meta-codesync

meta-codesync Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

@robhogan has exported this pull request. If you are a Meta employee, you can view the originating Diff in D111013892.

@meta-codesync

meta-codesync Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

This pull request has been merged in e6b154f.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged meta-exported

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant