Eslint-plugin-import: Slowness when using typescript parser with `project` option

Created on 8 Jul 2019  路  4Comments  路  Source: benmosher/eslint-plugin-import

A few of our users are reporting slowness when using our parser (@typescript-eslint/parser) with this plugin.

Having a quick look at the codebase, I notice that you invoke the parser yourself so you can gather information about other files.

https://github.com/benmosher/eslint-plugin-import/blob/05c3935/utils/parse.js#L9-L39

As part of this, you pass in the parserOptions from the root config. If the project/projects option is set in the parserOptions, it will cause our parser to do a complete typechecking parse cycle, which is often rather slow.

I'm still gathering information from users, we have some caching and such in the parser to attempt to circumvent this, but I think there's a double parse happening because you have a new instance of everything.

My hypothesis is that if we add the following lines at line 25 in the above file, it will make your parser invocation be a single-file, non type-checked parse, which should obviously be fast!

+  delete parserOptions.project
+  delete parserOptions.projects

Once I get some data from users I'll happily raise a PR for this - I just wanted to bring it to your attention first.

typescript

Most helpful comment

https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509298338

That's the result I was looking for!

Copy pasting comment content here for posterity.
This change will make an absolutely huge perf impact!


Some more measurements:

with project set, unmodified parse.js

$ eslint --ext js --ext jsx --ext ts --ext tsx --report-unused-disable-directives src
Rule                              | Time (ms) | Relative
:---------------------------------|----------:|--------:
import/namespace                  | 15160.952 |    88.9%
react/display-name                |   202.946 |     1.2%
import/no-duplicates              |   196.474 |     1.2%
import/no-self-import             |   192.724 |     1.1%
import/export                     |   192.706 |     1.1%
react/no-deprecated               |   158.083 |     0.9%
react/no-direct-mutation-state    |   151.298 |     0.9%
import/no-unresolved              |   109.693 |     0.6%
@typescript-eslint/no-unused-vars |   109.244 |     0.6%
react/no-string-refs              |   107.180 |     0.6%
Done in 32.28s

with project set, modified parse.js:

$ eslint --ext js --ext jsx --ext ts --ext tsx --report-unused-disable-directives src
Rule                              | Time (ms) | Relative
:---------------------------------|----------:|--------:
import/namespace                  |  4184.799 |    76.2%
import/no-duplicates              |   155.571 |     2.8%
import/no-self-import             |   150.559 |     2.7%
react/display-name                |   149.332 |     2.7%
react/no-direct-mutation-state    |   122.384 |     2.2%
react/no-deprecated               |   115.475 |     2.1%
import/no-unresolved              |    89.857 |     1.6%
react/no-string-refs              |    84.541 |     1.5%
@typescript-eslint/no-unused-vars |    82.722 |     1.5%
react/require-render-return       |    79.717 |     1.5%
Done in 12.85s

with project undefined, unmodified parse.js

$ eslint --ext js --ext jsx --ext ts --ext tsx --report-unused-disable-directives src
Rule                              | Time (ms) | Relative
:---------------------------------|----------:|--------:
import/namespace                  |  4281.347 |    75.8%
import/no-duplicates              |   179.059 |     3.2%
import/no-self-import             |   175.444 |     3.1%
react/display-name                |   145.123 |     2.6%
react/no-deprecated               |   123.419 |     2.2%
react/no-direct-mutation-state    |   118.101 |     2.1%
@typescript-eslint/no-unused-vars |    90.583 |     1.6%
react/no-string-refs              |    84.826 |     1.5%
import/no-unresolved              |    82.151 |     1.5%
react/require-render-return       |    80.526 |     1.4%
Done in 9.27s.

with project undefined, modified parse.js

$ eslint --ext js --ext jsx --ext ts --ext tsx --report-unused-disable-directives src
Rule                              | Time (ms) | Relative
:---------------------------------|----------:|--------:
import/namespace                  |  4170.759 |    76.6%
import/no-self-import             |   158.713 |     2.9%
import/no-duplicates              |   157.649 |     2.9%
react/display-name                |   136.888 |     2.5%
react/no-deprecated               |   118.751 |     2.2%
react/no-direct-mutation-state    |   107.730 |     2.0%
@typescript-eslint/no-unused-vars |    87.863 |     1.6%
react/no-string-refs              |    80.661 |     1.5%
react/require-render-return       |    79.535 |     1.5%
import/no-unresolved              |    77.654 |     1.4%
Done in 9.18s.

