NAV undefined
undefined
javascript

Fusebox

Build Status Fusebox-bundler

NPM

FuseBox is a bleeding edge bundler/module loader with super powers - Blazing speed, simplicity and ultimate flexibility.

It is blazing fast (it takes 50-100ms to re-bundle) which makes it extremely convenient for developers. It requires zero configuration to bundle such monsters like babel-core.

FuseBox loves typescript, and does not require any additional configuration. It will compile and bundle your code within a fraction of a second, yet offering a comprehensive loader API.

It is packed with features, and unfolds limitless possibilities of extending the API.

Join gitter channel, we are active! / View on github / Submit an issue / Contribute to this documentation

Minimum requirement

Node v6.0.0

Installation

npm install fuse-box --save-dev

FuseBox has many plugins in place to help you get started. All you need to do is install fuse-box from npm.

Sample projects

Learn how easy it is to fuse angular with less in 50ms!

react-example 50ms to fuse!

Why?

Effortless bundling

You have an npm library in mind? You can bundle it without any extra configuration. babel-core with all plugins? No problem, fusebox will take care of everything you need.

Typescript! Oh! We love typescript. You know what you need to do, to start transpiling and bundling typescript at the same time? Change .js to .ts Are you ready?

FuseBox will take care of ALL nodejs dependencies. We offer a comprehensive list of nodejs modules for browser out of the box. No worries, no matter what are you trying to bundle. It will work.

There is nothing that cannot be fused. Create a 3 liner config and bundle some heavy project! Do conventional import statements, use shared bundles, hack API, create crazy plugins!

And bundle it fast. Jaw-dropping fast.

Speed

It takes 50ms for a regular project, 100ms for a big project to re-bundle. It applies aggressive but responsible module caching, which makes it fly.

Check this benchmark:

1200 files to bundle

FuseBox 0.234s
Webpack 1.376s

1000 files to bundle / 10 times

FuseBox 2.257s
Webpack 13.591s

Built-in typescript support.

FuseBox is written in typescript, so I could not just proceed without a seamless typescript integration. In fact, you don't need to configure anything! Just point it to a typescript file, and FuseBox will do the rest.

fuse.bundle(">index.ts");

Comprehensive Loader API

Whatever you tempted mind would want - you can get it all here. Apply hacks, intercept require statements, use an amazing dynamic module loading, and many many other neat features!

Extensive plugins

Have an idea in mind? Just develop a plugin, it's extremely easy to make one. Besides, we have a few plugins, that will help you get started. Want to develop one? Read up here

Changelog

1.3.128

Cache fixes

Reference Description Date
ISSUE 396 Shared cache causes issues March 14, 2017 9:04 AM
ISSUE 399 Caching issues on windows March 14, 2017 9:06 PM

1.3.127

Urgent fixes

Reference Description Date
ISSUE 387 Regression in 1.3.124, cannot bundle axios March 12, 2017 8:04 PM

1.3.126

Hot FIX. Related to the broken CLI.

1.3.125

Important fixes before the major update

Reference Description Date
ISSUE 376 Natives config (e.g disable process) March 9, 2017 11:00 PM
ISSUE 377 Force typescript to bundle for commonjs unless ROLLUP March 9, 2017 11:01 PM

1.3.124

Rollup!

Reference Description Date
ISSUE 351 Unable to remove process module from bundle March 8, 2017 9:04 PM
PR 353 Fluent March 7, 2017 11:56 AM
ISSUE 370 Rollup built-in support March 8, 2017 8:11 PM

1.3.123

Matroshka Release

Reference Description Date
ISSUE 184 Standalone bundle March 4, 2017 9:47 PM
ISSUE 277 Multiple __fsbx_decorate generated February 28, 2017 2:18 PM
ISSUE 286 ~ in SASS files to include files from node_modules February 27, 2017 3:42 PM
ISSUE 316 [bug] aliasing is broken with babel February 27, 2017 11:09 AM
ISSUE 331 Simplified REGEXP (strings) March 3, 2017 9:29 AM
ISSUE 335 New arithmetic symbol ~ March 3, 2017 9:44 PM
ISSUE 337 Emit source-changed for the RawPlugin March 3, 2017 9:40 PM
ISSUE 340 SourceMap option will be removed (deprecated) March 4, 2017 10:48 PM
ISSUE 342 New arithmetic symbol ^ March 4, 2017 9:49 PM
ISSUE 343 CustomAPIFile Options for the most adventurous March 4, 2017 11:12 PM
PR 344 normalize filename March 5, 2017 3:57 PM
PR 345 Changed TypeScriptHelpers plugin test to support ts and tsx March 5, 2017 9:59 AM

1.3.122

Babushka Release

Reference Description Date
ISSUE 293 Include a sub package in a bundle February 24, 2017 2:34 PM
ISSUE 297 Importing modules which contain "process" fails February 24, 2017 2:56 PM
ISSUE 301 Support for scopes npm packages in arithmetic instructions February 24, 2017 2:22 PM
ISSUE 303 Error output written in white causes frustration February 24, 2017 2:22 PM
PR 304 Fixed checking if a module is a node_module on Windows February 24, 2017 2:38 PM
ISSUE 305 Nuke cache if any error occurs during bundling February 24, 2017 2:10 PM
PR 307 Improve plugin docs February 25, 2017 8:32 AM
PR 308 docs, easier deps February 25, 2017 10:36 AM
ISSUE 311 Dramatic cache improvements February 25, 2017 10:58 PM

1.3.121

Mamochka release features critical fixes related to caching, aliases, css e.t.c.

Reference Description Date
ISSUE 193 Cache not found fixes February 22, 2017 10:11 PM
ISSUE 256 Implicit index.jsx support February 22, 2017 10:04 PM
ISSUE 265 CSSResourcePlugin breaks build February 21, 2017 10:46 PM
ISSUE 266 Package executes on bundle load February 22, 2017 1:10 PM
ISSUE 269 Aliasing doesn't work for preact/preact-compat February 21, 2017 11:59 PM
PR 272 Fix RegExp test subject for plugins to apply February 21, 2017 11:27 PM
ISSUE 273 Empty file (sass) throws error February 22, 2017 1:39 PM
PR 274 Minor docs improvements February 22, 2017 7:18 AM
PR 276 Allows testing plugin RegEx even if the homeDir setting is set February 22, 2017 8:54 AM
PR 281 [TSX] Allow TSX files to be renamed back to TSX inside source maps February 22, 2017 3:20 PM
ISSUE 284 PostCssResourcePlugin cannot recover from error in watch mode February 23, 2017 11:46 AM
PR 285 Fixes user-defined sass includePaths from being overwritten February 22, 2017 10:01 PM
ISSUE 287 Extension probing refactor February 22, 2017 10:25 PM
ISSUE 290 Aliases are not applied to deep require() calls February 23, 2017 10:45 AM

