VS Code - Why you should always bundle a VS Code extension, and prefer esbuild as the bundler

VS Code - Why you should always bundle a VS Code extension, and prefer esbuild as the bundler

You should always bundle your VS Code extension. Bundled extensions load faster, and if you want your extension to run in VS Code web environments like github.dev and vscode.dev – you must bundle your extension.

Why esbuild?

The most popular bundlers probably are Rollup, Parcel, webpack, and esbuild. An advantage of esbuild is that it is performance-oriented and is written in Go. It is much faster than the aforementioned bundlers, somewhere between 10 and 100 times faster according to esbuild.

I was using Webpack and it did what I needed to do, however it was quite slow. This is not a big deal on my smaller projects, but you feel it as a project grows. Webpack has improved a lot over time but I found that is overly technical to use. I was always wary of upgrading because I was afraid of breaking the configuration!

Webpack requires a separate package to run on the command-line (webpack-cli) depending on what you want to use it for and what version you were using. Until version 4, it required configuration through a config file. Something like this for a VS Code extension:

// webpack.config.js
const path = require("path");

const config = {
target: "node",
entry: "./src/extension.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "extension.js",
libraryTarget: "commonjs2",
devtoolModuleFilenameTemplate: "../[resource-path]",
},
devtool: "source-map",
externals: {
vscode: "commonjs vscode",
},
};
module.exports = config;

The great thing about esbuild is that it is a throughbred commandline tool. You do not need to install another package. It is easy to configure just with npm scripts. I was surprised how easy it was setup. I didn’t feel like I needed to learn it like I did with webpack. It just could do what I needed with a few settings.

Configure esbuild

First, add esbuild as a dev dependecy:

npm i -D esbuild

Second, update the scripts in your package.json to configure esbuild for different modes. It depends on whether you have written your extension in JavaScript or TypeScript.

Configure the npm scripts for a JavaScript extension

The following is for an extension written in JavaScript and bundles the source to dist/extension.js:

{
"name": "the-amazing-bundled-javascript-extension",
"displayName": "The Amazing Bundled JavaScript Extension",
"scripts": {
"esbuild-base": "esbuild ./src/extension.js --bundle --outfile=dist/extension.js --external:vscode --format=cjs --platform=node",
"dev": "npm run esbuild-base -- --sourcemap --watch",
"vscode:prepublish": "npm run esbuild-base -- --minify",
"build": "vsce package"
}
}

The dev script is going to watch for changes as you work on your extension. It also produces a sourcemap which will make your debugging sessions relate breakpoints and errors to the original source files.

You can see a working example of using esbuild to bundle an extension written in JavaScript, you can check my extension File Bunny.

Configure the npm scripts for a TypeScript extension

For an extension written in TypeScript, it is really similar! The only difference is that your main file has the .ts suffix, esbuild does the transpilation for you without further ado. Here is an example package.json:

{
"name": "the-amazing-bundled-typescript-extension",
"displayName": "The Amazing Bundled TypeScript Extension",
"scripts": {
"esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=dist/extension.js --external:vscode --format=cjs --platform=node",
"dev": "npm run esbuild-base -- --sourcemap --watch",
"vscode:prepublish": "npm run esbuild-base -- --minify",
"build": "vsce package"
}
}

Dont’ forget to update your .vscodeignore to minimise the extension size

If this is your first time bundling the extension, don’t forget to update your .vscodeignore file to exclude unncessary files. The source files can now be excluded.This will reduce the size of your extension.

This is a good starting point for a .vscodeignore file:

.vscode
node_modules
src
test
*.md
!README.md
!CHANGELOG.md
!LICENSE.md

You will always want to include:

  1. You bundled extension file.
  2. README.md.
  3. The changelog.
  4. The license.
  5. The logo image.

The rest will depend on your project.

Tagged