Home Programming An Introduction to the esbuild Bundler — SitePoint

An Introduction to the esbuild Bundler — SitePoint

0
An Introduction to the esbuild Bundler — SitePoint

[ad_1]

esbuild is a quick bundler that may optimize JavaScript, TypeScript, JSX, and CSS code. This text will make it easier to stand up to hurry with esbuild and present you the right way to create your individual construct system with out different dependencies.

Desk of Contents
  1. How Does esbuild Work?
  2. Why Bundle?
  3. Why Use esbuild?
  4. Why Keep away from esbuild?
  5. Tremendous-quick Begin
  6. Instance Mission
  7. Mission Overview
  8. Configuring esbuild
  9. JavaScript Bundling
  10. CSS Bundling
  11. Watching, Rebuilding, and Serving
  12. Abstract

How Does esbuild Work?

Frameworks resembling Vite have adopted esbuild, however you should use esbuild as a standalone device in your individual initiatives.

  • esbuild bundles JavaScript code right into a single file in an identical strategy to bundlers resembling Rollup. That is esbuild’s main operate, and it resolves modules, studies syntax points, “tree-shakes” to take away unused capabilities, erases logging and debugger statements, minifies code, and gives supply maps.

  • esbuild bundles CSS code right into a single file. It’s not a full substitute for pre-processors resembling Sass or PostCSS, however esbuild can deal with partials, syntax points, nesting, inline asset encoding, supply maps, auto-prefixing, and minification. That could be all you want.

  • esbuild additionally gives an area improvement server with computerized bundling and hot-reloading, so there’s no must refresh. It doesn’t have all of the options provided by Browsersync, nevertheless it’s adequate for many circumstances.

The code under will make it easier to perceive esbuild ideas so you may examine additional configuration alternatives to your initiatives.

Why Bundle?

Bundling code right into a single file provides varied advantages. Listed here are a few of them:

  • you may develop smaller, self-contained supply information that are simpler to take care of
  • you may lint, prettify, and syntax-check code in the course of the bundling course of
  • the bundler can take away unused capabilities — referred to as tree-shaking
  • you may bundle different variations of the identical code, and create targets for older browsers, Node.js, Deno, and so forth
  • single information load sooner than a number of information and the browser doesn’t require ES module assist
  • production-level bundling can enhance efficiency by minifying code and eradicating logging and debugging statements

Why Use esbuild?

Not like JavaScript bundlers, esbuild is a compiled Go executable which implements heavy parallel processing. It’s fast and as much as 100 instances sooner than Rollup, Parcel, or Webpack. It might save weeks of improvement time over the lifetime of a undertaking.

As well as, esbuild additionally provides:

  • built-in bundling and compilation for JavaScript, TypeScript, JSX, and CSS
  • command-line, JavaScript, and Go configuration APIs
  • assist for ES modules and CommonJS
  • an area improvement server with watch mode and dwell reloading
  • plugins so as to add additional performance
  • complete documentation and an on-line experimentation device

Why Keep away from esbuild?

On the time of writing, esbuild has reached model 0.18. It’s dependable however nonetheless a beta product.

esbuild is ceaselessly up to date and choices might change between variations. The documentation recommends you follow a particular model. You may replace it, however you might must migrate your configuration information and delve into new documentation to find breaking adjustments.

Word additionally that esbuild doesn’t carry out TypeScript sort checking, so that you’ll nonetheless must run tsc -noEmit.

Tremendous-quick Begin

If obligatory, create a brand new Node.js undertaking with npm init, then set up esbuild regionally as a improvement dependency:

npm set up esbuild --save-dev --save-exact

The set up requires round 9MB. Test it really works by operating this command to see the put in model:

./node_modules/.bin/esbuild --version

Or run this command to view CLI assist:

./node_modules/.bin/esbuild --help

Use the CLI API to bundle an entry script (myapp.js) and all its imported modules right into a single file named bundle.js. esbuild will output a file utilizing the default, browser-targeted, immediately-invoked operate expression (IIFE) format:

./node_modules/.bin/esbuild myapp.js --bundle --outfile=bundle.js

You may set up esbuild in different methods if you happen to’re not utilizing Node.js.

Instance Mission

Obtain the instance information and an esbuild configuration from Github. It’s a Node.js undertaking, so set up the one esbuild dependency with:

npm set up

Construct the supply information in src to a construct listing and begin a improvement server with:

npm begin

Now navigate to localhost:8000 in your browser to view an internet web page exhibiting a real-time clock. Once you replace any CSS file in src/css/ or src/css/partials, esbuild will re-bundle the code and dwell reload the types.

esbuild example clock project

Press Ctrl|Cmd + Ctrl|Cmd to cease the server.

Create a manufacturing construct for deployment utilizing:

npm run construct

