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.