Been doing some HTMX lately using ExpressJS backend.
Unfortunately, my JS files are not automatically minified and bundled for the browser.
Using swc - Speedy Web Compiler, a blazingly fast tool written in rust, we can achieve some minifications and combining using some NodeJS scripts.
npm i -D @swc/cli @swc/core
Add the file .swcrc
{
"minify": true,
"jsc": {
"minify": {
"compress": {
"unused": true
},
"mangle": true
}
}
}
Source files: public/assets
Destination files: dist/public/assets
I could have a nice JSON config file but I just write the config on the script instead.
The script contains a list of JS files and will minify and combine them in groups and save into the dist/public/assets
dir.
File: scripts/bundle-js.js
const swc = require("@swc/core");
const fs = require('fs/promises');
const path = require('node:path');
const ROOT_DIR = path.join(__dirname, '..');
const SOURCE_DIR = path.join(ROOT_DIR, 'public');
const DEST_DIR = path.join(ROOT_DIR, 'dist', 'public');
// Organize js files in bundles
const bundles = [
{
bundle: 'assets/js/vendor-bundle.js',
files: [
'assets/vendors/vendor1/js/vendor1.js',
'assets/vendors/vendor2/js/vendor2.js',
'assets/vendors/vendor3/js/vendor3.js',
]
},
{
bundle: 'assets/js/main-bundle.js',
files: [
'assets/js/site.js',
'assets/js/tables.js',
'assets/js/custom1.js',
'assets/js/custom2.js',
]
},
{
bundle: 'assets/js/other-bundle.js',
files: [
'assets/js/other1.js',
'assets/js/other2.js',
]
},
];
async function minifyBundle(destFile, files) {
// Compile all file contents
let contents = '';
for (const file of files) {
const content = await fs.readFile(file);
contents = contents.concat(content.toString(), "\n");
}
const pathChunks = destFile.split('/');
const filename = pathChunks.pop();
let { code, map } = await swc.transform(contents, {
filename: filename,
sourceMaps: true,
isModule: false,
jsc: {
parser: {
syntax: "ecmascript",
},
transform: {},
},
});
// Save code and map
const destMap = `${destFile}.map`;
await fs.writeFile(destFile, code);
await fs.writeFile(destMap, map);
}
async function run() {
for (const bundle of bundles) {
const destPath = path.join(DEST_DIR, ...bundle.bundle.split('/'));
const files = bundle.files.map(file => {
return path.join(SOURCE_DIR, ...file.split('/'));
});
await minifyBundle(destPath, files);
}
}
run();
node scripts/bundle-js.js
The script will create the following files in the output directory.
dist/public/assets/js/vendor-bundle.js
dist/public/assets/js/main-bundle.js
dist/public/assets/js/other-bundle.js
These files can be referenced in the template like below:
<script src="/assets/js/vendor-bundle.js"></script>
<script src="/assets/js/main-bundle.js"></script>
<script src="/assets/js/other-bundle.js"></script>
That's it!