Study the CSS and JavaScript information within the construct listing to see the minified variations with out supply maps.

Mission Overview

The true-time clock web page is constructed in a construct listing utilizing supply information from src.

The package deal.json file defines 5 npm scripts. The primary deletes the construct listing:

"clear": "rm -rf ./construct",

Earlier than any bundling happens, an init script runs clear, creates a brand new construct listing and copies:

  1. a static HTML file from src/html/index.html to construct/index.html
  2. static pictures from src/pictures/ to construct/pictures/
"init": "npm run clear && mkdir ./construct && cp ./src/html/* ./construct/ && cp -r ./src/pictures ./construct",

An esbuild.config.js file controls the esbuild bundling course of utilizing the JavaScript API. That is simpler to handle than passing choices to the CLI API, which might grow to be unwieldy. An npm bundle script runs init adopted by node ./esbuild.config.js:

"bundle": "npm run init && node ./esbuild.config.js",

The final two npm scripts run bundle with both a manufacturing or improvement parameter handed to ./esbuild.config.js to regulate the construct:

"construct": "npm run bundle -- manufacturing",
"begin": "npm run bundle -- improvement"

When ./esbuild.config.js runs, it determines whether or not it ought to create minified manufacturing information (the default) or improvement information with computerized updates, supply maps, and a live-reloading server. In each circumstances, esbuild bundles:

  • the entry CSS file src/css/major.css to construct/css/major.css
  • the entry JavaScript file scr/js/major.js to construct/js/major.js

Configuring esbuild

package deal.json has a "sort" of "module" so all .js information can use ES Modules. The esbuild.config.js script imports esbuild and units productionMode to true when bundling for manufacturing or false when bundling for improvement:

import { argv } from 'node:course of';
import * as esbuild from 'esbuild';

const
  productionMode = ('improvement' !== (argv[2] || course of.env.NODE_ENV)),
  goal = 'chrome100,firefox100,safari15'.cut up(',');

console.log(`${ productionMode ? 'manufacturing' : 'improvement' } construct`);

Bundle goal

Word that the goal variable defines an array of browsers and model numbers to make use of within the configuration. This impacts the bundled output and adjustments the syntax to assist particular platforms. For instance, esbuild can:

  • increase native CSS nesting into full selectors (nesting would stay if "Chrome115" was the one goal)
  • add CSS vendor-prefixed properties the place obligatory
  • polyfill the ?? nullish coalescing operator
  • take away # from non-public class fields

In addition to browsers, you can too goal node and es variations resembling es2020 and esnext (the most recent JS and CSS options).

JavaScript Bundling

The only API to create a bundle:

await esbuild.construct({
  entryPoints: ['myapp.js'],
  bundle: true
  outfile: 'bundle.js'
});

This replicates the CLI command used above:

./node_modules/.bin/esbuild myapp.js --bundle --outfile=bundle.js

The instance undertaking makes use of extra superior choices resembling file watching. This requires a long-running construct context which units the configuration:


const buildJS = await esbuild.context({

  entryPoints: [ './src/js/main.js' ],
  format: 'esm',
  bundle: true,
  goal,
  drop: productionMode ? ['debugger', 'console'] : [],
  logLevel: productionMode ? 'error' : 'data',
  minify: productionMode,
  sourcemap: !productionMode && 'linked',
  outdir: './construct/js'

});

esbuild provides dozens of configuration choices. Right here’s a rundown of those used right here:

  • entryPoints defines an array of file entry factors for bundling. The instance undertaking has one script at ./src/js/major.js.

  • format units the output format. The instance makes use of esm, however you may optionally set iife for older browsers or commonjs for Node.js.

  • bundle set to true inlines imported modules into the output file.

  • goal is the array of goal browsers outlined above.

  • drop is an array of console and/or debugger statements to take away. On this case, manufacturing builds take away each and improvement builds retain them.

  • logLevel defines the logging verbosity. The instance above reveals errors throughout manufacturing builds and extra verbose data messages throughout improvement builds.

  • minify reduces the code measurement by eradicating feedback and whitespace and renaming variables and capabilities the place potential. The instance undertaking minifies throughout manufacturing builds however prettifies code throughout improvement builds.

  • sourcemap set to linked (in improvement mode solely) generates a linked supply map in a .map file so the unique supply file and line is out there in browser developer instruments. You may also set inline to incorporate the supply map contained in the bundled file, each to create each, or exterior to generate a .map file and not using a hyperlink from the bundled JavaScript.

  • outdir defines the bundled file output listing.

Name the context object’s rebuild() technique to run the construct as soon as — usually for a manufacturing construct:

await buildJS.rebuild();
buildJS.dispose(); 

Name the context object’s watch() technique to maintain operating and mechanically re-build when watched information change:

