Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Community Feedback: Project Service APIs #8030

Discussion options

Back in Relative TSConfig Projects with parserOptions.project = true > Project Services, we'd mentioned we're working on a replacement for parserOptions.project:

The downside of having users specify parserOptions.project at all is that @typescript-eslint/parser needs manual logic to create TypeScript Programs and associate them with linted files.
...
We're working on an option to instead call the same TypeScript "Project Service" APIs that editors such as VS Code use to create Programs for us instead. Project Services will automatically detect the TSConfig for each file (like project: true), and will also allow type information to be computed for JavaScript files without the allowJs compiler option (unlike project: true).

The EXPERIMENTAL_useProjectService parser API is that experimental alternative to parserOptions.project. We hope it brings simpler ESLint configurations, mildly faster lint times, and more closely aligned type information for lint rules to what editors produce.

module.exports = {
  // ...
  parser: '@typescript-eslint/parser',
  parserOptions: {
-   project: true,
+   EXPERIMENTAL_useProjectService: true,
    tsconfigRootDir: __dirname,
  },
  // ...
}

Note: simpler configurations are blocked on #7752. We'll update this pinned discussion once that PR lands.

As with any big new API, there's only so much testing we can do on our own without the community. Please try out the project service API and post back here with how it goes for you! We'd love to know: does it indeed solve those configuration and/or performance problems for you? Are there any bugs? Was it a smash success? Any and all posts about how it goes are welcome! ♥️

You must be logged in to vote

Replies: 7 comments · 23 replies

Comment options

FYI: I tested it in babel/babel and it worked fine, the overall lint time was reduced from 38 seconds to 36 seconds.

You must be logged in to vote
1 reply
@JoshuaKGoldberg
Comment options

Linking for reference: babel/babel#16192 - thanks for posting!

Comment options

I tried it out on a personal project (.eslintrc.json, tsconfig.json) and the lint time increased from 31 seconds to 34 seconds. (Using @typescript-eslint/eslint-plugin 6.17.0.)

Is there any logging I can enable? For example, to see if the same service is used for all files?

You must be logged in to vote
4 replies
@JoshuaKGoldberg
Comment options

JoshuaKGoldberg Jan 4, 2024
Maintainer Author

Giving this the ❤️ reaction as I'm enthused you're trying it out... and the 😕 reaction because I'm sad it's slower on your end. Darn.

Though, I tried a direct clone -> npx eslint . on that project, and saw it get slightly faster when I switched to either "project": true or "EXPERIMENTAL_useProjectService": true. Was that 31 -> 34 seconds measurement done as an average / through hyperfine or an equivalent?

Measurements from hyperfine "npx eslint ." -i on Node v20.2.0 on my M1 Max:

Baseline "project": true "EXPERIMENTAL_useProjectService": true
6.597 s ± 0.119 s 6.224 s ± 0.041 s 6.227 s ± 0.080 s

Note that EXPERIMENTAL_useProjectService had no lint complaints whereas the other two had identical ones. I didn't bother building fully to get a true comparison from 0 lint failures. Is there a quick set of build commands I can run?

@JoshuaKGoldberg
Comment options

JoshuaKGoldberg Jan 5, 2024
Maintainer Author

Oh, and I filed #8189 to track adding docs for logging info. That issue has some more info in it on the DEBUG flags you can provide.

@dcecile
Comment options

Thanks Josh! I've tried again with this hyperfine invocation:

hyperfine "./node_modules/.bin/eslint --ignore-path .gitignore --max-warnings 0 --ext ts,js,cjs ./dev-esbuild ./dev-node-loader ./dev-node-suppress ./dev-pnpm-test ./lib-collection ./lib-concurrency ./lib-error ./lib-hex-ts ./lib-payload ./lib-random ./lib-stream ./lib-style ./lib-test ./lib-time ./lib-widget ./svc-auth-guest-read ./svc-auth-guest-view ./svc-gateway-guest-run"

Setup:

  • git checkout ec6d173f390e02762145a762b97acebe5eaa0b9e
  • pnpm i
  • ./node_modules/.bin/tsc --build
Baseline "project": true "EXPERIMENTAL_useProjectService": true
16.655 s ± 0.760 s 16.232 s ± 0.196 s 15.897 s ± 0.203 s

This is a more positive result. Switched my repo over to EXPERIMENTAL_useProjectService now ✔️

What I previously saw as "34 seconds" was definitely user CPU time (nice that hyperfine reports this too), not total time, sorry for the confusion!

I got lots of logs with --debug, but didn't see any obvious differences with parserOptions.debugLevel or any obvious TypeScript problems. Is there anything I could look for that would relate to the new project service?

@JoshuaKGoldberg
Comment options

Is there anything I could look for that would relate to the new project service?

