Porting a TypeScript App from Node.js to Bun

Setting up the ts-node App

I have a technical blog built on Docusaurus, which uses a post-processing script to update the sitemap.xml file and patch HTML files. This script is implemented as a simple ts-node console app. My goal is to port this app from ts-node to Bun.

Installing Bun

I installed Bun on my Ubuntu machine using the following command:

curl https://bun.sh/install | sh

The installation process was straightforward, and I was able to verify that Bun was installed correctly.

Porting the Install from Yarn to Bun

With Bun installed, I opened up my project directory and triggered the installation of dependencies using bun install. This resulted in the creation of a new bun.lockb file alongside my package.json file. I deleted the yarn.lock file to avoid confusion.

Switching from @types/node to bun/types

I realized that the @types/node package had been installed, which contains TypeScript definitions for the Node.js runtime. Since I’m using Bun, I don’t need these definitions. I added the bun/types package to my project and removed @types/node and ts-node.

Addressing moduleResolution with Bun

When I navigated through my code in VS Code, I saw errors related to module resolution. I needed to explicitly state that I wanted to use the Node.js module resolution algorithm. I made a change to my tsconfig.json file to address this issue.

{
  "compilerOptions": {
    // ... other options ...
    "moduleResolution": "node",
    // ... other options ...
  }
}

Filing APIs with Bun

Although the module resolution errors were resolved, I still saw errors related to the fs.promises API. It seemed that the version of Bun I was using didn’t support this API. I replaced the fs.promises API with the Bun equivalents where possible.

import { promises as fs } from 'fs';
// Replace with:
import { fs } from 'bun';

Clarification about the fs.promises API

As I worked through addressing the fs.promises API error issue, I tweeted about my findings. Jarred Sumner, who works on Bun, kindly shared that the fs.promises API is implemented but the types aren’t.

Running the App

Before running the app, I updated the start script in package.json to use bun instead of ts-node. When I ran the app using bun start, I saw that it was executing instantaneously, which seemed surprising.

Top-level await and Bun

The issue was that my main function was asynchronous, but Bun wasn’t waiting for it to complete before terminating. I used top-level await to put things right.

async function main() {
  // ... code ...
}

await main();

GitHub Actions and Bun

I added the setup-bun action to my workflow so that Bun would be available in the GitHub Actions environment.

Performance Comparison: Bun vs. ts-node

I compared the performance of Bun and ts-node by running the app in GitHub Actions. Bun was about 50% faster than ts-node for this use case.

Leave a Reply

Your email address will not be published. Required fields are marked *