How to solve this basic ES6-module circular dependency problem?

/#!/JoePea joe at trusktr.io
Mon Oct 10 03:14:01 UTC 2016


I eliminated the circular dep, and all is well now. I suppose I learned
something that is really good to know if ever dealing with circular deps:

Always import circular deps after non-circular deps.


f.e.

this is bad:

```js
import CircularDep from 'somewhere'
import NonCircularDep from 'another-place'
```

and this is good:

```js
import NonCircularDep from 'another-place'
import CircularDep from 'somewhere'
```

This ensures that the non-circular deps are evaluated and ready before
hoisted circular-dep init functions are called (i.e. when using the
var/initDep trick).


*/#!/*JoePea

On Sat, Oct 8, 2016 at 7:46 PM, /#!/JoePea <joe at trusktr.io> wrote:

> Yep, I believe that the first error was solved by moving the import
> statement to the top!
>
> As for the second error, I think (but not yet sure) that the problem might
> be due to the circular dependency `src/motor-html/node ->
> src/motor/Transformable -> src/motor/Sizeable -> src/motor/Scene ->
> src/motor/ImperativeBase -> src/motor/Node -> src/motor-html/node`.
>
> The problem will be solved after I re-organize some things, but if you
> have any suggestions it would be greatly appreciated!
>
> I'm still not sure why it works in Reify and not in Webpack.
>
> - Joe
>
>
> */#!/*JoePea
>
> On Sat, Oct 8, 2016 at 5:23 PM, /#!/JoePea <joe at trusktr.io> wrote:
>
>> Hello Logan, I wonder if I can borrow your expertise for just a moment:
>>
>> I ran into another problem. Suppose I have the following code which is
>> very similar to the code I had before (using the `var` trick along with
>> `initC` function), but now Module C imports `someFunction` from a newly
>> added module.
>>
>> The problem is that module B will be evaluated before both Module C and
>> the utilities module. When `initC` is called inside of the B module, the
>> `initC` function will try and call `someFunction` but there will be an
>> error because the utilities module was not yet evaluated, so `someFunction`
>> is undefined.
>>
>> What would you recommend as a solution?
>>
>> Here's the code:
>>
>> ```js
>> // --- Entrypoint
>> import A from './A'
>> console.log('Entrypoint', new A)
>> ```
>>
>> ```js
>> // --- Module A
>>
>> import C, {initC} from './C'
>>
>> console.log('module A')
>> initC()
>>
>> class A extends C {
>>     // ...
>> }
>>
>> export {A as default}
>> ```
>>
>> ```js
>> // --- Module B
>>
>> import C, {initC} from './C'
>>
>> console.log('module B')
>> initC()
>>
>> class B extends C {
>>     // ...
>> }
>>
>> export {B as default}
>> ```
>>
>> ```js
>> // --- Module C
>>
>> import A from './A'
>> import B from './B'
>> import {someFunction} from './utilities'
>>
>> console.log('module C')
>>
>> var C
>>
>> initC()
>>
>> export function initC(){
>>     if (C) return
>>
>>     console.log('initC!!!')
>>     someFunction()
>>
>>     C = class C {
>>         constructor() {
>>             // this may run later, after all three modules are evaluated,
>> or
>>             // possibly never.
>>             console.log(A)
>>             console.log(B)
>>         }
>>     }
>> }
>>
>> export {C as default}
>> ```
>>
>> ```js
>> // utilities.js
>>
>> var someFunction = () => {...}
>>
>> export {
>>     someFunction,
>> }
>> ```
>>
>> I think a possible (though strange) solution is to change the import
>> order in Module C, f.e.:
>>
>> ```js
>> import A from './A'
>> import B from './B'
>> import {someFunction} from './utilities'
>> ```
>>
>> I haven't actually tested that example code, but I believe I'm having a
>> problem like this with Webpack+Babel here: https://github.com/trusktr/inf
>> amous/tree/circular-dep-bug
>>
>> In this case, there is a circular dependency between `src/motor/Scene`
>> and `src/motor/Sizeable` (which I've already planned to eliminate, but am
>> still curious about how I would solve it without eliminating the circular
>> dep). The modules import the defaults of each other, and I'm using the `var
>> Sizeable/initSizeable` trick. However, there's a third module, `Utility.js`
>> from which the `Sizeable` module needs to import something to use inside of
>> the `initSizeable` function.
>>
>> You can experience the error by simply opening `motor-scratch.html` in
>> your browser, in which case you'll get an error like
>>
>> ```
>> Sizeable.js:328: Uncaught TypeError: Cannot read property '
>> makeLowercaseSetterAliases' of undefined
>> ```
>>
>> where `makeLowercaseSetterAliases` is akin to the `someFunction` of my
>> earlier example. The odd thing is that everything works fine in Meteor's
>> Reify environment, and I'm only getting this error when using the bundle
>> made by Webpack+Babel.
>>
>> If I re-order the dependencies in `src/motor/Sizeable` so that they look
>> like this:
>>
>> ```js
>> import { makeLowercaseSetterAliases } from './Utility'
>> import XYZValues from './XYZValues'
>> import Motor from './Motor'
>> import Scene from './Scene'
>> ```
>>
>> then the error goes away, but I get another error:
>>
>> ```
>> node.js:129: Uncaught TypeError: Cannot read property 'prototype' of
>> undefined
>> ```
>>
>> I have a feeling that moving the import to the beginning solved the
>> problem, and that now I have another problem...
>>
>> If you'd like to test changes (f.e. moving the import statement), you can
>> simply:
>>
>> ```
>> npm install
>> npm run watch
>> ```
>>
>> which will automatically compile the global.js file after making changes
>> to anything in `src`, and you can reload the page in your browser.
>>
>> Any and all input you may have on this would be greatly appreciated!
>>
>> Thanks a ton!
>>
>> All the best,
>> - Joe
>>
>>
>> */#!/*JoePea
>>
>> On Tue, Aug 23, 2016 at 8:55 PM, Logan Smyth <loganfsmyth at gmail.com>
>> wrote:
>>
>>> > Should I open an issue there? And I think you're right about it
>>> should throw, because it is strange that the function can be executed
>>> before the module is ever evaluated (that seems like it should be
>>> impossible), and therefore a TDZ error would happen because the `let` line
>>> wasn't theoretically evaluated yet.
>>>
>>> If you'd like, go for it. To note, Babel 6 doesn't implement TDZ at all
>>> at the moment anyway.
>>>
>>> >  I don't see how it is possible with `var`. How is it that `var`s or
>>> `function`s can be hoisted *out of* the module? Is that part of spec? If
>>> so, then that is different hoisting from function-based hoisting of pre-ES6.
>>>
>>> They are not hoisted out of the module. It seems like you may be
>>> misunderstanding how modules are linked together.
>>>
>>> > Pre-ES6 "hoisting" in javascript happens on boundaries set by
>>> `function`s, and I thought that modules would be similar to function
>>> bodies, and therefore I thought that hoisting would be limited to within a
>>> module and did not expect hoisting to go beyond the module boundary to some
>>> scope that encompasses multiple modules. That to me is a different type of
>>> "hoisting" than what I know from pre-ES6 JavaScript's function-scope
>>> hoisting so it isn't necessarily the "same way it'd work with other cases
>>> of hoisting"; there is definitely some ES6-module-specific stuff happening
>>> that is a little different from pre-ES6 function-scope hoisting. (sidenote:
>>> I've asked the awesome Axel of 2ality to add these useful details to his
>>> articles.)
>>>
>>> Hoisting of var and functions behaves the same way in ES6 and in ES5 and
>>> you are correct there is no "beyond the module boundary". Let's clarify
>>> hoisting in a function.
>>>
>>> ```
>>> function fn() {
>>>     console.log(inner());
>>>
>>>     function inner(){ return "hello world"; }
>>> }
>>> fn();
>>> ```
>>> in this context, when an engine executes `fn()`, before any execution
>>> has happened inside the `fn`, the engine does the following
>>>
>>> 1. Create the conceptual function "scope", which exists immediately as
>>> soon as the function is called.
>>> 2. Look for all function declarations inside `fn` and create their
>>> variables and assign their values to point to function objects. This is
>>> "function hoisting".
>>> 3. Look for all var declarations inside `fn` and create variables with
>>> the value `undefined`. This is "var hoisting".
>>> 4. Look for all let/const declarations, and create uninitialized
>>> variables (these will throw when accessed)
>>> 5. A bunch of other stuff I'm skipping
>>> 6. Execute the function body itself.
>>>
>>> A very similar process happens for modules. You can think of it like
>>> steps 1-4 running, then before executing the module body (step 6), we
>>> recursively do this same process on every imported module. So by the time
>>> any module gets to step 6, every imported module, and every module those
>>> modules depend on, will have executed step 1-4, and have scopes that have
>>> been created, and variables that have been declared (possibly with a value,
>>> or possibly left uninitialized).
>>>
>>> So by the time you get to step 6, there isn't a "beyond the module
>>> boundary", when you access the imported variables in your module, the JS
>>> engine will reach across the module boundary for you, to get the current
>>> value of the variable in the imported module. This behavior of reaching
>>> across module scopes is what module syntax allows, and it is what enables
>>> live binding.
>>>
>>> Because of this live behavior, if you imported something that was
>>> defined with `let`, like your `let C;` example, it would cause a TDZ error
>>> because the variable was still "uninitialized", whereas if you make it a
>>> `var`, it will be initialized to `undefined`.
>>>
>>> > If I use the `var` method as you proposed (which is working in my
>>> Babel env), should I expect that method to always work in any theoretical
>>> 100%-implemented-to-spec ES6 environment, not just in Babel?
>>>
>>> Correct.
>>>
>>> On Tue, Aug 23, 2016 at 8:17 PM, /#!/JoePea <joe at trusktr.io> wrote:
>>>
>>>> > Damn, that's a Babel bug with the block scoping logic. That said, as
>>>> in my example, that needs to be `var C;` anyway, `let` would throw (in an
>>>> environment with working TDZ anyway). Changing it to `var` also stops the
>>>> duplicate printing.
>>>>
>>>> Should I open an issue there? And I think you're right about it should
>>>> throw, because it is strange that the function can be executed before the
>>>> module is ever evaluated (that seems like it should be impossible), and
>>>> therefore a TDZ error would happen because the `let` line wasn't
>>>> theoretically evaluated yet.
>>>>
>>>> I don't see how it is possible with `var`. How is it that `var`s or
>>>> `function`s can be hoisted *out of* the module? Is that part of spec? If
>>>> so, then that is different hoisting from function-based hoisting of pre-ES6.
>>>>
>>>> I was under the impression that the modules were like a function, and
>>>> hoisting would only happen inside the module. In that case, the `initC`
>>>> function could not possibly be available until the `C` module itself was
>>>> evaluated, so I was expecting for there to be an `undefined` error when
>>>> `initC` was called before the `C` module was evaluated.
>>>>
>>>> You say that
>>>>
>>>> > which means you can import and call a function declaration from any
>>>> module, even if that module hasn't started the `Evaluation` phase yet, the
>>>> same way it'd work with other cases of hoisting, where execution hasn't
>>>> reached the function declaration, but it is available early.
>>>>
>>>> Which makes sense based on what I see happening, but it seems strange
>>>> because it means that the scope of the module (as far as hoisting is
>>>> concerned) is not the module itself, but rather some outer scope that wraps
>>>> *all* the modules that import a given symbol.
>>>>
>>>> You said,
>>>>
>>>> > the same way it'd work with other cases of hoisting, where execution
>>>> hasn't reached the function declaration, but it is available early.
>>>>
>>>> Pre-ES6 "hoisting" in javascript happens on boundaries set by
>>>> `function`s, and I thought that modules would be similar to function
>>>> bodies, and therefore I thought that hoisting would be limited to within a
>>>> module and did not expect hoisting to go beyond the module boundary to some
>>>> scope that encompasses multiple modules. That to me is a different type of
>>>> "hoisting" than what I know from pre-ES6 JavaScript's function-scope
>>>> hoisting so it isn't necessarily the "same way it'd work with other cases
>>>> of hoisting"; there is definitely some ES6-module-specific stuff happening
>>>> that is a little different from pre-ES6 function-scope hoisting. (sidenote:
>>>> I've asked the awesome Axel of 2ality to add these useful details to his
>>>> articles.)
>>>>
>>>> If I use the `var` method as you proposed (which is working in my Babel
>>>> env), should I expect that method to always work in any theoretical
>>>> 100%-implemented-to-spec ES6 environment, not just in Babel?
>>>>
>>>> If so, then this may be one of the rare cases of "when we'd want to
>>>> actually use `var` instead of `let`" besides for cases when we want pre-ES6
>>>> hoisting which I think should be generally avoided in order to make code
>>>> less error-prone and easier to understand. This behavior would have been
>>>> nearly-impossible to know about without the knowledge gained from this
>>>> conversation (or from reading the spec in depth which can be difficult).
>>>>
>>>>
>>>>
>>>> */#!/*JoePea
>>>>
>>>> On Tue, Aug 16, 2016 at 10:48 AM, Logan Smyth <loganfsmyth at gmail.com>
>>>> wrote:
>>>>
>>>>> > Your `initC` solution is working in Meteor (Babel + Reify) and
>>>>> Webpack+Babel, but the `initC` logic seems to run twice, as if there are
>>>>> two C variables instead of one. The following code is based on yours, and
>>>>> the `console.log('initC!!!')` statement unexpectedly executes twice, and
>>>>> you'll see output like this:
>>>>>
>>>>> Damn, that's a Babel bug with the block scoping logic. That said, as
>>>>> in my example, that needs to be `var C;` anyway, `let` would throw (in an
>>>>> environment with working TDZ anyway). Changing it to `var` also stops the
>>>>> duplicate printing.
>>>>>
>>>>> > I'm also not sure how `initC` can be defined when it is called in
>>>>> the B module, which is evaluated before C and A. The evaluation order is B,
>>>>> C, A (depth first). Does the `initC` function get hoisted into a scope
>>>>> common with all three modules? That is the only way that would seem to
>>>>> explain it, but that seems to go against the intuition I had that each
>>>>> module had it's own module scope (as if it were wrapped inside a
>>>>> `function() {}`, and therefore I thought the `initC` function would be
>>>>> hoisted within the C module, and that with B being evaluated first I
>>>>> thought an "undefined" error would be thrown when it tried to execute
>>>>> `initC` (but that is not happening!). How is it that `initC` can be
>>>>> available to the B module before C is evaluated?
>>>>>
>>>>> There are two separate pieces to executing a module, `Instantiation`,
>>>>> and `Evaluation`, which are what comes into play here. When you tell a JS
>>>>> environment to execute a file, it will instantiate every ES6 module in
>>>>> dependency graph before beginning to execute _any_ of the modules. Babel
>>>>> does its best to simulate this behavior, though it's not perfect at it. One
>>>>> of the things that happens during module instantiation is that hoisted
>>>>> declarations are initialized, which means you can import and call a
>>>>> function declaration from any module, even if that module hasn't started
>>>>> the `Evaluation` phase yet, the same way it'd work with other cases of
>>>>> hoisting, where execution hasn't reached the function declaration, but it
>>>>> is available early.
>>>>>
>>>>> This behavior is why you can't use `let C` there, because when `B` is
>>>>> being evaluated, the `let C` line won't have run because the Evaluation
>>>>> phase of `C` hasn't started yet. You can however access the `initC`
>>>>> function because it is a function declaration. As long as there are no TDZ
>>>>> errors in what you're doing, that function can do whatever it would like,
>>>>> assuming it doesn't depend on other stuff that would require `Evaluation`
>>>>> to have finished in `C`, the same as what happens with hoisting normally.
>>>>> That means for instance you couldn't do
>>>>>
>>>>>     import B from './B';
>>>>>     var SOME_CONSTANT = "hello";
>>>>>
>>>>>     export function initC(){
>>>>>         return SOME_CONSTANT;
>>>>>     }
>>>>>
>>>>> because calling `initC` here would return `undefined` if called from
>>>>> inside a dependency cycle from `B`.
>>>>>
>>>>> On Sat, Aug 13, 2016 at 9:09 PM, /#!/JoePea <joe at trusktr.io> wrote:
>>>>>
>>>>>> Hi Logan,
>>>>>>
>>>>>> > The example I posted works properly with Babel's live-binding
>>>>>> implementation and should require less repetition. What were your thoughts
>>>>>> on it?
>>>>>>
>>>>>> Your `initC` solution is working in Meteor (Babel + Reify) and
>>>>>> Webpack+Babel, but the `initC` logic seems to run twice, as if there are
>>>>>> two C variables instead of one. The following code is based on yours, and
>>>>>> the `console.log('initC!!!')` statement unexpectedly executes twice, and
>>>>>> you'll see output like this:
>>>>>>
>>>>>> ```
>>>>>> module B
>>>>>> initC!!!
>>>>>> module C
>>>>>> initC!!!
>>>>>> module A
>>>>>> function A() { ... }
>>>>>> function B() { ... }
>>>>>> Entrypoint A {}
>>>>>> ```
>>>>>>
>>>>>> I'm also not sure how `initC` can be defined when it is called in the
>>>>>> B module, which is evaluated before C and A. The evaluation order is B, C,
>>>>>> A (depth first). Does the `initC` function get hoisted into a scope common
>>>>>> with all three modules? That is the only way that would seem to explain it,
>>>>>> but that seems to go against the intuition I had that each module had it's
>>>>>> own module scope (as if it were wrapped inside a `function() {}`, and
>>>>>> therefore I thought the `initC` function would be hoisted within the C
>>>>>> module, and that with B being evaluated first I thought an "undefined"
>>>>>> error would be thrown when it tried to execute `initC` (but that is not
>>>>>> happening!). How is it that `initC` can be available to the B module before
>>>>>> C is evaluated?
>>>>>>
>>>>>> This is the code I have:
>>>>>>
>>>>>> ```js
>>>>>> // --- Entrypoint
>>>>>> import A from './A'
>>>>>> console.log('Entrypoint', new A)
>>>>>> ```
>>>>>>
>>>>>> ```js
>>>>>> // --- Module A
>>>>>>
>>>>>> import C, {initC} from './C'
>>>>>>
>>>>>> console.log('module A')
>>>>>> initC()
>>>>>>
>>>>>> class A extends C {
>>>>>>     // ...
>>>>>> }
>>>>>>
>>>>>> export {A as default}
>>>>>> ```
>>>>>>
>>>>>> ```js
>>>>>> // --- Module B
>>>>>>
>>>>>> import C, {initC} from './C'
>>>>>>
>>>>>> console.log('module B')
>>>>>> initC()
>>>>>>
>>>>>> class B extends C {
>>>>>>     // ...
>>>>>> }
>>>>>>
>>>>>> export {B as default}
>>>>>> ```
>>>>>>
>>>>>> ```js
>>>>>> // --- Module C
>>>>>>
>>>>>> import A from './A'
>>>>>> import B from './B'
>>>>>>
>>>>>> console.log('module C')
>>>>>> let C
>>>>>>
>>>>>> export function initC(){
>>>>>>     if (C) return
>>>>>>
>>>>>>     console.log('initC!!!')
>>>>>>
>>>>>>     C = class C {
>>>>>>         constructor() {
>>>>>>             // this may run later, after all three modules are
>>>>>> evaluated, or
>>>>>>             // possibly never.
>>>>>>             console.log(A)
>>>>>>             console.log(B)
>>>>>>         }
>>>>>>     }
>>>>>> }
>>>>>>
>>>>>> initC()
>>>>>>
>>>>>> export {C as default}
>>>>>> ```
>>>>>>
>>>>>> */#!/*JoePea
>>>>>>
>>>>>> On Thu, Aug 11, 2016 at 10:26 AM, Logan Smyth <loganfsmyth at gmail.com>
>>>>>> wrote:
>>>>>>
>>>>>>> Keep in mind `let A = A;` is a TDZ error in any real ES6
>>>>>>> environment.
>>>>>>>
>>>>>>> The example I posted works properly with Babel's live-binding
>>>>>>> implementation and should require less repetition. What were your thoughts
>>>>>>> on it?
>>>>>>>
>>>>>>> On Thu, Aug 11, 2016 at 12:23 AM, /#!/JoePea <joe at trusktr.io> wrote:
>>>>>>>
>>>>>>>> Alright, so I believe I have found the solution. It is not possible
>>>>>>>> to guarantee a certain module evaluation order, but using some clever (but
>>>>>>>> tedious) conditional checking I believe the problem is solved (with two
>>>>>>>> caveats listed after):
>>>>>>>>
>>>>>>>> ```js
>>>>>>>> // --- Entrypoint
>>>>>>>> import A from './A'
>>>>>>>> console.log('Entrypoint', new A)
>>>>>>>> ```
>>>>>>>>
>>>>>>>> ```js
>>>>>>>> // --- Module A
>>>>>>>>
>>>>>>>> import C from './C'
>>>>>>>> import {setUpB} from './B'
>>>>>>>>
>>>>>>>> let A
>>>>>>>>
>>>>>>>> export
>>>>>>>> function setUpA(C) {
>>>>>>>>
>>>>>>>>     if (!A) {
>>>>>>>>         A = class A extends C {
>>>>>>>>             // ...
>>>>>>>>         }
>>>>>>>>     }
>>>>>>>>
>>>>>>>> }
>>>>>>>>
>>>>>>>> if (setUpA && C) setUpA(C)
>>>>>>>> if (setUpB && C) setUpB(C)
>>>>>>>>
>>>>>>>> export {A as default}
>>>>>>>> ```
>>>>>>>>
>>>>>>>> ```js
>>>>>>>> // --- Module B
>>>>>>>>
>>>>>>>> import C from './C'
>>>>>>>> import {setUpA} from './A'
>>>>>>>>
>>>>>>>> let B
>>>>>>>>
>>>>>>>> export
>>>>>>>> function setUpB(C) {
>>>>>>>>
>>>>>>>>     if (!B) {
>>>>>>>>         B = class B extends C {
>>>>>>>>             // ...
>>>>>>>>         }
>>>>>>>>     }
>>>>>>>>
>>>>>>>> }
>>>>>>>>
>>>>>>>> if (setUpA && C) setUpA(C)
>>>>>>>> if (setUpB && C) setUpB(C)
>>>>>>>>
>>>>>>>> export {B as default}
>>>>>>>> ```
>>>>>>>>
>>>>>>>> ```js
>>>>>>>> // --- Module C
>>>>>>>>
>>>>>>>> import A, {setUpA} from './A'
>>>>>>>> import B, {setUpB} from './B'
>>>>>>>>
>>>>>>>> class C {
>>>>>>>>     constructor() {
>>>>>>>>         // this may run later, after all three modules are
>>>>>>>> evaluated, or
>>>>>>>>         // possibly never.
>>>>>>>>         console.log(A)
>>>>>>>>         console.log(B)
>>>>>>>>     }
>>>>>>>> }
>>>>>>>>
>>>>>>>> if (setUpA && C) setUpA(C)
>>>>>>>> if (setUpB && C) setUpB(C)
>>>>>>>>
>>>>>>>> export {C as default}
>>>>>>>> ```
>>>>>>>>
>>>>>>>> The caveat is that this fails in both Babel environments and in
>>>>>>>> Rollup. For it to work in Babel environments, `let A` and `let B` have to
>>>>>>>> be changed to `let A = A` and `let B = B`, as per the [fault in Babel's
>>>>>>>> ES2015-to-CommonJS implementation](https://github
>>>>>>>> .com/meteor/meteor/issues/7621#issuecomment-238992688) pointed out
>>>>>>>> by Ben Newman. And it fails in Rollup because Rollup [isn't really creating
>>>>>>>> live bindings](https://github.com/rollup/rollup/issues/845), which
>>>>>>>> is fine in most cases, but doesn't work with these circular dependencies.
>>>>>>>> The Rollup output does not create the C reference before it is ever used in
>>>>>>>> the first pair of conditional checks, unlike what (I think) would happen
>>>>>>>> with real live bindings (please correct me if wrong). To understand what I
>>>>>>>> mean in the case of Rollup, just run `if (FOO) console.log(FOO)` in your
>>>>>>>> console, and you'll get an error because FOO is not defined. Had the
>>>>>>>> bindings been live, then FOO *would* be defined.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> */#!/*JoePea
>>>>>>>>
>>>>>>>> On Wed, Aug 10, 2016 at 5:04 PM, /#!/JoePea <joe at trusktr.io> wrote:
>>>>>>>>
>>>>>>>>> I found a solution that works in environments compiled by Babel,
>>>>>>>>> using the [workaround suggested by Ben Newman](
>>>>>>>>> https://github.com/meteor/meteor/issues/7621#issueco
>>>>>>>>> mment-238992688):
>>>>>>>>>
>>>>>>>>> ```js
>>>>>>>>> // --- Module A
>>>>>>>>>
>>>>>>>>> import C from './C'
>>>>>>>>>
>>>>>>>>> let A = A // @benjamn's workaround applied
>>>>>>>>>
>>>>>>>>> export
>>>>>>>>> function setUpA(C) {
>>>>>>>>>
>>>>>>>>>     A = class A extends C {
>>>>>>>>>         // ...
>>>>>>>>>     }
>>>>>>>>>
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> export {A as default}
>>>>>>>>> ```
>>>>>>>>>
>>>>>>>>> ```js
>>>>>>>>> // --- Module B
>>>>>>>>>
>>>>>>>>> import C from './C'
>>>>>>>>>
>>>>>>>>> let B = B // @benjamn's workaround applied
>>>>>>>>>
>>>>>>>>> export
>>>>>>>>> function setUpB(C) {
>>>>>>>>>
>>>>>>>>>     B = class B extends C {
>>>>>>>>>         // ...
>>>>>>>>>     }
>>>>>>>>>
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> export {B as default}
>>>>>>>>> ```
>>>>>>>>>
>>>>>>>>> ```js
>>>>>>>>> // --- Module C
>>>>>>>>>
>>>>>>>>> import A, {setUpA} from './A'
>>>>>>>>> import B, {setUpB} from './B'
>>>>>>>>>
>>>>>>>>> let C = class C {
>>>>>>>>>     constructor() {
>>>>>>>>>         // this may run later, after all three modules are
>>>>>>>>> evaluated, or
>>>>>>>>>         // possibly never.
>>>>>>>>>         console.log(A)
>>>>>>>>>         console.log(B)
>>>>>>>>>     }
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> setUpA(C)
>>>>>>>>> setUpB(C)
>>>>>>>>>
>>>>>>>>> export {C as default}
>>>>>>>>> ```
>>>>>>>>>
>>>>>>>>> ```js
>>>>>>>>> // --- Entrypoint
>>>>>>>>>
>>>>>>>>> import A from './A'
>>>>>>>>> console.log('Entrypoint', new A) // runs the console.logs in the C
>>>>>>>>> constructor.
>>>>>>>>> ```
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Although that works in my environment which is compiled from ES6
>>>>>>>>> modules to CommonJS by Babel, it [doesn't work in Rollup.js](
>>>>>>>>> http://goo.gl/PXXBKI), and may not work in other ES6 module
>>>>>>>>> implementations.
>>>>>>>>>
>>>>>>>>> Is there some solution that will theoretically work in any ES6
>>>>>>>>> module environment?
>>>>>>>>>
>>>>>>>>> */#!/*JoePea
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> _______________________________________________
>>>>>>>> es-discuss mailing list
>>>>>>>> es-discuss at mozilla.org
>>>>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20161009/8305f877/attachment-0001.html>


More information about the es-discuss mailing list