Problems loading babylon via es modules

hello friends! i’m experimenting with the future, today! :sunglasses:

i’m really excited since all the cool browsers support proper es-modules, so i’ve been upgrading my typescript libraries to emit es-modules and umd-modules, and in new projects, i’ve been experimenting with having no build-step at all. it feels very liberating!

so given the following code to load babylon via unpkg (which seems to be an awesome hipster replacement for npm, from the future!) (here’s this example on codepen)

import * as babylon from "https://unpkg.com/@babylonjs/core@4.0.0-beta.1/index.js"
document.body.textContent = "done!"

i’ve found two potential issues:

  1. module specifiers are lacking the required extensions

    • when we look at @babylonjs/core/index.js ( https://unpkg.com/@babylonjs/core@4.0.0-beta.1/index.js ) we can see that the emitted module specifiers do not have extensions

    • omitting the extensions is not valid, however in our example case, unpkg goes one extra step to counteract the problem: it will serve the .js file for any file requested without an extension (eg, “index” becomes “index.js” thanks to the unpkg server, but this is non-standard behavior that should not be relied on) (other npm-cdn’s, like jsdelivr, don’t seem to offer this helpful hack)

    • the lack of extensions causes loading babylon via jsdelivr to fail (which doesn’t have the ?module hack feature) — import * as babylon from "https://cdn.jsdelivr.net/npm/@babylonjs/core@4.0.0-beta.1/index.js"

    • note that there are ongoing debates ( https://github.com/nodejs/modules/issues/293 ) on whether the future of es modules on the web should be expressed as .js or .mjs

    • at first in my own projects, i found this requirement confusing, because typescript doesn’t have a compiler option to emit a particular extension in the module specifiers — however as a solution, i found that typescript seems to be designed, for this purpose, to ignore file extensions in your typescript sourcecode – such that you can tweak all of your module references (in your ts source) to, for example, export * from "./abstractScene.js"; (adding the .js) – and typescript is totally relaxed about it, and will emit the desired extension, and so typescript is smart enough to know that abstractScene.js is the same as abstractScene.ts

    • user workaround: rely on unpkg’s rewriting hack for now

    • inevitable babylon solution: add .js extensions to all module references in babylon’s typescript sourcecode (or otherwise .mjs, if that one floats your boat)

  2. "bare specifier" module references

    • the following error is thrown:

      Uncaught TypeError: Failed to resolve module specifier "tslib". Relative references must start with 
      either "/", "./", or "../".
      
    • there are some bare specifiers in babylon referencing tslib, at least for the decorator function, it looks to me

    • bare specifiers are reserved ( https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier ) for builtin modules, however in the future, we’ll be able to rewrite them via import-maps

    • how to fix it:

      • (future solution) we’ll use import-maps ( https://github.com/WICG/import-maps ) to remap the bare-specifiers

      • (workaround-a) we can use es-module-shims ( https://github.com/guybedford/es-module-shims ) to shim the import maps see the codepen demo ( https://codepen.io/ChaseMoskal/pen/BbqVOr?editors=1010 )

      • (workaround-b) unpkg has a ?module feature which rewrites bare specifiers (see my reply post for more details)

thanks for reading, and of course it goes without saying, i’m no authority on how es modules work — we’re all learning and developing these new ideas together, so if i seem misguided or there’s a different way to think about or avoid these problems, i’d love to discuss it, so please speak up!

if you’d like me to post a github issue, just let me know

cheers everybody, keep making web games :slight_smile:

edit: hmm, this form limits me to two real links, sorry but you’ll have to copy-paste if you want to follow them :wink:

// chase

1 Like

in further experimentation, i have found that the “bare specifier” module references situation is a non-issue for babylon

  • (real solution in the future) import-maps will solve this problem, and is in the standardization process

  • (workaround-a) unpkg has a really cool ?module feature which rewrites bare-specifiers to fully qualified unpkg links: this works today

    import * as babylon from "https://unpkg.com/@babylonjs/core@4.0.0-beta.1/index.js?module"
    
  • (workaround-b) use es-module-shims and use import maps today (codepen example)

    <script defer src="//unpkg.com/es-module-shims@0.2.1/dist/es-module-shims.js"></script>
    
    <script type="importmap-shim">
      {
        "imports": {
          "tslib": "//unpkg.com/tslib@1.9.3/tslib.es6.js"
        }
      }
    </script>
    
    <script type="module-shim">
      import * as babylon from "https://unpkg.com/@babylonjs/core@4.0.0-beta.1/index.js"
      console.log(babylon)
    </script>
    

however the lacking of extensions in module specifiers is a problem that should be addressed

The main issue would be the reference cross packages like materials-library and so on who are referencing the project through node_modules.

We are closely following those issues and won t miss to update as soon as there will be a standardization and agreement in favor of one particular extension.

Despite ts not requiring the extension some other tools associated with the build do not love them a lot at the moment so instead of hacking around those tools at the moment we prefer to wait for a strong standardization agreement.

I hope this will come soon and that the network related issue of unbundled files would also find a solution.

1 Like

Why does it take so long to load? Open the Network tab in your browser.

Playground: Plunker - ES-module and Babylon.js

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Document</title>
</head>

<body>
    <script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>

    <script type="importmap">
        {
            "imports": {
                "babylonjs": "https://unpkg.com/@babylonjs/core@5.8.2/index.js"
            }
        }
    </script>

    <script type="module">
        import * as BABYLON from "babylonjs";

        console.log(new BABYLON.Vector3(1, 2, 3));
    </script>

    <!-- <script type="module" src="js/bundle.js"></script> -->
</body>

</html>

Please, add Babylon.js to https://www.skypack.dev/ (or create conditions for it to be added there) It seems to be there: npm:@babylonjs/core | Skypack

<script type="module">
  import babylonjsCore from 'https://cdn.skypack.dev/@babylonjs/core';
</script>

But actually this link (https://cdn.skypack.dev/@babylonjs/core) has an error:

/*
 * [Package Error] "@babylonjs/core@v5.9.0" could not be built. 
 *
 * No build output found.
 *
 * How to fix:
 *   - If you believe this to be an error in Skypack, file an issue here: https://github.com/skypackjs/skypack-cdn/issues
 *   - If you believe this to be an issue in the package, share this URL with the package authors to help them debug & fix.
 *   - Use https://skypack.dev/ to find a web-friendly alternative to find another package.
 */

console.warn("[Package Error] \"@babylonjs/core@v5.9.0\" could not be built. ");
throw new Error("[Package Error] \"@babylonjs/core@v5.9.0\" could not be built. ");
export default null;

cc @RaananW

1 Like

We (still) don’t support importing directly in the browser, but I promise we are working on it :slight_smile:

2 Likes

Hey, what is the status of importing babylonjs directly in the browser? :slight_smile:
Is it this issue?

You can load @babylonjs/core in the browser. It should work as expected. The rest of the modules would not work due to issues with name-references. The main issue with es modules in the browsers is their dependencies. let’s say you import something from @babylonjs/core - the browser doesn’t know what @babylonjs/core is and doesn’t know where to get it from.
But since @babylonjs/core doesn’t have any external dependencies you can use it directly in browser.

Okey, so I am having the trouble with loading @babylonjs/core in the browser.
I have this PR:

which doesn’t add the BabylonJS code to the bundled file.
But when I serve it I get blank page with the error:
Uncaught TypeError: Failed to resolve module specifier "@babylonjs/core". Relative references must start with either "/", "./", or "../".

You will need to host the code as a static package somewhere. For example, you can use unpkg.
You will also need to import from the URL and not the package (for the same reason I provided before :-))

import .{ Scene } from "https://unpkg.com/@babylonjs/core@5.28.0/scene"

and so on.

I wouldn’t trust unpkg for production use, but it is good enough to experiment with it.

Hmm, I don’t think that is the issue here.

  1. I have index.html file where I’m getting the BabylonJS from the CDN
    cannon-es-debugger-babylonjs/index.html at feat/dont-bundle-babylonjs · neu5/cannon-es-debugger-babylonjs · GitHub
    And it’s ok.
  2. in this index.html file I import CannonDebugger
    import CannonDebugger from "cannon-es-debugger";
    which is my local file
    "cannon-es-debugger": "./dist/cannon-es-debugger.js"
  3. in this cannon-es-debugger I import the @babylonjs/core
import {
  Color3,
  MeshBuilder,
  Quaternion,
  StandardMaterial,
  Vector3,
} from "@babylonjs/core";

I have BabylonJS added as peerDependency so I don’t add it to the output file. And there is where I get that error - somehow it can’t read it from the node_modules (where @babylonjs/core is).
It doesn’t have the same problem with cannon-es. It loads it normally.
Looking at the main index.d.ts I see differences in declaring the module but I don’t know if that’s the issue here.

i promise you this is AN issue. I don’t know if there is any other issue, but ESM in-browser requires a URL and not a package. your browser doesn’t know what npm or an npm package is. it needs the URL from which to load the data.
Unless you bundle, and if you bundle then it is a matter of your packaging tool

@neu5 have you tried with fully specified module imports?
ie: UpdateInstance.ts from brianzinn/react-babylonjs

So, your Vector3 and Quaternion imports switch to:

import { Quaternion, Vector3 } from '@babylonjs/core/Maths/math.vector.js'

(react-babylonjs/Engine.tsx at master · brianzinn/react-babylonjs · GitHub)

I don’t have any reported issues, but also have not tried that library with jsdelivr or other
CDNs (although I have for other projects). You should get better tree-shaking results as well.

Hey @brianzinn. Thanks for getting involved!
Unfortunately the result is the same

Uncaught TypeError: Failed to resolve module specifier "@babylonjs/core/Maths/math.color". Relative references must start with either "/", "./", or "../".

did you put math.color or math.color.js in your code?

I tried both versions and I got the same result - it doesn’t work.

1 Like

Assuming you load in-browser, this is a browser error. You are trying to load from a package that is not a URL. try changing it to the link I provided you earlier

If not and you want to share a project - that will be great

Edit - and if you are using rollup, you will need to bundle babylon along with your code. it doesn’t happen per default.

Yes, this error is occurring in the browser console.

I can’t use the URL inside the JS file? :slight_smile: I tried and I get an error.
It’s not in the index.html, it’s JS file which is imported in index.html, it’s here:
https://github.com/neu5/cannon-es-debugger-babylonjs/blob/feat/dont-bundle-babylonjs/dist/cannon-es-debugger.js

The project:
https://github.com/neu5/cannon-es-debugger-babylonjs/blob/feat/dont-bundle-babylonjs/dist/cannon-es-debugger.js
All of this is currently at the feat/dont-bundle-babylonjs branch.

I use rollup (because it was in the forked repo already, I’m using it for the first time), I tried with Parcel2 and I was having the same issue.
In general I don’t want to bundle BabylonJS as it supposed to be a debugger library so I’m assuming the BabylonJS is being already installed and I have it as peerDependency so I exclude BabylonJS on purpose (and I have it installed in the node_modules).

To clarify, this is just my side project of my side project (:smile:), and it’s not really that important as it is only for demo page inside the library repo. I can create another example repo and it should work just fine.
I can also create minimal repo to reproduce this issue if you and/or @brianzinn want.
I am just wondering :thinking: why it’s working for the cannon-es and threeJS and not for the BabylonJS.
My two guesses are either BabylonJS is built differently or I messed up something in the config.

Not to derail the conversation, but I use rollup and don’t bundle babylon as it is a peer dep. That can be accomplished with external here is an example:
react-babylonjs/rollup.config.js