illogikal / bun-imba-template

Template for developing Imba code under Bun

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

imba-bun-template

First of all clone this project to your local machine. Make sure that Bun is installed and availble in the folder where you have cloned the project.

Install dependencies:

bun install

Run the project:

bun serve

Known issues:

  • Current version of Bun's watch function do not work well with WSL on Windows. Independent of how the watch is initiated: via code or CLI. At least on my working computer. Please share your results with me.
  • Though Imba files compiles as expected some of the functionality does not work on the frontend. For example self is undefined when is call in the tag functions.
  • L function does not work.
  • Some errors are not thrown. Bun likes silence.

How it works

I don't usually use create methods of different CLI tools to make bootstrap projects, bacause I like to know how things work under the hood. So let's dive into details...

Bun does everything that Imba used to do out of the box. The more such tools like Bun or Vite are established the more time Imba team will be able to spend on compiler or frontend framework.

And the first thing we need to make Bun and Imba work togehter is to show Bun how to compile *.imba files.

Backend development

To develop backend with Imba using Bun the only thing that is needed is the plugin for Bun. The working code of such plugin is pretty small:

const compiler = require("./node_modules/imba/dist/compiler.cjs");

export const imbaPlugin: BunPlugin = {
  name: "imba",
  async setup(build) {
    
    // when an .imba file is imported...
    build.onLoad({ filter: /\.imba$/ }, async ({ path }) => {
      let contents = '';

      // read and compile it with the imba compiler
      const file = await Bun.file(path).text();
      const out = compiler.compile(file, {
        sourcePath: path,
        platform: 'browser'
      })

      // the file has been successfully compiled
      if (!out.errors || !out.errors.length) {
        contents = out.js;
      }
      
      // and finally return the compiled source code as "js"
      return {
        contents,
        loader: "js",
      };
    });
  }
};

plugin(imbaPlugin);

This plugin lets Bun deal with .imba files (compile them to JavaScript). But it will not show errors produced by the Imba compiler.

That is why the plugin in the imba/plugin.ts file has much more code - more than a half of it is needed to print pretty error messages generated by the Imba compiler.

After the plugin is ready it should be preloaded before everything else via the settings in the bunfig.toml:

preload = ["./imba/plugin.ts"]

Well, this is enough to develop and host backend projects. Just run the command with the correct entrypoint file from CLI (same as node or imba):

bun run "./index.imba" ✔️

But developing a frontend project is another story...

Frontend development

Bundling the code

First of all Frontend development needs files bundling for serving them to the clients. But there is a problem - the current version of Bun (1.0.25) does not support plugins when the build function is called from CLI:

bun build "./src/index.imba" --outdir './public'

So we will need to call the Bun.build function from the code. And since we already could run Imba code it is not a problem - we will not get our hands dirty again by JavaScript 🤣. Here is the bare minimum code that is needed to bundle Imba files:

import {imbaPlugin} from './imba/plugin.ts'

export def bundle options
	await Bun.build
		plugins: [imbaPlugin] # THIS CAN'T BE MADE VIA CLI
		entrypoints: options.entrypoints || ['./src/index.imba']
		outdir: options.outdir || './public'
		target: options.target || 'node'
		sourcemap: options.sourcemap || 'none'
		minify: options.minify || true

This code is already written in the imba/core.imba file. It also includes logging messages to the terminal. So when you need to bundle you can just write:

import {bundle} from './imba/core.imba'

bundle 
	entrypoints: ['./src/index.imba']
	outdir: './public'
	minify: true

More on the parameters of Bun build function you can find here: https://bun.sh/docs/bundler

HTTP server

After the project is bundled it is a good idea to test how it works before deploying it to the hosting. And for that Bun has a fast built-in HTTP server. Here is the bare working minimum:

Bun.serve
	port: '8080'
	fetch: do(req)
		const path = './public' + new URL(req.url).pathname
		const file = await Bun.file(path)
		return new Response(file)
	error: do(err) return new Response(null, { status: 404 })

The second function in the /imba/core.imba file called serve implements HTTP server, watches source directory for changes and informs browser on the changes. It can be called pretty easily:

import {serve} from './imba/core.imba'

serve 
	source: './src'
	public: './public'
	entry: 'index.imba'
	port: 8080

Hot reload

To make frontend development a pleasure the project should be rebundled on every code change, and the browser should be informed about that to reload the updated version.

Bun has a built-in function to monitor changes in directory:

let watcher = watch(import.meta.dir + '.src', {recursive: true}, &) do(event, filename) 
	# first we need to recompile
	# second to send connected browsers a message to reload

# it is a good practice to kill watches when the program exits
process.on "SIGINT", do
	watcher.close!
	process.exit(0)

To be able to send message to connected browsers (even if you are developing on localhost the project could be opened in several tabs) the fronend code should keep connection with the server. To achive that the http server in the imba/core.imba file injects the hmr.html in the index.html.

The code in the hmr.html tries to download favicon.png to know if the server is alive. This is needed to get rid of ERR_CONNECTION_REFUSED errors in the browser console, which bothers developers with any other approach. And that is why favicon.png is needed for hot reload to properly work.

Moreover to show the status of hot reload the actual favicon is swapped for green circle when hot reload is working, and red circle - otherwise. So you don't need to open console to see the status of hot reload.

About

Template for developing Imba code under Bun

License:MIT License


Languages

Language:Imba 52.1%Language:TypeScript 32.5%Language:HTML 15.4%