1.3.120

Critical bug fixes

Reference Description Date
ISSUE 262 ResourceCSSPlugin -> handle hashtags in urls February 21, 2017 6:50 PM
ISSUE 263 Buffer is not bundled correctly February 21, 2017 6:46 PM

1.3.119

Reference Description Date
ISSUE 111 Allow real aliasing February 21, 2017 9:33 AM
ISSUE 159 EnvPlugin: The global process is not available after hot-reload February 20, 2017 9:52 PM
ISSUE 165 Error > Buffer is not defined February 20, 2017 3:20 PM
ISSUE 166 NodeJs Native Libraries February 20, 2017 3:20 PM
ISSUE 177 CSSPlugin brushup February 20, 2017 1:08 PM
ISSUE 207 Make HMR configurable February 21, 2017 12:03 PM
ISSUE 208 HMR seems to have problems connecting to FuseBox via HTTPS February 21, 2017 10:46 AM
PR 230 FuseBox loader duplication fix February 18, 2017 11:56 PM
ISSUE 233 Debug mode in context February 19, 2017 3:01 AM
ISSUE 239 User Package missing an entry point when bundled February 20, 2017 10:28 PM
PR 240 Cleanups / docs February 20, 2017 10:45 AM
ISSUE 243 CSSResourcePlugin should handle URLs that have params at the end February 20, 2017 12:56 PM
ISSUE 250 Module imports Injection February 20, 2017 3:24 PM
ISSUE 257 Reload the page with HMR if a package is not found in the scope. February 21, 2017 11:07 AM

1.3.118

Reference Description Date
PR 200 add expose possibilities February 17, 2017 6:41 PM
PR 204 Added Coffee support February 17, 2017 12:47 PM
PR 210 I like my coffee tested February 17, 2017 6:31 PM
PR 211 Reverted wrong fix #197 February 17, 2017 10:33 PM
PR 216 Pass the source file name to the transpiler February 18, 2017 12:18 AM
PR 218 Loader Types February 18, 2017 6:59 AM
PR 219 :memo: new entry in .travis.yml February 18, 2017 7:04 AM
PR 221 :memo: simply the docs for how to add a loader plugin :rose: February 18, 2017 7:02 AM

1.3.117

Reference Description Date
PR 158 HMR plugins February 16, 2017 10:48 PM
ISSUE 167 UglifyJS Plugin should report on progress and recalculate the bundle size February 16, 2017 10:45 PM
PR 192 Report the new size and time taken for UglifyJS February 16, 2017 10:31 PM
ISSUE 197 PostCSS plugin doesn't do anything with opts? February 18, 2017 7:08 AM
ISSUE 198 Shiming stopped working February 17, 2017 8:28 AM
PR 199 moving modules to ts to make it easier to code review / maintain February 17, 2017 7:34 AM
PR 201 :memo: rename to actual plugin name PostCSS February 17, 2017 7:12 AM

1.3.116

HMR Plugins

Reference Description Date
ISSUE 179 CSSResourcePlugin does not bundle Fonts February 15, 2017 8:43 PM
PR 182 Fix a typo. February 15, 2017 8:43 PM
ISSUE 188 Bundle in a bundle gets broken on scope collision with index.js February 16, 2017 9:42 PM
PR 190 change brightBlack to reset get neutral color on dark themes February 16, 2017 6:50 PM
PR 191 add notes to env, uglify, and json plugins February 16, 2017 7:28 PM

1.3.115

Reference Description Date
ISSUE 156 electron-fusebox - Node API February 15, 2017 9:52 AM
ISSUE 180 Babel fails on config without sourcemaps February 15, 2017 2:29 PM

1.3.114

Typescript

Reference Description Date
PR 154 Check if imported package has a browser-field and use it February 14, 2017 10:08 PM
ISSUE 162 add package name to the public API February 14, 2017 2:53 PM
ISSUE 164 transformTypescript February 14, 2017 10:58 PM
ISSUE 169 FuseBox.exists("app.html") triggers error February 14, 2017 10:07 PM

Configuration

The concept of FuseBox is simple. Bundle anything for frontend and server without a headache. Simply put, you can copy paste a simple config down below and bundle some heavy module like babel-core or babel-generator. But let's get started and break down all available options in fusebox.

App Root Path

We resolve a few relative paths to appRootPath for your convenience. Generally it's the folder containing package.json.

Initialisation

Initialise a fuse-box instance like so. Each instance will handle 1 bundle.

FuseBox.init({ /* you config is here */ })

Home directory

That's your source folder. It can be an absolute path, Or relative to appRootPath.

FuseBox.init({
  homeDir: "./src",
})

Out file

That's your bundle file. It can be an absolute path, Or relative to appRootPath.

fuse-box takes care of creating required directory structure for you!

FuseBox.init({
  homeDir: "./src",
  outFile: "./build/bundle.js",
})

Cache

You can turn off caching if you like. By default caching is on. FuseBox will create a folder .fusebox in your project path, and store related files. Don't forget to add it to .gitignore.

If things go wrong or things are not updating, delete the .fusebox folder to force clear the cache.

FuseBox.init({
  homeDir: "./src",
  outFile: "./build/bundle.js",
  cache: true,
})

Alternatively, you can disable cache by adding ^ to the arithmetic instructions

Debug and Log

Additional logging and debugging can be enabled, but keep in mind they can reduce performance.

FuseBox.init({
  homeDir: "./src",
  outFile: "./build/bundle.js",
  log: true,
  debug: true,
})

Custom modules folder

You probably would want to test a package some day, or just have an abstraction on top of your code. For that, you can use modulesFolder property. It behaves exactly the same like another npm module, just in a custom folder.

FuseBox.init({
    modulesFolder: "src/modules",
})

You local npm will have the highest priority. In essence, you can override fusebox's path of fs module if you like. Customize you packages in your own manner!

You don't need to create package.json - index.js will work just fine. It will be cached like any other npm module with version 0.0.0, so remember to toggle cache property in the config

Package name

You default package name is default, You don't need to change it if you are not planning on having isolated bundles. Any bundle added as a script tag will share default package, keep that in mind. If you want to release a package (say to npm), you probably would want set a different name (to avoid scope collision)