🙌 Love that you're asking this! At this point your best bet is probably to search for useProgramFromProjectService in the typescript-eslint source and see where that gets called. Logs generally come from a const log = debug(. If there are points you'd want logged but is not yet, Local Development -> Local Linting can get you to a place where you can add those locally and try them out. If you find anything particularly useful please do file an issue suggesting we add them. We don't have complete coverage of those and so can always look into adding more.

Comment options

I tried this out on my monorepo via this commit, but I got this error:

Error: Error while loading rule '@typescript-eslint/await-thenable': You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.
Occurred while linting D:\Repositories\isaacscript\scripts\getMonorepoPackageNames.ts
    at getParserServices (D:\Repositories\isaacscript\node_modules\@typescript-eslint\utils\dist\eslint-utils\getParserServices.js:29:15)
    at create (D:\Repositories\isaacscript\node_modules\@typescript-eslint\eslint-plugin\dist\rules\await-thenable.js:47:55)
    at Object.create (D:\Repositories\isaacscript\node_modules\@typescript-eslint\utils\dist\eslint-utils\RuleCreator.js:38:20)
    at createRuleListeners (D:\Repositories\isaacscript\node_modules\eslint\lib\linter\linter.js:895:21)
    at D:\Repositories\isaacscript\node_modules\eslint\lib\linter\linter.js:1066:110
    at Array.forEach (<anonymous>)
    at runRules (D:\Repositories\isaacscript\node_modules\eslint\lib\linter\linter.js:1003:34)
    at Linter._verifyWithoutProcessors (D:\Repositories\isaacscript\node_modules\eslint\lib\linter\linter.js:1355:31)
    at Linter._verifyWithoutProcessors (D:\Repositories\isaacscript\node_modules\eslint-plugin-eslint-comments\lib\utils\patch.js:181:42)

For reference, the CI run is here: https://github.com/IsaacScript/isaacscript/actions/runs/7415844579/job/20179746969

You must be logged in to vote
4 replies
@JoshuaKGoldberg
Comment options

JoshuaKGoldberg Jan 5, 2024
Maintainer Author

Ah that's misconfigured: it's not projectService yet. For now it's EXPERIMENTAL_useProjectService. #6403 tracks adding a notice for typos like that.

@Zamiell
Comment options

Ok. Can you edit the OP to clarify the +- instructions? That's what I was going off of.

@Zamiell
Comment options

I tried this out via this commit, but I got this new error: https://github.com/IsaacScript/isaacscript/actions/runs/7431948579/job/20223198672

@JoshuaKGoldberg
Comment options

JoshuaKGoldberg Jan 6, 2024
Maintainer Author

Aha! Thanks for mentioning. This is #8206: the project service doesn't (yet?) provide a tsconfig.json's compiler options for a neighboring file not included in that tsconfig.json.

I'm surprised I hadn't yet filed an issue for this. It's been on the back of my mind for a while... adding the issue to the v7 milestone too.

Comment options

Latest release (v7.8.0) gave me: "Too many files (>8) have matched the default project." See: https://github.com/tstyche/tstyche/actions/runs/8893774897/job/24420694811

This is GHA failure. I think it is worth taking a look at it, because the error is somehow strangely constructed (it grows and grows and grows ...)

Hard to read the message, but looks like all files in the repo are reported as belonging to the default project. Hm.. That sounds incorrect. There should be none. I was using EXPERIMENTAL_useProjectService in this repo for some time, it seemed that all files were included to one or another tsconfig.json.

You must be logged in to vote
7 replies
@black7375
Comment options

I also think the default value of 8 is too small.

@bradzacher
Comment options

@black7375 why? Could you provide more information? Saying you disagree without giving reasoning is not very useful for us in considering a new default.


In our opinion of you're using the default program for more than 8 files you're doing it wrong and will be causing a performance impact for your lint run.

The option is there to cover a few config files - not for anything more. Which is why we banned wide globs from the option.

@black7375
Comment options

I use a split screen on a wide monitor (two by default)
However, when writing code, frequently used utilities and types are separated into different files, and UI components are also separated.
Therefore, it was very easy to exceed the limit of eight as I worked.

@bradzacher
Comment options

I'm not quite sure what you're talking about here. The option and the value 8 has no relationship to the number of files you can open in your editor.

It's the number of files we allow you to cover with the default program - I.e. Files not covered by a tsconfig.json.

@black7375
Comment options

I was completely misunderstanding it - it didn't appear when I opened a small number of files, but when I opened a large number of files, the error appeared in the editor and I thought it meant the number of files opened.
Thank you for your kind response.

Comment options

Hi @JoshuaKGoldberg (and thanks for your work on typescript-eslint ♥️)

I tried this, using v8-alpha.20, on an Angular project, and saw some differences between projectService: true and the project: ['./tsconfig.app.json', './tsconfig.spec.json'] (typical Angular CLI setup).

Basically, typesript-eslint doesn't pick the correct tsconfig for linting the files when using projectService: true (I imagine it uses the first one it finds as they are side-by-side?)

