Installing Bun.

Or: More than a Fast JS Runtime.

·

11 min read

Installing Bun.

TL;DR.

Bun is a fast JavaScript runtime with TypeScript support. It is designed to be a drop-in replacement for Node.js. The Bun CLI has features like a runtime, bundler, test runner, package runner, package manager, and APIs. This post:

  • Covers Bun's installation,

  • Includes a quick start guide,

  • Introduces the package manager,

  • Describes TypeScript integration for it's APIs, and

  • Suggests how templates can be built, and used, for custom initialisations.

Attribution:

https://bun.sh/docs

An Introduction.

Bun is fast because it is written in a low-level programming language called Zig. Unlike other runtimes that use Chrome V8, Bun is powered by JavaScriptCore, the same JavaScript engine that is used by Apple Safari.

The purpose of this post is to introduce the Bun JS runtime.

Bun 1.0 was released on Friday 8th September 2023 and the CLI includes:

  • TypeScript support,

  • A runtime (bun run),

  • A bundler (bun build),

  • A test runner (bun test),

  • A package runner (bunx),

  • A package manager (bun install), and

  • A small (but growing) collection of useful APIs.

I will touch on each of these topics as per the official documentation.

The Big Picture.

A JavaScript runtime allows JS code to run on the server. The main advantage lies in the reduction of context switching: JavaScript running on the server and in the browser means there is ONE programming language involved during development. At least, that was an intent when Node was launched in 2009.

The introduction of ECMAScript 2015, colloquially called ES6, introduced new JavaScript features like const, let, arrow functions, and other features. Further developments would threaten jQuery, although it is still a very popular library.

Each year, ECMA would issue updated JavaScript standards and each browser vendor would release new features to match. As JavaScript continued to evolve, modern development practices also changed. These transitions were due to new tools and practices that evolved, hand in hand, with the annual ECMA releases.

The current range of development tools is wide and varied. Also, it is common to see a new framework, language, library, or runtime launch each week. For instance, Bun 1.0 and the Mojo Local Installer were unveiled on the same day!!

Suffice it to say that software engineering is a continually evolving field.

Prerequisites.

NOTE: The purpose of using containers is to isolate any running processes from other containers and the host system.

Installing Bun.

  • I connect VS Code to the 'bun' container (which requires the Microsoft 'Remote - SSH' extension and the 'F1 -> Remote-SSH: Open SSH Host...' command.)

  • From the VS Code terminal (use CTRL + ~ to show/hide the terminal) I install the unzip package:

sudo apt install unzip -y
  • I install Bun:
curl -fsSL https://bun.sh/install | bash
  • I set the source:
source $HOME/.bashrc
  • I check the version:
bun -v

Upgrading and Uninstalling Bun.

  • I can, if needed, upgrade Bun:
bun upgrade
  • I can, if needed, uninstall Bun:
rm -rf ~/.bun

Quickstart1.

  • I make a 'quickstart1' directory:
mkdir quickstart1
  • I change to that directory:
cd quickstart1
  • I scaffold a new project (and accept all the defaults):
bun init -y

Hello, Bun as a Hot Reload File.

  • I add the following to 'index.ts' file and save the changes:
const server = Bun.serve({
  port: 3000,
  fetch(req) {
    return new Response("Hello, from Bun.");
  },
});

console.log(`Listening on http://localhost:${server.port} ...`);
  • I run the 'index.ts' file from the terminal:
bun --hot run index.ts
  • I open a browser on my workstation and point it to 192.168.188.52:3000.

  • I stop the server (CTRL + C).

Hello, Bun as a Hot Reload Script.

  • I open the 'package.json' file.

  • I add the following:

  "scripts": {
    "start": "bun --hot run index.ts"
  },
  • I run the script from the terminal:
bun run start
  • I open a browser on my workstation and point it to 192.168.188.52:3000.

  • I stop the server (CTRL + C).

Installing a Package.

  • I install the 'figlet' package:
bun add figlet
  • FOR TYPESCRIPT USERS ONLY:
bun add -d @types/figlet
  • I replace the contents of the 'index.ts' file:
