Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transform examples/js to support modules #9562

Closed
ghost opened this issue Aug 22, 2016 · 339 comments
Closed

Transform examples/js to support modules #9562

ghost opened this issue Aug 22, 2016 · 339 comments

Comments

@ghost
Copy link

ghost commented Aug 22, 2016

The main source code has recently been ported to ES2015 modules (see #9310); however, some parts are still relying on global namespace pollution and have thus become unusable.

Specifically the things in /examples/js/ haven't been transformed to support modules yet; this makes them currently unusable from within environments such as webpack or SystemJS.

Edit, 11-20-2016: They can be used, but it requires a lot of setup: SystemJS, webpack. If you need assistance with these: Sorry, but this is the wrong thread to announce it! 😄

@ghost ghost changed the title Transform examples/js to support modules Transform examples/js to support modules Aug 22, 2016
@ghost
Copy link
Author

ghost commented Aug 22, 2016

Side note: You could theoretically use these from within webpack by injecting the global THREE variable (and others, depending on what you're trying to load) using imports-loader, but the problem still remains for SystemJS and native module evaluation that will eventually land in browsers.

@mrdoob
Copy link
Owner

mrdoob commented Aug 23, 2016

I don't know how this can be fixed. We can't turn those files into ES6 Modules because, not only browsers don't support them yet, we want to support old-ish browsers too.

So, as far as I can see, the "hack" for Webpack and SystemJS is the price to pay for now?

@andrevenancio
Copy link
Contributor

andrevenancio commented Aug 23, 2016

Either things like examples/js/postprocessing/ become a module of themselves or we need to come up with some kind of plugin logic.

@mrdoob what if github.com/threejs becomes a thing? You can create different repos inside of it which have a similar build system as the current threejs, which can be used via modules or people can just download the legacy code in the build folder. Again with the postprocessing in mind, it will have THREE has a dependency, and it would be both a module or you can use the /build/EffectComposer.js for legacy code?

@mrdoob
Copy link
Owner

mrdoob commented Aug 23, 2016

@mrdoob what if github.com/threejs becomes a thing?

I have a hard time maintaining one single repo already 😕

@andrevenancio
Copy link
Contributor

the idea was to give you and the maintainers more control over the releases, a bit like https://github.com/airbnb or https://github.com/facebook I'm happy to pick up some bits and bobs as I need them but you'll end up with github.com/randomuser/effectscomposer github.com/randomuser2/orbitcontrols :(

@GGAlanSmithee
Copy link
Contributor

GGAlanSmithee commented Aug 23, 2016

Does it really make sense to make the examples be modules though? If I understand it correctly, the examples are consumers of three, not a part of it. They are there to provide you with example usages of three.js.

I think a more correct way to go about it would be to move as much as possible to the core of three.js and rely on tree shaking to eliminate dead code. (I understand this will be problematic for users that does not want to use a build pipe of their own though..)

@andrevenancio
Copy link
Contributor

andrevenancio commented Aug 23, 2016

We're not saying we should have the examples as modules. We're saying that some of the files referenced in the examples folder aren't yet modularised, like OrbitControls.js or EffectsComposer.js or many others that are very often used in demos, prototypes and even in production. Those files, should be, as I was suggesting, modules of their own, outside of three.js

@satori99
Copy link
Contributor

I agree in regards to OrbitControls and the the other control classes. They are too useful to be just examples.

@killroy42
Copy link

I wrote a small universal module loader, which supports various module systems and falls back to global namespace. It makes it all work, just by including the script. Should I do a demo with it for the examples?

@mrdoob
Copy link
Owner

mrdoob commented Aug 23, 2016

They are too useful to be just examples.

What does that mean? Do you mean that they are too commonly used?

@satori99
Copy link
Contributor

Yeah exactly. Especially OrbitControls. It might just be me, but I end up including that file a lot.

@danrossi
Copy link

danrossi commented Aug 23, 2016

I've ported OrbitControls over to a module, you can do something similar. My version has some changes when to dispatch start events on movement.

It can then include it within Three.Js main app file ie

export { OrbitControls } from '../three-vr-orbitcontrols/src/OrbitControls.js';

I've had to include other examples that are not modules yet, you concat them in with rollup in the rollup script like so

var fileList = [
    "../three.js/examples/js/effects/StereoEffect.js",
    "../three.js/examples/js/effects/AnaglyphEffect.js",
    "../three.js/examples/js/controls/VrControls.js",
    "../three.js/examples/js/controls/DeviceOrientationControls.js"
];

var out = fileList.map(function(filePath){
    return fs.readFileSync(filePath, 'utf-8');
});


var footer = out.join("\n");


export default {
    entry: 'Three.js',
    dest: 'build/three.js',
    moduleName: 'THREE',
    format: 'umd',
    indent: '\t',
    plugins: [
        glsl()
    ],

    outro: outro,
    footer: footer
};

https://github.com/danrossi/three-vr-orbitcontrols

I've done something similar for the VREffect but have refactored and cleaned it up also.

https://github.com/danrossi/three-vreffect

@killroy42
Copy link

Perhaps we could start a small initiative to modularize these commonly used helpers from the examples. Clean them up and improve the quality (For example I rebuild the FPS controller to include keyboard controls, events, etc).

@mrdoob
Copy link
Owner

mrdoob commented Aug 23, 2016

What will happen to the people that don't use modules?

@ghost
Copy link
Author

ghost commented Aug 23, 2016

I find myself in the same position as @satori99, often needing a way to use Projector or CanvasRenderer, both being far from just an "example" with a good 1000 lines each.

@mrdoob People that don't use modules could just be pointed to versions of the project that had a pure ES5 codebase, so that they can pull the code via a <script> tag via global namespace pollution.

Essentially, the jQuery project did the same thing to gradually save themselves from supporting legacy browser versions (i.e. make a cut somewhere, support-wise).

@mrdoob
Copy link
Owner

mrdoob commented Aug 23, 2016

@mrdoob People that don't use modules could just be pointed to versions of the project that had a pure ES5 codebase, so that they can pull the code via a <script> tag via global namespace pollution.

So... maintaining two versions of the same code base?

Essentially, the jQuery project did the same thing to gradually save themselves from supporting legacy browser versions (i.e. make a cut somewhere, support-wise).

That's different. One thing is stopping supporting legacy browsers. What you guys are suggesting is stopping supporting new programmers.

@GGAlanSmithee
Copy link
Contributor

GGAlanSmithee commented Aug 23, 2016

Before we continue this discussion, I think it is important to highlight that this is "only" affecting people that are using a bundler already (emphasis mine):

Specifically the things in /examples/js/ haven't been transformed to support modules yet; this makes them currently unusable from within environments such as webpack or SystemJS.

Users that just want to include examples in a page can still do so (correct me if I'm wrong)

<script type="text/javascript" src="three.js"></script>
<script type="text/javascript" src="examples/js/OrbitControls.js"></script>
<script type="text/javascript" src="myapp.js"></script>

The group of users that does use a bundler is ofcourse very large and not unsignificant (I am one of these), but given that these users are already using a bundler, it might not be crazy to require them to do some manual step if they want to include examples in a custom build of three.

For example, something in the lines of what @danrossi have done, but we could modify the ´npm build´ script to take an additional, optional, list of examples to include in the bundle.

@danrossi
Copy link

My build is quite out there.

I have had to copy the rollup script config. the package json file into a seperate directory outside three.js. The Three.Js main app file which becomes out of sync with head commits of course and need to be updated manually.

the paths look like

project
   build-threejs
   three.js

I then reference the exports like this within the main app file. They have to be changed from the original reference path.

export { EventDispatcher } from '../three.js/src/core/EventDispatcher.js';

I've then included my refactored modules from examples.

The examples that have not been converted have to be appended after the build output with the example code above.

I've stripped the size by 200kb by removing exports that are not needed also from within this modified main app file.

@GGAlanSmithee
Copy link
Contributor

Yeah, I guess concatenating files like that is a bit hacky and also negates some of the possible benefits..

@Kdex, is your proposal to turn each example into an umd bundle? That would make it work in all env. at the cost of added noise..

@ghost
Copy link
Author

ghost commented Aug 23, 2016

So... maintaining two versions of the same code base?

My intent was to eventually kill off the way that three.js ships bundles that mess with global namespace. It might be too early for that, given that <script type="module"> and the System global are not quite there yet, but I don't think that any project should promote this way of shipping code to the user in the future anymore. Hence, what I meant to say was that three.js should eventually just work like so:

<!--
 Use the bundle if you need this to work with legacy browsers.
 This will inject `window.THREE`.

 WARNING: 0.80.2 is the last version that comes with `window.THREE`.
 If you need a newer version, please consider migrating to using modules.
-->
<script src="three.js"></script>

So that in the future, users could migrate to something along the lines of:

<script type="module">
    /* This would be the version that supports `examples/js` to be consumed */
    import THREE from "./vendor/three/three.min.js";
    import CanvasRenderer from "./vendor/three/examples/js/renderers/CanvasRenderer.js";
</script>

Another (maybe cleaner?) approach worth discussing might be to move examples/js into the core.
Pros:

  • No breaking change in terms of deployment (see outlined approach above)
  • Three can be consumed from bundlers and browsers
  • Full support for import syntax without hacking globals in with something like imports-loader

Cons:

  • For people using <script> tags only, the consumed bundle size will increase by a lot

If there's more cons, feel free to bring them up; but as for the bundle size: This only concerns users that want to inject three into global namespace anyway. If they see that this hurts site performance, it will as well act as an incentive to migrate to a build environment with something like rollup or tree shaking and create personalized bundles themselves.

@GGAlanSmithee brings up another good idea: With UMD, the bundle size won't increase for legacy environments, it allows being used with import, allows the examples to be transformed to use ES2015+ and prevents them to be moved to the core. On the other hand, this also makes three.js's build pipeline a little more complicated.

@mrdoob
Copy link
Owner

mrdoob commented Aug 23, 2016

So that in the future, users could migrate to something along the lines of:

<script>
    /* This would be the version that supports `examples/js` to be consumed */
    import THREE from "./vendor/three/three.min.js";
    import CanvasRenderer from "./vendor/three/examples/js/renderers/CanvasRenderer.js";
</script>

That was super helpful to see! Thanks! Can't wait for browsers to start supporting modules.

@andrevenancio
Copy link
Contributor

mrdoob commented 7 hours ago
What will happen to the people that don't use modules?

they will still be able to use script tags in their html with references to either a CDN or a local copy like so:

<html>
    <head>
        <title>Three.js</title>
    </head>
    <body>
        <script src="https://cdn.rawgit.com/mrdoob/three.js/dev/build/three.js"></script>
        <script src="https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/js/controls/OrbitControls.js"></script>
    </body>
</html>

One of the advantages, for me, of having three.js as an npm module, means I avoid having to add a script tag per library in the html, or merge vendor scripts, copy files around from src to dest folders etc.

But having three.js as a module but not being able to import in the same way, OrbitControls, PostProcessing, ExploderGeometry etc, its not convenient.

We can easily have the community helping exporting every utility on the examples folder to a modularised version with backward compatibility (as three.js currently is). But I reckon its something you should do (at least create the official repositories, so people can contribute to them), or we'll end up having a couple of different versions of OrbitControls like we currently do in npmjs.

@ghost
Copy link
Author

ghost commented Aug 24, 2016

@andrevenancio If every example becomes its own official npm module written with ES2015 modules, they would essentially all do import THREE from "three"; in order to provide functionality by using what this THREE instance provides it, right? This is both useful as well as dangerous.

If some guy's personal three.js project does an import THREE from "three"; too, there is a chance that the versions of his project's THREE instance and, say his three-canvas-renderer module's THREE instance are different and not thus not only prevent a proper rollup, but can also introduce weird bugs that are hard to debug (I ran into failing subclass checks before, having introduced two vastly different three.js files by accident via <script>. I'd imagine that the same could happen by dividing the examples into npm modules).

Wouldn't it be safer if we could have all that stuff in one place, e.g. something like

import THREE, { CanvasRenderer } from "three";

?

This would guarantee that the THREE instance for CanvasRenderer also matches the instance of THREE you're importing, as they both originate from the same npm module. This ensures that the rollup can be fully leveraged and should prevent weird bugs.

At least, that's the npm side of it. On the GitHub side, it's really a matter of personal preference if you want to split the examples up into different repositories.

@andrevenancio
Copy link
Contributor

Again, I never said examples should be their own npm module.
What I said, and keep trying to explain, is that some utilities that only can be found in examples/js/ should be moved elsewhere.

Example, OrbitControls, or everything in the postprocessing folder.

And yes, I agree with you, we should be able to import it such as

import THREE, { OrbitControls } from 'three'

@danrossi
Copy link

The build could generate each file into place. into examples/js/ and can be imported as individual modules in src/examples/ or contrib/src/ ?

@borismus
Copy link
Contributor

I'd also really like this to be possible, both via

require('./node_modules/three/examples/js/controls/VRControls'); 

and via modules:

import VRControls from './node_modules/three/examples/js/controls/VRControls'

Could either manually add JS boilerplate to the end of each example (doesn't scale):

if (module && module.exports) {
  module.exports = ModuleName;
} else {
  THREE.ModuleName = ModuleName;
}

Or do something more clever with a build step. Having all of the example code in a unified 'three' bundle seems not particularly friendly for non-npm users, unless I'm missing something.

@mrdoob
Copy link
Owner

mrdoob commented Sep 20, 2016

There is no way to do a ES6 module code that also works with non-ES6 code without transpiling, right?

@donmccurdy
Copy link
Collaborator

the jsm folder will be published with r102, see 7b53b66

@garyo
Copy link
Contributor

garyo commented Mar 3, 2019

This is great! Glad to see GLTFLoader. I notice that jsm/exporters/GLTFExporter isn't there yet though... is that coming?

@marcofugaro
Copy link
Contributor

marcofugaro commented Mar 3, 2019

@mrdoob good start! can I contribute to the modularize.js script? what are the issues you see going forward?

@oparisy
Copy link
Contributor

oparisy commented Mar 10, 2019

Can those new "examples" modules be used from TypeScript already?

Taking OrbitControls as an example, I tried to import it from three.js r102 using:

import { OrbitControls }  from 'three/examples/jsm/controls/OrbitControls';

But got the following compilation error:

Could not find a declaration file for module 'three/examples/jsm/controls/OrbitControls'.
'(...)/node_modules/three/examples/jsm/controls/OrbitControls.js' implicitly has an 'any' type.
Try 'npm install @types/three' if it exists or add a new declaration (.d.ts) file containing "declare module 'three';"

@types/three is part of my dev dependencies, and I can see it contains a declaration for OrbitControls, but it does not seem related to this work as its latest modification is 2 Nov 2018 on DefinitelyTyped.

Am I doing something wrong? Or should definitions be added alongside examples/jsm/ files (as is already the case for src/ files) for this to work?

Edit: maybe #15939 is related, especially considering I intend to import GLTFLoader too?

@Methuselah96
Copy link
Contributor

@oparisy #15947 should fix it.

@oparisy
Copy link
Contributor

oparisy commented Mar 11, 2019

@Methuselah96 Thanks! I gave it a try by copying the files you contributed to node_modules. I can confirm that this solved my compilation issue, and that those type definitions seem up to date so far (at least for OrbitControls and GLTFLoader).

@Poyoman39
Copy link

Poyoman39 commented Mar 14, 2019

A ready to go workaround. Tested within a Create-React-App project :

window.THREE = THREE;
import('three/examples/js/loaders/GLTFLoader').then(
  // Note : window. is required here to make it works.
  const loader = new window.THREE.GLTFLoader();
  // Have fun here
)

Use it inside componentDidMount (for react users) or a bootstrap function.

@oparisy
Copy link
Contributor

oparisy commented Mar 14, 2019

@Poyoman39 hopefully you'll be able to import from jsm soon and such workarounds will not be needed anymore.

@180and170

This comment has been minimized.

@rahul169
Copy link

Hello Mr. Doob,

We are using three-js Revision 103 with AngularJs Version 7, but the Json loader is removed from ThreeJs (Revision 103). As we want to use Geometry Data Json Model, we are using Legacy Json Loader but it is in Deprecated list.

Also, we are trying to use Legacy Json Loader with Angular 7 but it's Module or .ts file is not available.

Please suggest what will be the procedure to handle Json Loader or Legacy Json Loader with AngularJs Version 7.

Thanks

@donmccurdy
Copy link
Collaborator

@rahul169 please use the forums (http://discourse.threejs.org/) for help. This is a long thread with many commenters. :)

@xiaoqufengdi
Copy link

may be you should put three.js and example together.

@hrahimi270
Copy link

Right now, how can I import GLTFLoader or OrbitControls from Threejs examples in React?
It throws an error for me when I intend to use it!

@Mugen87
Copy link
Collaborator

Mugen87 commented May 26, 2019

@hrahimi270 Please redirect your help questions to the forum.

Also have a look at: https://threejs.org/docs/index.html#manual/en/introduction/Import-via-modules

@backspaces
Copy link

Note that the Import-via-modules page does not use legal import syntax which requires a complete path to the module. Thus all the example presume you are using webpack or some other tooling.

Yet three is just fine for full-path importing, simply begin your path with /, ./, ../ and so on, and terminates with .js.

It is a shame that es6 modules have proven so difficult, and that there is no "registry" that would let the bare imports on the Import-via-modules page work without webpack etc.

@looeee
Copy link
Collaborator

looeee commented Jul 9, 2019

Perhaps we can close this issue and keep track of any remaining work in #16688 ?

@Mugen87
Copy link
Collaborator

Mugen87 commented Jul 9, 2019

We should first close this when the ES6 module to UMD conversion is established see #16920

@Kleenox
Copy link

Kleenox commented Sep 25, 2019

Hello, I'm going to leave my 0.02 here, about the "modularization" of examples. Hate to give negative feedback, but I believe this is feedback I'd like to have if this was my project. I needed a WebGL library for a quick solution, so I fetched Three, with which I had a great experience around 5 years for a different project, where I was able to use it to quickly prototype something and it was all very smooth and one of those great experiences where you find a piece of software that just works and that is a breeze to integrate in your project. Today the examples are using modules and that just made it too cumbersome to use in my particular use case. I struggled with it for an hour. Sure, I could have dedicated some more time to alter my project and make it all work, but in the end I just found it easy to use a different webgl engine that doesn't use modules and it's just a bunch of scripts to include and it literally took me minutes to start visualizing my 3d data. This other engine (I don't want to be rude and mention it) was a wonderful experience compared to fighting modules with Three. Just like Three was for me 5 years ago.

Just a thought, maybe you really needed to make the examples use modules to make it all work for you, but you definitely lost something valuable here. Very impressed with how far the project has come otherwise.

@Mugen87
Copy link
Collaborator

Mugen87 commented Sep 25, 2019

@Kleenox Thanks for feedback! Unfortunately, this is a topic where we can not please everyone. Most people developing now with npm and a bundler. The focus on modules makes the live for all these people easier. At a certain point examples/js will be completely removed (see #16920 (comment)).

@Mugen87
Copy link
Collaborator

Mugen87 commented Sep 25, 2019

Hence, I think this issue can be closed now since the modularization is actually done and examples/js is going to be removed.

@Mugen87 Mugen87 closed this as completed Sep 25, 2019
@conor909
Copy link

I've been searching all morning and still cant import GLTFLoader in my next.js / react / es6 app. Also the "Import via modules" docs don't work:
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
I had to use an external package
import OrbitControls from 'orbit-controls-es6'

But I'm lost at how to import GLTFLoader

@Mugen87
Copy link
Collaborator

Mugen87 commented Nov 25, 2019

Please do not post help requests in closed issues. Use the forum or stackoverflow instead.

Repository owner locked as resolved and limited conversation to collaborators Nov 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests