In your module B, `class B` should `extends C` instead of A, so both classes `A` and `B` extend from `C`.
I made a reproduction that you can run (assuming you have Meteor installed). See the following with instructions: https://github.com/meteor/ meteor/issues/7621#issuecomment-238923360 But, anyways, the example you just gave is almost identical to my [original example](https://esdiscuss.org/topic/how-to-solve-this- basic-es6-module-circular-dependency-problem#content-0) (except for my `B` class extends from the `C` class). I can make a reproduction of that too if you want, but what happens is that the environment will try to execute module A before executing module C, at which point `C` is `undefined` inside of module `A`. Basically, the result would be the same as writing: ```js class A extends undefined {} // throws an Error ``` I noticed that both Meteor and Webpack will try and execute modules A and B *first*, then finally C, so I thought I could export the setup functions andrun them after defining the C class so that even if modules A and B run first the actual class definitions would run after the C class definition. You can see that I'm trying to take advantage of "live bindings" in order for this to work (but it didn't, hence I have to pass C into the setup functions). I have a feeling that both Meteor's and Webpack's implementations aren't up-to-spec as far as live bindings with circular dependencies, but I could be wrong. > You shouldn't need a `setUpA` export, especially called by one of its dependencies. Just declare and initialize that crap when it's being declared. That's what I thought at first too, but it's not the case, and I'm trying to find a solution. */#!/*JoePea On Wed, Aug 10, 2016 at 2:41 AM, Isiah Meadows <[email protected]> wrote: > First of all, I'll point out that even if it's an internal API, you should > just initialize them immediately. You already have an otherwise fully > initialized C, so you should just add them whenever it comes. You shouldn't > need a `setUpA` export, especially called by one of its dependencies. Just > declare and initialize that crap when it's being declared. > > ```js > /* index.js */ > import A from './app/A' > console.log('Entrypoint', A) > ``` > > ```js > /* app/A.js */ > import C from './C' > > export default class A eclxtends C { > // ... > } > > // set up A here > console.log('Module A') > ``` > > ```js > /* app/B.js */ > import C from './C' > > export default class B extends A { > // ... > } > > // set up B here > console.log('Module B') > ``` > > ```js > /* app/C.js */ > import A from './A' > import B from './B' > > export default class C { > constructor() { > // this may run later, after all three modules are evaluated, or > // possibly never. > console.log(A) > console.log(B) > } > } > > // set up C > console.log('Module C') > ``` > > What's your full output, anyways? That would help me best explain what's > going on, though. > > On Wed, Aug 10, 2016, 02:47 /#!/JoePea <[email protected]> wrote: > >> When I try this same code with Webpack, I get the *exact same results*: >> the `console.log` statements in the exact same order, where the last output >> shows that `A` in the entry point is `undefined`). >> >> Am I misunderstanding something about live bindings? Is there some >> guaranteed order in which these modules should be evaluated? >> >> The reason why I'm after a solution for the circular dependency is >> because in my real-world case I need to use `instanceof A` and `intanceof >> B` within the `C` superclass defined in module C. This is a case of the >> Fragile Base Class Problem where a class should usually *not* have >> knowledge of it's subclasses, but the base class in my case is intended to >> be internal only, not a part of the public API that end users will extend >> from. >> >> */#!/*JoePea >> >> On Tue, Aug 9, 2016 at 11:12 PM, /#!/JoePea <[email protected]> wrote: >> >>> I can get the whole thing to work if I pass the C dependency into the >>> `setUpA` and `setUpB` functions as follows, but oddly `A` is `undefined` in >>> the Entrypoint module at the `console.log` statement, which makes it seem >>> to me like live bindings aren't working the I was expecting. >>> >>> ```js >>> // --- Entrypoint >>> >>> import A from './app/A' >>> console.log('Entrypoint', A) // HERE, output: "Entrypoint undefined" >>> ``` >>> >>> ```js >>> // --- Module A >>> >>> import C from './C' >>> >>> let A >>> >>> export >>> function setUpA(C) { >>> >>> console.log('setUpA') >>> console.log(C) >>> >>> A = class A extends C { >>> // ... >>> } >>> >>> } >>> >>> console.log('Module A', C, setUpA) >>> >>> export {A as default} >>> ``` >>> >>> ```js >>> // --- Module B >>> >>> import C from './C' >>> >>> let B >>> >>> export >>> function setUpB(C) { >>> >>> console.log('setUpB', C) >>> >>> B = class B extends C { >>> // ... >>> } >>> >>> } >>> >>> console.log('Module B', C, setUpB) >>> >>> 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) >>> console.log('Module C', A) >>> >>> setUpB(C) >>> console.log('Module C', B) >>> >>> export {C as default} >>> ``` >>> >>> */#!/*JoePea >>> >>> On Tue, Aug 9, 2016 at 9:59 PM, /#!/JoePea <[email protected]> wrote: >>> >>>> It seems that the environment I'm in (Meteor uses [reify]( >>>> https://github.com/benjamn/reify)) tries to evaluate A and B first, so >>>> I thought I could take advantage of "live bindings" by changing my modules >>>> to the following: >>>> >>>> ```js >>>> // --- Entrypoint >>>> >>>> import A from './app/A' >>>> console.log('Entrypoint', A) >>>> ``` >>>> >>>> ```js >>>> // --- Module A >>>> >>>> import C from './C' >>>> >>>> let A >>>> >>>> export >>>> function setUpA() { >>>> >>>> console.log('setUpA') >>>> console.log(C) >>>> >>>> A = class A extends C { >>>> // ... >>>> } >>>> >>>> } >>>> >>>> console.log('Module A', C, setUpA) >>>> >>>> export {A as default} >>>> ``` >>>> >>>> ```js >>>> // --- Module B >>>> >>>> import C from './C' >>>> >>>> let B >>>> >>>> export >>>> function setUpB() { >>>> >>>> console.log('setUpB', C) >>>> >>>> B = class B extends C { >>>> // ... >>>> } >>>> >>>> } >>>> >>>> console.log('Module B', C, setUpB) >>>> >>>> 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() >>>> console.log('Module C', A) >>>> >>>> setUpB() >>>> console.log('Module C', B) >>>> >>>> export {C as default} >>>> ``` >>>> >>>> As you can see, modules A and B simply export the code that should be >>>> evaluated (note the live bindings). Then finally, the C module is evaluated >>>> last. At the end of the C module, you see that it calls `setUpA` and >>>> `setUpB`. When it fires `setUpA`, an error is thrown on the second >>>> `console.log` that `C` is undefined (or, specifically, `C.default` is >>>> `undefined` because the ES6 modules are compiled into CommonJS form). >>>> >>>> I thought that if `C` was a live binding, then it should be ready by >>>> the time the `setUpA` function is called. Should this in fact be the case? >>>> >>>> */#!/*JoePea >>>> >>>> On Tue, Aug 9, 2016 at 5:36 PM, John Lenz <[email protected]> >>>> wrote: >>>> >>>>> Without a way to load "later" (aka "soft") dependencies, ES6 module >>>>> will continue to be more or less broken for circular dependencies. >>>>> >>>>> On Tue, Aug 9, 2016 at 4:11 PM, Tab Atkins Jr. <[email protected]> >>>>> wrote: >>>>> >>>>>> On Tue, Aug 9, 2016 at 4:00 PM, /#!/JoePea <[email protected]> wrote: >>>>>> > True, and so that's why I'm wondering if the module system can see >>>>>> that it >>>>>> > can satisfy all module requirements if it simply evaluates module C >>>>>> first, >>>>>> > followed by A or B in any order. It is easy for us humans to see >>>>>> that. It >>>>>> > would be nice for the module system to see that as well (I'm not >>>>>> sure if >>>>>> > that is spec'd or not). >>>>>> >>>>>> That knowledge requires, at minimum, evaluating the rest of each >>>>>> module, beyond what is expressed in the `import` statements. That's >>>>>> assuming there's no dynamic trickery going on that would invalidate >>>>>> whatever assumptions it can draw from surface-level analysis. >>>>>> >>>>>> Because of this, only the `import` statements are declaratively >>>>>> available to the module system to work with. Based on that, it >>>>>> definitely can't make any ordering assumptions; all it knows is that A >>>>>> imports C, B imports C, and C imports both A and B, making a circular >>>>>> import. >>>>>> >>>>>> ~TJ >>>>>> _______________________________________________ >>>>>> es-discuss mailing list >>>>>> [email protected] >>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>> >>>>> >>>>> >>>> >>> >> _______________________________________________ >> es-discuss mailing list >> [email protected] >> https://mail.mozilla.org/listinfo/es-discuss >> >
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

