eslint-plugin-import: Eslint is slow when used with webpack

I am wondering if it is possible to boost up performance when using eslint-plugin-import with webpack.

In my project:

  • I have 4500 modules (find node_modules/ -type f -name "package.json" | wc -l)
  • I have 500 files that I am eslinting
  • I use 8 aliases in webpack config resolve.alias
  • I use 8 loader rules in webpack config module.rules.

Eslinting lasts ~30s, and I can see that import/no-unresolved takes most of the time:

Rule                              | Time (ms) | Relative
:---------------------------------|----------:|--------:
import/no-unresolved              |  4983.197 |    28.2%
react/prop-types                  |  1694.653 |     9.6%
react/sort-comp                   |  1321.233 |     7.5%
react/no-multi-comp               |  1264.966 |     7.2%
react/require-render-return       |  1221.941 |     6.9%
import/no-named-as-default        |   665.776 |     3.8%
react/no-deprecated               |   524.655 |     3.0%
indent                            |   261.081 |     1.5%
import/no-named-as-default-member |   248.505 |     1.4%
react/jsx-curly-spacing           |   198.665 |     1.1%

Any suggestions on improvement?

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Comments: 29 (14 by maintainers)

Commits related to this issue

Most upvoted comments

I found the problem! I am using webpack.config.babel.js and the problem is that it reads and transpiles the entire webpack config each time it resolves the rule. And there are lots of calls, in my case over 150 of them.

I suggest the config is read once and then cached. I will make a pr.

I upgraded my webpack config to be a factory (exporting a function instead of a simple object like CRA does) and my ESLint timing went through the roof.

So instead of setting the webpack config factory directly to import/resolver, I’m using a file where I import the factory and export the result.

// webpack.config.eslint.js
const configFactory = require('./webpack.config');

module.exports = configFactory('development');

Before

Rule Time (ms) Relative
import/no-unresolved 82850.597 54.6%
import/no-named-as-default-member 47993.517 31.6%
indent 3229.228 2.1%
import/named 3056.267 2.0%
react/void-dom-elements-no-children 1000.009 0.7%
react/no-deprecated 858.790 0.6%
react/jsx-no-bind 784.099 0.5%
react/no-direct-mutation-state 731.675 0.5%
react/destructuring-assignment 593.046 0.4%
react/sort-comp 483.998 0.3%

After

Rule Time (ms) Relative
import/no-unresolved 10543.458 32.5%
import/no-named-as-default-member 3781.838 11.7%
indent 2865.881 8.8%
react/void-dom-elements-no-children 817.273 2.5%
react/no-deprecated 810.339 2.5%
react/jsx-no-bind 759.052 2.3%
react/no-direct-mutation-state 696.619 2.1%
react/destructuring-assignment 589.438 1.8%
import/named 446.033 1.4%
react/no-typos 416.777 1.3%

@gfx the first rule will always be slow because it’s building up an import graph; see #1408 for typescript-specific slowness.

@mieszko4 how did you get the table with rule execution times?

@mikhailChibel prefix the call with TIMING=1

Could it be cached permanently but with an md5 hash of the file’s contents, so any change busts the cache?

After some further investigation I concluded that caching it would not speed it up (too much) since require() that is used caches the call already. It is simply slow because of babel the first time it is called. Each subsequent time it is quick as it uses the cached version already.

I’ve created a pr anyway as it moves out some common processing.