Skip to content

Parsing Svelte files with types is slow, and slows down more as more files are added to project #1084

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

Open
2 tasks done
mcous opened this issue Feb 25, 2025 · 5 comments
Open
2 tasks done
Labels
perf Improvement of performance

Comments

@mcous
Copy link

mcous commented Feb 25, 2025

Before You File a Bug Report Please Confirm You Have Done The Following...

  • I have tried restarting my IDE and the issue persists.
  • I have updated to the latest version of the packages.

What version of ESLint are you using?

9.21.0

What version of eslint-plugin-svelte are you using?

3.0.0-next.18

What did you do?

Configuration
import ts from 'typescript-eslint';
import svelteParser from 'svelte-eslint-parser';

export default ts.config({
  files: ['**/*.svelte'],
  languageOptions: {
    parser: svelteParser,
    parserOptions: {
      parser: ts.parser,
      projectService: true,
      extraFileExtensions: ['.svelte'],
      tsconfigRootDir: import.meta.dirname,
    },
  },
});
<script lang="ts">
  let { name = 'A' }: { name: number } = $props();
</script>

<h1>Hello from Component {name}</h1>

What did you expect to happen?

With type-checking enabled, and without any lint rules enabled, linting a Svelte file should take about the same amount of time as linting a TypeScript file. Lint times should be roughly on par with type-checking times.

What actually happened?

When types are enabled, the linting time of Svelte components increases dramatically as the number of files in the project (Svelte or otherwise) increase. This occurs with both projectService: true and project: './tsconfig.json'. This lint increase occurs even if the ESLint config doesn't enable any rules. The performance issue appears to happen during the parse stage.

In a simple project with 1000 Svelte files and 1000 TS files, and the ESLint config above (parsers configured, no rules enabled), my type-checking / lint times are:

Task Time (M2 MacBook Air)
Type-check (svelte-check) 7 seconds
Lint TS, with projectService 4 seconds
Lint Svelte, without types 3 seconds
Lint Svelte, with projectService 45 seconds 20 seconds (see sveltejs/svelte-eslint-parser#704)
Lint Svelte, with project 20 seconds

In a closed-source real-life project that I work on, linting our TS files takes under a minute, while linting the Svelte files takes 5 to 8 minutes depending on the machine.

Link to GitHub Repo with Minimal Reproducible Example

I've created a reproduction repository with a few simple ESLint configs that configure the TS and Svelte parsers and nothing else, along with a script to create fixture components and modules:

https://github.com/mcous/eslint-svelte-ts-perf

Additional comments

Thanks for all your work on this project! I'm very happy to dedicate time and energy to investigating this issue further, but I'd need some guidance to do so.

I think this issue may be related to #954

@baseballyama
Copy link
Member

Thank you! This is a great REPL, and I was able to reproduce the issue. I’ll take some time to investigate it soon.

@43081j
Copy link
Contributor

43081j commented Mar 27, 2025

i had a quick look at this

one thing to note is that this block of code:
https://github.com/sveltejs/svelte-eslint-parser/blob/73479f9dd3ca7c27cd0ce9a65f4885b13c0132de/src/parser/script.ts#L59-L77

will create a new program for every file. TSESLint will realise there's no program or service, and create one under the hood.

so maybe there is some saving to be had by reusing a project/program somewhere in the parser

similarly, we actually parse nothing (but still create a program) in order to detect if something is TSESLint:
https://github.com/sveltejs/svelte-eslint-parser/blob/73479f9dd3ca7c27cd0ce9a65f4885b13c0132de/src/parser/parser-object.ts#L56-L72

this could probably just use value.meta.name, no? it will be set to "typescript-eslint/parser" or some such thing

@mcous
Copy link
Author

mcous commented Apr 24, 2025

@baseballyama I've updated my repro repository at mcous/eslint-svelte-ts-perf to get sveltejs/svelte-eslint-parser#704, which has sped up projectService significantly. The core problem remains though, there's now just parity between project and projectService, both of which exhibit slow linting that continues to slow down as files are added

I dug in a little further into the items that @43081j was able to find:

similarly, we actually parse nothing (but still create a program) in order to detect if something is TSESLint:
parser-object.ts#L56-L72
This could probably just use value.meta.name, no? it will be set to "typescript-eslint/parser" or some such thing

Happily, it looks like this code path is a fallback that doesn't typically get hit. The suggestion is definitely correct though

one thing to note is that this block of code:
script.ts#L59-L77
will create a new program for every file. TSESLint will realise there's no program or service, and create one under the hood.

From watching TSESLint's debug log, I'm not sure it's creating a new program for every file. It seems like it might be correctly re-using the program. However, I do see some suspicious logs that make me think there could be problem caused by the file contents on disk that the TS program reads do not match the file contents we pass to the TSESLint parser.

I wonder if a custom TS program is needed that overrides the file read method, similar to how the Svelte Language Server works

@43081j
Copy link
Contributor

43081j commented Apr 24, 2025

its been a while, but from what i remember, i did observe it making a new program every time (by debugging it, not by viewing logs).

things may have changed since, though.

it'd be easy to check by putting a breakpoint inside TSESLint where it creates the program

@codercms
Copy link

codercms commented Apr 25, 2025

@43081j I can confirm that it still creates new program for each .svelte file, it can be easy checked by setting DEBUG env var to typescript-eslint:typescript-estree:* and then running eslint . cmd

This behavior can be observed in stdout as something like that:

typescript-eslint:typescript-estree:create-program:createProjectProgram Creating project program for: src\lib\screens\Header\Notifications\NotificationsButton.svelte +208ms

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
perf Improvement of performance
Projects
None yet
Development

No branches or pull requests

4 participants