Modifying ES6 module exports

/#!/JoePea joe at trusktr.io
Tue Dec 22 10:01:28 UTC 2015


In my case I was using Webpack+babel-loader. I guess that makes sense,
about the run-time dependencies. After solving the problem by wrapping
the module logic for A and B in functions then calling those functions
in main.js. I then encountered another problem: package C imports the
default from package B, and at initialization-time B is undefined in C
even though there's no circular dependency there. I was able to solve
that problem by wrapping init-time logic in a function and again
executing that in main.js. So, basically, moving all to calls in
main.js solved both of these problems. I can follow what you're saying
about the circular dep between A and B, but I'm not at all sure why B
would be undefined in C after having moved A and B logic to main.js,
leaving C logic still in the C module.

On Mon, Dec 21, 2015 at 8:05 PM, Logan Smyth <loganfsmyth at gmail.com> wrote:
> To start, an object is definitely not what I'd expect. The core thing to
> remember with ES6 modules is that while imports are live bindings, you still
> need to write your code in such a way that it has no run-time circular
> dependencies.
>
> In your specific examples, you are importing `A` first, which will begin `B`
> loading. Since `A` has not finished executing yet, and B will be attempting
> to access the the `A` import before the `A.js` file has begin executing. I'm
> not 100% sure off the top of my head, but in a real ES6 environment, that
> would either result in the `A` import being `undefined`, or attempting to
> access it would throw a temporal dead zone error due to you accessing a
> variable before it has been initialized.
>
> If your `A.js` file comment "// modify the A object adding methods that
> reference B." adds those references without actually accessing `B` in any
> way, then the solution to your issue (in a real environment anyway) would be
> to import `B.js` _first_ so that you do not have circular
> initialization-time dependencies.
>
> In this case, are you using Babel's ES6 module syntax transpiling, or
> Webpack with another transpiler?
>
>
> On Mon, Dec 21, 2015 at 7:25 PM, /#!/JoePea <joe at trusktr.io> wrote:
>>
>> I'm curious, what should happen when module A imports the default from
>> module B which imports the default from module A? Should the exported
>> A object exist inside of module B?
>>
>> Here's two modules A and B (simplified from some real code that I
>> have), which have a circular dependency:
>>
>> ```js
>> // A.js
>> import B from './B'
>> let A = window.globalThing
>> export default A
>>
>> // modify the A object adding methods that reference B.
>> ```
>>
>> ```js
>> // B.js
>> import A from './A'
>> let B = new window.OtherThing
>> export default B
>>
>> console.log(A) // an empty object, {}
>>
>> // modify the B object which depends on A existing (not being some
>> temporary
>> // object like I get with Webpack) while the module is evaluated.
>> ```
>>
>> When using Webpack, this code fails because the reference to A in B is
>> some empty object. If I import both A and B into main.js, then A is
>> defined as epected:
>>
>> ```js
>> // main.js
>> import A from './A'
>> import B from './B'
>>
>> console.log(A) // An object with a bunch of properties, as expected.
>> ```
>>
>> I can fix my problem by exporting functions to do setup of A and B,
>> and running those functions in main.js, like this:
>>
>> ```js
>> // A.js
>> import B from './B'
>> let A = window.globalThing
>> export default A
>>
>> A.setup = () => {
>>   // modify the A object adding methods that reference B.
>> }
>> ```
>>
>> ```js
>> // B.js
>> import A from './A'
>> let B = new window.OtherThing
>> export default B
>>
>> console.log(A) // an empty object, {}
>>
>> B.setup = () => {
>>   console.log(A) // the expected object!! Why?
>>
>>   // modify the B object which depends on A existing (not being some
>> temporary
>>   // object like I get with Webpack) while the module is evaluated.
>> }
>> ```
>>
>> When using Webpack, this code fails because the reference to A in B is
>> just `{}`, some empty object. If I import both A and B into main.js,
>> then A is defined as epected:
>>
>> ```js
>> // main.js
>> import A from './A'
>> import B from './B'
>>
>> A.setup()
>> B.setup()
>>
>> // no more problems!
>>
>> console.log(A) // An object with a bunch of properties, as expected.
>> ```
>>
>> So, if I run the setup for A and B in main.js instead of in the module
>> evaluation, everything works perfectly.
>>
>> This led me to wonder: What is the expected behavior of circular
>> dependencies in ES2015? Have you had this 'empty object' problem
>> before? Is that expected to happen, or might there be a bug in
>> Webpack?
>>
>> I guess the problem makes sense: How can the first method (running
>> relying on module evaluation for setting up A and B exports) work if
>> module B depends on the evaluation of module A which depends on the
>> evaluation of module B?
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>
>


More information about the es-discuss mailing list