Oleg Mikhailov
Oleg Mikhailov

Reputation: 6081

Remove unused javascript code based on coverage report

There is a big javascript library (~ 40 000 lines of code) and an application which uses less than 50% of the library's code.

There is a test which utilizes all the needed functionality from the library and can produce coverage report.

How to remove programmatically every unused line of code relying on the test?

Note: coverage report contains indices of lines which were executed but it is not accurate: closing braces are ignored, lines with method names are marked as executed even if the method body was not etc.

Upvotes: 27

Views: 17548

Answers (7)

Bugakov  Mikhail
Bugakov Mikhail

Reputation: 1

You can process the report by leaving only the lines specified in the "ranges" in the "text"

const coverageReport = [
  {
    "url": "http://127.0.0.1:8080/index.js",
    "ranges": [
      {
        "start": 96,
        "end": 197
      },
      {
        "start": 294,
        "end": 434
      },
      {
        "start": 469,
        "end": 482
      },
      {
        "start": 511,
        "end": 574
      },
      {
        "start": 598,
        "end": 606
      },
      {
        "start": 630,
        "end": 655
      }
    ],
    "text": "function unused() {\n  console.log('unused1');console.log('unused2');\n  console.log('unused3');\n}\n\nfunction used() {\n  console.log('used1');console.log('used2');\n  console.log('used3');\n}\n\nused();\n\nfunction unused2() {\n  console.log('unused1');console.log('unused2');\n  console.log('unused3');\n}\n\nfunction used2() {\n  console.log('used1');console.log('used2');\n  console.log('used3');\n}\n\nused2();\n\nif (true) {\n  console.log('used4');\n} else {\n  console.log('unused4');\n}\n\nif (false) {\n  console.log('unused5');\n} else {\n  console.log('used5');\n}\n\ntrue ? console.log('used6') : console.log('unused6');\nfalse ? console.log('unused7') : console.log('used7');\n"
  }
];

const deleteUnusedCode = (fileObjs) => {
  const outFileObjs = [];
  for (const fileObj of fileObjs) {
    const {url, ranges, text} = fileObj;
    let outText = '';
    for (const {start, end} of ranges) {
      outText += text.slice(start, end);
    }
    outFileObjs.push({url, text: outText})
  }
  return outFileObjs
}

const showFileObjs = (fileObjs) => {
  for (const {url, text} of fileObjs) {
    console.log(url);
    console.log(text);
    console.log();
  }
}

console.log('before:');
showFileObjs(coverageReport);

console.log('after:');
showFileObjs(deleteUnusedCode(coverageReport));

Yes, after this we have only the js code used, but unfortunately it is not valid. You can manually fix the errors and then everything will be ok.

I wrote a project in which I showed the use of a coverage report to generate files based on it: https://github.com/fosemberg/coverage-report-handler

Upvotes: 0

Moji Izadmehr
Moji Izadmehr

Reputation: 2524

In order to automatically remove unused code from bundle, we have:

  1. Tree shaking
  2. Ugliy and Minification tools such as uglifyjs, Terser
  3. Google closure compiler (best results)

However, in order to find the unused assets, to remove manually, you can use deadfile library: https://m-izadmehr.github.io/deadfile/

It can simply find unused files, in any JS project.

Without any config, it supports ES6, JSX, and Vue files: enter image description here

Upvotes: 2

Swati
Swati

Reputation: 575

There are two techniques to eliminate dead code and it is possible using javascript build systems- webpack.

  1. Dead code elimination (DCE) : compiler optimisation- It excludes which is not needed in the program.

  2. Tree Shaking It works in reverse direction, includes only what is actually needed in the program.

Click here for detailed configuration.

Upvotes: 4

user3297291
user3297291

Reputation: 23372

Closure Compiler provides some quite advanced unused code clean up features. Some examples:

Removing a dead code block

function hello(name) {
  alert('Hello, ' + name);
}

function hi(name) {
    alert('Hi, ' + name);
}

hello('New user 1');
hello('New user 2');

Compiles to:

alert("Hello, New user 1");
alert("Hello, New user 2");

completely stripping away the hi function and inlining hello. (live demo)

Moving to a more complicated case

As the code gets more complicated, it finds new ways to optimize. For example:

let greeted = 0;

function hello(name) {
  greeted += 1;
  alert('Hello, ' + name);
}

function hi(name) {
  greeted += 1;
  alert('Hi, ' + name);
}

hello('New user ' + greeted);
hello('New user ' + greeted);

Becomes:

var a = 0;
function b() {
  var c = "New user " + a;
  a += 1;
  alert("Hello, " + c);
}
b();
b();

(live demo)

Make sure you turn on the ADVANCED_OPTIMIZATIONS compilation level to enable dead code removal.

Upvotes: 5

Benjamin RD
Benjamin RD

Reputation: 12034

You can try to use:

npm install -g fixmyjs
fixmyjs <filename or folder>

This is the fixmyjs project

it is a great tool for cleanup, it appears to lack compatibility with some versions of ecmascript

Upvotes: 7

nisar
nisar

Reputation: 1065

You can use some java script automation tools to remove your unwanted codes, first you need to install either one of the below js liblary(node must).
tree-shaking

UglifyJS2

visit any one sites. or you can remove using chrome dev tools(but be careful, test many cases before you identify unwanted codes, because this procees will identify the unwanted code means, which codes did not executed by you way of process or your test cases)

Remove Ugly JS code by chrome dev

This worked fine for my case(but my js code less than 10k lines)

Upvotes: 2

Harri
Harri

Reputation: 364

This approach will not work I fear. Not that easy and not with the data you have available at least.

  1. The coverage report for your test which utilizes all the needed functionality is using which coverage metric? Does it excercise all values, conditions and possible combinations thereof? If not, you may miss out on usage of some part of the code.

  2. If your coverage report is not accurate you cannot rely on it for removal actions. Although braces

Given a sufficiently good test suite you can use the code coverage reports for hints however. Remove code that is reported as unused, re-run your tests and check whether they still pass. Repeat until nore more code snippets can be removed.

Upvotes: 1

Related Questions