Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save lysender/ad3ab41f1770653e256b2f5a0e6b351a to your computer and use it in GitHub Desktop.
Save lysender/ad3ab41f1770653e256b2f5a0e6b351a to your computer and use it in GitHub Desktop.
NodeJS - minify and bundle css files using lightningcss

Been doing some HTMX lately using ExpressJS backend.

Unfortunately, my CSS files are not automatically minified and bundled for the browser.

Using lightningcss, a blazingly fast tool written in rust, we can achieve some minifications and combining using some NodeJS scripts.

Install lightningcss in your project

npm i -D lightningcss lightningcss-cli

Note: You may not need the cli package but it can be useful when testing broken css syntax.

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 CSS files and will minify and combine them in groups and save into the dist/public/assets dir.

Note: CSS may reference resources using relative path so it is tricky to bundle them together.

You may bundle together related CSS files or switch to absolute path.

File: scripts/bundle-css.js

const fs = require('fs/promises');
const lightningcss = require('lightningcss');
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 css files in bundles
const bundles = [
  {
    bundle: 'assets/vendors/vendor1/vendor1-bundle.css',
    files: [
      'assets/vendors/vendor1/css/file1.css',
      'assets/vendors/vendor1/css/file2.css',
      'assets/vendors/vendor1/css/file3.css',
    ]
  },
  {
    bundle: 'assets/css/main-bundle.css',
    files: [
      'assets/css/style.css',
      'assets/css/tables.css',
      'assets/css/custom.css',
    ]
  },
  {
    bundle: 'assets/css/other-bundle.css',
    files: [
      'assets/css/other1.css',
      'assets/css/other2.css',
      'assets/css/other3.css',
    ]
  },
];

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 } = lightningcss.transform({
    filename: filename,
    code: Buffer.from(contents),
    minify: true,
    sourceMap: true
  });

  // 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-css.js

Output

The script will create the following files in the output directory.

  • dist/public/assets/vendors/vendor1/vendor1-bundle.css
  • dist/public/assets/css/main-bundle.css
  • dist/public/assets/css/other-bundle.css

Refencing the bundled files

These files can be referenced in the template like below:

<link rel="stylesheet" href="/assets/vendors/vendor1/vendor1-bundle.css" />
<link rel="stylesheet" href="/assets/css/main-bundle.css" />
<link rel="stylesheet" href="/assets/css/other-bundle.css" />

That's it!

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