It's imperative having a unique name (matching an npm package) when publishing a bundle to NPM.

FuseBox.init({
    package: "mySuperLib",
})

If you want to have an entry point (main) file, define it like so:

FuseBox.init({
    package:{
        name: "mySuperLib",
        main: "index.ts",
    },
})

If you don't want to have your package execute on load, make sure your instruction does not have > in it.

Here is an example how make a package:

FuseBox.init({
    package: {
        name: "super-name",
        entry: "index.ts",
    },
    homeDir: `/src-package`,
    outFile: `build/packages/super-name.js`,
}).bundle("index.ts")

Global variables

You can expose your package variables to window (in browser) and exportsin node respectively.

FuseBox.init({
    // exposes window.mySuperLib
    globals: { default: "mySuperLib" },
})

Whereas key is the name of a package and value is an alias that groups exports. "default" is your current project.

You can also expose your packages exports to window or exports.

// assuming mySuperLib exports a "handler" and "logger" property

FuseBox.init({
    // exposes window.superHandler
    globals: { "mySuperLib": { "handler": "superHandler"} },

    // OR
    // exposes window.handler and window.logger
    globals: { "mySuperLib": "*" },
})

Please, note, that in order to expose your package, a bundle must have a package name

Sourcemaps

Sourcemaps in FuseBox are enabled by setting the sourceMaps property in a FuseBox configuration object:

sourceMaps: true

You can also provide an object to allow vendor sourcemaps:

sourceMaps: { project: true, vendor: true }

vendor sourcemaps will be generated correctly with disabled cache. It's a known bug

Sourcemaps currently work with typescript and BabelPlugin see the SourceMapPlainJsPlugin

Standalone

By default FuseBox injects API in every bundle. That can be overridden by setting:

{ standalone : false }

Alternatively, you add ! symbol to the arithmetics

! >index.ts

Load the API from the CDN:

<script type="text/javascript" src="https://unpkg.com/fuse-box/dist/fusebox.min.js"></script>

List of plugins

plugins option expects an array of plugins, See Plugin API

FuseBox.init({
    plugins:[
        fsbx.TypeScriptHelpers(),
        fsbx.JSONPlugin(),
        [fsbx.LESSPlugin(), fsbx.CSSPlugin()],
    ],
})

Plugin chaining

A plugin can be chained. For example, if you want to make SassPlugin work:

FuseBox.init({
    plugins:[
        [fsbx.SassPlugin(), fsbx.CSSPlugin()],
    ]
})

How it works:

