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

Bug when using mathbox + THREE.Color in a bundle #49

Closed
ChristopherChudzicki opened this issue Dec 27, 2022 · 7 comments · Fixed by #55
Closed

Bug when using mathbox + THREE.Color in a bundle #49

ChristopherChudzicki opened this issue Dec 27, 2022 · 7 comments · Fixed by #55

Comments

@ChristopherChudzicki
Copy link
Collaborator

ChristopherChudzicki commented Dec 27, 2022

@sritchie brought this up in ChristopherChudzicki/mathbox-react#203, but fundamentally it's an issue with the way Mathbox imports stuff.

Expectation: Users consuming the mathbox library should be able to write code similar to that below, bundle it with some bundler (webpack, vite, turbopack, etc) and have it work

import * as MB from "mathbox"
import * as THREE from "three"

const mathbox = MB.mathBox(/* options... */)
mathbox
  .cartesian()
  .axis({ color: new THREE.Color(0xff4136) })

Actuality: Code like the above does not work because:

  1. This library (mathbox) imports from three/src. For example, import { Color } from "three/src/math/Color.js"; ( example)
  2. This library (mathbox) uses instanceof checks like value instanceof Color (example)
  3. Userland people like above import from three (such as the code above), which means userland people use three/build/three.module.js. Consequently, instanceof checks won't work.

See https://github.com/ChristopherChudzicki/mathbox-color-bug for a full example.

Notes

@sritchie
Copy link
Collaborator

I looked into this a bit (while dealing with some madness around an exploded frozen pipe at our house, otherwise I would put up a full PR...)

It looks like internally, threejs deals with this with methods like isColor: https://github.com/mrdoob/three.js/blob/dev/src/math/Color.js#L59

isVector2: https://github.com/mrdoob/three.js/blob/dev/src/math/Vector2.js#L5

etc... so my guess is that that is the best way for us to move forward here. Thoughts @ChristopherChudzicki ?

@ChristopherChudzicki
Copy link
Collaborator Author

ChristopherChudzicki commented Dec 27, 2022

Submitting this in haste b/c i have to run... hopefully didn't forget something.

I would start off with the question "Do we want to support userland imports like import ... from "three". I think the answer is definitively "Yes". We could tell userland people "You should import from "three/src". But that feels (a) less natural, and (b) is potentially troublesome for builds 1, and (c) seems fragile. Regarding (c)... importing their src files really doesn't seem like expected to usage to me. They do include "src" in their package.js exports + files lists, but their src files really seem like more an implementation detail to me: would they really consider renaming a src file a breaking change?

OK, why did we start importing from "three/src" in the first place? Answer: to make mathbox easier for bundlers to tree-shake. Note!: Whether we do import { Color } from "three" or import { Color } from "three/src" does not affect mathbox's bundle size since those are external/peerDependencies, but the difference can affect how easy it is for userland apps (like math3d-next or your projects) to tree-shake.

Suggestion

My suggestion: Stop importing from "three/src". Replace all our threejs imports with import { Color, Whatever } from "three".

Pros:

Cons:

  • Although some bundlers can still treeshake the unused portions of ThreeJS, webpack seems unable to. I'm not sure if it's really unable to, or if I just haven't gotten the config correct2.

@sritchie Question:
Left to my own devices, I would switch to "three" imports and tell people to use vite/rollup/whatever instead of webpack if they want better treeshaking. But I'm unsure how important webpack is to you + clojurescript ecosystem.

Footnotes

  1. Using import { Color } from "three/src/Three.js" works fine the minimal example in mathbox-color-bug. But it did not work in the storybook example at https://github.com/ChristopherChudzicki/mathbox-react/pull/203. Importing from "three/src" crashed storybook for some reason I never fully figured out.

  2. I probably won't try much longer. Webpack is frustrating. Thus I switched to vite (for math3d-next) and rollup (for mathbox-react).

@ChristopherChudzicki
Copy link
Collaborator Author

@sritchie Re using properties like isVector2 or isColor: I think this could work and I'm OK with that if the the above suggestion—using import ... from "three"—seems like a no-go to you. It occurs to me that this would mean that if I import { Color } from "three" and use a bundler that can successfully tree-shake such imports, then I still end up with two copies of Color: one from my userland import and one from mathbox. Of course, we could tell userland people to use three/src/ imports, but I don't love that for reason above.

