Why did the method of importing change in r137?

I’ve started a new project with webpack and r140, and I’ve been using the usual importing method on all my classes:

import * as THREE from "three";

However, when importing with a GLTFLoader, I got a new error I’ve never seen before:

import * as THREE from "three";
import { GLTFLoader, GLTF } from "three/examples/jsm/loaders/GLTFLoader";

WARNING: Multiple instances of Three.js being imported.
instances

I found the solution was to add an alias to my webpack config. The changelog says that r137 started using import-maps, so I’m guessing the issues are probably related.

My question is why did the method of importing components from /examples change? Was it to get better tree-shaking? I’ve been seeing several users running into importing issues on Twitter, StackOverflow, and here lately, especially since import-maps have very poor browser support.

So I’m just trying to get a better understanding, why did this change to import-maps take place?

1 Like

Is there a way to disable that warning?

I don’t think there is, because it’s part of the Three.js module itself. Additionally, I wouldn’t want to disable that warning because I do want to know if I’m accidentally importing multiple instances.

The ES6 module syntax is actually incomplete without import maps. Only with import maps you can use bare module specifiers in browsers which is a prerequisite for a unique import syntax.

The problem is that modules like OrbitControls should have a unique way for importing dependencies. Before import maps were introduced, it was necessary to have different imports in the npm package and the repository.

npm:

import { EventDispatcher, Vector3 } from 'three';

Repo:

import { EventDispatcher, Vector3 } from '../../build/three.module.js';

Import maps solve this difference by always using the npm style.

1 Like

This syntax ought to work in webpack and other modern bundlers both before and after r137 though, shouldn’t it? Import maps solve a problem in web browsers but I don’t think you need a manual alias in a bundler here, I’m using webpack with r139 without this issue.

And yeah, the “Multiple instances of threejs…” warning is a pretty important one — can cause very odd bugs when this happens.

Re: WARNING: Multiple instances of Three.js being imported

If you are using webpack, and you have the alias directive, e.g.,

...
    alias: {
        three: path.resolve('./node_modules/three')
    },
...

then webpack will ensure that all occurrences of three that it finds in any of your imported scripts while bundling, will be linked with what it finds at ./node_modules/three.

If you are still seeing this error in the browser console after you’ve recreated the bundle, then it is likely that you are importing a script outside of the bundle. Check your HTML doesn’t import any other libs outside of the bundle.

If you are importing the bundle, then you also don’t need to set any import maps because all the required scripts are already referenced and included in the bundle.

Also check the network tab in your browser developer tools to see whether the bundle is being downloaded, plus any other JS scripts that me be also downloaded as well, that may contain another reference to threejs outside of the bundle.

I have checked this with Three r140.2 and the glTFloader and don’t have this problem since all my scripts were in the single bundle created by webpack. No need for importmap in this case.

You could also try npm install again to ensure the installed libs in your node_modules folder actually match what is written in your package.json before bundling.

2 Likes

So what would be the answer to “why” for a casual reader?

Its not really why you need it, its if you need it.
I think the misunderstanding is that if you are using webpack, then you don’t need importmaps as well in your html. because you are no longer using bare module specifiers to reference the threejs libraries client side.

If you need a webpack alias configuration then something else would seem to be wrong, I think. A minimal repro would be helpful for understanding what’s going on.

2 Likes

I’ll put something together.

I agree this is most likely something else because there is nothing in that code you posted that imo could pull two three bundles.

If a 3rd party library does any one of these …

  • pulls from three/src/… instead of “three”
  • has three declared as a dependency instead of a peerDependency

Then you can get versioning or namespace conflicts which may result in multiple three being loaded.

The first mistake can also be in your own codebase, if one module pulls three and another three/src/…

Maybe this is a good reason for Three.js to choose which one it publishes, instead of it publishing both.