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 <[email protected]> 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/ > infamous/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 <[email protected]> > 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 <[email protected]> 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 <[email protected]> >>> 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 <[email protected]> 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 <[email protected]> >>>>> 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 <[email protected]> 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 <[email protected]> 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#issuecomment-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 >>>>>>> [email protected] >>>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>>> >>>>>>> >>>>>> >>>>> >>>> >>> >> >
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

