Remove unused CSS / JavaScript code from your project

In this article, we'll review one of the latest additions to the Chrome Developer Tools as well as an interesting Node.js library that helps developers to work with DevTools in a programmatic way.

Code coverage

Usually, when thinking about code coverage, we think about a percentage value indicating how automated tests cover much of the codebase. And this is absolutely valid - under most circumstances, this is exactly the right definition of code coverage.

However, in the context of Chrome's DevTool, this term refers to something different. The "coverage" tab - found in Chrome 59+ - helps us to reveal CSS and JavaScript code that is not being used by our application, making sure that we can shave off unused bytes and aim for a better performing website.

Enabling the coverage tab

A prerequisite to having the coverage tab visible at the first place is to have Chrome 59+ installed.

Enabling this tab is possible via the "Command Menu" in Chrome - which can be opened by pressing Cmd+Shift+P on a Mac or Ctrl+Shift+P on Windows and Linux. Once we have the Command Menu open, we can type in Show Coverage which will enable this feature for us.

The coverage tab can be found by clicking the three horizontal dots under Sources, and then selecting More tools > Coverage. The result we get from this coverage check is immensely useful: it shows us how much of the CSS and JavaScript code is being used by our current application, as seen in the screenshot below:

Furthermore, it can also highlight the lines that are present in our codebase albeit not being used:

It's important to note that some CSS rules might be in effect only after user actions such as hover or click. Bear this in mind - the same applies for JavaScript as well.

At the time of writing this article, there's no built-in feature in Chrome that would export only the code being used. But don't worry, there's a solution for that.

Puppeteer

Puppeteer is a Node.js based API library that allows us to control Chrome / Chromium over the DevTools protocol. Think about it this way: whatever we can do with the DevTools in the browser, we can now do it in an automated, programmatic fashion via Puppeteer. This includes:

  • Creating a screenshot of a page or exporting it to PDF
  • Automate UI testing
  • Generate pre-rendered content (Server-Side Rendering)
  • Test Chrome extensions
  • And a lot more

Does this mean, that we can run the code coverage check as well? Absolutely, yes!

Running coverage check via Puppeteer

Let's go ahead and install Puppeteer first via npm: npm i puppeteer, and once that's done we can create a small JavaScript app to run the coverage check:

const puppeteer = require('puppeteer');
const util = require('util');

(async () => {
 const browser = await puppeteer.launch();
 const page = await browser.newPage();
 await page.coverage.startCSSCoverage();
 await page.goto('http://localhost:8080');
 const css_coverage = await page.coverage.stopCSSCoverage();
 console.log(util.inspect(css_coverage, { showHidden: false, depth: null }));
 await browser.close();
})();

The code above returns the following information:

[ { url: 'http://localhost:8080/site.css',
    ranges: [ { start: 0, end: 28 }, { start: 55, end: 86 } ],
    text:
     '.important {\n  color: red;\n}\n.test {\n  color: green;\n}\n.more {\n  visibility: hidden;\n}' } ]

The most important thing to note here is the ranges property because it helps us determine which lines are used by our application. Basically, bytes 0 to 28 and 55 to 86 are used by our application, which means that bytes between 29-54 are unused and therefore should be deleted from the CSS file in question.

Using a combination of Puppeteer and the fs built-in Node.js module, we can write a small script that removes unused code and creates a new file with only the code that our application is using.

Let's update our code and add some logic to it that iterates through the actual document (by using the information found in the text property), and it saves the result to a new file that only has the CSS actually being used::

let final_css_bytes = '';
let total_bytes = 0;
let used_bytes = 0;

for (const entry of css_coverage) {
  total_bytes += entry.text.length;
  for (const range of entry.ranges) {
    used_bytes += range.end - range.start - 1;
    final_css_bytes += entry.text.slice(range.start, range.end) + '\n';
  }
}

fs.writeFile('./final_css.css', final_css_bytes, error => {
  if (error) {
    console.log('Error creating file:', error);
  } else {
    console.log('File saved');
  }
});

At this point it makes sense to run all our tests again. Removing code certainly has its benefits as well as some side-effects. Make sure that your site is still functional before pushing changes to production.

We can do a similar thing for JavaScript files as well. Furthermore, we can now add this step to our build process where we could do something like remove comments, remove unused code via Puppeteer and finally minify CSS and/or JavaScript.

Conclusion

In this article, we saw how to use the code coverage check feature in Google Chrome's DevTools as well as learnt how to execute coverage checks in an automated fashion via Puppeteer. When it comes to production applications, always make sure that only the absolutely necessary CSS and JavaScript files and code are served in order to help the performance of the application.