eslint-plugin-import: `no-cycle` hangs since v2.23.0

With both v2.23.0 and v2.23.1, running eslint on the entire project hangs. Also, it will make vscode eslint plugin hang as well when saving a file (with fix on save enabled).

.eslintrc:

{
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "project": "tsconfig.json"
  },
  "rules": {
    "import/no-cycle": ["error", { "ignoreExternal": true, "maxDepth": 7 }]
  },
  "settings": {
    "import/parsers": {
      "@typescript-eslint/parser": [".ts", ".tsx"]
    },
    "import/resolver": {
      "typescript": {
        "project": "tsconfig.json"
      }
    }
  }
}

tsconfig.json:

{
    "compilerOptions": {
        "paths": {
            "@common": ["src/app/common"],
        }
    }
}

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 4
  • Comments: 27 (11 by maintainers)

Most upvoted comments

Haven’t checked 2.23.4 yet, but the issue was still present in 2.23.3.

Later edit: 2.23.4 is also affected.

I’m wondering if there’s an excessive number of repeated calls, such that there’d be a performance win from memoizing that result.

Thanks for the tips @walkerburgin and @ljharb - This matches exactly what I found doing some digging in a medium sized repo of ~2k modules. Saw total lint times cut from 5m to 2m 15s by. Opened #2612

@Rogdham hm, interesting. That makes sense, and seems like a good change, but it might break editor/eslintd integrations that sometimes do long-running eslint runs.

@ljharb madge is using dependency-tree, which does its own memoization (the visited property): https://github.com/pahen/madge/blob/aa49a0a94fd966be25141c92c75f97d90ad73dc6/lib/tree.js#L109

I’m not sure things like captureJsDoc would be possible with it. I’m also not sure what would be the effect in case of different configurations (typescript/babel/swc/esbuild/webpack etc.). Maybe the option you suggested initially would be better, to improve the memoization in ExportMap 🤷🏻‍♂️

I suspect the filename is required, because of the potential for repeated dependencies, but that does suggest that memoizing based on both would provide some improvement.

As mentioned above, both the eslint cli and the vscode extension hang. What I noticed:

  • eslint cli hangs when run for the entire project
  • eslint cli doesn’t hang when no-cycle is disabled
  • vscode doesn’t hang when a single file is opened (might be a bit slower than before though, not sure if it felt faster before because of caching)
  • vscode hangs when a file is saved and fix on save is enabled

For what it’s worth, applying some simple caching (patch below) top of v2.25.4 makes quite a difference. There are probably some unexpected side-effects though.

Timing measured with TIMING=1 yarn eslint ... for the no-cycle rule on a private repo:

  • without patch: did not finish in 30min
  • with patch: 15s

Note that this patch already improved things a lot on v2.22.1 (57s → 10s).

diff --git a/src/rules/no-cycle.js b/src/rules/no-cycle.js
index e61c3be..d338a79 100644
--- a/src/rules/no-cycle.js
+++ b/src/rules/no-cycle.js
@@ -11,2 +11,5 @@ import docsUrl from '../docsUrl';
 
+const traversed = new Set();
 // todo: cache cycles / deep relationships for faster repeat evaluation
@@ -77,3 +80,2 @@ module.exports = {
       const untraversed = [{ mget: () => imported, route:[] }];
-      const traversed = new Set();
       function detectCycle({ mget, route }) {

We recently tried to upgrade a large codebase from v2.21.2 to 2.24.2 and saw our lint time jump from ~30 seconds to ~14 minutes.

After profiling, it looks like almost all of the time is being spent here:

image

And if I change our config from this:

"import/no-cycle": [
    "error",
    {
        ignoreExternal: true,
        maxDepth: 2,
    },
],

to this:

"import/no-cycle": [
    "error",
    {
        ignoreExternal: false,
        maxDepth: 2,
    },
],

Short circuiting the call to resolve() here, the performance improves significantly.

We similarly can’t publish the codebase, but I’d be happy to test out any proposed changes and report back.