await buildJS.watch();

The context object ensures subsequent builds are processed incrementally and that they reuse work from earlier builds to enhance efficiency.

JavaScript enter and output information

The entry src/js/major.js file imports dom.js and time.js modules from the lib sub-folder. It finds all parts with a category of clock and units their textual content content material to the present time each second:

import * as dom from './lib/dom.js';
import { formatHMS } from './lib/time.js';


const clock = dom.getAll('.clock');

if (clock.size) {

  console.log('initializing clock');

  setInterval(() => {

    clock.forEach(c => c.textContent = formatHMS());

  }, 1000);

}

dom.js exports two capabilities. major.js imports each however solely makes use of getAll():




export operate get(selector, doc = doc) {
  return doc.querySelector(selector);
}


export operate getAll(selector, doc = doc) {
  return Array.from(doc.querySelectorAll(selector));
}

time.js exports two capabilities. major.js imports formatHMS(), however that makes use of the opposite capabilities within the module:




operate timePad(n) {
  return String(n).padStart(2, '0');
}


export operate formatHM(d = new Date()) {
  return timePad(d.getHours()) + ':' + timePad(d.getMinutes());
}


export operate formatHMS(d = new Date()) {
  return formatHM(d) + ':' + timePad(d.getSeconds());
}

The ensuing improvement bundle removes (tree shakes) get() from dom.js however consists of all of the time.js capabilities. A supply map can also be generated:


operate getAll(selector, doc = doc) {
  return Array.from(doc.querySelectorAll(selector));
}


operate timePad(n) {
  return String(n).padStart(2, "0");
}

operate formatHM(d = new Date()) {
  return timePad(d.getHours()) + ":" + timePad(d.getMinutes());
}

operate formatHMS(d = new Date()) {
  return formatHM(d) + ":" + timePad(d.getSeconds());
}


var clock = getAll(".clock");
if (clock.size) {
  console.log("initializing clock");
  setInterval(() => {
    clock.forEach((c) => c.textContent = formatHMS());
  }, 1e3);
}

(Word that esbuild can rewrite let and const to var for correctness and pace.)

The ensuing manufacturing bundle minifies the code to 322 characters:

operate o(t,c=doc){return Array.from(c.querySelectorAll(t))}operate e(t){return String(t).padStart(2,"0")}operate l(t=new Date){return e(t.getHours())+":"+e(t.getMinutes())}operate r(t=new Date){return l(t)+":"+e(t.getSeconds())}var n=o(".clock");n.size&&setInterval(()=>{n.forEach(t=>t.textContent=r())},1e3);

CSS Bundling

CSS bundling within the instance undertaking makes use of an identical context object to JavaScript above:


const buildCSS = await esbuild.context({

  entryPoints: [ './src/css/main.css' ],
  bundle: true,
  goal,
  exterior: ['/images/*'],
  loader: {
    '.png': 'file',
    '.jpg': 'file',
    '.svg': 'dataurl'
  },
  logLevel: productionMode ? 'error' : 'data',
  minify: productionMode,
  sourcemap: !productionMode && 'linked',
  outdir: './construct/css'

});

It defines an exterior possibility as an array of information and paths to exclude from the construct. Within the instance undertaking, information within the src/pictures/ listing are copied to the construct listing so the HTML, CSS, or JavaScript can reference them immediately. If this was not set, esbuild would copy information to the output construct/css/ listing when utilizing them in background-image or related properties.

The loader possibility adjustments how esbuild handles an imported file that’s not referenced as an exterior asset. On this instance:

  • SVG pictures grow to be inlined as information URIs
  • PNG and JPG pictures are copied to the construct/css/ listing and referenced as information

CSS enter and output information

The entry src/css/major.css file imports variables.css and parts.css from the partials sub-folder:


@import './partials/variables.css';
@import './partials/parts.css';

variables.css defines default customized properties:


:root {
  --font-body: sans-serif;
  --color-fore: #fff;
  --color-back: #112;
}

parts.css defines all types. Word:

  • the physique has a background picture loaded from the exterior pictures listing
  • the h1 is nested inside header
  • the h1 has a background SVG which will probably be inlined
  • the goal browsers require no vendor prefixes

*, *::earlier than, ::after {
  box-sizing: border-box;
  font-weight: regular;
  padding: 0;
  margin: 0;
}

physique {
  font-family: var(--font-body);
  coloration: var(--color-fore);
  background: var(--color-back) url(/pictures/internet.png) repeat;
  margin: 1em;
}


header {

  & h1 {
    font-size: 2em;
    padding-left: 1.5em;
    margin: 0.5em 0;
    background: url(../../icons/clock.svg) no-repeat;
  }

}

.clock {
  show: block;
  font-size: 5em;
  text-align: heart;
  font-variant-numeric: tabular-nums;
}

