no-restricted-paths is slow when many imports/excepts
The rule appears to be building and validating the excepts for each zone every time an import is encountered. This makes it very slow when you have many files/imports, zones, and/or excepts.
I narrowed it down to this validation part here:
const hasValidExceptionPaths = absoluteExceptionPaths
.every((absoluteExceptionPath) => isValidExceptionPath(absoluteFrom, absoluteExceptionPath))
https://github.com/benmosher/eslint-plugin-import/blob/master/src/rules/no-restricted-paths.js#L88
Could it instead validate the exception paths once when the rule is initialised?
How many exception paths do you have? O.o
The line you referenced happens when checkForRestrictedImportPath is called; and it's called when the rule visits nodes because it needs that node information (that's what absoluteFrom is). I don't think it can be moved at all.
Quite a few (but we're working on that), but what we have more of is files and imports - it's a large codebase, and I'm trying to use this rule and/or no-restricted-imports to help improve and enforce separation.
I saw that absoluteFrom is derived from the basePath and zone.from, I don't see anything node-specific. The error it would produce is Restricted path exceptions must be descendants of the configured from path for that zone., which isn't node-specific. I might have missed something though.
The importPath used in const absoluteImportPath = resolve(importPath, context) is the specifier that shows up in the code; it's not known in advance.
Yes, I don't see that it's used for this validation though. Looks like a validation of the rule configuration, that the excepts does not backtrack to a parent of from.
Thanks for the quick responses, and apologies if I'm missing it.
importPath comes from the node, it's used in https://github.com/benmosher/eslint-plugin-import/blob/master/src/rules/no-restricted-paths.js#L89 to validate the exception paths.
Just a thought - if we supported globs/stars in the exceptions, would that let you consolidate your list significantly?
It does, but I don't see it (importPath) or absoluteImportPath used on L88-89 to validate the exception paths themselves:
const hasValidExceptionPaths = absoluteExceptionPaths
.every((absoluteExceptionPath) => isValidExceptionPath(absoluteFrom, absoluteExceptionPath))
It's only used later on L96-97 to check if an offending import is excepted:
const pathIsExcepted = absoluteExceptionPaths
.some((absoluteExceptionPath) => containsPath(absoluteImportPath, absoluteExceptionPath))
Looking at it from another angle, it's the validation for this part of the config, which doesn't sound node-specific. I'll take your word for it though if I'm still not making sense.
An optional except may be defined for a zone, allowing exception paths that would otherwise violate the related from. Note that except is relative to from and cannot backtrack to a parent directory.
Globs would help consolidate the list, yes (I see there's a PR #1708). Though I've gone with no-restricted-imports from ESLint core now.
aha, i do see what you mean.
it would split up the validation a bit, but a PR that hoists whatever can be hoisted out of the visitor into create sounds perfect.
@ljharb I prepared a PR, could you please have a look when you have a chance? On the project I work on, it made the check an order of magnitude faster.
Absolutely! Note that tests are currently broken on master (#1986) so i might not be able to land it just yet.
I'm experiencing slow performance for this rule as well. You can see the configuration in:
https://github.com/pixiebrix/pixiebrix-extension/blob/2604923b0f29c63bae5e1fbd690db4e33bd4b23c/.eslintrc.js#L15-L16 (note that dropping the glob only maybe saves 1 second)
In short, I don't want files in /A to import from /B and vice versa, for 5 such folders, but this rule appears to be taking a long time just for that:
| Rule | Time (ms) | Relative |
|---|---|---|
| import/no-restricted-paths | 30474.413 | 50.6% |
| @typescript-eslint/no-confusing-void-expression | 5398.132 | 9.0% |
| @typescript-eslint/no-floating-promises | 3957.692 | 6.6% |
| @typescript-eslint/promise-function-async | 2753.125 | 4.6% |
| @typescript-eslint/no-redeclare | 829.458 | 1.4% |
| react/display-name | 825.262 | 1.4% |
| react/no-direct-mutation-state | 723.626 | 1.2% |
| unicorn/prevent-abbreviations | 690.028 | 1.1% |
| @typescript-eslint/no-misused-promises | 662.684 | 1.1% |
| unicorn/template-indent | 544.756 | 0.9% |
I think it might not actually be the specific rule though:
See #2340 (comment); whichever is the first rule to build the ExportMap will be the slowest.
Indeed, if you maximally enable the useful import rules, you may find another rule is the 30s one.
I disabled the rule, nothing else came close to it, so it's definitely one of the slowest ones. The other one is import/no-cycle, which by itself takes twice the time, so it's disabled in regular runs.
See comparison in:
- https://github.com/pixiebrix/pixiebrix-extension/pull/7593