Npm run build does not include three.js dependencies

The full version of this question is on stack-overflow

I used npm to install vite and three.js, and was able to test things locally using npm run dev. Now that I’m looking to create a static site I’m running npm run build, but when I go to preview it with num run preview it appears that my main.js script is not running three.js. My guess for this is that three.js dependencies are not being included from the “node_modules” folder, but directly including the three folders to dist doesn’t fix my issue on preview.

I need more information. Can you show me your import statements.

Sure,

We can’t execute the code based on the screenshot, nor can we see the error(s) that you might be seeing. Please share as much detail as you can, both for setup and the error messages in the JS console, ideally so that someone else can reproduce the error quickly.

Have you inspected the node_modules folder to see if three is there? three.js itself does not have any dependencies that its users need to install.

Yes, I have the three module installed. Here was my process:
0. Installed npm, nodejs, and git

  1. Initialize a vanilla-js project using ‘npm init vite’ as a project named portfolio
  2. Change directory using ‘cd portfolio’
  3. Intall node ‘npm install’
  4. run ‘npm i three’
    From here I was able to build a portfolio using the three library referenced in ‘main.js’ and some CSS. I tested this app using ‘npm run dev’. My site is at a point where I am ready to deploy it to a hosting service like heroku as a static site.
  5. Running ‘npm run build’ to create a /dist folder contaitning my HTML, some (not all) of my assets, and compressed a CSS and JS file.
  6. Going to test this static site using ‘npm run preview’ (and following the localhost link), I see that none of my three.js code is rendering like it was in the ‘npm run dev’ localhost.

You can replicate these results by cloning my repository in a vite project :

  1. ‘npm init vite’ (then setup vanilla JS project named “vite-project” and change directory like steps 1-2)

  2. ‘cd vite-project’

  3. ‘npm install’

  4. ‘npm i three’

  5. Install portfolio repository to vite-project `git clone https://github.com/MC-Meesh/Portfolio

  6. Delete vite boilerplate contents and transfer repository content to the root directory of vite project (I did not delete the public folder, but I did delete ‘vite.svg’). At this point my directory looks like this:

  7. Test using ‘npm run dev’ (works properly) and attempt to build/test using steps 5-6 from the above paragraph.
    This is where I have this issue of my canvas from three.js not loading in the background, despite things working fine for dev. When running ‘npm run build’ I get the following:

As you can see only 2 assets and the compressed CSS+JS files are included. Running ‘npm run preview’ results in the missing canvas issue.

Thanks in advance. Please let me know what other information I can provide to make things clear.

1 Like

Thanks for the criticism on my inquiries - still learning the best way to go about getting help on these issues. See below for an updated process to replicate my results.

1 Like

it looks weird. every static file should be in public. build will not include node_modules, that’s the point, it will bundle everything you need and discard everything you dont.

i find the git clone afterwards suspect, too.

edit:

i just tried and it works :man_shrugging:

npm init vite
cd projectdir
npm install
npm install three
npm run dev

... making threejs run

npm run build

i cd’d into “dist” and ran npx surge to upload it to a hoster and that’s the result: https://frightened-mountain.surge.sh

2 Likes

ps here’s the repo if you need it: GitHub - drcmda/vite-three-js

1 Like

The one other thing I’d flag here is the import from three/src/core/Clock, that should probably just be an import from three instead.

1 Like

I was able to follow @drcmda’s steps without any issue and replicated this with my repository - however I still had the same problem of my three.js content not loading. The issue was a bug in my main.js script that I still haven’t found a workaround to, which was preventing things from loading properly in the build (dist) version.

NOTE: I posted this to a new form, as it is no longer a npm build issue.

The problem is that I am trying to animate text that was loaded using fontLoader.load(). For example, I add text to the scene using the following:

fontLoader.load(
  'node_modules/three/examples/fonts/droid/droid_serif_regular.typeface.json',
  (droidFont) => {
    const textGeometry = new TextGeometry('Scroll to Start', {
      size: 5,
      height: 1,
      font: droidFont,
      bevelSize: 5,
      bevelThickness: 2,
    
    });
    const introTexture = new THREE.TextureLoader().load('suntexture.png');
    const textMaterial = new THREE.MeshBasicMaterial({map: introTexture, transparent:true, opacity: .5});
    globalThis.introText = new THREE.Mesh(textGeometry, textMaterial);
    introText.position.set(-5, 37, -140);
    scene.add(introText);
  }
);

Then, I want it to gently oscillate on the screen so as to not appear static, to do this I would include something like this in my animation function (called at the end of main.js):

function introAnimate() {
    introText.position.y += (Math.sin(clock.getElapsedTime())/62.8)
    introText.rotation.y += (Math.cos(clock.getElapsedTime())/700)
}

The problem with this is that it says that introText is not defined, given it was declared in a function. I tried to fix this by first declaring them as var or const (didn’t work), then adding globalThis. or window. (ie window.introText). But the problem persists.

I tried implementing the solution from this discourse but got the same reference errors when trying to animate mesh.position.y in my updated introAnimate() function. To be honest, I am surprised the npm run dev version ran correctly in the first place given this reference error. Any suggestions on how to fix this would be much appreciated.

that is not a path. there is no node_modules. the bundler takes the things you need and creates a single file from it, all folders etc are collapsed. that string isn’t going to tell the bundler that droid_serif_regular.typeface.json is an asset you need, it’s just a string. you need to put all static assets into /public, and then you can access it like so: “/droid_serif_regular.typeface.json”.

i am not sure but i don’t think three/examples/fonts is even shipped with npm, i don’t think it is. these folders only exist on github.

1 Like

Thank you, this was the exact solution.

It is also worth noting that the font will not immediately load when hosted given fontLoader.load() runs asynchronously as pointed out here. So while the font is loading, a let introText = null declaration is necessary as noted here.

So while the font is loading, a let introText = null

one more hint, you can try to avoid callback hell and nulling variables. treat async ops for what they are, javascript has high level primitives for that.

async function app() {
  const fontLoader = new FontLoader()
  const font = await new Promise(res => fontLoader.load("/font.json", res))
  const geometry = new TextGeometry('Scroll to Start', { font })
  ...

if you have multiple assets avoid waterfalls* like this:

const [font, image, model] = await Promise.all([
  new Promise(res => fontLoader.load("/font.json", res)),
  new Promise(res => textureLoader.load("/image.jpg", res)),
  new Promise(res => gltfLoader.load("/model.glb", res)),
])
...

this will give you much more confidence and code will be less fragile.

* a waterfall is when assets load one after the other which is a huge waste of time

1 Like

Thank you so much for the advice! This is a much better way to handle asynchronous operations and will dramatically speed up my script - as it has grown in complexity. Thank you for taking the time to provide this feedback and help improve my development skills.