Parcel: bundling an es6 module produces an export with a "default" key

Created on 26 Jan 2019  路  11Comments  路  Source: parcel-bundler/parcel

馃悰 bug report

I'm opening this as a bug report because the behavior is unexpected, but I'm not sure what "correct" behavior is. There is some dialog here: https://github.com/MikeMcl/bignumber.js/issues/217 and there is a very minimal setup here: https://github.com/quorumcontrol/minimal-fail-bignumber/tree/master/bignumber-alone which produces the output in the current behavior section.

bignumber.js specifies a module file which is an ES6 module. It has the following code: https://github.com/MikeMcl/bignumber.js/blob/master/bignumber.mjs#L2832

When compiled into the dist.js the es6 module is compiled and the module.exports contains a "default" key instead of module.exports equaling Bignumber - which breaks packages which use bignumber.js

馃帥 Configuration (.babelrc, package.json, cli command)

have no .bablerc - but the setup is linked above: https://github.com/quorumcontrol/minimal-fail-bignumber/tree/master/bignumber-alone

馃 Expected Behavior

It's a simple compile so it should "just work"

馃槸 Current Behavior

/Users/tobowers/code/minimal-fail-bignumber/bignumber-alone/dist/index.js:2727
let x = new BigNumber(123.4567);
        ^

TypeError: BigNumber is not a constructor
    at Object.parcelRequire.Focm.bignumber.js (/Users/tobowers/code/minimal-fail-bignumber/bignumber-alone/dist/index.js:2727:9)
    at newRequire (/Users/tobowers/code/minimal-fail-bignumber/bignumber-alone/dist/index.js:49:24)
    at parcelRequire.xi/C (/Users/tobowers/code/minimal-fail-bignumber/bignumber-alone/dist/index.js:81:5)
    at Object.<anonymous> (/Users/tobowers/code/minimal-fail-bignumber/bignumber-alone/dist/index.js:107:3)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
+ popd
~/code/minimal-fail-bignumber/bignumber-alone

馃拋 Possible Solution

I'm actually unclear what the expected behavior from an es6 module compiled should be... but "default" key doesn't work the same as just "require" when using it in a node project.

馃敠 Context

I'm trying to bundle ipld-dag-cbor and ran into this error.

馃捇 Code Sample

https://github.com/quorumcontrol/minimal-fail-bignumber/tree/master/bignumber-alone

馃實 Your Environment

| Software | Version(s) |
| ---------------- | ---------- |
| Parcel | 1.11.0 |
| Node | v10.11.0 |
| npm/Yarn | npm 6.7.0 |
| Operating System | ox |

Bug Stale

Most helpful comment

Is it not an implementation detail that a default export is implemented as a named export, i.e. that

export default BigNumber

is implemented as

export { default: BigNumber }

and

import BigNumber from 'bignumber.js';

is sugar for

import {default as BigNumber} from 'bignumber.js';

Surely, consumers using require

const BigNumber = require('bignumber.js');

should reasonably expect to get the default export.

Edit:

Considering this further, a module can of course have named exports as well as a default export, so require can't just return the default export if there is one.

CommonJS and ES modules just don't mix very well, which is why it is problematic for parcel to always grab pkg.module even when a package is consuming a dependency with require. I think webpack and rollup can be configured to not use or give precedence to pkg.module per package.

How about that if parcel determines that a module only has a default export and no named exports, a consumer's require returns the default export directly, rather than an object with a named export, default?

All 11 comments

This is how es6 modules work.

Node鈥檚 commonjs require !== es6 imports

es6 imports use default

Sent with GitHawk

But what鈥檚 happening is that packages using require are getting the es6 module from big number which doesn鈥檛 work. Happens in the bundling process, works without parcel in the middle.

I think Parcel might follow a different version than node does, as node only looks for the main field in pkg.json and Parcel looks at the most correct version to use for the selected target (would have to double check some of Parcel's code to be able to detail how it works though).

As far as I can tell from the source of bignumber, using

const {BigNumber} = require('bignumber.js');

should work in both cases

However, bignumber is a dependency for a variety of other packages and they haven鈥檛 all switched over to that syntax. See the other directories in my minimal repro.

Parcel looks at the most correct version to use for the selected target

Parcel looks for the browser field if it is bundling for the browser, otherwise it seems to always give precedence to the module field over the main field irrespective of whether consumers are using require or import.

thanks @MikeMcl

Is it not an implementation detail that a default export is implemented as a named export, i.e. that

export default BigNumber

is implemented as

export { default: BigNumber }

and

import BigNumber from 'bignumber.js';

is sugar for

import {default as BigNumber} from 'bignumber.js';

Surely, consumers using require

const BigNumber = require('bignumber.js');

should reasonably expect to get the default export.

Edit:

Considering this further, a module can of course have named exports as well as a default export, so require can't just return the default export if there is one.

CommonJS and ES modules just don't mix very well, which is why it is problematic for parcel to always grab pkg.module even when a package is consuming a dependency with require. I think webpack and rollup can be configured to not use or give precedence to pkg.module per package.

How about that if parcel determines that a module only has a default export and no named exports, a consumer's require returns the default export directly, rather than an object with a named export, default?

I just ran into this too. As another data point, here's what I'm running:

const forEach = require('callbag-for-each')
const interval = require('callbag-interval')
forEach(console.log)(interval(1000))

This works if I run it with node, but fails if I use parceljs. I think this should work consistently in both environments since it's common to share code between node and the browser

Using node v11.4.0

staltz/callbag-interval#7

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs.

Was this page helpful?
0 / 5 - 0 ratings