Building a template with Bun
Hihi! Lately i've been using a lot of bun in my side projects, why? because it cames with a lot of things that I use already solved
You can check the repository i'm using for example here
- Typescript? โ
- Single Ejecutable File? โ
- Multiple Architectures? โ
- Test runner? โ
- Coverage? โ
- Pipelines? โ
With all of these already we can make sure to build a suitable product, giving quality and speed, since all of these things are really easy to setup, let's dive in.
Typing ๐ฆ
We have two ways, use typescript or jsdocs, since I'm already confortable with TS and I know what i'm doing I'll always use JS instead but never lost the habit of adding types thats when jsdocs comes to save the day.
Here is an example of a function using jsdocs in javascript
/**
* @description Notify the user that the message is too long
* @param { Object } options - Options object
* @param { string } options.notification - Message to notify the user
* @param { string } options.std - Content to be evaluated
* @param { number } options.maxLength - Max length of the message
* @param { import("#types").sendMessage } options.fn - Function to send the message
* @returns { undefined }
*/
function sendMessageIfLong(options) {
const { notification, std, maxLength, fn } = options;
const notificationMessage = notification + 'sending in parts...';
const delay = 2000;
const isStdValid = std !== null;
if (isStdValid) {
const isLargeMessage = std.length > maxLength;
if (isLargeMessage) {
fn(notificationMessage || noOutputMessage + 'sendMessageIfLong');
sleep(delay);
const splitMessage = splitString(std, maxLength);
splitMessage.forEach(part => fn(part));
}
}
}
Did I mention that we can create documentation with the jsdocs typing? with esdocs we can create something like this! https://bun-template.jonathan.com.ar/
Binary ๐ข
But why binarys tho? well not everything is a web!! (you could use it with web anyways if you are lazy and run it with ./binary & if is for a sample project) but now being serious, there are some projects where a binary comes handy and I love to have a easy way to do it, or to not have to install nothing more.
With something like this in our package.json "build": "bun build ./src/index.js --compile --outfile lib/app_name"
we are up to go and whats better than having binarys at our disposal?
Architecture ๐
Having multiple architecture to built in! Yes I love this as a fan of ARM64 or ARMV7 I always had to do work arounds to build docker images and transform apps (mostly for ARMV7 not for ARM64) but anyways, this is so cool. You should give it a try
bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myapp
Testing ๐งช
Now here we have two things, the hability to do unit testing with some sort of jest (or also import jest), integration test if we install supertest
and add coverage if we configure our bunfig.toml
To add coverage to our projects is something like this
[test]
coverage = true
coverageThreshold = { line = 0.7, function = 0.5 }
It is true that at the time i'm writing this bun do not have any sort of test reporter, which is really needed on enterprise level to create junit files for example and add graphics to platforms like github or azure devops for example. You can follow this thread to get in touch about it here and contribute to make it real
By the way tests are extremely fast!
Here are some of the test that are running
import { SampleApp } from "src";
import { describe, it, expect, beforeAll } from 'bun:test';
describe("SampleApp Class", () => {
/**
* @type {SampleApp}
*/
let sampleApp;
beforeAll(() => {
sampleApp = new SampleApp();
});
it("should be an instance of SampleApp", () => {
expect(sampleApp).toBeInstanceOf(SampleApp);
});
it("should have a method called 'sampleMethod'", () => {
expect(sampleApp.sampleMethod).toBeInstanceOf(Function);
expect(sampleApp.sampleMethod()).toBe("Hello World!");
});
});
Yes this second one is running against a deployed database and still is running like if it was mocked, hell so fast haha
import { describe, it, expect, beforeAll, afterAll } from 'bun:test';
import { LibsqlDialect } from '@libsql/kysely-libsql';
import { Kysely } from 'kysely';
import { db } from '#db';
import { config } from 'src/config/config';
import { createTable, dropTable } from './utils/sql';
describe('db file', () => {
/** @type { import("kysely").Kysely<any> } */
let newDb;
beforeAll(async () => {
newDb = new Kysely({
dialect: new LibsqlDialect({
url: config.db.url,
authToken: config.db.authToken
})
});
});
it('should be an instance of Kysely', () => {
expect(db).toBeInstanceOf(Kysely);
});
it('should be an instance of Kysely', () => {
expect(newDb).toBeInstanceOf(Kysely);
});
it('should create a table with the name of users', async () => {
await createTable(newDb, 'sampleUsers');
const usersTable = await newDb.selectFrom('sampleUsers').selectAll().execute();
expect(usersTable).toEqual([]);
});
afterAll(async () => {
await dropTable(newDb, 'sampleUsers');
});
});
Pipelines ๐ง
Bun already made not only a dockerimage but an action in github, which comes handly to generate our documention, do testing and more things!
Here is a complete example
name: build, test and run
on:
workflow_dispatch:
push:
branches: [ "develop", "master" ]
pull_request:
branches: [ "develop", "master" ]
jobs:
build:
name: build and test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop'
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: set environment variables
run: |
touch .env
echo "DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }}" >> .env
echo "TURSO_URL=${{ secrets.TURSO_URL }}" >> .env
echo "TURSO_TOKEN=${{ secrets.TURSO_TOKEN }}" >> .env
- name: install dependencies, build and test
run: |
bun install
bun run test
bun run build:arm
- uses: actions/upload-artifact@v2
with:
name: executor
path: lib/executor_arm64
if-no-files-found: error
run:
name: run
runs-on: self-hosted
if: github.ref == 'refs/heads/master'
needs: build
steps:
- uses: actions/download-artifact@v2
with:
name: executor
- name: move to $HOME/apps
run: mv executor_arm64 $HOME/apps/executor_arm64
- name: run executor
run: |
chmod +x $HOME/apps/executor_arm64
$HOME/apps/executor_arm64 &
Conclusion ๐
Bun is awesome and it makes javascript fun outside the browser without shooting yourself in the leg.
Subscribe to my newsletter
Read articles from Jonathan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by