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

ES6 example modules do not provide exports (in node) #19575

Closed
3 tasks done
geoynomous opened this issue Jun 6, 2020 · 14 comments
Closed
3 tasks done

ES6 example modules do not provide exports (in node) #19575

geoynomous opened this issue Jun 6, 2020 · 14 comments

Comments

@geoynomous
Copy link

geoynomous commented Jun 6, 2020

Description of the problem

Following https://threejs.org/docs/#manual/en/introduction/Import-via-modules

import * as THREE from 'three';
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';

in an index.mjs for Node results in

import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';
         ^^^^^^^^^^^^^
SyntaxError: The requested module 'three/examples/jsm/loaders/STLLoader.js' does not provide an export named 'STLLoader'

I know it's node and not a browser - but some threejs features such as loaders are perfectly usable without display - and the NON-jsm version works perfect in my setup

require('three/examples/js/loaders/STLLoader.js');

BTW: same problem when copy-pasting the example from the website

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

Three.js version
  • r117
Browser
  • node 12.18.0
OS
  • macOS
Hardware Requirements (graphics card, VR Device, ...)
@Mugen87
Copy link
Collaborator

Mugen87 commented Jun 6, 2020

in an index.mjs for Node results in

Such imports in node.js only work if the imported files are also mjs files, which is obviously not true for the npm package.

@gkjohnson
Copy link
Collaborator

Such imports in node.js only work if the imported files are also mjs files, which is obviously not true for the npm package.

In the package.json you can actually specify that a package is of type module and node will automatically try to load everything as module, meaning you don't have to specify the extension as mjs. They suggest denoting the "type" of the package whether it's commonjs or module in order to future proof the package so it should probably be done anyway. You can see in the docs here.

Presumably this would mean that the file in the "main" field should be module rather than commonjs compatible (which we're not doing) but I know has been discussed. At least I believe using "type" : "module" should enable everything to work when the user is explicitly importing the build/three.module.js file in their application.

@geoynomous
Copy link
Author

geoynomous commented Jun 6, 2020

In my own package it already defines "type": "module". So this doesn't help. Try a minimal node package after

  • npm init
  • npm i three -s

I writing as the js files in examples are marked as deprecated now - but until this problem is solved it's not a good idea to remove them ...

@gkjohnson
Copy link
Collaborator

In my own package it already defines "type": "module". So this doesn't help.

The docs state that "type: "module" must be in the packages package.json so putting "type" : "module" in your own applications package.json won't help. It will have to be put in three.js' package.json. The feature I linked is designed to address the problem you're referring to.

@donmccurdy
Copy link
Collaborator

donmccurdy commented Jun 6, 2020

I don't think we can set type: module on the entire repository. Certainly not any time soon...

Perhaps you can use something like esm in the meantime?

@donmccurdy
Copy link
Collaborator

donmccurdy commented Jun 6, 2020

I writing as the js files in examples are marked as deprecated now...

That is a good point. The workarounds for node.js are harder than the workarounds for the browser here... there are 6+ months before those files are actually removed, so it would be good to figure out a workflow for node.js users.

I wonder if putting a package.json with {"type": "module"} in just the examples/jsm/ folder would help? (source) 🤔

@gkjohnson
Copy link
Collaborator

gkjohnson commented Jun 6, 2020

@donmccurdy

I don't think we can set type: module on the entire repository. Certainly not any time soon...

It would require that the "main" field be set to "three.module.js" in the package.json. Realistically though what would be lost by doing this now? What environment is relying on "main" pointing to a UMD file? I can't think of any. All package bundlers now resolve to "module" by default and using the old script tag method doesn't rely on the package.json file anyway. Node.js seems like the only platform that depends on it and that environment now supports modules -- and in fact we're breaking support in that environment for examples by not putting the module file there. The UMD file can still be built and distributed but I'm just not seeing any value in referencing it in the package.json.

I wonder if putting a package.json with {"type": "module"} in just the examples/jsm/ folder would help? (source) 🤔

This won't work and just expose the problem we've seen before with codesandbox in node. The app calling require( 'three' ) will resolve to the UMD file while all of the examples will be importing from three.module.js so two version of three will be imported and there will be unexpected behavior due to that.

I propose we set "main" to point to "build/three.module.js" unless there's some environment or use case that I've missed that will break. But I can't think of one other than older versions of node and it's such a hack to get three.js working with examples in that environment right now anyway.

@donmccurdy
Copy link
Collaborator

Realistically though what would be lost by doing this now? What environment is relying on "main" pointing to a UMD file? ... All package bundlers now resolve to "module" by default...

I disagree with this — people still use tools like Browserify. Deleting examples/js is going to be a bumpy process for a lot of developers. My company still uses CommonJS entrypoints in third-party code heavily, too.

Maybe we'll be in a position to reconsider this after examples/js is deleted in December. But until then I do feel very strongly that we shouldn't drop the main CommonJS entry: this would hurt a lot of web users to benefit node.js users, and that tradeoff does not seem justified right now.

@gkjohnson
Copy link
Collaborator

I disagree with this — people still use tools like Browserify. Deleting examples/js is going to be a bumpy process for a lot of developers. My company still uses CommonJS entrypoints in third-party code heavily, too.

I forgot about Browserify. If there are still processes that rely on the main field then I agree it shouldn't be removed, yet.

I'm less familiar with Browserify but hypothetically if we were to change main to point to module would users be able to "migrate" by changing all instances of require( 'three' ) to require( 'three/build/three.min.js' )? Or alias it? Just trying to find ways to move things forward while keeping as many people happy as possible. I agree that node support is a pretty minor use case, though, and it's probably not worth worrying about until the end of the year, then.

@geoynomous
Copy link
Author

geoynomous commented Jun 6, 2020

Some good news - I hope at least ... node 12.18.0 (which has ESM support) seems to work when the file names are mjs

build/three.module.js -> build/three.mjs
examples/jsm/loaders/STLLoader.js -> examples/jsm/loaders/STLLoader.mjs (and in there the import of three.module.js must be changed of course)

Then

import * as THREE from 'three/build/three.mjs';
console.log('three', THREE);
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.mjs';
console.log('STLLoader', STLLoader);

in

node --experimental-modules index.(m)js

works

Please cross check!

@geoynomous
Copy link
Author

Another update:

node -r esm

works too. Still ... it would be nice to know the recommended node way.

@Mugen87
Copy link
Collaborator

Mugen87 commented Jun 7, 2020

Please cross check!

Yes, that does work but it's unrealistic to change the file extensions because of this use case. Using the esm package seems the best approach for now (the node based three.js converters for OBJ and FBX also use esm).

Maybe we'll be in a position to reconsider this after examples/js is deleted in December.

Sounds good. When examples/js is removed and the module only workflow is established, changing package.json should be reconsidered.

@mrdoob
Copy link
Owner

mrdoob commented May 11, 2021

I think this can be closed now?

@donmccurdy
Copy link
Collaborator

Recap — Node.js supports ES6 Modules in modern versions, but makes design choices that cause interoperability to be difficult for library authors who also need to support the web and backward-compatibility.

For three.js users, I'd suggest using esm as a workaround in Node.js.

For the three.js project, I think our current state is probably the best it can be under the circumstances. We cannot use "type": "json" in the root package.json because the source code is not entirely modules, and renaming things to .cjs or .mjs is not going to happen. Perhaps it would be possible to put {"type": "module"} into examples/jsm/package.json and just affect those files; if someone would like to test that with Node.js please do.

But with current Node.js support, and our current understanding of the problem, there doesn't appear to be anything to do here. I'd vote to close this as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants