38 comments

[ 2.5 ms ] story [ 93.5 ms ] thread
> The solution I've used here is to put the KeyframeTrack prototype and constructor function in separate files, which allows for a stable sort.

Thanks. I've always wanted to organize my JavaScript projects like C projects.

It's an interesting exercise, but now we have to learn how to use Rollup to hack on Three.js (I run my own fork with optimizations built in for stereo rendering, and know a few other people who do similar things). I also fundamentally disagree that plugin repos are a good idea: part of the appeal of Three.js is that it is a drop-in file that you can use anywhere for 3D graphics.

Why do you need to learn Rollup, assuming that the build system is already configured for you?
> I also fundamentally disagree that plugin repos are a good idea

Part of the reason I was motivated to do this work is that I love Three's batteries-included approach – I don't need to spend a long time configuring my project or trying to figure out how to glue different libraries together (I've written more about this here https://medium.com/@Rich_Harris/small-modules-it-s-not-quite...). Nothing has changed vis-a-vis Three being a drop-in file.

But it's a double-edged sword – batteries-included historically means including code that you're not using in your project. ES modules give us the best of both worlds via tree-shaking. And once you've learned them (you're not learning Rollup, you're learning JavaScript – this is a standard we're talking about) you'll find it's much more logical than the alternative. Trust me!

The KeyframeTrack thing is just a special case resulting from the codebase's history. It could easily be fixed more elegantly, it was just outside the scope of this PR.

So serious questions here, because I actually want to solve these problems, but I haven't found a good solution yet.

I build a framework that runs on top of Three.js. I need to not only consume Three.js and make a bundle for demos, but I also need to create a library bundle that other people can consume. Every time I've looked into Webpack or Browserify to try to do this, the only documented use cases I've seen have been for consuming, not publishing libraries.

What would you suggest as a workflow for providing drop-in packages? Most of my users are not very technical and usually don't have Node installed. They might be semi-comfortable getting a basic, essentially static site going (though they are often running on RoR), or they're hacking on scripts almost exclusively on CodePen. They want to be able to script-tag include JS files and get to work on their projects.

It should be clear that this conversion was primarily done via a script. So it is mostly an automated conversion.

If you are running forks of ThreeJS, like we are with https://Clara.io, you can run the same conversion script on your repo and then merge in the new ThreeJS.

I haven't done it yet, hopefully in the coming week, but it should mostly work.

Next up I hope is moving ThreeJS to full ES6 classes. :)

It's actually a lot of fun to write a script like the one they used. I highly recommend it if you have a dependency that is large and not split up into modules. I dabbled with converting Three.js into modules through a script a couple years ago, took only an afternoon to get it working initially.

I'll have to take a look to see how the official version compares to my hack of an attempt. If Inyone else is curious: https://github.com/zfedoran/modulite-three.js/blob/master/au...

FFS "Rollup", I gather there was not enough choice in this area, so why not create YET ANOTHER ONE.
I've used Rollup and it worked extremely well for bundling JS that uses modules into a single file. The documentation is very good and the community is quite responsive.

The only thing that would be better is not needing Rollup in the first place.

What's the current status of browser support for ES6 modules?

It's available in Edge if you have Windows Insider (the Edge equivalent of Chrome Canary). There are some bugs there though.

Note that the browser's module support is stricter than Node's, you have to point your imports to the specific file you want to import and include `.js` on the end, for example this won't work:

https://github.com/mrdoob/three.js/blob/dev/src/materials/Ma...

in script[type=module]. So there will still need to be a refactor of THREE.js even after browser support starts coming in.

We probably won't see module, or a good module, until big HTTP2 support. Having a single file has some huge gains for HTTP, mostly in the overhead cost of a request. HTTP2 will get rid of some of that overhead via streaming files and header compression. Sending a ton of different javascript files is too costly right now.
Rollup huh, I guess Webpack is now too de jour and will join Gulp in the pit that is Old Javascript.
Actually Webpack and Rollup go hand-in-hand. Rollup just compiles JavaScript ES6 modules into a single file, dealing with only `import` and `export` statements and some tree-shaking. Webpack handles everything else, like ES6-to-ES5 conversion, bundling dependencies, styles and everything else you need to get your code shippable to browsers and node.
>Actually Webpack and Rollup go hand-in-hand. Rollup just compiles JavaScript ES6 modules into a single file

Isn't that webpacks gig. Or what that gulp? Or was that browserify? Or was that Jello? Or was that...

Doesn't seem so terrible to me for people to have a choice in what tool they use.
You seem to be complaining that innovation is happening.