So yes, while project is set, this seems to be a BIG performance improvement.

All 4 comments

Might deleting those result in different paths tho, potentially causing lint errors?

Passing in project/projects will cause the parser to load your tsconfig file from the specified location and invoke a full typescript parser cycle, which will cause typescript to parse every single file referenced within the tsconfig file up-front.

Without the project, we just act like a normal eslint parser - we load the file you tell us and parse it in isolation.

We don't use the project option to resolve filenames, so there won't be anything that breaks without it.

The project config is completely optional, and only required if the user wants to use rules that require type information. As the import rules don't use type information, it won't impact anything.

Thanks, makes sense to me.

https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509298338

That's the result I was looking for!

Copy pasting comment content here for posterity.
This change will make an absolutely huge perf impact!


Some more measurements:

with project set, unmodified parse.js

$ eslint --ext js --ext jsx --ext ts --ext tsx --report-unused-disable-directives src
Rule                              | Time (ms) | Relative
:---------------------------------|----------:|--------:
import/namespace                  | 15160.952 |    88.9%
react/display-name                |   202.946 |     1.2%
import/no-duplicates              |   196.474 |     1.2%
import/no-self-import             |   192.724 |     1.1%
import/export                     |   192.706 |     1.1%
react/no-deprecated               |   158.083 |     0.9%
react/no-direct-mutation-state    |   151.298 |     0.9%
import/no-unresolved              |   109.693 |     0.6%
@typescript-eslint/no-unused-vars |   109.244 |     0.6%
react/no-string-refs              |   107.180 |     0.6%
Done in 32.28s

with project set, modified parse.js:

$ eslint --ext js --ext jsx --ext ts --ext tsx --report-unused-disable-directives src
Rule                              | Time (ms) | Relative
:---------------------------------|----------:|--------:
import/namespace                  |  4184.799 |    76.2%
import/no-duplicates              |   155.571 |     2.8%
import/no-self-import             |   150.559 |     2.7%
react/display-name                |   149.332 |     2.7%
react/no-direct-mutation-state    |   122.384 |     2.2%
react/no-deprecated               |   115.475 |     2.1%
import/no-unresolved              |    89.857 |     1.6%
react/no-string-refs              |    84.541 |     1.5%
@typescript-eslint/no-unused-vars |    82.722 |     1.5%
react/require-render-return       |    79.717 |     1.5%
Done in 12.85s

with project undefined, unmodified parse.js

$ eslint --ext js --ext jsx --ext ts --ext tsx --report-unused-disable-directives src
Rule                              | Time (ms) | Relative
:---------------------------------|----------:|--------:
import/namespace                  |  4281.347 |    75.8%
import/no-duplicates              |   179.059 |     3.2%
import/no-self-import             |   175.444 |     3.1%
react/display-name                |   145.123 |     2.6%
react/no-deprecated               |   123.419 |     2.2%
react/no-direct-mutation-state    |   118.101 |     2.1%
@typescript-eslint/no-unused-vars |    90.583 |     1.6%
react/no-string-refs              |    84.826 |     1.5%
import/no-unresolved              |    82.151 |     1.5%
react/require-render-return       |    80.526 |     1.4%
Done in 9.27s.

with project undefined, modified parse.js

$ eslint --ext js --ext jsx --ext ts --ext tsx --report-unused-disable-directives src
Rule                              | Time (ms) | Relative
:---------------------------------|----------:|--------:
import/namespace                  |  4170.759 |    76.6%
import/no-self-import             |   158.713 |     2.9%
import/no-duplicates              |   157.649 |     2.9%
react/display-name                |   136.888 |     2.5%
react/no-deprecated               |   118.751 |     2.2%
react/no-direct-mutation-state    |   107.730 |     2.0%
@typescript-eslint/no-unused-vars |    87.863 |     1.6%
react/no-string-refs              |    80.661 |     1.5%
react/require-render-return       |    79.535 |     1.5%
import/no-unresolved              |    77.654 |     1.4%
Done in 9.18s.

So yes, while project is set, this seems to be a BIG performance improvement.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pahan35 picture pahan35  路  3Comments

gaearon picture gaearon  路  3Comments

daltonamitchell picture daltonamitchell  路  3Comments

leonid-bauxy picture leonid-bauxy  路  3Comments

migueloller picture migueloller  路  3Comments