Ell
Ell3d ago

How do I bundle with deno (with javascript) instead of esbuild?

I have the following project structure:
backend/main.mts
common/util.ts # imported by both frontend and backend
frontend/frontend.mts # statically depends on e.g. threejs
frontend/loader.mts # dynamically depends on frontend.mts
backend/main.mts
common/util.ts # imported by both frontend and backend
frontend/frontend.mts # statically depends on e.g. threejs
frontend/loader.mts # dynamically depends on frontend.mts
which produce:
static/build/frontend.mjs # loaded by loader.mjs via `import("frontend.mjs")`
static/build/loader.mjs # loaded in index.html via <script src="loader.mjs">
static/build/frontend.mjs # loaded by loader.mjs via `import("frontend.mjs")`
static/build/loader.mjs # loaded in index.html via <script src="loader.mjs">
I would like to create two bundles: 1. loader.mjs which contains both 'static' dependencies, those imported like:
import "threejs"
import "qrcode-generator"
import "threejs"
import "qrcode-generator"
and a dynamically imported frontend (hot-reloaded manually) like:
import(`frontend.mjs?t=${Date.now}`).then(...)
import(`frontend.mjs?t=${Date.now}`).then(...)
2. frontend.mjs which is not really a bundle but is frontend.mts de-typescripted [+minified for production] * frontend.mjs also does import * as THREE from "threejs" but I want this to be marked as external when "bundling" because it will already be present in the loader.mjs bundle * the result is that a small frontend.mjs is dynamically reloaded by loader.mjs but the common dependencies are not hot reloaded The end result is that I have: * loader.mjs containing the frontend-hotloader and common dependencies loaded in the html via <script> * frontend.mjs which will be hot reloaded I'm not sure how to do this with Deno or whether it's even the right tool. My key requirements are: * the frontend should not reload its dependencies when hot-reloading (since the loader.mjs bundle should already contain them) * bundling should be almost as fast as esbuild (i.e. instant, pretty much) * it should not be a PITA Thank you for reading this far and any advice is appreciated! Ell
3 Replies
Ell
EllOP3d ago
I have a few ideas: 3. I've tried using esbuild to do this - it can do the static analysis and stuff using "jsr:@deno/esbuild-plugin" but then it seems to fail if I call it from the root directory. That means I'd have to cwd which is fine but in general it also seems like using esbuild + Deno is generally unsupported (the plugin not respecting esBuilds absWorkingDirectory is evidence of this, IMO, as well as that there are multiple plugins with nothing seemingly officially supported) 4. I've tried using the deno_emit + deno_graph API but they seem not to support importMaps or npm:-style imports. I'm currently getting errors of this shape:
error: Uncaught (in promise) Error: Unable to output during bundling: load_transformed failed: failed to analyze module: failed to resolve npm:three@^0.179.1 from file:///home/elliot/projects/localgame/frontend/loader.mts: Cannot resolve "npm:three@^0.179.1" from "file:///home/elliot/projects/localgame/frontend/loader.mts". const ret = new Error(getStringFromWasm0(arg0, arg1));
(from createGraph, but I'd have to re-check where exactly it comes from) I'm just not sure what the "done thing" is. [FYI I don't come from a web development background] I would be happy to be wrong about deno_emit and deno_graph not supporting the normal imports that Deno itself supports, by the way!
marvinh.
marvinh.3d ago
esbuild is the wrong tool for the job. It does no HMR and neither does deno bundle. They just do one thing: combine JS files into fewer files also deno bundle uses esbuild internally, the difference is just that it's preconfigured
Ell
EllOP2d ago
is "HMR" hot module reload? I can do that part myself manually Well. I want to avoid reloading my frontend causing loading multiple copies of dependencies (hence shifting them to the "loader" bundle where they are statically loaded only once) here manually means doing import("frontend.mjs") at runtime, something I don't expect (or want) the bundler to do for me So I'm asking here strictly about bundling. I don't think what I want to achieve is possible because there is no way for my frontend.mjs to consume the static esm imports from its parent loader.mjs without manually passing them down, which is annoying. So I will just use esbuild splitting: true instead.

Did you find this page helpful?