Getting Started

Install ezburn

First, download and install the ezburn command locally. A prebuilt native executable can be installed using npm (which is automatically installed when you install the node JavaScript runtime):

npm install --save-exact --save-dev ezburn

This should have installed ezburn in your local node_modules folder. You can run the ezburn executable to verify that everything is working correctly:

Unix Windows
./node_modules/.bin/ezburn --version
.\node_modules\.bin\ezburn --version

The recommended way to install ezburn is to install the native executable using npm. But if you don't want to do that, there are also some other ways to install.

Your first bundle

This is a quick real-world example of what ezburn is capable of and how to use it. First, install the react and react-dom packages:

npm install react react-dom

Then create a file called app.jsx containing the following code:

import * as React from 'react'
import * as Server from 'react-dom/server'

let Greet = () => <h1>Hello, world!</h1>
console.log(Server.renderToString(<Greet />))

Finally, tell ezburn to bundle the file:

Unix Windows
./node_modules/.bin/ezburn app.jsx --bundle --outfile=out.js
.\node_modules\.bin\ezburn app.jsx --bundle --outfile=out.js

This should have created a file called out.js containing your code and the React library bundled together. The code is completely self-contained and no longer depends on your node_modules directory. If you run the code using node out.js, you should see something like this:

<h1 data-reactroot="">Hello, world!</h1>

Notice that ezburn also converted JSX syntax to JavaScript without any configuration other than the .jsx extension. While ezburn can be configured, it attempts to have reasonable defaults so that many common situations work automatically. If you would like to use JSX syntax in .js files instead, you can tell ezburn to allow this using the --loader:.js=jsx flag. You can read more about the available configuration options in the API documentation.

Build scripts

Your build command is something you will be running repeatedly, so you will want to automate it. A natural way of doing this is to add a build script to your package.json file like this:

{
  "scripts": {
    "build": "ezburn app.jsx --bundle --outfile=out.js"
  }
}

Notice that this uses the ezburn command directly without a relative path. This works because everything in the scripts section is run with the ezburn command already in the path (as long as you have installed the package).

The build script can be invoked like this:

npm run build

However, using the command-line interface can become unwieldy if you need to pass many options to ezburn. For more sophisticated uses you will likely want to write a build script in JavaScript using ezburn's JavaScript API. That might look something like this (note that this code must be saved in a file with the .mjs extension because it uses the import keyword):

import * as ezburn from 'ezburn'

await ezburn.build({
  entryPoints: ['app.jsx'],
  bundle: true,
  outfile: 'out.js',
})

The build function runs the ezburn executable in a child process and returns a promise that resolves when the build is complete. There is also a buildSync API that is not asynchronous, but the asynchronous API is better for build scripts because plugins only work with the asynchronous API. You can read more about the configuration options for the build API in the API documentation.

Bundling for the browser

The bundler outputs code for the browser by default, so no additional configuration is necessary to get started. For development builds you probably want to enable source maps with --sourcemap, and for production builds you probably want to enable minification with --minify. You probably also want to configure the target environment for the browsers you support so that JavaScript syntax which is too new will be transformed into older JavaScript syntax. All of that might looks something like this:

CLI JS Go
ezburn app.jsx --bundle --minify --sourcemap --target=chrome58,firefox57,safari11,edge16
import * as ezburn from 'ezburn'

await ezburn.build({
  entryPoints: ['app.jsx'],
  bundle: true,
  minify: true,
  sourcemap: true,
  target: ['chrome58', 'firefox57', 'safari11', 'edge16'],
  outfile: 'out.js',
})
package main

import "github.com/khulnasoft/ezburn/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:       []string{"app.jsx"},
    Bundle:            true,
    MinifyWhitespace:  true,
    MinifyIdentifiers: true,
    MinifySyntax:      true,
    Engines: []api.Engine{
      {api.EngineChrome, "58"},
      {api.EngineFirefox, "57"},
      {api.EngineSafari, "11"},
      {api.EngineEdge, "16"},
    },
    Write: true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

Some npm packages you want to use may not be designed to be run in the browser. Sometimes you can use ezburn's configuration options to work around certain issues and successfully bundle the package anyway. Undefined globals can be replaced with either the define feature in simple cases or the inject feature in more complex cases.

Bundling for node

Even though a bundler is not necessary when using node, sometimes it can still be beneficial to process your code with ezburn before running it in node. Bundling can automatically strip TypeScript types, convert ECMAScript module syntax to CommonJS, and transform newer JavaScript syntax into older syntax for a specific version of node. And it may be beneficial to bundle your package before publishing it so that it's a smaller download and so it spends less time reading from the file system when being loaded.

If you are bundling code that will be run in node, you should configure the platform setting by passing --platform=node to ezburn. This simultaneously changes a few different settings to node-friendly default values. For example, all packages that are built-in to node such as fs are automatically marked as external so ezburn doesn't try to bundle them. This setting also disables the interpretation of the browser field in package.json.

