How to Bundle Deno Typescript for the Browser

1. Create a new file: bundle.ts

Make sure to update entryPoints and outdir.

        import * as esbuild from "[email protected]/mod.js";
        import { denoPlugins } from "jsr:@luca/[email protected]";
            plugins: [...denoPlugins()],
            entryPoints: ["<input>/<dir>/script.ts"],
            outdir: "<output>/<dir>/",
            bundle: true,
            platform: "browser",
            format: "esm",
            target: "esnext",
            minify: true,
            sourcemap: true,
            treeShaking: true,
        await esbuild.stop();

2. Run it!

deno run --allow-read --allow-write --allow-env --allow-net --allow-run bundle.ts

You can create a deno task to make running this easier. Add this to your deno.jsonc:

            "tasks": {
              "bundle": "deno run --allow-read --allow-write --allow-env --allow-net --allow-run bundle.ts"

Helpful resources:

In regards to deno bundle

For those that don't know, Deno actually came with a tool to do a limited kind of bundling out-of-the-box: deno bundle. However, the maintainers have marked it for deprecation. 😢

If you want background into why it's being deprecated, you can read all the discourse in this massive GitHub issue. You'll quickly realize that this move was received quite poorly by the Deno community...

Here is a great summary by a passionate community member:

R.I.P. deno bundle

To anyone arriving here from Google, this should summarize the whole thread.

Note: I'm not a maintainer, I just liked deno bundle a lot and didn't understand why support was being dropped

  1. Why was it deprecated: If the maintainers could have, they would go back in time and never advertise bundling as a feature. What seemed straightforward at the start, regular http imports, continued and still continues to evolve new edge cases. Bundle is not used for Deno compile, and because of CSS/PNG/etc. it is also not used for web bundling, making the use case fairly niche; basically offline non-compiled CLI apps. New Typescript features and EcmaScript features like top-level await added a lot of maintenance work. All of that might have been manageable. However, Node.js compatibility played a role by forcing either an even-more narrow use case (can't bundle node-dependent offline CLI apps) or an extreme difficulty increase (trying to fully bundle node modules; which is provably impossible to perfectly automate). The final straw was likely additional complexity like this ongoing TC39 proposal with a new import keyword that partially-imports WASM modules.
  2. Transparency issue: I'm going to guess that the lack of transparency from the Deno team stemmed from having a gut feeling that the bundler was bad/costly without having a concrete way to demonstrate it. This issue was first closed because of community feedback and a lack of concrete reasons, but then effectively reopened after those nebulous reasons accumulated again over time. There was some other vagueness, but it was addressed after requests to clarify.
  3. Deno Philosophy Concerns: There were, and possibly still are, concerns that deprecating this indicates a major change in the Deno all-in-one it-just-works philosophy. I don't think this is the case. I think Ryan and the rest of the team were somewhat avoiding commenting on philosophy because of uncertainty. Which to me is a good sign that the core philosophy isn't changing. E.g. Node.js was not confirmed as mission-critical in the way I was starting to suspect it might be, but it also wasn't denied as an important feature. On one hand, there's death-from-lack-of-adoption and on the other is becoming the evil we as Deno devs seek to destroy. Ryan probably thinks about that trade off more than any of us. I'll miss the bundler, but I'm relieved to say; I think Deno is still in good hands.
  4. What now: If you want to help, I've made deno_bundle which uses the deno esbuild plugin (made by the Deno team) to try and replicate the just-works deno build behavior. The builtin deno bundle functionality will be preserved but pseudo-archived by being moved to deno_emit.

~ Source by Jeff Hykin

The "Deno Philosophy Concerns" item is the one I'm most passionate about.

When I first got into programming, I didn't know where to start, so I just picked up whatever my friends, or favourite coding Youtuber, was using. One of my first projects was a website with some very basic search functionality written in Javascript. I had already briefly used Java and C++ before, which made me aware of strongly typed languages, but which also made me aware of how painful it was to work without them. After a quick search, I discovered Typescript and fell in love almost instantly.

That was short-lived, however.

The amount of configuration and knowledge I needed just to get my project to run nearly killed it. At first, it was kind of fun. I loved learning all the little intricacies that came with ECMAScript standards, transpiling, minifying, tree-shaking, etc. That soon became the only thing I ever did. Webpack alone broke the camel's back. I didn't want to wake up at 80 years old and realize the only thing I did was set up entry points and output directories and ECMAScript feature/browser targets, etc. I wanted to build cool projects, bring fun+value into peoples' lives - have something to show for all the time spent on the computer. Why wasn't there a drop-in solution with reasonable defaults? Something that would allow me to get back to what I should've been doing this whole time?

The "all-in-one"/"it-just-works" philosophy is what brought me to Deno in the first place. When I started using it, I thought to myself—"they understand". Linting, formatting, bundling out-of-the-box with sensible defaults. I was back. I was shipping again. A literal breath of fresh air for me, air in my sails.

Fast-forward to today, and it looks like we're back to the good old days of handwritten, bespoke config files + scripts just to get Typescript to run in the browser!


"The best way to get the right answer on the Internet is not to ask a question; it’s to post the wrong answer."