esbuild is an ultra lightweight and fast bundler (based in Rust) used in NodeJS to compile javascript projects. It works using Nodejs and can bundle any javascript and typescript projects generically like other bundlers. It typically takes <100ms to bundle a javascript project, and about 2 seconds if you add the type generation plugin.
esbuild supports the full spectrum of javascript bundling needs we've encountered so far and we have documented and abstracted its typical features fairly well with our settings. See their website for more information.
Code: tinybuild/esbuild
Create an index.js (or whatever name) file serving as the entry point script for your webapp or library e.g.:
if(typeof window !== 'undefined') alert('Tinybuild successful!')
console.log('Tinybuild succesful!')
Now create a bundler.js file which we'll execute with node bundler.js
import {bundle} from 'tinybuild'
bundle({
entryPoints: ['index.js'],
outfile: 'dist/index',
bundleBrowser: true, //plain js format
bundleESM: false, //.esm format
bundleTypes: false, //you need a .tsconfig for this to work
bundleHTML: true
})
Our presets will let you quickly bundle apps, browser and node modules, generate types, create "executable" html files from entry points, "webpacked" globals and init scripts, and more with esbuild (minification, sourcemaps, js or browser version targeting, etc.), all with just one input object!
The other half of our preset tools in tinybuild include a boilerplate node development server with optional https, hot reloading, pwa support, python server interoperation examples, and websocket presets so it can work as a quick and dirty production server as well.
Any unlisted settings are just typical esbuild settings, which can be configured per build type via the .options tag (e.g. config.options.browser = {…more esbuild settings})
//found in esbuild/bundler.js
const bundlerSettings = {
bundleBrowser:true, //create plain js build? Can include globals and init scripts
bundleESM:false, //create esm module js files
bundleTypes:false, //create .d.ts files, //you need a .tsconfig for this to work
bundleNode:false, //create node platform plain js build, specify platform:'node' to do the rest of the files
bundleIIFE:false, //create an iife build, this is compiled temporarily to create the types files
bundleCommonJS:false, //cjs format outputted as .cjs
bundleHTML:false, //wrap the first entry point file as a plain js script in a boilerplate html file, frontend scripts can be run standalone like a .exe!
entryPoints:['index.ts'], //entry point file(s). These can include .js, .mjs, .ts, .jsx, .tsx, or other javascript files. Make sure your entry point is a ts file if you want to generate types
outfile:'dist/index', //exit point file, will append .js as well as indicators like .esm.js, .node.js for other build flags
//outdir:[] //exit point files, define for multiple bundle files
bundle:true,
platform: 'browser', //'node' //bundleNode will use 'node' mode by default
minify: true,
sourcemap: false,
external: ['node-fetch'], // []; //we use node-fetch a lot
allowOverwrite:true,
loader: {
'.html': 'text', //not always necessary but it doesn't hurt
'.png' : 'file',
'.jpg' : 'file',
'.gif' : 'file',
'.svg': 'file',
'.woff': 'file',
'.woff2': 'file',
'.ttf': 'file',
'.eot': 'file',
'.mp3': 'file',
'.mp4': 'file',
'.json': 'text',
},
outputs:{ //overwrites main config settings for specific use cases, you can also set the bundleBrowser etc.. settings to these objects
node:{
external:[] //externals for node environment builds
}
//esm:{}
//commonjs:{}
//browser:{}
//iife:{}
}
//globalThis:null //'brainsatplay'
//globals:{['index.ts']:['Graph']}
//init:{['index.ts']:function(bundle) { console.log('prepackaged bundle script!', bundle); }.toString() }
}
//the rest are based on what esbuild offers
Browser bundling simply breaks projects down into plain js format, usable e.g. in script tags.
This includes support for setting module global names as well as specifying specific class names or functions, variables, etc you want to expose via the globalThis:string
and globals:{'entryPoints.js':['ClassToSetOnglobalThis']}
settings.
The init:{'entryPoints.js':function(bundle){}}
setting lets you specify scripts to append to bundles e.g. to initialize bundled class instances with default settings. It's painless! Currently these extra settings only apply to the browser bundle, which you could override to format for esm via .outputs.browser.format = 'esm' or .outputs.browser.platform = 'node'.
ESM bundles are for enabling the import
/export
syntax either in es6 or later javascript environments. Default scripts in browsers or nodejs do not support import
/export
.
You need a tsconfig in your project root for this to work, we edited the plugin to work with any js/jsx/ts/tsx entry points.
These function sort of like header files in strongly-typed C, which show you all of the functions/classes/variables/etc. and their expected input/output formats (including detailed formatting for objects or array types) in each respective script file. Nice for reference, VSCode otherwise does this on-the-fly even in .js files when working.
This is a setting to generate a plain js file with platform:'node'
targeted. If you include this setting in the bundler settings object it will apply to all bundles. It allows node modules like fs
or ws
etc. to be bundled as needed.
We use it in some cases to create frontend and backend libraries from the same files that otherwise use the same function calls just with different dependencies if running a client or server, as browsers lack backend server libraries and node lacks frontend tools like animations or the DOM (without an engine anyway).
These are other bundle formats. We temporarily generate IIFE files so the types plugin runs correctly but it isn't saved unless specified.
This is an extension to the browser bundler (including the global installs) that will wrap the browser bundle in HTML boilerplate.
If your script entry point is to your app, then this can automatically be served, and if the app does not require served files etc. then it essentially serves like a built .exe file.
You may also use it if you want to write quick library or frontend tests with init scripts and test serving scripts to execute in-browser. Currently this test setting only applies to the browser bundle, which you could override to format for esm via .outputs.browser.format = 'esm' or .outputs.browser.platform = 'node'.
This custom plugin is active by default. The only prerequisite is that your worker files have 'worker.js' or '.worker' (e.g. for ts worker files) in their path. Then import your workers like this:
import worker from 'worker.js' //or if the worker is a ts file, '.worker' will resolve the separate worker bundle too
//export { worker } //you can re-export the bundled worker blob/path or use it in your app/library
let w = new Worker(worker);
And esbuild will bundle the worker with your distribution! You'll find the bundled workers in the same output file location. You can bundle workers directly in your app/library using our plugin (applied by default) which bundles and inserts the worker code as an inline object url.
If you set the blobWorkers setting to false using the workerPlugin manually in your config, it will supply the expected url to the server's node_modules folder when you install the worker library. You can set the bundler settings for the worker in the workerPlugin initialization as well, it defaults to only setting minifyWhitespace:true
in the bundler settings.
Find our graphscript examples for multiple worker implementations for graphics rendering with threejs.
For modules including node libraries that are meant to interoperate with the browser, make sure you exclude them in via the 'external' setting.
To specify specific bundler settings (overwriting the main object), set e.g. settings.bundleESM OR settings.outputs.esm = { …more_esbuild_settings }. Node by default has its own 'external' setting for example so it can include any node modules excluded for browser bundling otherwise.
esbuild doesn't have the greatest documentation. You could say there's a lot to… unpack… Anyway, go crazy! This is the best bundler we've used! We've compensated for esbuild's lack of umd support with our globals settings. No complaints so far!