Importing Babylon with ES6 without npm resolution

I’m trying to import Babylon into a project using module syntax. The project is inside an ES Module for top-level await. What I’m currently trying to do:

import * as BABYLON from '../node_modules/babylonjs/babylon.js';

Which doesn’t work. I’ve also tried import BABYLON from ... which doesn’t work. If it’s not apparent, this app is running in a browser environment so npm module resolution (e.g. import * as BABYLON from 'babylonjs') does not work either.

What is the proper NPM package for babylonjs to use that I can import?

if you are not bundling, importing will not work like that. If you want to use import in a browser-facing code you will need to import from a URL and not a directory.
The recommended way to deal with it is to bundle your application using a bundler (webpack, for example)

1 Like

Currently, I’m importing the libraries via a script tag like so:

<script src='../node_modules/babylonjs/babylon.js' >
<script type='module' src='app.js' >

Which works up until I need to do some code fragmentation like follows:
sub.js:

export default function example(){
  return BABYLON.Mesh; //function does something using Babylon
}

app.js

import example from './sub.js';
example();

In which case I would want to simply add import { Mesh } from '../node_modules/babylonjs/babylon.js' to sub.js which does not work of course.

I’ve tried to use bundlers and find they can be very difficult to work with… How would you suggest using Webpack with this? I’ve spent hours trying to get Webpack and/or Rollup to work but I still run into issues with them. For example, when using Rollup I get errors saying that Babylon does not have a default export or that It does not have any exports.

you are combining a few concepts and different approaches to load code to javascript.
If you use imports, use imports - i.e., import in app.js and don’t load beforehand and populate the global namespace (as you are doing now).
You should either use UMD - the BABYLON namespace will be available and you will be able to use it all over, or use es modules - import what you need using the full (HTTP) URL of the file you are trying to load.
A bundler will take care of that for you. very personal redommendation - “fight” a bundler until you understand the ins and outs so you can bunel your final applications, or use pure es modules.

2 Likes

I’m currently trying to use pure ES modules, and plan on continuing to do so.

import what you need using the full (HTTP) URL of the file you are trying to load.

That is what I’m trying to do. Relative URLs should work fine (which they do for all the fragmentation).

yes, but the node_modules folder is (probably) not being hosted, AND, this is the UMD version… you should use @babylonjs/core and import from there and not the UMD version

2 Likes

Thanks for the tip!

Using @babylonjs/core works flawlessly though the part about using ../node_modules/@babylonjs/core/... is slightly annoying.

Hmm… I tried testing and it appears that the loaders are not working correctly as they try to import @babylonjs/core/... which doesn’t work (using @babylonjs/loaders). Using babylonjs-loaders at least loads correctly though gives the error Cannot read properties of undefined (reading 'SceneLoader'), even after I tried adding import * as BABYLON from '../../node_modules/@babylonjs/core/index.js' before it.

This is because our es6 modules are using imports from npm packages and not from URLs.

You can try using import-maps to define what @babylonjs/core means, but it is not supported in all browsers yet (or better yet - only in chromium).
Otherwise - a bundler, sadly (if you are using the loaders as well)

1 Like

Using babylonjs-loaders though it loads properly, it just doesn’t detect @babylon/core.

you can’t fight those things :slight_smile: the UMD packages will live well with the UMD packages, the es6 packages will live with es6 packages. There is no easy way around it…

1 Like

How would suggest importing the loaders using the es6 core?

I tried manually adding the loaders to the es6 SceneLoader plugins (which actually works), though I won’t be using it since it requires importing the UMD core which defeats the purpose of moving to the es6 modules in the first place (not loading the entirety of Babylon when I don’t need to):

import { SceneLoader } from '../../node_modules/@babylonjs/core/Loading/sceneLoader.js';

window._sl = SceneLoader; //for debugging in devtools
import '../../node_modules/babylonjs/babylon.js';
import '../../node_modules/babylonjs-loaders/babylon.glTFFileLoader.js';
SceneLoader._RegisteredPlugins['.glb'] = { plugin: new window.LOADERS.GLTFFileLoader(), isBinary: true }
SceneLoader._RegisteredPlugins['.gltf'] = { plugin: new window.LOADERS.GLTFFileLoader(), isBinary: false }

I’m also now getting the error that the engine cannot get the shaders. For example:
<project root>/src/src/Shaders/rgbdDecode.fragment.fx gets requested, when the actually URL should be <project root>/node_modules/@babylonjs/core/Shaders/rgbdDecode.fragment.fx.

I will look into it since it appears that the request for shaders attaches the shader path to the path of the current page rather than using a relative URL. Maybe a PR?

In the mean time, I’ve marked your reply as the answer to the question and though I would still like an answer to the loaders problem, the reply covers the topic.

the only way to do that with the current package structure is either using a bundler or import-maps.
If you don’t want to bundle and you don’t want to use import maps, the only way for you would be to load the UMD version of babyolon and babylon loaders using a script tag (mind you - not a module script tag, a regular one), and use the global BABYLON namespace.

2 Likes

I’ve found that the erroneous shader loading is because the engine tries to load shaders from ShaderStore.ShadersRepository relative to the current URL rather than Babylons URL. After manually adjusting the value to ../node_modules/@babylonjs/core/Shaders/, the URL was <project root>/node_modules/@babylonjs/core/Shaders/rgbdDecode.fragment.fx.

To my dismay the shader file did not exist, instead being moved into node_modules/@babylonjs/core/Shaders/rgbdDecode.fragment.js as an export. I presume that this has to do with trying the load the UMD loaders instead of the ES6 shaders?

It’s possible to resolve the issue by using relative paths for imports, for example, in GLTFFileLoader.ts:

import { AssetContainer } from "@babylonjs/core/assetContainer.js";

could be replaced with

import { AssetContainer } from "../../core/assetContainer.js";

Which works without needing a node environment. I’ve verified that this works and will do a PR.

there is a reasoning behind this structure. those are different packages, and this will break everyone else’s bundling if done this way.
I know I sound like a broken record, but you have two options here - use a bundler or use mapping for your esm packages. Otherwise, you are more than welcomed to run a script on your local copy of the package.

2 Likes