Development flow

FuseBox offers a very convenient fully customisable development server and a Hot Module Reload module, which is flexible and can support any project needs

Development Server

FuseBox utilises express.js to launch a web server. To create it, define fuse.dev function right after your initilisation

const fuse = FuseBox.init({
    homeDir : "src",
    output : "dist/$name.js"
});
fuse.dev(/* options here*/)
// Bundle configuration
fuse.run()

Note

To avoid problems with HMR settings (bellow) set your development server config before you define further bundle configuration

Served root

FuseBox will automatically serve your build folder e.g. with output: "build/$name.js" it will serve the folder build/.

You can change it by passing in a string value to the root option (It can be an absolute path, Or relative to homeDir

fuse.dev({
    root: 'public/build',
})

Port

By default you will get express application running and a socket server (if HMR is enabled) bound to port 4444. To change the port provide port option.

fuse.dev({
    port : 8080
})

Opening in browser

Specify open option to open the url in your default browser

fuse.dev({
    open : true
    port : 8080
})

Note

You need opn package to be installed.

Express application access

You can access express() application once initialised like so:

fuse.dev({ root: false }, server => {
    const dist = path.resolve("./dist");
    const app = server.httpServer.app;
    app.use("/static/", express.static(path.join(dist, 'static')));
    app.get("*", function(req, res) {
        res.sendFile(path.join(dist, "index.html"));
    });
})

The example above will server static files under the current build folder. To make sure FuseBox does not automatically add routes for you, set {root : false}

Watching for changes

FuseBox can automatically re-bundle your bundles on file change. FuseBox Producer makes sure that only 1 wather is bound (chokidar).

const app = fuse.bundle("app")
    .instructions(`> app.tsx`)
    .watch()

watch() accepts a simplified regex (if you want to customise watched folder). Let's watch server and client bundle at the same time.

fuse.bundle("client/app")
    .instructions(`> app.tsx`)
    .watch("client/**")

fuse.bundle("server/app")
    .instructions(`> index.ts`)
    .watch("server/**")

In this example whenever a change happen in homeDir FuseBox will figure out which bundle requires re-triggering. It's implemented in order to save up your resources as physically only 1 watcher is defined.

A typical configuration for production builds would look like:

if (!production) { app.hmr().watch() }

Chokidar options

Additionally, you can provide chokidar options to producers' run()

fuse.run({chokidar : {ignored: /(^|[\/\\])\../} })

Hot Module Reload

HMR or Hot Module Reload, allows users to receive event on file change and update modules in memory (in browser)

Note

HMR works ONLY with enabled cache

To enable HMR, simple add hmr() to your bundle chain

const app = fuse.bundle("app")
    .instructions(`> app.tsx`)
    .hmr()

You can add hmr() options to all of your bundles, however only the first defined will actually recevie an HMRPlugin (FuseBox injects it automatically). It's important when dealing with vendor bundles

HMRPlugin() is an internal plugin which is being injected by FuseBox Producer. You will have corresponding modules bundled along with your project. Make sure to disable it when doing production builds, for example with vendor:

const vendor = fuse.bundle("vendor")
        .instructions(`~ **/**.{ts,tsx}`)
    if (!production) { vendor.hmr(); }

Custom HMR

You can tell which files FuseBox needs to reload. In fact you implement the entire logic yourself. In order to do that, place hmr.ts somewhere in your project, and put these contents:


const customizedHMRPlugin = {
    hmrUpdate: ({ type, path, content }) => {
        if (type === "js") {
            FuseBox.flush();
            FuseBox.dynamic(path, content);
            if (FuseBox.mainFile) {
                FuseBox.import(FuseBox.mainFile)
            }
            return true;
        }
    }
}

let alreadyRegistered = false;
if (!process.env.hmrRegistered) {
    process.env.hmrRegistered = false;
    FuseBox.addPlugin(customizedHMRPlugin);
}

Note

This a RUNTIME plugin, don't attempt on adding addPlugin to your configuration

Import that file in your entry point:

import "./hmr"
// code below
Name Description
FuseBox.flush() Removes files from memory
FuseBox.dynamic(path, content) Registers a new module dynamically
FuseBox.import(FuseBox.mainFile) Imports an entry point

You can flush files selectively by providing a callback to the flush function

FuseBox.flush(file => {
    if( /store/.test(file)){
        return false;
    }
    return true;
})

Custom socket URI

Sometimes, especially when dealing with HTTPS on a localhost, it is required to modify the socket URI to work with ws instead of wss://

fuse.dev({
   socketURI: "ws://localhost:3333",
})

Existing server

If you have an existing http application (java, python, nodejs - it does not matter), you can easily integrate HMR with it.

fuse.dev({
   port: 8080,
   httpServer: false,
})

In this configuration port: 8080 corresponds to a socket server port, having httpServer: false makes it work only in socket mode. Once you page it loaded, FuseBox API will try to connect to :8080 port an start listening to events.