@ChristopherChudzicki
Copy link
Collaborator Author

Related:

Though I haven't found anything particularly enlightening yet.

@sritchie
Copy link
Collaborator

@ChristopherChudzicki thanks for doing all of this research. Let me ask the resident Google Closure Compiler expert on the Clojure slack if he thinks that my switch over to the three/src imports is actually doing anything here, of if Closure can successfully tree-shake from "three" imports. I'll get this done in the next day or so!

@sritchie
Copy link
Collaborator

@ChristopherChudzicki based on that tree-shaking thread I'm convinced that I don't have a good mental model of how any of this works, and I am very happy to switch to "three" imports.

THAT SAID — using the isColor etc methods might also be a good change, because if some consuming project decides that THEY want to import from just the file, their code will break for the opposite reason that my example broke.

It seems that threejs has added these to every object where we are using instanceof checks, and dodging instanceof completely feels like a good change.

wdyt @ChristopherChudzicki ? is this more work for not much gain?

@ChristopherChudzicki
Copy link
Collaborator Author

@sritchie 👍 , I agree; I am inclined to do both:

  • simplify imports to "three" ... library end users can use simple "three" imports, and some bundlers can still tree shake sufficiently well. If your bundler can't, that's a bundler problem.
  • use isColor to check if object is color, isVector2 for vector2, etc, for the reason you mention

ChristopherChudzicki added a commit to unconed/threestrap that referenced this issue Jan 7, 2023
Previously we were importing from specific three/src files. Why change? Because:
    - we can still tree shake
    - we were actually (indeirectly) using bare imports before, too. E.g., ThreeStrap imports from "three/examples/jsm/controls/OrbitControls", and OrbitControls imports bare "three"
    - "three" seems like the more official import pattern; it's what is documented at https://threejs.org/docs/#manual/en/introduction/Installation

See also unconed/mathbox#49
ChristopherChudzicki added a commit that referenced this issue Jan 7, 2023
Previously we were importing from specific three/src files. Why change? Because:
    - we can still tree shake
    - we were actually (indeirectly) using bare imports before, too: MathBox (via Threestrap) imports from "three/examples/jsm/controls/OrbitControls", and OrbitControls imports bare "three"
    - "three" seems like the more official import pattern; it's what is documented at https://threejs.org/docs/#manual/en/introduction/Installation

See also #49
ChristopherChudzicki added a commit that referenced this issue Jan 7, 2023
Previously we were importing from specific three/src files. Why change? Because:
    - we can still tree shake
    - we were actually (indeirectly) using bare imports before, too: MathBox (via Threestrap) imports from "three/examples/jsm/controls/OrbitControls", and OrbitControls imports bare "three"
    - "three" seems like the more official import pattern; it's what is documented at https://threejs.org/docs/#manual/en/introduction/Installation

See also #49
sritchie pushed a commit to unconed/threestrap that referenced this issue Jan 7, 2023
Previously we were importing from specific three/src files. Why change? Because:
    - we can still tree shake
    - we were actually (indeirectly) using bare imports before, too. E.g., ThreeStrap imports from "three/examples/jsm/controls/OrbitControls", and OrbitControls imports bare "three"
    - "three" seems like the more official import pattern; it's what is documented at https://threejs.org/docs/#manual/en/introduction/Installation

See also unconed/mathbox#49
ChristopherChudzicki added a commit that referenced this issue Jan 8, 2023
* simplify threejs imports

Previously we were importing from specific three/src files. Why change? Because:
    - we can still tree shake
    - we were actually (indeirectly) using bare imports before, too: MathBox (via Threestrap) imports from "three/examples/jsm/controls/OrbitControls", and OrbitControls imports bare "three"
    - "three" seems like the more official import pattern; it's what is documented at https://threejs.org/docs/#manual/en/introduction/Installation

See also #49

* npm install with new threestrap

Now that it's published

* reformat

* fix primitive import
@sritchie sritchie linked a pull request Jan 11, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants