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 6145ff546d..d0f347b842 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 @@ -186,6 +186,61 @@ test('exports named members', () => { compare([importExportPlugin], code, expected, opts); }); +test('renames existing `exports` declarations in module scope', () => { + const code = ` + const exports = 'foo'; + export const bar = 'bar'; + console.log(exports, bar); + `; + + const expected = ` + Object.defineProperty(exports, '__esModule', {value: true}); + const _exports = 'foo'; + const bar = 'bar'; + console.log(_exports, bar); + exports.bar = bar; + `; + + compare([importExportPlugin], code, expected, opts); +}); + +test('handles an export named "exports"', () => { + const code = ` + export const exports = {a: 'foo'}; + `; + + const expected = ` + Object.defineProperty(exports, '__esModule', {value: true}); + const _exports = { + a: 'foo', + }; + exports.exports = _exports; + `; + + compare([importExportPlugin], code, expected, opts); +}); + +test('allows mixed esm and cjs exports', () => { + const code = ` + export const foo = 'foo'; + exports.bar = 'bar'; + module.exports.baz = 'baz'; + export default class {} + `; + + const expected = ` + Object.defineProperty(exports, '__esModule', {value: true}); + const foo = 'foo'; + exports.bar = 'bar'; + module.exports.baz = 'baz'; + class _default {} + exports.default = _default; + exports.foo = foo; + `; + + compare([importExportPlugin], code, expected, opts); +}); + test('exports destructured named object members', () => { const code = ` export const {foo,bar} = {foo: 'bar',bar: 'baz'}; @@ -266,6 +321,24 @@ test('enables module exporting when something is exported', () => { `); }); +test('renames bindings', () => { + const code = ` + const module = 'foo'; + let exports = 'bar'; + var global = 'baz'; + const require = {}; + `; + + const expected = ` + const _module = 'foo'; + let _exports = 'bar'; + var _global = 'baz'; + const _require = {}; + `; + + compare([importExportPlugin], code, expected, opts); +}); + test('supports `import {default as LocalName}`', () => { const code = ` import { diff --git a/packages/metro-transform-plugins/src/import-export-plugin.js b/packages/metro-transform-plugins/src/import-export-plugin.js index 9cf2f09835..ee3eb63987 100644 --- a/packages/metro-transform-plugins/src/import-export-plugin.js +++ b/packages/metro-transform-plugins/src/import-export-plugin.js @@ -495,6 +495,14 @@ function importExportPlugin({types: t}: {types: Types, ...}): PluginObj { state.imports = []; state.importAll = t.identifier(state.opts.importAll); state.importDefault = t.identifier(state.opts.importDefault); + + // Rename declarations at module scope that might otherwise conflict + // with arguments we inject into the module factory. + // Note that it isn't necessary to rename importAll/importDefault + // because Metro already uses generateUid to generate unused names. + ['module', 'global', 'exports', 'require'].forEach(name => + path.scope.rename(name), + ); }, exit(path: NodePath, state: State): void {