The ensuing improvement bundle expands the nested syntax, inlines the SVG, and generates a supply map:


:root {
  --font-body: sans-serif;
  --color-fore: #fff;
  --color-back: #112;
}


*,
*::earlier than,
::after {
  box-sizing: border-box;
  font-weight: regular;
  padding: 0;
  margin: 0;
}
physique {
  font-family: var(--font-body);
  coloration: var(--color-fore);
  background: var(--color-back) url(/pictures/internet.png) repeat;
  margin: 1em;
}
header h1 {
  font-size: 2em;
  padding-left: 1.5em;
  margin: 0.5em 0;
  background: url('information:picture/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><model>*{fill:none;stroke:%23fff;stroke-width:1.5;stroke-miterlimit:10}</model></defs><circle cx="12" cy="12" r="10.5"></circle><circle cx="12" cy="12" r="0.95"></circle><polyline factors="12 4.36 12 12 16.77 16.77"></polyline></svg>') no-repeat;
}
.clock {
  show: block;
  font-size: 5em;
  text-align: heart;
  font-variant-numeric: tabular-nums;
}



The ensuing manufacturing bundle minifies the code to 764 characters (the SVG is omitted right here):

:root{--font-body: sans-serif;--color-fore: #fff;--color-back: #112}*,*:earlier than,:after{box-sizing:border-box;font-weight:400;padding:0;margin:0}physique{font-family:var(--font-body);coloration:var(--color-fore);background:var(--color-back) url(/pictures/internet.png) repeat;margin:1em}header h1{font-size:2em;padding-left:1.5em;margin:.5em 0;background:url('information:picture/svg+xml,<svg...></svg>') no-repeat}.clock{show:block;font-size:5em;text-align:heart;font-variant-numeric:tabular-nums}

Watching, Rebuilding, and Serving

The rest of the esbuild.config.js script bundles as soon as for manufacturing builds earlier than terminating:

if (productionMode) {

  
  await buildCSS.rebuild();
  buildCSS.dispose();

  await buildJS.rebuild();
  buildJS.dispose();

}

Throughout improvement builds, the script retains operating, watches for file adjustments, and mechanically bundles once more. The buildCSS context launches a improvement internet server with construct/ as the foundation listing:

else {

  
  await buildCSS.watch();
  await buildJS.watch();

  
  await buildCSS.serve({
    servedir: './construct'
  });

}

Begin the event construct with:

npm begin

Then navigate to localhost:8000 to view the web page.

Not like Browsersync, you’ll want so as to add your individual code to improvement pages to dwell reload. When adjustments happen, esbuild sends details about the replace by way of a server-sent occasion. The only possibility is to completely reload the web page when any change happens:

new EventSource('/esbuild').addEventListener('change', () => location.reload());

The instance undertaking makes use of the CSS context object to create the server. That’s as a result of I favor to manually refresh JavaScript adjustments — and since I couldn’t discover a method for esbuild to ship an occasion for each CSS and JS updates! The HTML web page consists of the next script to switch up to date CSS information and not using a full web page refresh (a hot-reload):

<script sort="module">
// esbuild server-sent occasion - dwell reload CSS
new EventSource('/esbuild').addEventListener('change', e => {

  const { added, eliminated, up to date } = JSON.parse(e.information);

  // reload when CSS information are added or eliminated
  if (added.size || eliminated.size) {
    location.reload();
    return;
  }

  // change up to date CSS information
  Array.from(doc.getElementsByTagName('hyperlink')).forEach(hyperlink => {

    const url = new URL(hyperlink.href), path = url.pathname;

    if (up to date.consists of(path) && url.host === location.host) {

      const css = hyperlink.cloneNode();
      css.onload = () => hyperlink.take away();
      css.href = `${ path }?${ +new Date() }`;
      hyperlink.after(css);

    }

  })

});

Word that esbuild doesn’t presently assist JavaScript hot-reloading — not that I’d belief it anyway!

Abstract

With slightly configuration, esbuild may very well be sufficient to deal with all of your undertaking’s improvement and manufacturing construct necessities.

There’s a complete set of plugins do you have to require extra superior performance. Remember these typically embody Sass, PostCSS, or related construct instruments, so that they successfully use esbuild as a activity runner. You may all the time create your individual plugins if you happen to want extra light-weight, customized choices.

I’ve been utilizing esbuild for a 12 months. The pace is astounding in comparison with related bundlers, and new options seem ceaselessly. The one minor draw back is breaking adjustments that incur upkeep.

esbuild doesn’t declare to be a unified, all-in-one construct device, nevertheless it’s most likely nearer to that objective than Rome.

[ad_2]

Supply hyperlink

LEAVE A REPLY

Please enter your comment!
Please enter your name here