Skip to content

Commit

Permalink
Define __esModule interop flag when requiring ES module from CommonJS (
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett committed May 8, 2019
1 parent a50ddd5 commit 84b3085
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 2 deletions.
@@ -0,0 +1 @@
module.exports = require('./b');
@@ -0,0 +1 @@
export default 2;
13 changes: 13 additions & 0 deletions packages/core/integration-tests/test/scope-hoisting.js
Expand Up @@ -1204,5 +1204,18 @@ describe('scope hoisting', function() {
let output = await run(b);
assert.deepEqual(output, 42);
});

it('should insert __esModule interop flag when importing from an ES module', async function() {
let b = await bundle(
path.join(
__dirname,
'/integration/scope-hoisting/commonjs/interop-require-es-module/a.js'
)
);

let output = await run(b);
assert.equal(output.__esModule, true);
assert.equal(output.default, 2);
});
});
});
3 changes: 2 additions & 1 deletion packages/core/parcel-bundler/src/Asset.js
Expand Up @@ -9,6 +9,7 @@ const syncPromise = require('./utils/syncPromise');
const logger = require('@parcel/logger');
const Resolver = require('./Resolver');
const objectHash = require('./utils/objectHash');
const t = require('babel-types');

/**
* An Asset represents a file in the dependency tree. Assets can have multiple
Expand Down Expand Up @@ -204,7 +205,7 @@ class Asset {
if (!this.id) {
this.id =
this.options.production || this.options.scopeHoist
? md5(this.relativeName, 'base64').slice(0, 4)
? t.toIdentifier(md5(this.relativeName, 'base64')).slice(0, 4)
: this.relativeName;
}

Expand Down
4 changes: 4 additions & 0 deletions packages/core/parcel-bundler/src/builtins/helpers.js
Expand Up @@ -4,6 +4,10 @@ function $parcel$interopDefault(a) {
: {d: a};
}

function $parcel$defineInteropFlag(a) {
Object.defineProperty(a, '__esModule', {value: true});
}

function $parcel$exportWildcard(dest, source) {
Object.keys(source).forEach(function(key) {
if(key === "default" || key === "__esModule") {
Expand Down
27 changes: 26 additions & 1 deletion packages/core/parcel-bundler/src/scope-hoisting/concat.js
Expand Up @@ -9,6 +9,7 @@ const {getName, getIdentifier} = require('./utils');

const EXPORTS_RE = /^\$([^$]+)\$exports$/;

const ESMODULE_TEMPLATE = template(`$parcel$defineInteropFlag(EXPORTS);`);
const DEFAULT_INTEROP_TEMPLATE = template(
'var NAME = $parcel$interopDefault(MODULE)'
);
Expand Down Expand Up @@ -142,10 +143,11 @@ module.exports = (packager, ast) => {
);
}

let asset = assets.get(id.value);
let mod = packager.resolveModule(id.value, source.value);

if (!mod) {
if (assets.get(id.value).dependencies.get(source.value).optional) {
if (asset.dependencies.get(source.value).optional) {
path.replaceWith(
THROW_TEMPLATE({MODULE: t.stringLiteral(source.value)})
);
Expand All @@ -161,6 +163,29 @@ module.exports = (packager, ast) => {
if (!isUnusedValue(path)) {
let name = getName(mod, 'exports');
node = t.identifier(replacements.get(name) || name);

// Insert __esModule interop flag if the required module is an ES6 module with a default export.
// This ensures that code generated by Babel and other tools works properly.
if (
asset.cacheData.isCommonJS &&
mod.cacheData.isES6Module &&
mod.cacheData.exports.default
) {
let binding = path.scope.getBinding(name);
if (binding && !binding.path.getData('hasESModuleFlag')) {
if (binding.path.node.init) {
binding.path
.getStatementParent()
.insertAfter(ESMODULE_TEMPLATE({EXPORTS: name}));
}

for (let path of binding.constantViolations) {
path.insertAfter(ESMODULE_TEMPLATE({EXPORTS: name}));
}

binding.path.setData('hasESModuleFlag', true);
}
}
}

// We need to wrap the module in a function when a require
Expand Down

0 comments on commit 84b3085

Please sign in to comment.