import figlet from "figlet";

const server = Bun.serve({
  fetch() {
    const body = figlet.textSync("Hello, from Figlet Bun!");
    return new Response(body);
  },
  port: 3000,
});
  • I use the 'package.json' script command to run the server.
bun run start
  • I open a browser on my workstation and point it to 192.168.188.52:3000.

  • I stop the server (CTRL + C).

TypeScript.

  • I install the TypeScript definitions for the built-in APIs:
bun add -d bun-types
  • I add the following to my 'tsconfig.json' file:
{
  "compilerOptions": {
    "types": ["bun-types"],
  }
}

NOTE: I can now reference the Bun global in my TypeScript files.

  • I add the following to the bottom of the 'index.ts' file:
console.log(`Bun version: ` + Bun.version);
  • The following 'tsconfig.json' entries are a set of recommended compilerOptions for a Bun project:
{
  "compilerOptions": {
    // add Bun type definitions
    "types": ["bun-types"],

    // enable latest features
    "lib": ["esnext"],
    "module": "esnext",
    "target": "esnext",

    // if TS 5.x+
    "moduleResolution": "bundler",
    "noEmit": true,
    "allowImportingTsExtensions": true,
    "moduleDetection": "force",
    // if TS 4.x or earlier
    // "moduleResolution": "nodenext",

    "jsx": "react-jsx", // support JSX
    "allowJs": true, // allow importing `.js` from `.ts`
    "esModuleInterop": true, // allow default imports for CommonJS modules

    // best practices
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true
  }
}

NOTE: If I run bun init in a new directory, this tsconfig.json will be generated automatically.

  • I use the 'package.json' script command to run the server.
bun run start
  • I open a browser on my workstation and point it to 192.168.188.52:3000.

  • I stop the server (CTRL + C).

DOM Types.

  • If I need to add DOM types to my project, I add the following triple-slash directives at the top of any TypeScript file in my project:
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />

Templates.

  • In VS Code, I close the 'quickstart1' directory using File > Close Folder [CTRL + K F].

  • In the VS Code terminal, I make a 'quickstart2' directory in the home (~) directory:

mkdir quickstart2
  • In VS Code, I click the blue 'Open Folder' button, select the 'quickstart2' directory, and click the blue 'OK' button.

  • I open the VS Code terminal with CTRL + ~.

  • In the terminal, I change to the 'quickstart2' directory:

cd quickstart2
  • I scaffold an empty project with the interactive bun init command (and I use the -y flag to auto-accept the default settings):
bun init -y
  • I template a new Bun project with bun create:
bun create <template> [<destination>]

From a GitHub Template.

  • I can download the contents of a GitHub repo to disk:
bun create <user>/<repo>
  • I can also use the following command:
bun create github.com/<user>/<repo>
  • I can optionally specify a name for the destination folder (otherwise the repo name will be used):
bun create <user>/<repo> mydir
  • I can also use the following command:
bun create github.com/<user>/<repo> mydir

From a Local Template.

  • My templates should live in one of the following directories:

    • $HOME/.bun-create/<name> (for global templates), and

    • <project root>/.bun-create/<name> (for project-specific templates).

NOTE: I can customize the global template path by setting the BUN_CREATE_DIR environment variable.

  • I navigate to $HOME/.bun-create:
cd $HOME/.bun-create
  • I create a new directory with the desired name of my template:
mkdir foo
  • I change to the new directory:
cd foo
  • I create a package.json file:
touch ./package.json
  • I open the file with Nano:
sudo nano package.json
  • I add the following, save the changes, and exit Nano:
{
  "name": "foo"
}
  • I create a new directory:
mkdir ~/quickstart3
  • I change to that directory:
cd ~/quickstart3
  • I verify that Bun is correctly finding my local template:
bun create foo

Setup Logic for Templates.

I can specify pre- and post-install setup scripts in the "bun-create" section of my local template's package.json file:

{
  "name": "@bun-examples/simplereact",
  "version": "0.0.1",
  "main": "index.js",
  "dependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "bun-create": {
    "preinstall": "echo 'Installing...'", // a single command
    "postinstall": ["echo 'Done!'"], // an array of commands
    "start": "bun run echo 'Hello world!'"
  }
}