FuseBox tests each file running it through the plugin list. If it sees an array, it test for the first Plugin on the list test (which is .scss in our case. First element on the list could be a Regular Expression or a simplified version of it:


[".scss",fsbx.SassPlugin(), fsbx.CSSPlugin()] // simple and clean
[/\.scss$/,fsbx.SassPlugin(), fsbx.CSSPlugin()] // more verbose

Simplified RegExp

FuseBox understands wildcards which are converted to RegExp

For example:

plugins : [
    ["styles/*.css", CSSPlugin({group: "bundle.css"})] // will group files under "styles" folder
    ["components/*.css", CSSPlugin()] // will inline all styles that match components path
]

Auto import

If you are into black magic, this API is for you.

FuseBox.init({
    autoImport: {
        Inferno: "inferno",
    },
})

Whereas the key Inferno (uppercase) is a variable name, and inferno (lowercase) is a require statement.

Your code is being analysed for variable declarations. If you use the Inferno variale in your code in any way but declaring it, FuseBox will inject the require statement var Inferno = require("inferno")

Example:

Inferno.doMagic()

Will result in:

var Inferno = require("inferno");
Inferno.doMagic()

However var Inferno = {}; will do nothing.

Natives

FuseBox automatically imports this packages stream, process, Buffer, http

Some cases, however, require omitting:

FuseBox.init({
   natives : {
      process : false
   }
})

Use stream, process, Buffer, http keys to override default settings.

Alias

If you are coming from WebPack this feature might be helpful.

Alias is an experimental feature; the API might change in the future. using Alias breaks sourcemaps of a file where it's being used as it is required to re-generated the source code (this will be fixed soon)

FuseBox.init({
    alias: {
        "faraway": "~/somewhere/far/away/",
    },
})

You can also alias npm packages:

FuseBox.init({
    alias: {
        "babel-utils": "babel/dist/something/here/utils",
    },
})

In your code, you would use it in a way similar to this:

import utils from "babel-utils"
import faraway from "faraway"

console.log(utils, faraway);

Behind the scenes, (assuming the previous code block is homeDir/src/index.js) this code is actually transformed into:

import utils from "../node_modules/babel/dist/something/here/utils"
import faraway from "../somewhere/far/away/"

console.log(utils, faraway);

Shimming

For those libraries that are bundled to work in window (jquery) for example, you need to provide a shimming configuration. FuseBox will not do analysis on that file, it will simply add it to the top of the bundle.

FuseBox.init({
   shim: {
        jquery: {
            source: "node_modules/jquery/dist/jquery.js",
            exports: "$",
        },
   }
});

You can remove source option if you load a library using the script tag (for example from a CDN). After it has been shimmed, you can use FuseBox API, or import/require statement to obtain it.

import * as foo from "jquery"
console.log(foo);

The key jquery in our case is used to define package name: for example, you can replace jquery with foo and use import "foo" to get a jquery instance.

Example shim config:

shim: {
   "react-native-web": { exports: "require('react-native')"},
}

Now you can reference it like window.ReactNative, and require function is at your convenience.

Important to note, shims will not be analyzed, which means they should be transpiled before importing, or imported into an environment that does not need them to be transpiled. Shimming works similar to requirejs and jspm.

For an example, see shimming in the fuse config and how it can be accessed in your code

Server Bundle

In case you are running your bundle in electron for example, you might want to make fuse think that it is running on server.

FuseBox.init({
    serverBundle: true,
})

Use it ONLY for electron environment. This is a very special case that allows FuseBox to be run in browser but behave as if it's running on server.

Don't run that bundle in a traditional browser.

Full Config

An example using the available config options might look similar to:

// remember, unless you transpile your fuse.js, es6 will not work in your fuse.js
// so using `require` is the easiest. 
// destructuring with `require` is supported with the current node version.
//
// importing can also be done with the syntax:
// import {FuseBox, BabelPlugin} from "fuse-box"
import fsbx from "fuse-box"
const FuseBox = fsbx.FuseBox

const config = {
  homeDir: "./src",
  outFile: "./build/bundle.js",
  log: true,
  debug: true,
  plugins:[
    fsbx.BabelPlugin({
      // test is optional
      // this would make it only parse `.jsx` files
      // it defaults to `js` and `jsx`
      test: /\.jsx$/,

      // if no config is passed in,
      // it will use the .babelrc closest to homeDir
      config: {
        "sourceMaps": true,
        "presets": ["latest"],
        "plugins": [
          "transform-react-jsx",
          "transform-object-rest-spread",
          "transform-decorators-legacy",
          "transform-class-properties",
          "add-module-exports",
        ],
      },

      // this is default `true`
      // setting this to false
      // means babel will parse _all imported node modules_
      limit2project: true,
    }),

    fsbx.TypeScriptHelpers(),
    fsbx.JSONPlugin(),
    fsbx.HTMLPlugin({ useDefault: false })

    // these css plugins are chained
    [
      fsbx.LESSPlugin(),
      fsbx.CSSPlugin({
        // file is the file.info.absPath
        // more info on that in the `Plugin API` section
        outFile: (file) => `./tmp/${file}`
      })
    ],

    fsbx.SourceMapPlainJsPlugin(),
  ],

  shim: {
    "react-native-web": {
      exports: `require("react-native")`,
    },
  },

  alias: {
    // node modules
    "babel-utils": "babel/dist/something/here/utils",

    // homeDir
    "faraway": "~/somewhere/far/away/",
  },

  // this works in a similar way to how things such as `process.env`
  // are automatically added for the browser
  autoImport: {
    // used any time we do `Inferno.anything` in the source code
    Inferno: "inferno",
  },

  // is for the package `canadaEh`
  // will export everything that your entry point
  // think of it as `entry point exports`
  // and it also adds the exports to `global` or `window`
  globals: { "canadaEh": "*" },

  // package can just be a string naming your package
  package: {
    // name must be unique
    name: "canadaEh",
    // main is optional, it is used as an optional entry point
    main: "index.ts",
  },

  sourceMap: {
    bundleReference: "sourcemaps.js.map",
    outFile: "sourcemaps.js.map",
  },
}

if (process.env.NODE_ENV === 'production') {
  // [options] - UglifyJS2 options
  const prodPlugins = [
    fsbx.UglifyJSPlugin(options),
    fsbx.EnvPlugin({ NODE_ENV: "production" }),
  ]
  config.plugins = config.plugins.concat(prodPlugins)
}

// this is the same as
// const fuse = new FuseBox(config)
const fuse = FuseBox.init(config)

// --- bundling

let frontEndDev = true
let vendors = [
  'commander',
  'tosource',
  'flipbox',
  'path',
  'fs',
]
const vendorInst = "[async-registry.js]" + vendors.map(vendor => ' +' + vendor).join('')
const appInst = ">[index.ts]" + vendors.map(vendor => ' -' + vendor).join('')

const multipleBundles = {
    "./build/vendorBundle.js": vendorInst,
    "./build/appBundle.js": appInst,
}

const singleBundle = `
    > [index.ts]
    + [**/*.html]
    + [**/*.js]
    + [**/*.ts]
    + [**/*.css]
`

if (frontEndDev) fuse.devServer(singleBundle)
else fuse.bundle(multipleBundles)

Bundle

Arithmetic instructions

FuseBox uses an arithmetic approach to bundling. We don't support anything else, as this approach is the most simple and flexible.

let fuse = FuseBox.init({
    homeDir: "src/",
    globals: { default: "myLib"},
    outFile: "./out.js"
});

fuse.bundle(">index.js");

With arithmetic instructions, you can explicitly define which files go to the bundle, which files skip external dependencies e.g.

fuse.bundle(">index.ts [lib/**/*.ts]");

In this case, you will get everything that is required in the index, as well as everything that lies under lib/ folder with one condition - any external libraries will be ignored.

Arithmetic Symbols

Examples for better understanding

> index.js [**/*.js] - Bundle everything without dependencies, and execute index.js.

[lib/*.js] +path +fs - Bundle all files in lib folder, ignore node modules except for path and fs

[**/*.js] - Bundle everything without dependencies

**/*.js - Bundle everything with dependencies

**/*.js -path - Bundle everything with dependencies except for module path

Making many bundles at once

You can specify many { outFile: bundleStr }. Your config (excluding outFile) will be copied for every single process.

For example:

fuse.bundle({
    "_build/test_vendor.js": "+path",
    "_build/app.js": ">[index.ts]"
});

Fluent

Arithmetic instructions can be expressed using a more verbose, fluent api: see tests for more examples here

Single Fluent Bundle

const instructions = fsbx.Fluent
  .init()
  .startBundle('./dist/noflo.js')
  .excludeDeps() // also can be used as .ignoreDeps
  .execute("src/lib/NoFlo.coffee")
  .add("src/lib/*.coffee")
  .exclude('fs') // also can be used as .ignore
  .include('process')
  .noApi()
  .noCache()
  .finishBundle()
  .finish()

// becomes:
// ^ ! >[src/lib/NoFlo.coffee] -fs +process
const bundle = fuse.bundle(instructions)

Multi Fluent Bundle

const instructions = fsbx.Fluent
  .init()

  .startBundle('./dist/noflo.js')
  .excludeDeps()
  .execute("src/lib/NoFlo.coffee")
  .finishBundle()

  .startBundle('./dist/bundle.js')
  .excludeDeps()
  .add("spec/**/*.coffee")
  .finishBundle()

  .startBundle('./dist/specs.js')
  .includeDeps()
  .add("spec/**/*.coffee")
  .exclude('path')
  .finishBundle()

  .finish()

// becomes:
// {
//   './dist/noflo.js': '>[src/lib/NoFlo.coffee]',
//   './dist/bundle.js': '+[spec/**/*.coffee]',
//   './dist/specs.js': '+spec/**/*.coffee -path'
// }
const bundles = fuse.bundle(instructions)

Bundle in a bundle

The super powers of FuseBox allow merging bundles inside of bundles without code redundancy. The API of a second bundle will be removed, and 2 bundles will be fused together, keeping only one shared Fusebox API.

Only one thing you need to consider before that - packaging.

Your current project is called "default" This is by design. All dynamic modules will register themselves into it automatically.

If you want to require a bundle it must have a different namespace. Unless you want to keep it shared. Read up on package naming for better understanding.

Bundle your first package, then make sure your master/main bundle does not have the same package name (otherwise they will share filename scopes) and require it like any other file.

import * as myLib from "./bundles/myLib.js"

FuseBox sniffs its own creations and restructures the code accordingly.

Importing Bundles

you can import using the fusebox api wrapper that is built into the bundle

const bundled = require("./magic/yourOutFile.js")
const exports = bundled.FuseBox.import("./yourBundle.js");

or you can import the file directly using FuseBox

const fsbx = require("fuse-box")
const FuseBox = fsbx.FuseBox

const bundled = require("./magic/yourOutFile.js")
const bundled = FuseBox.import("./yourBundle.js")

Scoping / Fused

If you have more than one bundle and require them, they will be fused behind the scenes. That is to say, they will be able to import from each other. This is possible because FuseBox is not just a bundler, but a full featured virtual environment! See an example using fusing.

Typescript

Typescript works out of the box, additional configuration is not required. Make sure you have the typescript compiler installed.

npm install typescript --save-dev

Now let's define a simple configuration

FuseBox.init({
    homeDir: "src/",
    outFile: "./out.js",
    sourceMaps: true
}).bundle(">index.ts");

FuseBox automatically switches to typescript mode by detecting the extension .ts / .tsx. FuseBox compiles and bundles your files.

For your own convenience you can also add the Typescript helpers plugin.

tsConfig

FuseBox comes with default ts options so you don't need a tsconfig.

If you have a tsconfig file in your homeDir or any directory up the file tree (e.g. appRootPath), it will be picked up automatically.

Alternatively you can use the tsconfig option to customize the path to tsconfig.json (It can be an absolute path, Or relative to appRootPath).

FuseBox.init({
    tsConfig: "tsconfig.json",
})

Irrespective of the settings in tsconfig.json:

Rollup

For those how are into minimalist bundles, FuseBox offers built-in Rollup support.

At the moment it works only on typescript projects, this is because typescript is used to transpile your code from es6 to es5 in the virtual file system.

Installation

npm install rollup typescript

Requirements

There are few things that you need to consider before jumping it:

If you are comfortable with the above setup, you can proceed by checking out the fuse-box-rollup-example:

git clone git@github.com:fuse-box/fuse-box-rollup-example.git
npm install
node fuse

How it works

Configuration

rollup: {
    bundle: {
        moduleName: "Fuse4ever",
    },
    entry: "index.js",
    treeshake: true,
}

Bundle contains configuration that happen at the latest stage of rollup. Everything is a primary configuration. You will not be able to use Rollup plugins (for now), as FuseBox injects it's own plugin for resolving modules. However, this means you are able to use the rest of the features FuseBox has to offer, such as aliases

Loader API

How it works

Well, it's pure magic - nodejs wrapper for browser. FuseBox wrapper provides 100% compatible nodejs ecosystem, having virtual files and virtual packages. Everything is registered and shared by the API. It means, that you can have two script tags that will fuse and merge each other.

Plugins inject dependent packages/javascript code, that becomes a part of FuseBox loader. In principal, a plugin might work at build time and runtime, which unfolds some crazy optimisation possibilities.

FuseBox bundle works in both environments. Essentially, it does not matter where you run it. FuseBox will persist itself in browser window, or nodejs globals.

Every bundle contains a 3.7k footer with FuseBox API (1.6KB gzipped).

Import

Import is 100% compatible with commonjs specification. You can require folders, skip file extensions (fusebox will guess it).

FuseBox.import("./foo/bar");

Require external packages will work as well

FuseBox.import("fs");

Please note that some libraries like "fs" are faked in the browser. Meaning that it won't spit out an error, but won't work as expected on the server for known reasons. Nodejs environment, however, will get authentic "fs" module. (Concerns http, net, tty e.t.c )

Point to the root

You can use ~ symbol to point to your project's homeDir in order to fix relative path messes such as ../../../../../utils.

// es5
require("~/lib/utils")
// es6
import * as utils from "~/lib/utils";

Lazy Load

Lazy load works out of the box.

Imagine that bundle does not have myModule.js, and it's a CommonJs module. In order to lazy load it, put your target file next to it available via HTTP, and load it like so:

FuseBox.import("./myModule.js", (module) => {
})

If a module is not found within FuseBox' context, it tries to load it via HTTP request. Once obtained, the API caches the exports, allowing to request that module synchronously later on.

import "./myModule".

You can load other bundles as well. For example here

Wildcard import

With wildcard imports * you can require all files that match a particular pattern. A wildcard with no default extension will fallback to .js. For example

FuseBox.import("./batch/*")

Will result in:

{ "batch/a.js" : {}, "batch/b.js" : {} }

Whereas a.js and b.js are files in folder batch

You can require all JSON files for example:

FuseBox.import("./batch/*.json")

Or match a particular pattern

FuseBox.import("./batch/*-component")

Note that you can use all of the above with require statement too.

require("~/stuff/boo").hello

or

require("~/stuff/boo.js").hello

Remove

You can completely remove a module from memory.

FuseBox.remove("./foo")

FuseBox events

It is possible to intercept require statements. You can catch "before-import" and "after-import" events like so:

FuseBox.on("before-import", (exports, require, module, __filename, __dirname, pkg) => {                
});

FuseBox.on("after-import", (exports, require, module, __filename, __dirname, pkg) => {                
});

However, it is not recommended. But if you want to play god, you can use that functionality.

Dynamic modules

Another feature that allows you to register commonjs modules at runtime in browser and on server accordingly

FuseBox.dynamic("foo/bar.js", "module.exports = {foo : 'bar'}")

Note how foo/bar.js is set. It's called "FuseBox path", which should not start with slashes or periods. FuseBox will register a file in the defaultproject. Hence, it is possible to override your local files, but impossible to do the same with external packages.

It's imperative to bear in mind file's path as well. Having foo/bar.js as a dynamic module, means that its require context will be pointed to the folder foo To make it clear, let's register two files

FuseBox.dynamic("foo/bar.js", "module.exports = {foo : 'bar'}")
FuseBox.dynamic("foo/wow.js", "require('./bar')")

See how wow.js is referring to the foo/bar.js. A dynamic module is a fully functional FuseBox file, that plays nicely with the rest of the bundle and vice versa. Your bundle won't have any problems using a wildcard import on the dynamic modules too.

require("~/foo/*") // will give 2 files

It's impossible to transpile dynamic modules at the moment. You can easily do it yourself, since the API accepts a string,

Loader Plugins

Loader plugins can intercept hmr updates to override the default behavior. Here is the current plugin interface:

interface LoaderPlugin {
    /** 
     * If true is returned by the plugin
     *  it means that module change has been handled
     *  by plugin and no special work is needed by FuseBox
     **/
    hmrUpdate?(evt: SourceChangedEvent): boolean;
}

/** Where */
type SourceChangedEvent = {
    type: 'js' | 'css',
    content: string,
    path: string
}

You register a plugin using FuseBox.addPlugin(YourPlugin).

FuseBox.addPlugin({
  hmrUpdate: ({ type, path, content }) => {
    if (type === "js") {
      window.location.reload();
      return true;
    }
  }
});
FuseBox.addPlugin({
  hmrUpdate: (evt) => {
    console.log('HMR Update', evt, 'Please reload the window');
    return true;
  }
});

Loader Plugin Examples

Plugin API

Plugin API has a very powerful mechanism of manipulating the output.

Let's take a look a plugin's interface first

interface Plugin {
    test?: RegExp;
    opts?: any;
    init?: { (context: WorkFlowContext) };

    transform: { (file: File, ast?: any) };
    transformGroup?(file: File): any;
    onTypescriptTransform?: { (file: File) };

    dependencies?: string[];

    preBundle?(context: WorkFlowContext);
    bundleStart?(context: WorkFlowContext);
    bundleEnd?(context: WorkFlowContext);
    postBundle?(context: WorkFlowContext);

    // available, but not implemented yet
    preBuild?(context: WorkFlowContext);
    postBuild?(context: WorkFlowContext);
}

Spec

test [RegExp]

Defining test will filter files into your plugin. For example \.js$ If specified you plugin's transform will get triggered upon transformation. It's optional.

dependencies

dependencies a list of npm dependencies your plugin might require. If provided, then the dependencies are loaded on the client before the plugin is invoked. For example this case

init

Happens when a plugin is initialized. It is common practice to reset your plugin state in this method.

transform

transform if your plugin has a test property, fusebox will trigger transform method sending [file][src-file] as a first argument.

triggers

bundleStart

Happens on bundle start. A good place to inject your custom code here. For example here

bundleEnd

All files are bundled. But it has not been finalized and written to a file.

preBundle

Triggered after adding shims.

postBundle

Triggered after the bundle source has been finalized, but before it is written to file. UglifyPlugin uses this trigger.

see the source code that triggers these plugin methods

Alternative content

If for some reason we want to preserve file contents for a later reuse and override the output, we can use file.alternativeContent which affects directly bundling process over here

It can be use for the concat technique for example

If an array of plugins is passed, those plugins will be chained

FuseBox.init({
    plugins: [
        fsbx.JSONPlugin(),
        [fsbx.LESSPlugin(), fsbx.CSSPlugin()],
    ],
})

Transform

Helpers

File

AST

To use the AST, you need to know if the AST has been loaded already. You can do this by checking whether file.analysis.ast is not undefined.

If it has not been loaded, it can be loaded by doing:

file.loadContents()
file.analysis.parseUsingAcorn()
file.analysis.analyze()

If the babel plugin has been used, the AST will be loaded using babel babylon.

You can load any ast parser you'd like by

if (!file.analysis.ast) {
  const result = YourAST(file.contents)
  file.analysis.loadAst(result.ast)
  file.analysis.analyze()
}

read the FileAnalysis source for more

Transforming typescript

You can tranform typescript code before it actually gets to transpiling

const MySuperTranformation = {
    onTypescriptTransform: (file) => {
        file.contents += "\n console.log('I am here')";
    }
}
FuseBox.init({
    plugins: [MySuperTranformation],
})

Concat files

It is possible to concat files into one using the plugin API. There is ConcatPlugin which serves as an example for the subject.

In order to understand how it works imagine a plugin chain:

[/\.txt$/, fsbx.ConcatPlugin({ ext: ".txt", name: "textBundle.txt" })],

We have 2 files, a.txt and b.txt which are captured by the plugin API, and each of them is redirected to the ConcatPlugin's transform, which looks like this:

public transform(file: File) {
    // Loading the contents of file a.txt or b.txt
    file.loadContents();

    let context = file.context;

    // create a file group in the context with name which is set in the plugin configuration
    // let's say "txtBundle.txt"
    let fileGroup = context.getFileGroup(this.bundleName);
    if (!fileGroup) {
        fileGroup = context.createFileGroup(this.bundleName);
    }
    // Adding current file (say a.txt) as a subFile
    fileGroup.addSubFile(file);

    // making sure the current file refers to an object at runtime that calls our bundle
    file.alternativeContent = `module.exports = require("./${this.bundleName}")`;
}

When we register a new file group context.createFileGroup("txtBundle.txt") FuseBox creates a fake or a virtual file which is added to the dependency tree. This file has a special mode, called groupMode.

We need to alter the output as well using alternative content. Original contents will be ignored by the Source bundler.

After FuseBox has bundled all files related to your current project, it checks for groups over here, iterates and executes plugins. Then each plugin is tested accordingly (now our file name is called txtBundle.txt with .txt extension) and executes transformGroup of a plugin if set.

You should understand that txtBundle.txt behaves like any other file, with one exception - it does not call transform but tranformGroup instead.

public transformGroup(group: File) {
    let contents = [];
    group.subFiles.forEach(file => {
        contents.push(file.contents);
    });
    let text = contents.join(this.delimiter);
    group.contents = `module.exports = ${JSON.stringify(text)}`;
}

Now our bundle has a virtual file which looks like this:

___scope___.file("textBundle.txt", function(exports, require, module, __filename, __dirname){
    module.exports = "hello\nworld"
});

Things to experiment with

Plugin API source code

Dev Server and HMR

To run a development server all you need is to change bundle to devServer

Launch express and socket

FuseBox.init({
    homeDir: "src",
    outFile: "build/out.js",
}).devServer(">index.ts");

the devServer function takes an optional options parameter as a second argument.

Changing the served folder

FuseBox will automatically serve your build folder e.g. with outFile: "build/out.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 appRootPath):

devServer(">index.ts", {
   root: 'public/build',
});

Changing the port

By default you will get express application running and a socket server bound to port 4444. To change the port provide port option.

devServer(">index.ts", {
   port: 8081,
});

Disable HMR

If you not into Hot Reload, you can disable it:

devServer(">index.ts", {
   hmr: false,
});

Custom socket URI

You can customize the URI if required.

devServer(">index.ts", {
   socketURI: "wss://localhost:3333",
});

Express api

Access express application like so:

const self = devServer(">index.ts", {
   port: 8081,
});
self.httpServer.app.use(/* your middleware */);

You can set root to false if you want to manually configure the root path

const self = devServer(">index.ts", {
   port: 8081,
   root: false,
});
self.httpServer.serveStatic("*", "build/static");

serveStatic is convenience method, that looks like this:

this.app.use(userPath, express.static(ensureUserPath(userFolder)));

You can also create an SPA server which fallbacks to index.html

import express import 'express'
import path import 'path'
const server = devServer('>index.js', {
    port: 8081,
});
server.httpServer.app.use(express.static(path.join('build','static')));
server.httpServer.app.get('*', function(req, res) {
    res.sendFile(path.join('build', 'index.html'));
});

Custom emitter

You can manually send events on file change like so:

fuse.devServer(">index.ts", {
    emitter: (self, fileInfo) => {
        self.socketServer.send("source-changed", fileInfo);
    }
});

Integrate with existing app

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

fuse.devServer(">app.tsx", {
    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.

See the fuse-box-express-example for a more detailed setup.

Tips

Below are some tips that will improve your experience and help you avoiding gotchas while using FuseBox.

General tips

TypeScript tips

Electron tips

Electron has 2 JavaScript environments. One is Node JS (called main) and one is Chromium (called renderer).

Electron gives the renderer process the ability to call native commands by proxying through the electron module.

To make this setup work with FuseBox, you'll need to add these two options to the bundle targetting your renderer.

{
  serverBundle: true,
  shim: {
    electron: { exports: "global.require('electron')" },
  },
}

The serverBundle command tells FuseBox to make available some of the Node JS modules (path, fs, etc.) to that JS environment.

The shim acts as a pass through instructing FuseBox to trust us that electron is available to require. The global. is important since FuseBox as already called dibs on require(). They left us global.require() for these types of shenanigans. :beers:

No need to include or exclude +electron from your bundles. Just these few lines will do the job.

Happy Electron-ing!

Built-in plugins

Fusebox contains premade plugins that should help you to get started.

note: some of the plugins need to install external dependencies for them to function correctly. kindly take this into account.

CSS Plugin

CSSPlugin is used to handle .css syntax. As such, it should always be at the end of any CSS processing chain (see #list-of-plugins for examples of plugin chains), as it handles everything that is relating to bundling, reloading and grouping css styles.

see the mastering css with fusebox example

Inline CSS

plugins: [
  CSSPlugin(),

That configuration converts all .css files into a format that allows including them directly into javascript. For example:

import './main.css'

Write css to the filesystem

The outFile option is used to write css files to the bundle directory

let tmp = './tmp'
plugins: [
    CSSPlugin({
        outFile: (file) => `${tmp}/${file}`,
    }),
]

FuseBox will automatically inject your files into the HEAD using link tags when imported

import 'directory/main.css'

creates

<link rel="stylesheet" type="text/css" href="main.css">

Head injection

CSSPlugin automatically appends css styles or stylesheets into the HEAD by default as in the above example. You can override this behavior by setting {inject: false}

plugins: [
    CSSPlugin({
        outFile: (file) => `${tmp}/${file}`,
        inject: false,
    }),
]

If you want to keep the magic but configure the injection yourself, you can provide a callback to the inject parameter to customise your css file resolver in the browser

plugins: [
    CSSPlugin({
        outFile: (file) => `${tmp}/${file}`,
        inject: (file) => `custom/${file}`,
    }),
]

Will result in:

<link rel="stylesheet" type="text/css" href="custom/main.css">

Grouping files

You can group many css files into a single file. Imports of any individual file will be converted into imports of the grouped file.

plugins: [
    CSSPlugin({group: "bundle.css"}),
]

the group option should not contain any relative or absolute paths. This is a virtual file in the dependency tree. You can use all parameters described above to customise the behaviour. For example

plugins: [
    CSSPlugin({
        group: "bundle.css"
    })
]
plugins : [
    CSSPlugin({
        group: "app.css",
        outFile: `${tmp}/app.css`
    })
]

NOTE! outFile must be a string (not a callback) when used with the group option.

Check out the tests here

CSSResourcePlugin

This program is designed to make it easy to import a css library from an npm package. Let's try to make the jstree library work

import "jstree/dist/jstree.js";
import "jstree/dist/themes/default/style.css";

style.css has relative resources (images, fonts), which need to be copied in order to use it. CSSResourcePlugin solves this problem. It re-writes the URL and copies files to a destination specified by user,

Copy files

plugins: [
   [/node_modules.*\.css$/,
      fsbx.CSSResourcePlugin({
          dist: "build/resources",
          resolve: (f) => `/resources/${f}`
      }), fsbx.CSSPlugin()]
]

resolve in our case is the actual path on browser. f is a modified file name (you don't need to change it)

Inline

You can inline images as well, converting them to base64 data images inside the CSS

plugins: [
   [/node_modules.*\.css$/,
    fsbx.CSSResourcePlugin({
      inline: true,
    }), fsbx.CSSPlugin()],
]

HINT: disable cache while playing with the options, as npm modules along with css files are heavily cached

Less Plugin

Install less first.

npm install less --save-dev

The less plugin generates CSS, and must be chained prior to the CSSPlugin to be used:

plugins:[
  [fsbx.LESSPlugin(), fsbx.CSSPlugin()],
],

Sourcemaps are not yet properly handled. Development is ongoing on this feature

PostCSS

Install postcss and any postcss plugins first

npm install precss postcss --save-dev

PostCSS generates CSS, and must be chained prior to the CSSPlugin to be used:

const precss = require("precss");
const POST_CSS_PLUGINS = [precss()];

plugins:[
  [fsbx.PostCSS(POST_CSS_PLUGINS), fsbx.CSSPlugin()],
],

Sourcemaps are not yet properly handled. Development is ongoing on this feature

StylusPlugin

stylus generates CSS, and must be chained prior to the CSSPlugin to be used:

plugins:[
  [fsbx.StylusPlugin(), fsbx.CSSPlugin()]
],

Raw Plugin

Make files export text data

plugins:[
 RawPlugin([".txt", "inline-styles/*.css"])
],

Automatically enables extensions in the context. Make sure you have a valid mask with extension at the end of it. For example RawPlugin(["asdf"]) will throw an error. RawPlugin(["hello.txt"]) will enable txt.

SassPlugin

Sass generates CSS, and must be chained prior to the CSSPlugin to be used:

npm install node-sass

Usage:

plugins:[
  [SassPlugin({ /* options */ }), CSSPlugin()],
],

By default, you have 3 macros available:

That same as home directory

@import '$homeDir/test2.scss';

Your application root.

@import '$appRoot/src/test.scss';

Tilde that points to node_modules

@import '~bootstrap/dist/bootstrap.css';

You can override any of these by providing a key:

plugins: [
    [ SassPlugin({ macros: { "$homeDir": "custom/dir/" }}), CSSPlugin() ]
]

HTML Plugin

plugins: [
  fsbx.HTMLPlugin({ useDefault: false }),
]

Toggle useDefault to make HTML files export strings as default property. For example with useDefault: true you will be able to import HTML files like so:

import tpl from "~/views/file.html"

With useDefault: true, is as if the html file contains this:

export default `
  <!DOCTYPE html>
  <title>eh</title>
`

ImageBase64Plugin

Works greatly if you want to have images bundled

npm install base64-img --save-dev
plugins: [
    fsbx.ImageBase64Plugin(),
]

Example for react

const image = require("./icons/image.png")
<img src={image} />

Babel plugin

The babel plugin is used to transpile code to different dialects of javascript. The npm babel-core package must be installed to use the babel plugin.

For example, to transpile JSX, you can use this configuration:

npm install babel-core babel-preset-es2015 babel-plugin-transform-react-jsx
 plugins: [
    fsbx.BabelPlugin({
        test: /\.jsx$/, // test is optional
        config: {
            sourceMaps: true,
            presets: ["es2015"],
            plugins: [
                ["transform-react-jsx"],
            ],
        },
    })
]

limit2project is default true, to use this plugin across an entire project (including other modules like npm)

Note, that if you want to have sourceMaps in place, set sourceMaps to true. Read sourceMaps section for better understanding how sourceMaps are defined.

JSON plugin

The JSON plugin allows .json files to be imported as javascript objects

plugins: [
    fsbx.JSONPlugin(),
]

SVG Plugin

The SVG plugin allows importing svg graphics files into javascript source for use in styles and as image source.

here is an example usage, and the source file that imports the SVG

plugins: [
    fsbx.SVGPlugin(),
]

BannerPlugin

Add a comment with static text at the top of the bundle.

plugins: [
    // Add a banner to bundle output
    fsbx.BannerPlugin('// Hey this is my banner! Copyright 2016!'),
]

UglifyJSPlugin

Compresses the javascript code by using UglifyJS2

plugins: [
    // [options] - UglifyJS2 options
    fsbx.UglifyJSPlugin(options),
]

Passing in no options will cause the outFile to be minified with default options.

SourceMapPlainJsPlugin

npm i source-map

sourceMap: {
  bundleReference: "sourcemaps.js.map",
  outFile: "sourcemaps.js.map",
},
plugins: [
    fsbx.SourceMapPlainJsPlugin(),
],

EnvPlugin

Creates environment variables for both client and server at build time.

plugins: [
   fsbx.EnvPlugin({ NODE_ENV: "production" }),
],

Access it with process.env.${ENVIRONMENT_VARIABLE_NAME} as in:

console.log(process.env.NODE_ENV)

The order of plugins is important: environment variables created with this plugin will only be available to plugins further down the chain, so EnvPlugin should be early in the list of plugins.

plugins: [
   fsbx.BabelPlugin({ /* settings */ }), // <-- won't have NODE_ENV set
   fsbx.EnvPlugin({ NODE_ENV: "production" }),
   fsbx.BabelPlugin({ /* settings */ }), // <-- will have NODE_ENV set
]

ReplacePlugin

The EnvPlugin will define a value for you, but if somewhere along the line that value changes (for example, something setting process.env.NODE_ENV = 'magic';), the value will change. In contrast, the ReplacePlugin will replace that key, with the value you provide, for example, instead of process.env.NODE_ENV, the value is replaced with a string. This allows UglifyJSPlugin to remove "dead code" and enables you to use production mode with modules that rely on this behaviour.

example:

your config

plugins: [
  ReplacePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
],

your code

if (process.env.NODE_ENV === 'production') console.log('production!')

result

if ('production' === 'production') console.log('production!')

after uglifying

console.log('production!')

CoffeePlugin

Allows CoffeeScript compilation of .coffee files

plugins: [
   fsbx.CoffeePlugin({
       // Options passed to the coffeescript compiler
   }),
],

Typescript helpers

This plugin adds required typescript functions to the bundle. Please note that it adds only the ones that are actually used, helping to avoid unnecessary code.

This list shows the possible helpers.

Available helpers:

Name Description
__assign Generic typescript helper
__awaiter Generic typescript helper
__decorator Generic typescript helper + additional fusebox meta data patched
__extends Generic typescript helper
__generator Generic typescript helper
__param Generic typescript helper

If you spot an error or a missing helper, please submit an issue or a pull request. If needed, you can always create your own plugin, based on this class code

Using the plugin

Simply add TypeScriptHelpers to your plugin list. No further configuration required. FuseBox will take care of everything else. To avoid unnecessary AST (which is heavy) this plugin does a simple RegExp, and tests for declarations. It is absolutely safe, and your code is not modified in any way.

const fsbx = require("fuse-box");
let fuse = fsbx.FuseBox.init({
    homeDir: "test/fixtures/cases/ts",
    outFile: "./out.js",
    plugins: [fsbx.TypeScriptHelpers()],
});

Extended metadata properties

You can have access to the entire environment of a file, using reflect-metadata. Make sure you have it installed first

npm install reflect-metadata

Then, include it in your entry point

import "reflect-metadata";

Now, you can access "commonjs" variables via fusebox metadata property

export function testDecorator() {
    return function (target, key: string, descriptor: PropertyDescriptor) {
        Reflect.getMetadata("fusebox:__filename", target, key);
        Reflect.getMetadata("fusebox:__dirname", target, key);
        Reflect.getMetadata("fusebox:require", target, key); // Local "require" function
        Reflect.getMetadata("fusebox:module", target, key);
        Reflect.getMetadata("fusebox:exports", target, key);
    }
}

3rd party plugins

Name Description Link
Gulp Adapter Adapt gulp plugins to work with fuse-box unlight/fusebox-gulp-plugin
ESLinter ESlint plugin for fuse-box DoumanAsh/fuse-box-eslint-plugin
Closure Compiler Fusebox Google Closure Compiler Plugin matthiasak/fusebox-closure-plugin
proxying / stubbing imports Fusebox proxying / stubbing imports Plugin tomitrescak/proxyrequire
Process Run any tasks (npm, tsc, etc.) after fuse-box bundled your code RPDeshaies/fuse-box-process-plugin

Examples