hello friends! i’m experimenting with the future, today!
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:
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)
"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
edit: hmm, this form limits me to two real links, sorry but you’ll have to copy-paste if you want to follow them
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.
/*
* [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;
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.
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"
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
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 "../".
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.
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 (), 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 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