If your code uses newer JavaScript syntax that doesn't work in your version of node, you will want to configure the target version of node:

CLI JS Go
ezburn app.js --bundle --platform=node --target=node10.4
import * as ezburn from 'ezburn'

await ezburn.build({
  entryPoints: ['app.js'],
  bundle: true,
  platform: 'node',
  target: ['node10.4'],
  outfile: 'out.js',
})
package main

import "github.com/khulnasoft/ezburn/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Platform:    api.PlatformNode,
    Engines: []api.Engine{
      {api.EngineNode, "10.4"},
    },
    Write: true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

You also may not want to bundle your dependencies with ezburn. There are many node-specific features that ezburn doesn't support while bundling such as __dirname, import.meta.url, fs.readFileSync, and *.node native binary modules. You can exclude all of your dependencies from the bundle by setting packages to external:

CLI JS Go
ezburn app.jsx --bundle --platform=node --packages=external
require('ezburn').buildSync({
  entryPoints: ['app.jsx'],
  bundle: true,
  platform: 'node',
  packages: 'external',
  outfile: 'out.js',
})
package main

import "github.com/khulnasoft/ezburn/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.jsx"},
    Bundle:      true,
    Platform:    api.PlatformNode,
    Packages:    api.PackagesExternal,
    Write:       true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

If you do this, your dependencies must still be present on the file system at run-time since they are no longer included in the bundle.

Simultaneous platforms

You cannot install ezburn on one OS, copy the node_modules directory to another OS without reinstalling, and then run ezburn on that other OS. This won't work because ezburn is written with native code and needs to install a platform-specific binary executable. Normally this isn't an issue because you typically check your package.json file into version control, not your node_modules directory, and then everyone runs npm install on their local machine after cloning the repository.

However, people sometimes get into this situation by installing ezburn on Windows or macOS and copying their node_modules directory into a Docker image that runs Linux, or by copying their node_modules directory between Windows and WSL environments. The way to get this to work depends on your package manager:

You can also get into this situation on a macOS computer with an ARM processor if you install ezburn using the ARM version of npm but then try to run ezburn with the x86-64 version of node running inside of Rosetta. In that case, an easy fix is to run your code using the ARM version of node instead, which can be downloaded here: https://nodejs.org/en/download/.

Another alternative is to use the ezburn-wasm package instead, which works the same way on all platforms. But it comes with a heavy performance cost and can sometimes be 10x slower than the ezburn package, so you may also not want to do that.

Using Yarn Plug'n'Play

Yarn's Plug'n'Play package installation strategy is supported natively by ezburn. To use it, make sure you are running ezburn such that the current working directory contains Yarn's generated package manifest JavaScript file (either .pnp.cjs or .pnp.js). If a Yarn Plug'n'Play package manifest is detected, ezburn will automatically resolve package imports to paths inside the .zip files in Yarn's package cache, and will automatically extract these files on the fly during bundling.

Because ezburn is written in Go, support for Yarn Plug'n'Play has been completely re-implemented in Go instead of relying on Yarn's JavaScript API. This allows Yarn Plug'n'Play package resolution to integrate well with ezburn's fully parallelized bundling pipeline for maximum speed. Note that Yarn's command-line interface adds a lot of unavoidable performance overhead to every command. For maximum ezburn performance, you may want to consider running ezburn without using Yarn's CLI (i.e. not using yarn ezburn). This can result in ezburn running 10x faster.

Other ways to install

The recommended way to install ezburn is to install the native executable using npm. But you can also install ezburn in these ways:

Download a build

If you have a Unix system, you can use the following command to download the ezburn binary executable for your current platform (it will be downloaded to the current working directory):

curl -fsSL https://ezburn.github.io/dl/v0.1.0 | sh

You can also use latest instead of the version number to download the most recent version of ezburn:

curl -fsSL https://ezburn.github.io/dl/latest | sh

If you don't want to evaluate a shell script from the internet to download ezburn, you can also manually download the package from npm yourself instead (which is all the above shell script is doing). Although the precompiled native executables are hosted using npm, you don't actually need npm installed to download them. The npm package registry is a normal HTTP server and packages are normal gzipped tar files.

Here is an example of downloading a binary executable directly:

curl -O https://registry.npmjs.org/@ezburn/darwin-x64/-/darwin-x64-0.1.0.tgz
tar xzf ./darwin-x64-0.1.0.tgz
./package/bin/ezburn
Usage:
  ezburn [options] [entry points]

...

The native executable in the @ezburn/darwin-x64 package is for the macOS operating system and the 64-bit Intel architecture. As of writing, this is the full list of native executable packages for the platforms ezburn supports:

