Skip to content

Instantly share code, notes, and snippets.

@lysender
Last active August 19, 2023 11:50
Show Gist options
  • Save lysender/0154a4ef4a27df8bbc132aa72e29fd4d to your computer and use it in GitHub Desktop.
Save lysender/0154a4ef4a27df8bbc132aa72e29fd4d to your computer and use it in GitHub Desktop.
NodeJS - minify and bundle js files for browser using swc

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.

Install swc in your project

npm i -D @swc/cli @swc/core

Configure

Add the file .swcrc

{
  "minify": true,
  "jsc": {
    "minify": {
      "compress": {
        "unused": true
      },
      "mangle": true
    }
  }
}

Directory structure

Source files: public/assets

Destination files: dist/public/assets

Custom script

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();

Run

node scripts/bundle-js.js

Output

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

Refencing the bundled files

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment