Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slow createProjectProgram/createWatchProgram #2398

Closed
vilicvane opened this issue Aug 17, 2020 · 7 comments · Fixed by #2330 or #2418
Closed

Slow createProjectProgram/createWatchProgram #2398

vilicvane opened this issue Aug 17, 2020 · 7 comments · Fixed by #2330 or #2418
Assignees
Labels
breaking change This change will require a new major version to be released bug Something isn't working package: typescript-estree Issues related to @typescript-eslint/typescript-estree
Milestone

Comments

@vilicvane
Copy link
Contributor

vilicvane commented Aug 17, 2020

Repro

{
  "parserOptions": {
    "project": "./*/**/tsconfig.json"
  }
}

I use ./*/**/tsconfig.json to ignore the root tsconfig.json so that it compares with the later configuration. Using ./**/tsconfig.json will not change the performance.

The @mufan/code-* packages below involves symlinks.

Please let me know if this is not obvious enough so I can prepare an actual repro.

debug output

$ eslint .
  typescript-eslint:typescript-estree:parser parserOptions.project matched projects: [
  './node_modules/@bcoe/v8-coverage/dist/lib/tsconfig.json',
  './node_modules/@bcoe/v8-coverage/tsconfig.json',
  './node_modules/@mufan/code-boilerplates/tsconfig.json',
  './node_modules/@mufan/code-templates/tsconfig.json',
  './node_modules/@mufan/code/tsconfig.json',
  './node_modules/@mufan/eslint-plugin/src/tsconfig.json',
  './node_modules/fastq/test/tsconfig.json',
  './node_modules/rxjs/src/tsconfig.json',
  './node_modules/tsconfig-paths/test/tsconfig.json',
  './node_modules/uri-js/tsconfig.json',
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +0ms
  typescript-eslint:typescript-estree:parser ignore list applied to parserOptions.project: [
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +3ms
  typescript-eslint:typescript-estree:createProjectProgram Creating project program for: /root/workspace/magicspace/packages/cli/src/program/@command.ts +0ms
  typescript-eslint:typescript-estree:createWatchProgram File did not belong to any existing programs, moving to create/update. /root/workspace/magicspace/packages/cli/src/program/@command.ts +0ms
  typescript-eslint:typescript-estree:createWatchProgram Creating watch program for /root/workspace/magicspace/packages/cli/src/program/tsconfig.json. +0ms
  typescript-eslint:typescript-estree:createWatchProgram Creating watch program for /root/workspace/magicspace/packages/core/src/library/tsconfig.json. +906ms
  typescript-eslint:typescript-estree:createWatchProgram Creating watch program for /root/workspace/magicspace/packages/utils/src/library/tsconfig.json. +632ms
  typescript-eslint:typescript-estree:createWatchProgram Creating watch program for /root/workspace/magicspace/packages/utils/src/test/tsconfig.json. +399ms
  typescript-eslint:typescript-estree:parser parserOptions.project matched projects: [
  './node_modules/@bcoe/v8-coverage/dist/lib/tsconfig.json',
  './node_modules/@bcoe/v8-coverage/tsconfig.json',
  './node_modules/@mufan/code-boilerplates/tsconfig.json',
  './node_modules/@mufan/code-templates/tsconfig.json',
  './node_modules/@mufan/code/tsconfig.json',
  './node_modules/@mufan/eslint-plugin/src/tsconfig.json',
  './node_modules/fastq/test/tsconfig.json',
  './node_modules/rxjs/src/tsconfig.json',
  './node_modules/tsconfig-paths/test/tsconfig.json',
  './node_modules/uri-js/tsconfig.json',
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +3s
  typescript-eslint:typescript-estree:parser ignore list applied to parserOptions.project: [
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +0ms
  typescript-eslint:typescript-estree:createProjectProgram Creating project program for: /root/workspace/magicspace/packages/cli/src/program/@space.ts +3s
  typescript-eslint:typescript-estree:createWatchProgram Found existing program for file. /root/workspace/magicspace/packages/cli/src/program/@space.ts +1s
  typescript-eslint:typescript-estree:parser parserOptions.project matched projects: [
  './node_modules/@bcoe/v8-coverage/dist/lib/tsconfig.json',
  './node_modules/@bcoe/v8-coverage/tsconfig.json',
  './node_modules/@mufan/code-boilerplates/tsconfig.json',
  './node_modules/@mufan/code-templates/tsconfig.json',
  './node_modules/@mufan/code/tsconfig.json',
  './node_modules/@mufan/eslint-plugin/src/tsconfig.json',
  './node_modules/fastq/test/tsconfig.json',
  './node_modules/rxjs/src/tsconfig.json',
  './node_modules/tsconfig-paths/test/tsconfig.json',
  './node_modules/uri-js/tsconfig.json',
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +1s
  typescript-eslint:typescript-estree:parser ignore list applied to parserOptions.project: [
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +0ms
  typescript-eslint:typescript-estree:createProjectProgram Creating project program for: /root/workspace/magicspace/packages/cli/src/program/@types/prompts.d.ts +1s
  typescript-eslint:typescript-estree:createWatchProgram Found existing program for file. /root/workspace/magicspace/packages/cli/src/program/@types/prompts.d.ts +1s
  typescript-eslint:typescript-estree:parser parserOptions.project matched projects: [
  './node_modules/@bcoe/v8-coverage/dist/lib/tsconfig.json',
  './node_modules/@bcoe/v8-coverage/tsconfig.json',
  './node_modules/@mufan/code-boilerplates/tsconfig.json',
  './node_modules/@mufan/code-templates/tsconfig.json',
  './node_modules/@mufan/code/tsconfig.json',
  './node_modules/@mufan/eslint-plugin/src/tsconfig.json',
  './node_modules/fastq/test/tsconfig.json',
  './node_modules/rxjs/src/tsconfig.json',
  './node_modules/tsconfig-paths/test/tsconfig.json',
  './node_modules/uri-js/tsconfig.json',
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +998ms
  typescript-eslint:typescript-estree:parser ignore list applied to parserOptions.project: [
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +0ms

But if I change the project pattern and ignore the node_modules before ignore list applied, then it lints a lot faster:

{
  "parserOptions": {
    "project": "./!(node_modules)/**/tsconfig.json"
  }
}
debug output

$ eslint .
  typescript-eslint:typescript-estree:parser parserOptions.project matched projects: [
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +0ms
  typescript-eslint:typescript-estree:parser ignore list applied to parserOptions.project: [
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +2ms
  typescript-eslint:typescript-estree:createProjectProgram Creating project program for: /root/workspace/magicspace/packages/cli/src/program/@command.ts +0ms
  typescript-eslint:typescript-estree:createWatchProgram File did not belong to any existing programs, moving to create/update. /root/workspace/magicspace/packages/cli/src/program/@command.ts +0ms
  typescript-eslint:typescript-estree:createWatchProgram Creating watch program for /root/workspace/magicspace/packages/cli/src/program/tsconfig.json. +0ms
  typescript-eslint:typescript-estree:createWatchProgram Creating watch program for /root/workspace/magicspace/packages/core/src/library/tsconfig.json. +843ms
  typescript-eslint:typescript-estree:createWatchProgram Creating watch program for /root/workspace/magicspace/packages/utils/src/library/tsconfig.json. +641ms
  typescript-eslint:typescript-estree:createWatchProgram Creating watch program for /root/workspace/magicspace/packages/utils/src/test/tsconfig.json. +385ms
  typescript-eslint:typescript-estree:parser parserOptions.project matched projects: [
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +2s
  typescript-eslint:typescript-estree:parser ignore list applied to parserOptions.project: [
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +1ms
  typescript-eslint:typescript-estree:createProjectProgram Creating project program for: /root/workspace/magicspace/packages/cli/src/program/@space.ts +2s
  typescript-eslint:typescript-estree:createWatchProgram Found existing program for file. /root/workspace/magicspace/packages/cli/src/program/@space.ts +307ms
  typescript-eslint:typescript-estree:parser parserOptions.project matched projects: [
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +87ms
  typescript-eslint:typescript-estree:parser ignore list applied to parserOptions.project: [
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +0ms
  typescript-eslint:typescript-estree:createProjectProgram Creating project program for: /root/workspace/magicspace/packages/cli/src/program/@types/prompts.d.ts +87ms
  typescript-eslint:typescript-estree:createWatchProgram Found existing program for file. /root/workspace/magicspace/packages/cli/src/program/@types/prompts.d.ts +87ms
  typescript-eslint:typescript-estree:parser parserOptions.project matched projects: [
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +20ms
  typescript-eslint:typescript-estree:parser ignore list applied to parserOptions.project: [
  './packages/cli/src/program/tsconfig.json',
  './packages/core/src/library/tsconfig.json',
  './packages/utils/src/library/tsconfig.json',
  './packages/utils/src/test/tsconfig.json'
] +0ms

Versions

package version
@typescript-eslint/typescript-estree 3.9.0
@typescript-eslint/parser 3.9.0
TypeScript 3.9.7
node 14.4.0
npm n/a
@vilicvane vilicvane added package: typescript-estree Issues related to @typescript-eslint/typescript-estree triage Waiting for maintainers to take a look labels Aug 17, 2020
@bradzacher
Copy link
Member

Sorry, what is your issue about exactly?
What is slow?

Could you please clarify?


If all of your packages are in the packages folder, why not just provide: ["packages/**/tsconfig"]?

@bradzacher bradzacher added awaiting response Issues waiting for a reply from the OP or another party and removed triage Waiting for maintainers to take a look labels Aug 17, 2020
@vilicvane
Copy link
Contributor Author

vilicvane commented Aug 17, 2020

@bradzacher Please compare the time in the command outputs, the former one has all subsequent createProjectProgram taking around 1s, and the later one seems much better. The overall performance is something like 40s vs 4s in my case.

Unfortunately I am writing a boilerplate so that I would like a more general configuration if possible. I can use the later configuration suggested in the first comment, but just want to let you guys know this weird issue exists.

Note these two configurations result in the same tsconfig.json list after filter applied.

@bradzacher
Copy link
Member

Which times are you talking about exactly?
Looking at the millisecond diffs they're close enough with a margin of error.

The thing I'd be more interested in is the total lint time for the workspace.
Does the difference in config meaningfully impact the total lint time?

@vilicvane
Copy link
Contributor Author

Yes, it's 40s vs 4s in my case.

The first configuration results in:

typescript-eslint:typescript-estree:createProjectProgram Creating project program for: /root/workspace/magicspace/packages/cli/src/program/@types/prompts.d.ts +1s
typescript-eslint:typescript-estree:createWatchProgram Found existing program for file. /root/workspace/magicspace/packages/cli/src/program/@types/prompts.d.ts +1s

And the second configuration results in:

typescript-eslint:typescript-estree:createProjectProgram Creating project program for: /root/workspace/magicspace/packages/cli/src/program/@types/prompts.d.ts +87ms
typescript-eslint:typescript-estree:createWatchProgram Found existing program for file. /root/workspace/magicspace/packages/cli/src/program/@types/prompts.d.ts +87ms

It is happening for every subsequent files, so the time accumulates.

@bradzacher
Copy link
Member

It's hard to provide any guidance here, as there isn't much information here.
I'm going to need you to create a repro repo for this if you want me to investigate further.

@vilicvane
Copy link
Contributor Author

vilicvane commented Aug 18, 2020

https://github.com/vilic/bug-repros/tree/eslint-2398

git clone --single-branch --branch eslint-2398 https://github.com/vilic/bug-repros.git
cd bug-repros
yarn
yarn lint

Again, if it's not that obvious, please let me know so that I would try to minimize the repro when I get extra time.

@bradzacher bradzacher added breaking change This change will require a new major version to be released bug Something isn't working and removed awaiting response Issues waiting for a reply from the OP or another party labels Aug 23, 2020
@bradzacher bradzacher added this to the 4.0.0 milestone Aug 23, 2020
@bradzacher bradzacher self-assigned this Aug 23, 2020
@bradzacher
Copy link
Member

So this problem is because glob takes that long to scan your node_modules folder.

The glob library is weird here. I remember having problems with their ignore option, so I ended up building the ignore list manually:

projects = projects.reduce<string[]>(
(projects, project) =>
projects.concat(
isGlob(project)
? globSync(project, {
cwd: extra.tsconfigRootDir,
})
: project,
),
[],
);
// Normalize and sanitize the ignore regex list
const ignoreRegexes: RegExp[] = [];
if (Array.isArray(ignoreListInput)) {
for (const ignore of ignoreListInput) {
if (ignore instanceof RegExp) {
ignoreRegexes.push(ignore);
} else if (typeof ignore === 'string') {
ignoreRegexes.push(new RegExp(ignore));
}
}
} else {
ignoreRegexes.push(/\/node_modules\//);
}
// Remove any paths that match the ignore list
const filtered = projects.filter(project => {
for (const ignore of ignoreRegexes) {
if (ignore.test(project)) {
return false;
}
}
return true;
});

I've done some research and globby is a much better library for this, as you can provide a set of ! prefixed globs that are applied whilst the glob is being evaluated. It also does some caching under the hood by default.

> time(() => glob.sync('./*/**/tsconfig.json'))
Execution time (hr): 2s 146.633635ms

> time(() => glob.sync('./!(node_modules)/**/tsconfig.json'))
Execution time (hr): 0s 33.76215ms

> time(() => globby.sync(['./*/**/tsconfig.json']))
Execution time (hr): 0s 165.163592ms

> time(() => globby.sync(['./*/**/tsconfig.json', '!node_modules']))
Execution time (hr): 0s 32.117896ms

The old code allowed regexes, so it's going to be too hard to change this right now.
I'll make this switch and release it with 4.0

bradzacher added a commit that referenced this issue Aug 23, 2020
Fixes #2398

If the user has a particularly large node_modules folder and uses globs for `parserOption.project`, then the `glob` library can spend a decent chunk of time searching the `node_modules` folder.
In some cases, this can be on the order of hundreds to thousands of milliseconds.
This wouldn't be a problem, but for safety and correctness during a persistent parse, we have to do this check for every call to the parser (i.e. every file that's being linted).
Over a whole project, this can easily add up to many, many seconds wasted.

Previously, we:
- applied the project globs, one by one
- then manually excluded `tsconfig`s from the list.

This meant that we are always slow. I remember I did this because I had issues getting `glob`'s `ignore` option to work at all.

## The solution

`globby` is a better glob library:
- it accepts an array of globs, which saves us doing manual looping
- it supports exclusion globs (globs prefixed with `!`), which  are evaluated as part of the glob process
- it has caching built in by default

This allows us to evaluate all of the `project` globs at once, as opposed to one at a time (so should be less duplicated work).
This also allows us to evaluate the `projectFolderIgnoreList` at the same time as the `project` globs (so should be no useless work done).

All of these together should cut the glob evaluation time down to ~50ms for the first parse, and ~2ms for each parse after that (due to caching).
For comparison, previously, in bad cases we would spend ~3-500ms, per project, per parsed file.

Example to illustrate how much faster this can potentially be:
For a project that provides 2 globs and has 100 files.
Before: 300ms * 2 * 100 = 60,000ms (60s)
After: 50ms + 2 * 100 = 250ms

This should also save a non-trival amount of time in other, more optimal setups.

BREAKING CHANGE:
- removes the ability to supply a `RegExp` to `projectFolderIgnoreList`, and changes the meaning of the string value from a regex to a glob.
@bradzacher bradzacher linked a pull request Aug 23, 2020 that will close this issue
@bradzacher bradzacher linked a pull request Aug 23, 2020 that will close this issue
bradzacher added a commit that referenced this issue Aug 24, 2020
Fixes #2398

If the user has a particularly large node_modules folder and uses globs for `parserOption.project`, then the `glob` library can spend a decent chunk of time searching the `node_modules` folder.
In some cases, this can be on the order of hundreds to thousands of milliseconds.
This wouldn't be a problem, but for safety and correctness during a persistent parse, we have to do this check for every call to the parser (i.e. every file that's being linted).
Over a whole project, this can easily add up to many, many seconds wasted.

Previously, we:
- applied the project globs, one by one
- then manually excluded `tsconfig`s from the list.

This meant that we are always slow. I remember I did this because I had issues getting `glob`'s `ignore` option to work at all.

## The solution

`globby` is a better glob library:
- it accepts an array of globs, which saves us doing manual looping
- it supports exclusion globs (globs prefixed with `!`), which  are evaluated as part of the glob process
- it has caching built in by default

This allows us to evaluate all of the `project` globs at once, as opposed to one at a time (so should be less duplicated work).
This also allows us to evaluate the `projectFolderIgnoreList` at the same time as the `project` globs (so should be no useless work done).

All of these together should cut the glob evaluation time down to ~50ms for the first parse, and ~2ms for each parse after that (due to caching).
For comparison, previously, in bad cases we would spend ~3-500ms, per project, per parsed file.

Example to illustrate how much faster this can potentially be:
For a project that provides 2 globs and has 100 files.
Before: 300ms * 2 * 100 = 60,000ms (60s)
After: 50ms + 2 * 100 = 250ms

This should also save a non-trival amount of time in other, more optimal setups.

BREAKING CHANGE:
- removes the ability to supply a `RegExp` to `projectFolderIgnoreList`, and changes the meaning of the string value from a regex to a glob.
bradzacher added a commit that referenced this issue Aug 24, 2020
Fixes #2398

If the user has a particularly large node_modules folder and uses globs for `parserOption.project`, then the `glob` library can spend a decent chunk of time searching the `node_modules` folder.
In some cases, this can be on the order of hundreds to thousands of milliseconds.
This wouldn't be a problem, but for safety and correctness during a persistent parse, we have to do this check for every call to the parser (i.e. every file that's being linted).
Over a whole project, this can easily add up to many, many seconds wasted.

Previously, we:
- applied the project globs, one by one
- then manually excluded `tsconfig`s from the list.

This meant that we are always slow. I remember I did this because I had issues getting `glob`'s `ignore` option to work at all.

## The solution

`globby` is a better glob library:
- it accepts an array of globs, which saves us doing manual looping
- it supports exclusion globs (globs prefixed with `!`), which  are evaluated as part of the glob process
- it has caching built in by default

This allows us to evaluate all of the `project` globs at once, as opposed to one at a time (so should be less duplicated work).
This also allows us to evaluate the `projectFolderIgnoreList` at the same time as the `project` globs (so should be no useless work done).

All of these together should cut the glob evaluation time down to ~50ms for the first parse, and ~2ms for each parse after that (due to caching).
For comparison, previously, in bad cases we would spend ~3-500ms, per project, per parsed file.

Example to illustrate how much faster this can potentially be:
For a project that provides 2 globs and has 100 files.
Before: 300ms * 2 * 100 = 60,000ms (60s)
After: 50ms + 2 * 100 = 250ms

This should also save a non-trival amount of time in other, more optimal setups.

BREAKING CHANGE:
- removes the ability to supply a `RegExp` to `projectFolderIgnoreList`, and changes the meaning of the string value from a regex to a glob.
bradzacher added a commit that referenced this issue Aug 24, 2020
Fixes #2398

If the user has a particularly large node_modules folder and uses globs for `parserOption.project`, then the `glob` library can spend a decent chunk of time searching the `node_modules` folder.
In some cases, this can be on the order of hundreds to thousands of milliseconds.
This wouldn't be a problem, but for safety and correctness during a persistent parse, we have to do this check for every call to the parser (i.e. every file that's being linted).
Over a whole project, this can easily add up to many, many seconds wasted.

Previously, we:
- applied the project globs, one by one
- then manually excluded `tsconfig`s from the list.

This meant that we are always slow. I remember I did this because I had issues getting `glob`'s `ignore` option to work at all.

## The solution

`globby` is a better glob library:
- it accepts an array of globs, which saves us doing manual looping
- it supports exclusion globs (globs prefixed with `!`), which  are evaluated as part of the glob process
- it has caching built in by default

This allows us to evaluate all of the `project` globs at once, as opposed to one at a time (so should be less duplicated work).
This also allows us to evaluate the `projectFolderIgnoreList` at the same time as the `project` globs (so should be no useless work done).

All of these together should cut the glob evaluation time down to ~50ms for the first parse, and ~2ms for each parse after that (due to caching).
For comparison, previously, in bad cases we would spend ~3-500ms, per project, per parsed file.

Example to illustrate how much faster this can potentially be:
For a project that provides 2 globs and has 100 files.
Before: 300ms * 2 * 100 = 60,000ms (60s)
After: 50ms + 2 * 100 = 250ms

This should also save a non-trival amount of time in other, more optimal setups.

BREAKING CHANGE:
- removes the ability to supply a `RegExp` to `projectFolderIgnoreList`, and changes the meaning of the string value from a regex to a glob.
bradzacher added a commit that referenced this issue Aug 29, 2020
Fixes #2398

If the user has a particularly large node_modules folder and uses globs for `parserOption.project`, then the `glob` library can spend a decent chunk of time searching the `node_modules` folder.
In some cases, this can be on the order of hundreds to thousands of milliseconds.
This wouldn't be a problem, but for safety and correctness during a persistent parse, we have to do this check for every call to the parser (i.e. every file that's being linted).
Over a whole project, this can easily add up to many, many seconds wasted.

Previously, we:
- applied the project globs, one by one
- then manually excluded `tsconfig`s from the list.

This meant that we are always slow. I remember I did this because I had issues getting `glob`'s `ignore` option to work at all.

## The solution

`globby` is a better glob library:
- it accepts an array of globs, which saves us doing manual looping
- it supports exclusion globs (globs prefixed with `!`), which  are evaluated as part of the glob process
- it has caching built in by default

This allows us to evaluate all of the `project` globs at once, as opposed to one at a time (so should be less duplicated work).
This also allows us to evaluate the `projectFolderIgnoreList` at the same time as the `project` globs (so should be no useless work done).

All of these together should cut the glob evaluation time down to ~50ms for the first parse, and ~2ms for each parse after that (due to caching).
For comparison, previously, in bad cases we would spend ~3-500ms, per project, per parsed file.

Example to illustrate how much faster this can potentially be:
For a project that provides 2 globs and has 100 files.
Before: 300ms * 2 * 100 = 60,000ms (60s)
After: 50ms + 2 * 100 = 250ms

This should also save a non-trival amount of time in other, more optimal setups.

BREAKING CHANGE:
- removes the ability to supply a `RegExp` to `projectFolderIgnoreList`, and changes the meaning of the string value from a regex to a glob.
bradzacher added a commit that referenced this issue Aug 29, 2020
Fixes #2398

If the user has a particularly large node_modules folder and uses globs for `parserOption.project`, then the `glob` library can spend a decent chunk of time searching the `node_modules` folder.
In some cases, this can be on the order of hundreds to thousands of milliseconds.
This wouldn't be a problem, but for safety and correctness during a persistent parse, we have to do this check for every call to the parser (i.e. every file that's being linted).
Over a whole project, this can easily add up to many, many seconds wasted.

Previously, we:
- applied the project globs, one by one
- then manually excluded `tsconfig`s from the list.

This meant that we are always slow. I remember I did this because I had issues getting `glob`'s `ignore` option to work at all.

## The solution

`globby` is a better glob library:
- it accepts an array of globs, which saves us doing manual looping
- it supports exclusion globs (globs prefixed with `!`), which  are evaluated as part of the glob process
- it has caching built in by default

This allows us to evaluate all of the `project` globs at once, as opposed to one at a time (so should be less duplicated work).
This also allows us to evaluate the `projectFolderIgnoreList` at the same time as the `project` globs (so should be no useless work done).

All of these together should cut the glob evaluation time down to ~50ms for the first parse, and ~2ms for each parse after that (due to caching).
For comparison, previously, in bad cases we would spend ~3-500ms, per project, per parsed file.

Example to illustrate how much faster this can potentially be:
For a project that provides 2 globs and has 100 files.
Before: 300ms * 2 * 100 = 60,000ms (60s)
After: 50ms + 2 * 100 = 250ms

This should also save a non-trival amount of time in other, more optimal setups.

BREAKING CHANGE:
- removes the ability to supply a `RegExp` to `projectFolderIgnoreList`, and changes the meaning of the string value from a regex to a glob.
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 24, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
breaking change This change will require a new major version to be released bug Something isn't working package: typescript-estree Issues related to @typescript-eslint/typescript-estree
Projects
None yet
2 participants