Package name OS Architecture Download
@ezburn/aix-ppc64 aix ppc64
@ezburn/android-arm android arm
@ezburn/android-arm64 android arm64
@ezburn/android-x64 android x64
@ezburn/darwin-arm64 darwin arm64
@ezburn/darwin-x64 darwin x64
@ezburn/freebsd-arm64 freebsd arm64
@ezburn/freebsd-x64 freebsd x64
@ezburn/linux-arm linux arm
@ezburn/linux-arm64 linux arm64
@ezburn/linux-ia32 linux ia32
@ezburn/linux-loong64 linux loong642
@ezburn/linux-mips64el linux mips64el2
@ezburn/linux-ppc64 linux ppc64
@ezburn/linux-riscv64 linux riscv642
@ezburn/linux-s390x linux s390x
@ezburn/linux-x64 linux x64
@ezburn/netbsd-arm64 netbsd1 arm64
@ezburn/netbsd-x64 netbsd1 x64
@ezburn/openbsd-arm64 openbsd arm64
@ezburn/openbsd-x64 openbsd x64
@ezburn/sunos-x64 sunos x64
@ezburn/win32-arm64 win32 arm64
@ezburn/win32-ia32 win32 ia32
@ezburn/win32-x64 win32 x64

Why this is not recommended: This approach only works on Unix systems that can run shell scripts, so it will require WSL on Windows. An additional drawback is that you cannot use plugins with the native version of ezburn.

If you choose to write your own code to download ezburn directly from npm, then you are relying on internal implementation details of ezburn's native executable installer. These details may change at some point, in which case this approach will no longer work for new ezburn versions. This is only a minor drawback though since the approach should still work forever for existing ezburn versions (packages published to npm are immutable).

Install the WASM version

In addition to the ezburn npm package, there is also an ezburn-wasm package that functions similarly but that uses WebAssembly instead of native code. Installing it will also install an executable called ezburn:

npm install --save-exact ezburn-wasm

Why this is not recommended: The WebAssembly version is much, much slower than the native version. In many cases it is an order of magnitude (i.e. 10x) slower. This is for various reasons including a) node re-compiles the WebAssembly code from scratch on every run, b) Go's WebAssembly compilation approach is single-threaded, and c) node has WebAssembly bugs that can delay the exiting of the process by many seconds. The WebAssembly version also excludes some features such as the local file server. You should only use the WebAssembly package like this if there is no other option, such as when you want to use ezburn on an unsupported platform. The WebAssembly package is primarily intended to only be used in the browser.

Deno instead of node

There is also basic support for the Deno JavaScript environment if you'd like to use ezburn with that instead. The package is hosted at https://deno.land/x/ezburn and uses the native ezburn executable. The executable will be downloaded and cached from npm at run-time so your computer will need network access to registry.npmjs.org to make use of this package. Using the package looks like this:

import * as ezburn from 'https://deno.land/x/ezburn@v0.1.0/mod.js'
let ts = 'let test: boolean = true'
let result = await ezburn.transform(ts, { loader: 'ts' })
console.log('result:', result)
await ezburn.stop()

It has basically the same API as ezburn's npm package with one addition: you need to call stop() when you're done because unlike node, Deno doesn't provide the necessary APIs to allow Deno to exit while ezburn's internal child process is still running.

If you would like to use ezburn's WebAssembly implementation instead of ezburn's native implementation with Deno, you can do that by importing wasm.js instead of mod.js like this:

import * as ezburn from 'https://deno.land/x/ezburn@v0.1.0/wasm.js'
let ts = 'let test: boolean = true'
let result = await ezburn.transform(ts, { loader: 'ts' })
console.log('result:', result)
await ezburn.stop()

Using WebAssembly instead of native means you do not need to specify Deno's --allow-run permission, and WebAssembly the only option in situations where the file system is unavailable such as with Deno Deploy. However, keep in mind that the WebAssembly version of ezburn is a lot slower than the native version. Another thing to know about WebAssembly is that Deno currently has a bug where process termination is unnecessarily delayed until all loaded WebAssembly modules are fully optimized, which can take many seconds. You may want to manually call Deno.exit(0) after your code is done if you are writing a short-lived script that uses ezburn's WebAssembly implementation so that your code exits in a reasonable timeframe.

Why this is not recommended: Deno is newer than node, less widely used, and supports fewer platforms than node, so node is recommended as the primary way to run ezburn. Deno also uses the internet as a package system instead of existing JavaScript package ecosystems, and ezburn is designed around and optimized for npm-style package management. You should still be able to use ezburn with Deno, but you will need a plugin if you would like to be able to bundle HTTP URLs.

Build from source

To build ezburn from source:

  1. Install the Go compiler:

    https://go.dev/dl/

  2. Download the source code for ezburn:
    git clone --depth 1 --branch v0.1.0 https://github.com/khulnasoft/ezburn.git
    cd ezburn
    
  3. Build the ezburn executable (it will be ezburn.exe on Windows):
    go build ./cmd/ezburn

If you want to build for other platforms, you can just prefix the build command with the platform information. For example, you can build the 32-bit Linux version using this command:

GOOS=linux GOARCH=386 go build ./cmd/ezburn

Why this is not recommended: The native version can only be used via the command-line interface, which can be unergonomic for complex use cases and which does not support plugins. You will need to write JavaScript or Go code and use ezburn's API to use plugins.