NOTE: After cloning a template, bun create will automatically remove the "bun-create" section from package.json before writing it to the destination folder.

CLI Flags.

FlagDescription
--forceOverwrite existing files
--no-installSkip installing node_modules & tasks
--no-gitDon’t initialize a git repository
--openStart & open in-browser after finish

Environment Variables.

NameDescription
GITHUB_API_DOMAINIf you’re using a GitHub enterprise or a proxy, you can customize the GitHub domain Bun pings for downloads
GITHUB_API_TOKENThis lets bun create work with private repositories or if you get rate-limited

How bun create Works.

When you run bun create ${template} ${destination}, here’s what happens:

IF remote template

  1. GET registry.npmjs.org/@bun-examples/${template}/latest and parse it

  2. GET registry.npmjs.org/@bun-examples/${template}/-/${template}-${latestVersion}.tgz

  3. Decompress & extract ${template}-${latestVersion}.tgz into ${destination}

    • If there are files that would overwrite, warn and exit unless --force is passed

IF GitHub repo

  1. Download the tarball from GitHub’s API

  2. Decompress & extract into ${destination}

    • If there are files that would overwrite, warn and exit unless --force is passed

ELSE IF local template

  1. Open local template folder

  2. Delete destination directory recursively

  3. Copy files recursively using the fastest system calls available (on macOS fcopyfile and Linux, copy_file_range). Do not copy or traverse into node_modules folder if exists (this alone makes it faster than cp)

  4. Parse the package.json (again!), update name to be ${basename(destination)}, remove the bun-create section from the package.json and save the updated package.json to disk.

    • IF Next.js is detected, add bun-framework-next to the list of dependencies

    • IF Create React App is detected, add the entry point in /src/index.{js,jsx,ts,tsx} to public/index.html

    • IF Relay is detected, add bun-macro-relay so that Relay works

  5. Auto-detect the npm client, preferring pnpm, yarn (v1), and lastly npm

  6. Run any tasks defined in "bun-create": { "preinstall" } with the npm client

  7. Run ${npmClient} install unless --no-install is passed OR no dependencies are in package.json

  8. Run any tasks defined in "bun-create": { "preinstall" } with the npm client

  9. Run git init; git add -A .; git commit -am "Initial Commit";

    • Rename gitignore to .gitignore. NPM automatically removes .gitignore files from appearing in packages.

    • If there are dependencies, this runs in a separate thread concurrently while node_modules are being installed

    • Using libgit2 if available was tested and performed 3x slower in microbenchmarks

Ecosystem.

The following is a collection of guides for using various tools and frameworks with Bun.

App Development.

Ecosystem for BunNative Support and Site
NuxtYes
PrismaYes
React and JSXYes
SveltKitYes (similar to Next and Nuxt)
A Discord botYes
AstroNo
Next.jsNo
RemixNo
SolidStartNo
ViteNo

Server Development.

EcosystemDescription
HonoHono is a lightweight ultrafast web framework designed for the edge.
ElysiaElysia is a Bun-first performance-focused web framework.
ExpressExpress provides a thin layer of fundamental web application features.
StricJSStricJS is a Bun framework for building high-performance web applications and APIs.
MongoDB and MongooseMongoDB and Mongoose work out of the box with Bun.
SSR for ReactServer-Side Rendering (unlike Static Site Generation, Client-Side Rendering, and Incremental Site Rendering).

The Results.

Bun offers a powerful and efficient alternative to Node.js, providing a range of features such as TypeScript support, a bundler, test runner, package runner, and package manager. By following the installation and quickstart guide, I can easily adopt Bun and harness its potential to improve my JavaScript projects.

In Conclusion.

Although Bun has finally released version 1.0, it has a long way to go if it wants to match the features provided by Node. Initial support for Bun appears very high but as more developers evaluate its features, shortcomings are bound to appear. The real test involves how quickly the developers can address these failings.

Until next time: Be safe, be kind, be awesome.