I suspect this is an issue with the underlying APIs, as we saw the same thing in our IDEs (VS Code or Webstorm), so maybe there is nothing to do for typescript-eslint. Or maybe I missed an obvious configuration to add somewhere, so I thought it was worth a shot.

As a minimal repro, I pushed this repo (without Angular, just 2 typescript files): https://github.com/cexbrayat/tseslint-config-repro
The project has 2 tsconfig files:

  • tsconfig.app.json, to compile src/app.ts and adds types from other.d.ts, with a test function typed as any (unused in the compiled code).
  • tsconfig.test.json, to compile src/test.ts and adds types from test.d.ts, with a test function properly typed (used in the compiled code).

You can run:

pnpm i
pnpm lint

to see the failure.
Using project instead of projectService in the eslint config makes the command succeed.

You must be logged in to vote
3 replies
@bradzacher
Comment options

I haven't run the code - but my basic understanding would have been that TS will pick the tsconfig.json by default which is configured to include all files - so it'll pick up app.ts, test.ts, other.d.ts and test.d.ts under the one config file.
It'll probably error on one of the .d.ts files (probably test.d.ts cos it's alphabetically second) if you turn off skipLibCheck.

@JoshuaKGoldberg
Comment options

Yup, @bradzacher is right - tseslint is going with the proper behavior here.

The root tsconfig.json includes all files. It doesn't have an include and doesn't use project references - so any consumer of the project service will use it for all files when there isn't any nested tsconfig.json to override it. That includes both tseslint's project service and editors such as VS Code. If you want to have VS Code and the typescript-eslint project service use tsconfig.*.json files instead, you'll want to use TypeScript project references.

Does that all make sense and sound reasonable to you @cexbrayat?

I wonder if we'll want to add this to the troubleshooting docs as part of #9085...

@cexbrayat
Comment options

Thank you for taking the time to look into it @bradzacher and @JoshuaKGoldberg
Yes, it makes sense, I'll bring that up to the Angular CLI team, because it should probably be fixed there.
Thanks again

Comment options

Hey, what are the default values of allowDefaultProject and defaultProject when projectService: true, please?

You must be logged in to vote
1 reply
@JoshuaKGoldberg
Comment options

Ah good note, these aren't explicitly called out in https://typescript-eslint.io/packages/parser#projectserviceoptions. But they're both undefined/falsy by default.

Note also #9807 for suggesting setting defaultProject's default to 'tsconfig.json'.

Comment options

Hi! I'm forwarding this question from project service API:

import-js/eslint-import-resolver-typescript#282 (comment)

Is there any guide/docs about how to use/implement projectService in a third-party library (eslint-import-resolver-typescript in this case)?

Which was asked in answer this question of mine concerning eslint-import-resolver-typescript:

Hi! I'm wondering if the resolver currently or plans on supporting project: true like typescript-eslint.io/packages/parser#project ?

As well as plans to support useProjectService once out of experimental phase (currently experimental as EXPERIMENTAL_useProjectService typescript-eslint.io/packages/parser#experimental_useprojectservice ) which solves a few more issues (including performance) with finding the tsconfig.

You must be logged in to vote
3 replies
@JoshuaKGoldberg
Comment options

Hmm, I don't know what information we could provide here that would be useful to you. parserOptions.projectService is a parser option (within language options) as defined by the ESLint API: https://eslint.org/docs/latest/use/configure/language-options.

We don't maintain eslint-import-resolver-typescript in typescript-eslint and aren't familiar with how it works. My advice would be to talk with the maintainers there. If you have any specific questions for us, we could take a look.

@bradzacher
Comment options

Yeah there's nothing special about the project service - that's our side of the fence. On the 3rd party side the thinking is just in terms of non-type-aware / pure syntactic rules vs type-aware rules.

Right now eslint-plugin-import and friends consume no type info currently. But they could do if they wanted. They could check the parser services on the context and consume the type info instead of manually building state from disk manually.

These are probably the mode relevajt docs:
https://typescript-eslint.io/developers/custom-rules#typed-rules

@Samuel-Therrien-Beslogic
Comment options

Thank you for the answer. I have to say this area goes a bit over my head, I was mostly wondering if https://github.com/import-js/eslint-plugin-import & https://github.com/un-ts/eslint-plugin-import-x could use a similar approach as typescript-eslint to find the tsconfigs instead of having to do:

{
  "settings": {
    "import-x/resolver": { // Updated to import-x
      "typescript": {
        "alwaysTryTypes": true,
        "project": [ // Could this be `"project": true` or use the project service ?
          "tsconfig?(.*).json",
          // From our Angular preset
          "projects/*/tsconfig?(.*).json",
          "*/tsconfig?(.*).json" // For example: e2e/tsconfig.json
        ]
      }
    }
  }
}

But I don't myself know or understand exactly what for and how they use that config. I've transferred this answer to the developper.

I also just found back the following discussion which is relevant: un-ts/eslint-plugin-import-x#40 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
10 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.