Utilizing Bun.js as a bundler – SOS
Bun.js is a brand new (as of 2023) JavaScript runtime that’s nonetheless very a lot in growth, with it’s major focus being on excessive velocity. I’ve been following it for some time however till at present haven’t had excuse to make use of it.
The creator, Jarred Sumner, introduced on Twitter at present that they’ve shipped a beta model of a brand new code bundler for Bun, exhibiting some loopy velocity will increase over different bundlers. This piqued my curiosity, as I take advantage of a mix of Webpack, Browserify and Uglify on my aspect initiatives, on this case my pill PWA that I constructed for my youngsters kidzfun.art, which work however are actually gradual.
My present workflow may end up in a 5 – 7 second await all my JS recordsdata to rebuild once I save a file, and I believed that Bun might assist with this. It seems I used to be proper! …. with caveats.
You may see the docs for Bun.construct()
at https://bun.sh/docs/cli/build , and they’re effectively written and fairly complete.
My necessities have been to
- Construct a number of recordsdata shortly, every of which imports a number of different third get together recordsdata from
node_modules
. - Construct minified and non-minified recordsdata
- The ensuing file could be included immediately in a browser utilizing a
<script>
tag.
Getting began
I began off by working default construct code (for Bun v0.6.1)
const myFiles = [...];
await Bun.construct({
entrypoints: [myFiles],
outdir: './construct'
});
by including a script to my bundle.json
file
"build-browser": "bun scripts/build-browser.js"
and this labored simply high-quality. Extra importantly, it was crazily quick. As an alternative of 5 seconds it now appeared to complete because the Enter key was nonetheless touring again upwards from executing the command. Good!
Minification
Minification looks simple in the docs, however sadly it’s the place the beta nature of Bun reveals up. Operating the code above with minification
const myFiles = [...];
await Bun.construct({
entrypoints: [myFiles],
outdir: './construct',
minify: true
});
ends in an error being thrown that shuts down the method if there may be multiple entry level file.
Looking the online didn’t flip up something, however the resolution is to solely move a single entry level file path to Bun.construct()
in case you are minifying the code. Throw that in a for
loop to get by way of all of the recordsdata and it runs simply high-quality!
A second subject with the default minification is that it broke my app in unusual ways in which I couldn’t observe down – I’m guessing that it’s rewriting the code in a roundabout way that’s not totally secure but. I solved it by turning off the syntax
minification choice
const myFiles = [...];
await Bun.construct({
entrypoints: [myFiles],
outdir: './construct',
minify:{
whitespace: true,
identifiers: true,
syntax: false // Setting this to false fixes the difficulty
}
});
Eradicating Exports
Bun inserts code that appears like this on the backside of the constructed file, on this case from a file referred to as account.ts
var account_default = {};
export {
account_default as default
};
In the event you load this in a browser <script>
tag it would throw an error. I couldn’t discover a technique to inform Bun how you can not output this, so I needed to write a comparatively easy operate to detect this on the finish of every output file and take away it.
Watch points
I’ve some code that makes use of the node-watch module to mechanically re-run the construct when a file adjustments. Below the hood this makes use of the fs.watch
operate, which it seems Bun doesn’t but help. Here’s the Github issue monitoring it. I attempted to make use of the native Bun watch performance, however this executed the script code which isn’t what I’m searching for.
I got here up with a hacky resolution that works pretty effectively, the place I take advantage of the RunOnSave extension for VS Code to execute
contact ./.last_modified_timestamp
each time I save a file. Then in my construct script I take advantage of setInterval
to examine the final modified time of this file and re-run the construct if it has modified. Hacky however it works. Hopefully Bun will implement fs.watch
quickly and I can throw out this code.
operate construct() {
...
}
const timestampFilePath = `${rootDir}/.last_modified_timestamp`;
if (fs.existsSync(timestampFilePath)) {
let lastModifiedRootFolder = 0;
setInterval(() => {
const stat = fs.statSync(timestampFilePath);
if (stat.mtime.getTime() !== lastModifiedRootFolder) {
lastModifiedRootFolder = stat.mtime.getTime();
construct();
}
}, 500);
}
Vercel construct failures
As soon as all the things was working simply high-quality regionally on my Mac, I pushed the department to Github so Vercel would construct it (it’s a NextJS software). This threw up a brand new subject. My construct script makes use of the native Node exec()
operate to maneuver and replica recordsdata. This works simply high-quality on my Mac, however when working the construct within the cloud setting all these calls would fail. There’s one thing unfinished with Bun’s implementation of the child_process
module that breaks when run within the Vercel construct setting.
My resolution to this was to easily change all these execSync
calls to make use of the Node fs
capabilities, e.g.
import fs from 'fs';
....
fs.copyFileSync(srcPath, destPath);
fs.renameSync(path, `${rootDir}/public/${fileName}`);
Epilogue
After a number of hours of labor, studying up on Bun and dealing my means by way of these points, I now have a a lot less complicated construct system that runs within the blink of a watch. My Vercel construct occasions have decreased from 2 minutes to only 50 seconds (that’s all React stuff & fetching node_modules). My watch script runs in a number of milliseconds as an alternative of 5 or extra seconds, My code is far less complicated and I’ve eliminated Webpack, Browserify and Uglify from my initiatives.
Thanks a lot to the Bun group for an amazing venture. At the same time as early as it’s at time of writing (mid 2023), it’s extremely helpful, and as they work by way of all of the kinks it would solely get extra so. I look ahead to utilizing it extra within the months and years to return!
Revealed by Shane O’Sullivan
I’m a software program engineer and supervisor from Eire. I spent 7 years working in Eire from 2003 – 2010, then ten years in Silicon Valley from 2010 to 2020.
In California I spent about 6.5 years at Fb Engineering, the final three of which I used to be an engineering supervisor within the Advertisements organisation specializing in buyer dealing with merchandise for creating and managing adverts.
At Stripe I constructed the Developer Productiveness organisation, with groups that have been answerable for using the Ruby language, testing infrastructure, documentation, developer tooling (e.g. IDE integrations) and extra.
At Promise, I used to be Head of Engineering from 2018 – 2020, answerable for constructing the primary few iterations of our merchandise, hiring for all product roles, assembly with shoppers and traders, and the rest wanted to get a tiny startup bootstrapped and profitable.
Now I’m again in Eire, engaged on my subsequent firm. Coming quickly (as of early 2021!).
This weblog incorporates my numerous musings on all issues technical/attention-grabbing on the interweb and past.
View all posts by Shane O’Sullivan
Revealed