diff --git a/packages/metro-transform-plugins/src/__tests__/import-export-plugin-test.js b/packages/metro-transform-plugins/src/__tests__/import-export-plugin-test.js index 4eade05492..6fbfd15ba6 100644 --- a/packages/metro-transform-plugins/src/__tests__/import-export-plugin-test.js +++ b/packages/metro-transform-plugins/src/__tests__/import-export-plugin-test.js @@ -15,8 +15,10 @@ import collectDependencies from 'metro/private/ModuleGraph/worker/collectDepende const {compare, transformToAst} = require('../__mocks__/test-helpers'); const importExportPlugin = require('../import-export-plugin'); +const generate = require('@babel/generator').default; // $FlowFixMe[untyped-import] @babel/code-frame const {codeFrameColumns} = require('@babel/code-frame'); +const vm = require('vm'); const opts = { importAll: '_$$_IMPORT_ALL', @@ -156,8 +158,8 @@ test('exports members of another module directly from an import (as default)', ( var _foo = require('bar').foo; var _baz = require('bar').baz; - exports.default = _foo; exports.baz = _baz; + exports.default = _foo; `; compare([importExportPlugin], code, expected, opts); @@ -233,8 +235,8 @@ test('allows mixed esm and cjs exports', () => { exports.bar = 'bar'; module.exports.baz = 'baz'; class _default {} - exports.default = _default; exports.foo = foo; + exports.default = _default; `; compare([importExportPlugin], code, expected, opts); @@ -360,6 +362,69 @@ test('exports members of another module directly from an import (as namespace)', `); }); +test('places export all above explicit exports', () => { + const code = ` + export * from 'foo'; + export {baz} from 'bar'; + const bax = 'bax'; + export default bax; + `; + + const expected = ` + Object.defineProperty(exports, '__esModule', {value: true}); + + var _baz = require('bar').baz; + const bax = 'bax'; + + var _default = bax; + + var _foo = require("foo"); + + for (var _key in _foo) { + exports[_key] = _foo[_key]; + } + + exports.baz = _baz; + exports.default = _default; + `; + + compare([importExportPlugin], code, expected, opts); +}); + +test('explicit exports override export all at runtime', () => { + const transformedCode = generate( + transformToAst( + [importExportPlugin], + ` + export * from 'foo'; + export const overridden = 'explicit named'; + export default 'explicit default'; + `, + opts, + ), + ).code; + const context = { + exports: {} as {[string]: unknown}, + require: (id: string) => { + if (id !== 'foo') { + throw new Error(`Unexpected module: ${id}`); + } + return { + default: 'star default', + overridden: 'star named', + sourceOnly: 'source only', + }; + }, + }; + + vm.runInNewContext(transformedCode, context); + + expect(context.exports.__esModule).toBe(true); + expect(context.exports.default).toBe('explicit default'); + expect(context.exports.overridden).toBe('explicit named'); + expect(context.exports.sourceOnly).toBe('source only'); +}); + test('enables module exporting when something is exported', () => { const code = ` foo(); diff --git a/packages/metro-transform-plugins/src/import-export-plugin.js b/packages/metro-transform-plugins/src/import-export-plugin.js index db8295491f..a838355d37 100644 --- a/packages/metro-transform-plugins/src/import-export-plugin.js +++ b/packages/metro-transform-plugins/src/import-export-plugin.js @@ -522,20 +522,6 @@ export default function importExportPlugin({ body.unshift(e.node); }); - state.exportDefault.forEach( - (e: {local: string, loc: ?SourceLocation, ...}) => { - body.push( - withLocation( - exportTemplate({ - LOCAL: t.identifier(e.local), - REMOTE: t.identifier('default'), - }), - e.loc, - ), - ); - }, - ); - state.exportAll.forEach( (e: {file: string, loc: ?SourceLocation, ...}) => { body.push( @@ -569,6 +555,20 @@ export default function importExportPlugin({ }, ); + state.exportDefault.forEach( + (e: {local: string, loc: ?SourceLocation, ...}) => { + body.push( + withLocation( + exportTemplate({ + LOCAL: t.identifier(e.local), + REMOTE: t.identifier('default'), + }), + e.loc, + ), + ); + }, + ); + if ( state.exportDefault.length || state.exportAll.length || diff --git a/packages/metro/src/integration_tests/__tests__/__snapshots__/import-export-test.js.snap b/packages/metro/src/integration_tests/__tests__/__snapshots__/import-export-test.js.snap index b20e405717..7418d1fa9b 100644 --- a/packages/metro/src/integration_tests/__tests__/__snapshots__/import-export-test.js.snap +++ b/packages/metro/src/integration_tests/__tests__/__snapshots__/import-export-test.js.snap @@ -20,6 +20,9 @@ Object { "arrayRest": Array [ "export-destructuring: ARRAY_REST", ], + "exportStarDefault": "export-star-overrides: DEFAULT", + "exportStarOverridden": "export-star-overrides: OVERRIDDEN", + "exportStarSourceOnly": "export-star-source: SOURCE_ONLY", "foo": "export-null: FOO", "importStar": Object { "default": "export-2: DEFAULT", diff --git a/packages/metro/src/integration_tests/__tests__/import-export-test.js b/packages/metro/src/integration_tests/__tests__/import-export-test.js index a26f5da727..9b5d7b3a33 100644 --- a/packages/metro/src/integration_tests/__tests__/import-export-test.js +++ b/packages/metro/src/integration_tests/__tests__/import-export-test.js @@ -39,6 +39,15 @@ test('builds a simple bundle', async () => { ]); expect(object.extraData.namespaceReExportDefault).toBe('export-2: DEFAULT'); expect(object.extraData.namespaceReExportFoo).toBe('export-2: FOO'); + expect(object.extraData.exportStarDefault).toBe( + 'export-star-overrides: DEFAULT', + ); + expect(object.extraData.exportStarOverridden).toBe( + 'export-star-overrides: OVERRIDDEN', + ); + expect(object.extraData.exportStarSourceOnly).toBe( + 'export-star-source: SOURCE_ONLY', + ); expect(object).toMatchSnapshot(); expect(cjs).toEqual(expect.objectContaining(cjs.default)); diff --git a/packages/metro/src/integration_tests/basic_bundle/import-export/export-star-overrides.js b/packages/metro/src/integration_tests/basic_bundle/import-export/export-star-overrides.js new file mode 100644 index 0000000000..d17eef6ef9 --- /dev/null +++ b/packages/metro/src/integration_tests/basic_bundle/import-export/export-star-overrides.js @@ -0,0 +1,17 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow strict + */ + +'use strict'; + +export * from './export-star-source'; + +export const overridden = 'export-star-overrides: OVERRIDDEN'; + +export default 'export-star-overrides: DEFAULT'; diff --git a/packages/metro/src/integration_tests/basic_bundle/import-export/export-star-source.js b/packages/metro/src/integration_tests/basic_bundle/import-export/export-star-source.js new file mode 100644 index 0000000000..db1c34a509 --- /dev/null +++ b/packages/metro/src/integration_tests/basic_bundle/import-export/export-star-source.js @@ -0,0 +1,17 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow strict + */ + +'use strict'; + +export default 'export-star-source: DEFAULT'; + +export const overridden = 'export-star-source: OVERRIDDEN'; + +export const sourceOnly = 'export-star-source: SOURCE_ONLY'; diff --git a/packages/metro/src/integration_tests/basic_bundle/import-export/index.js b/packages/metro/src/integration_tests/basic_bundle/import-export/index.js index c0574ed7e9..7a67bd69a8 100644 --- a/packages/metro/src/integration_tests/basic_bundle/import-export/index.js +++ b/packages/metro/src/integration_tests/basic_bundle/import-export/index.js @@ -25,6 +25,10 @@ import {foo} from './export-null'; import primitiveDefault, { foo as primitiveFoo, } from './export-primitive-default'; +import exportStarDefault, { + overridden as exportStarOverridden, + sourceOnly as exportStarSourceOnly, +} from './export-star-overrides'; declare var require: RequireWithUnstableImportMaybeSync; @@ -34,6 +38,9 @@ export {foo as default} from './export-4'; export const extraData = { arrayFirst, arrayRest, + exportStarDefault, + exportStarOverridden, + exportStarSourceOnly, foo, importStar, myDefault,