If you don't want to deal with the inevitable 'options' that are going to be offered up by a community as large as the JS community, you could 1. pick a smaller community, 2. stick to old tech (e.g. at our company we still have an app running on RequireJS that runs just fine -- RequireJS is over 6 years old). 3. calmly re-assess tech choices every year or so.

>You seem to be complaining that innovation is happening.

Not at all. I'm complaining that all of these seemingly similar tools keep superseding each other and solve...what exactly.

Why do you think competitiveness of tooling is a bad thing? I understand why it could feel overwhelming, but you really can just pick one (assuming it appears to do what you want) and if it does, stick with it.
I don't know about that. Searching around for conversations surrounding `gulp vs webpack`, people seem generally unconvinced that they can replace gulp with just webpack.
You can run arbitrary tasks in Gulp. The most common task is build. But that build task is chained together via scripting and plugins.

Webpack only deals with the build. But instead of script, it aims for a more declarative API with config files.

So Webpack only covers the build part of Gulp. Which leaves all of the other scripting capabilities of Gulp. However, a major opinion now is to just write vanilla JS scripts and call them via npm commands and hooks. Gulp has strange api, is callback heavy, and everything needs a Gulp plugin to work. Plain javascript scripts can be closer to normal javascript, and npm can use the tool's CLI directly.

The only benefit Rollup brings over Webpack is tree-shaking, which will be in Webpack 2.0.
That's not true. Even without tree-shaking, Rollup produces bundles that are smaller and faster to execute than the equivalent Webpack bundle, because Webpack needs to inline a module loader whereas Rollup moves all the code into the same scope. They're fundamentally different designs.
I wonder what justifies turning normal code into a string to eval():

https://github.com/mrdoob/three.js/pull/9310/files#diff-2ac7...

becomes

https://github.com/mrdoob/three.js/pull/9310/files#diff-ff6e...

On the surface it seems bad.

This does not look like an eval. It seems to be a script that generates a JS file. The string is not eval'ed, it is concatenated.
Good point, either way it is ugly that normal code is now a string. It is in rollup.config.js so maybe this is imposed by "Rollup", but I'm not about to go find how the "outro" option differs from the "footer" option in yet another JS build tool.
AudioContext is a special case – unlike most of the THREE.* exports, THREE.AudioContext is a getter (which initializes a Web Audio context). ES module namespaces can't have getters, so for backwards compatibility in the generated UMD bundle it was necessary to add that snippet to the bottom.
Why is this a good thing? Just seems like different syntax to do the same thing.

You can theoretically dynamically resolve the different exports asynhronously, but you could do that by exporting functions before.

ES6 makes me think I am going crazy. Everyone seems so excited to have new sugar and a ton of busywork upgrading code and resolving runtime incompatibilities. I have yet to see any feature that seems like an actual benefit to the conservative working programmer.

Maybe destructuring.

Smaller file sizes.
Awesome, virtually all of Rich Harris' work is pure gold - this, Rollup, RactiveJS, etc, etc. Pragmatic and solves real development problems.
It's hard to see this as anything other than an ad for rollup. There's been many attempts to modernize the Three.js codebase. Not sure why this is the one that went through.

I'm all for bringing Three.js up to date and applying modern JS tools to it. The maintainers of Three usually push back against these efforts. This PR still doesn't address the many fundamental problems with the quality of Three's source code and the attitudes of the maintainers. I wouldn't personally use Three on a large project, the low quality ecosystem will eventually harm you. It's designed as library to spin up small projects quickly. It's good at that. Beyond that I wouldn't recommend it.

For the record I believe Babylon (another WebGL utility library) has similar problems with the source code. I don't have a suggestion for the "best" WebGL library to use.

Is it, though? If "Hi, I'm the author of the ONLY tool that does bundling based on how ES2015 modules work, and applied that to your codebase, which made it both more useful to future developers and made it smaller as bundle" is an ad, I'd like to see more ads everywhere. Other than rollup there are no bundlers that perform ES2015 module bundling, which means there is no efficient tree shaking, no load-order independency, etc.

I suspect the reason it went through is because it _actually_ modernized the codebase: unlike previous ideas, just swapping ES5 notation for a newer slightly more efficient ES6 notation, this refactor actually changes the load, parse, and bundle efficiency of the codebase, from a non-standard module style to what we've now collectively agreed on JS-native module should look like; something that before ES6 simply did not exist. There were choices made by individuals or organizations (commonjs, umd, etc), but only as of ES6 are modules _actually_ a formal construct in JS, with an official spec that formalizes how imports work. Any code changes that bring older codebases in line with the new spec are fantastic: this codebase just got massively future-proofed without any impact whatsoever on its integrity.

(the one example that didn't run turned out to be a bug in the current example, not in the refactor)