From be19629979f6aba63de39b1d2c3e986a4545c824 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 6 Dec 2023 13:13:29 -0500 Subject: [PATCH 01/27] docs: blog post on parserOptions.projectService --- .../2023-09-18-parser-options-project-true.md | 4 +- ...24-03-01-parser-options-project-service.md | 298 ++++++++++++++++++ packages/website/docusaurusConfig.ts | 3 + 3 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 packages/website/blog/2024-03-01-parser-options-project-service.md diff --git a/packages/website/blog/2023-09-18-parser-options-project-true.md b/packages/website/blog/2023-09-18-parser-options-project-true.md index a28bb69c30ec..d0921211cd2b 100644 --- a/packages/website/blog/2023-09-18-parser-options-project-true.md +++ b/packages/website/blog/2023-09-18-parser-options-project-true.md @@ -4,7 +4,7 @@ authors: name: Josh Goldberg title: typescript-eslint Maintainer url: https://github.com/JoshuaKGoldberg -description: Simplifying how many projects resolve their +description: Simplifying how many projects resolve their TSConfigs for typed linting. slug: parser-options-project-true tags: [parser, parser options, project, tsconfig] title: Relative TSConfig Projects with `parserOptions.project = true` @@ -141,7 +141,7 @@ We're working on an option to instead call the same TypeScript "Project Service" 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`). We hope this option will eventually become the standard way to enable typed linting. -However, because it's so new and untested, we're keeping it under the `EXPERIMENTAL_` prefix for at least all of the `6.X` versions. +However, because it's so new and untested, we're keeping it under the `EXPERIMENTAL_` prefix for at least all most of the `6.X` versions. See [Packages > Parser > `EXPERIMENTAL_useProjectService`](/packages/parser#experimental_useprojectservice) for more information. diff --git a/packages/website/blog/2024-03-01-parser-options-project-service.md b/packages/website/blog/2024-03-01-parser-options-project-service.md new file mode 100644 index 000000000000..bfc6184ea3f9 --- /dev/null +++ b/packages/website/blog/2024-03-01-parser-options-project-service.md @@ -0,0 +1,298 @@ +--- +authors: + - image_url: https://www.joshuakgoldberg.com/img/josh.jpg + name: Josh Goldberg + title: typescript-eslint Maintainer + url: https://github.com/JoshuaKGoldberg +description: Using a faster and more convenient "Project Service" API for configuring typed linting. +slug: parser-options-project-service +tags: [parser, parser options, project, project service, tsconfig] +title: Straightforward Typed Linting with `parserOptions.projectService` +--- + +["Typed linting"](/linting/typed-linting), or enabling ESLint rules to tap into the power of the TypeScript type checker, is one of the best parts of typescript-eslint. +But typed linting hasn't always been straightforward to configure or performant at runtime. + +With typescript-eslint 7.0, we're marking as stable a `parserOptions.projectService` option that uses more powerful TypeScript APIs than previous typed linting implementations. +We've found it to bring the following benefits: + +- ✍️ **Configuration**: simpler ESLint configs for typed linting and no ancillary `tsconfig.eslint.json` +- ⚡️ **Performance**: faster linting both in CLIs and in editors +- 🧠 **Predictability**: more closely aligns type information for lint rules to what editors produce + +This blog post will cover how `parserOptions.projectService` improves performance, simplifies configurations, and brings linting type information much closer to what editors such as VS Code run with. + + + +## Introducing the Project Service + +Back in [Relative TSConfig Projects with `parserOptions.project = true` > Project Services](https://typescript-eslint.io/blog/parser-options-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. +> Manual Program creation logic comes with a few issues: +> +> - Complex project setups can be difficult to get right. +> - For example, [typescript-eslint does not yet support Project References](https://github.com/typescript-eslint/typescript-eslint/issues/2094). +> - The TypeScript compiler options used in the user's editor might differ from the compiler options in the TSConfigs they specified on disk. +> - Files not included in created Programs can't be linted with type information, even though editors still typically surface type information when editing those files. +> - Most commonly, `.eslintrc.(c)js` files can be tricky to lint, resulting in the dreaded [_TSConfig does not include this file_ error](/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file). +> +> 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`). +> +> We hope this option will eventually become the standard way to enable typed linting. +> However, because it's so new and untested, we're keeping it under the `EXPERIMENTAL_` prefix for all of the `6.??` versions. + +Following several months of discussion and testing, we believe the new Project Service API is ready to be used by real-world projects. +We've found them to be generally faster at runtime and more straightforward to configure. + +We're therefore promoting the `parserOptions.EXPERIMENTAL_useProjectService` option to the stable **`parserOptions.projectService`** in typescript-eslint v7. + +## Onboarding to the Project Service + +You can change over to the new Project Service API by replacing `project` with `projectService` in your ESLint configuration: + + + +### Flat ESLint Config + +```diff title="eslint.config.js" +export default tseslint.flatConfig({ + // ... + languageOptions: { + parser: tseslint.parser, + parserOptions: { +- project: true, ++ projectService: true, + tsconfigRootDir: __dirname, + }, + }, + // ... +}); +``` + +### Legacy ESLint Config + +```diff title=".eslintrc.cjs" +module.exports = { + // ... + parser: '@typescript-eslint/parser', + parserOptions: { +- project: true, ++ projectService: true, + tsconfigRootDir: __dirname, + }, + // ... +} +``` + + + +Everything else around linting, including running ESLint and configuring rules, should work the same. + +### Including Additional Files + +One long-standing pain point of typed linting is enabling type information for files not included in the project's `tsconfig.json`, such as an `eslint.config.js` or `vitest.config.ts`. +Common solutions in the legacy Program API were to either skip type checking for those files or to create a separate `tsconfig.eslint.json` that enabled `compilerOptions.allowJs = true`. + +Now, the new Project Service API allows for a configuration object specifying `additionalFiles` globs. +Those globs allowlist files that should have type information despite not being included in the project's TSConfig. + +```js +parserOptions: { + projectService: { + additionalFiles: [ + /* ... */ + ]; + } +} +``` + +For example, specifying `projectService.additionalFiles: ['./*']` would solve the common case of projects that have root-level files such as `eslint.config.js` and `vitest.config.ts`: + + + +### Flat ESLint Config + +```js title="eslint.config.js" +export default tseslint.flatConfig({ + // ... + languageOptions: { + parser: tseslint.parser, + parserOptions: { + projectService: { + additionalFiles: ['./*'], + }, + tsconfigRootDir: __dirname, + }, + }, + // ... +}); +``` + +### Legacy ESLint Config + +```js title=".eslintrc.cjs" +module.exports = { + // ... + parser: '@typescript-eslint/parser', + parserOptions: { + projectService: { + additionalFiles: ['./*'], + }, + tsconfigRootDir: __dirname, + }, + // ... +}; +``` + + + +:::tip +This means you should be able to remove any lint-only `tsconfig.eslint.json` files! +🥳 +::: + +Note that `projectService.additionalFiles` should only include files that aren't included in the project's `tsconfig.json`. +Creating fallback type information for additional files is slower than including those files in the `tsconfig.json`. +If a file is included in both the `tsconfig.json` and `projectService.additionalFiles`, typescript-eslint will report a parsing error: + +```plaintext +${filePath} was included by projectService.additionalFiles but also was found in the project service. Consider removing it from projectService.additionalFiles. +``` + +See [feat(typescript-estree): add allowDefaultProjectForFiles project service allowlist option](https://github.com/typescript-eslint/typescript-eslint/pull/7752) for more details on the API and [chore: add performance package with a project service hyperfine comparison (#7870)](https://github.com/typescript-eslint/typescript-eslint/pull/7870) for more details on performance. + +### New Starter Configs + +The following ESLint configs are starting recommendations for typed linting using the project service: + + + +## Flat ESLint Config + +```js title="eslint.config.js" +import tseslint from '@typescript-eslint/core'; +import js from '@eslint/js'; + +export default tseslint.flatConfig({ + extends: [js.configs.recommended, tseslint.configs.recommendedTypeChecked], + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + languageOptions: { + parser: tseslint.parser, + parserOptions: { + projectService: { + additionalFiles: ['./*'], + }, + tsconfigRootDir: __dirname, + }, + }, +}); +``` + +## Legacy ESLint Config + +```js title=".eslintrc.cjs" +module.exports = { + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended-type-checked', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + projectService: { + additionalFiles: ['./*'], + }, + tsconfigRootDir: __dirname, + }, + plugins: ['@typescript-eslint'], + root: true, +}; +``` + + + +See [typescript-eslint/examples](https://github.com/typescript-eslint/examples) for more references of how to configure linting in different projects. + +## Performance Comparisons + +In addition to simplifying configuration, we have also found the new Project Service API to result in faster lint times than the legacy program APIs. +We measured several common project scales: + +- 👶 Linting a "tiny" project (~dozen files) +- 🧒 Linting a "small" project (~100 files) +- 🧑 Linting a "medium" project (~1,000 files) +- 🧓 Linting a "large" project (~5,000 files, with project references) + +We also measured across different linting scenarios: + +- "Full cold start" of linting the entire project +- "Partial cold start" of seeing editor suggestions after opening the project in VS Code +- "Warm start" of seeing editor suggestions after changing a file in VS Code + +See [typescript-eslint/project-apis-performance-comparison](https://github.com/typescript-eslint/project-apis-performance-comparison) for details on the performance measurements. + +### Full Cold Start + +These measurements were taken by running `npm run lint` on the respective projects. + +| API | 👶 Tiny | 🧒 Small | 🧑 Medium | 🧓 Large | +| ------------------ | ------- | -------- | --------- | -------- | +| Legacy Program | | | | | +| 🆕 Project Service | | | | | +| 𝚫 Delta | | | | | + +### Partial Cold Start + +These measurements were taken by recording restarting VS Code on the respective projects with a single file open. + +| API | 👶 Tiny | 🧒 Small | 🧑 Medium | 🧓 Large | +| ------------------ | ------- | -------- | --------- | -------- | +| Legacy Program | | | | | +| 🆕 Project Service | | | | | +| 𝚫 Delta | | | | | + +### Warm Start + +These measurements were taken by recording VS Code on the respective projects with a single file open and modifying that file. + +| API | 👶 Tiny | 🧒 Small | 🧑 Medium | 🧓 Large | +| ------------------ | ------- | -------- | --------- | -------- | +| Legacy Program | | | | | +| 🆕 Project Service | | | | | +| 𝚫 Delta | | | | | + +## Next Steps + +### Giving Feedback + +We'd love to hear from you on how this option works for you. +Does it live up to what we've promised, and/or does it have bugs we haven't fixed yet? +Please do post in the [Community Feedback: Project Service APIs](https://github.com/typescript-eslint/typescript-eslint/discussions/8030) GitHub Discussions post on how it goes for you. + +For support in setting up the new APIs, feel free to ask on [the typescript-eslint Discord](https://discord.gg/FSxKq8Tdyg)'s `#project-service` channel. +We'd be happy to help you try out `parserOptions.projectService`. + +### Long Term Vision + +Our hope is that the Project Service API becomes the standard way to work with typed linting over the next few major versions. +Our intent is to roll it out according to the following rough timeline: + +- **v7**: Rename `parserOptions.EXPERIMENTAL_useProjectService` to `parserOptions.projectService` +- **v8**: Rename `parserOptions.project` to something like `parserOptions.DEPRECATED_legacyProjectProgram` and rename `parserOptions.projectService` to `parserOptions.project` +- **v9**: Remove `parserOptions.DEPRECATED_legacyProjectProgram` + +Our plan is to always recommend setting `parserOptions.project` in the docs. +What that refers to will intentionally switch from the legacy program API to the new Project Service API in v8. + +That timeline is a rough prediction that may change as users try out the new API. +Our priority will be to improve the new Project Service API so that it works in all places the legacy project program behavior does. +We won't remove the legacy project program behavior unless and until the new Project Service API is able to fully replace it. + +## Supporting typescript-eslint + +If you enjoyed this blog post and/or use typescript-eslint, please consider [supporting us on Open Collective](https://opencollective.com/typescript-eslint). +We're a small volunteer team and could use your support to make the ESLint experience on TypeScript great. +Thanks! 💖 diff --git a/packages/website/docusaurusConfig.ts b/packages/website/docusaurusConfig.ts index 625e6008a08f..f5bee5939f3b 100644 --- a/packages/website/docusaurusConfig.ts +++ b/packages/website/docusaurusConfig.ts @@ -19,6 +19,9 @@ const beforeDefaultRemarkPlugins: MDXPlugin[] = [tabsPlugin]; const githubUrl = 'https://github.com/typescript-eslint/typescript-eslint'; const presetClassicOptions: PresetClassicOptions = { + blog: { + beforeDefaultRemarkPlugins, + }, docs: { id: 'rules-docs', path: '../eslint-plugin/docs/rules', From 105a406d0cca06f24a0089e335b8757a5973072b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Sun, 24 Dec 2023 00:23:33 -0500 Subject: [PATCH 02/27] Update packages/website/blog/2023-09-18-parser-options-project-true.md --- packages/website/blog/2023-09-18-parser-options-project-true.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/blog/2023-09-18-parser-options-project-true.md b/packages/website/blog/2023-09-18-parser-options-project-true.md index d0921211cd2b..fdcadd0a6fe3 100644 --- a/packages/website/blog/2023-09-18-parser-options-project-true.md +++ b/packages/website/blog/2023-09-18-parser-options-project-true.md @@ -141,7 +141,7 @@ We're working on an option to instead call the same TypeScript "Project Service" 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`). We hope this option will eventually become the standard way to enable typed linting. -However, because it's so new and untested, we're keeping it under the `EXPERIMENTAL_` prefix for at least all most of the `6.X` versions. +However, because it's so new and untested, we're keeping it under the `EXPERIMENTAL_` prefix for at least all of the `6.X` versions. See [Packages > Parser > `EXPERIMENTAL_useProjectService`](/packages/parser#experimental_useprojectservice) for more information. From a10720443b5552560059cc89a6030d8599bd8161 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 24 Dec 2023 00:32:21 -0500 Subject: [PATCH 03/27] Better diffs and a bit of streamlining --- ...24-03-01-parser-options-project-service.md | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/packages/website/blog/2024-03-01-parser-options-project-service.md b/packages/website/blog/2024-03-01-parser-options-project-service.md index bfc6184ea3f9..8920f888d718 100644 --- a/packages/website/blog/2024-03-01-parser-options-project-service.md +++ b/packages/website/blog/2024-03-01-parser-options-project-service.md @@ -56,14 +56,16 @@ You can change over to the new Project Service API by replacing `project` with ` ### Flat ESLint Config -```diff title="eslint.config.js" +```js title="eslint.config.js" export default tseslint.flatConfig({ // ... languageOptions: { parser: tseslint.parser, parserOptions: { -- project: true, -+ projectService: true, + // Remove this line + project: true, + // Add this line + projectService: true, tsconfigRootDir: __dirname, }, }, @@ -73,17 +75,19 @@ export default tseslint.flatConfig({ ### Legacy ESLint Config -```diff title=".eslintrc.cjs" +```js title=".eslintrc.cjs" module.exports = { // ... parser: '@typescript-eslint/parser', parserOptions: { -- project: true, -+ projectService: true, + // Remove this line + project: true, + // Add this line + projectService: true, tsconfigRootDir: __dirname, }, // ... -} +}; ``` @@ -95,20 +99,10 @@ Everything else around linting, including running ESLint and configuring rules, One long-standing pain point of typed linting is enabling type information for files not included in the project's `tsconfig.json`, such as an `eslint.config.js` or `vitest.config.ts`. Common solutions in the legacy Program API were to either skip type checking for those files or to create a separate `tsconfig.eslint.json` that enabled `compilerOptions.allowJs = true`. -Now, the new Project Service API allows for a configuration object specifying `additionalFiles` globs. +Now, the new Project Service API allows for a configuration object specifying any number of `additionalFiles` globs. Those globs allowlist files that should have type information despite not being included in the project's TSConfig. -```js -parserOptions: { - projectService: { - additionalFiles: [ - /* ... */ - ]; - } -} -``` - -For example, specifying `projectService.additionalFiles: ['./*']` would solve the common case of projects that have root-level files such as `eslint.config.js` and `vitest.config.ts`: +For example, specifying `parserOptions.projectService.additionalFiles: ['./*']` would solve the common case of projects that have root-level files such as `eslint.config.js` and `vitest.config.ts`: @@ -282,7 +276,6 @@ Our intent is to roll it out according to the following rough timeline: - **v7**: Rename `parserOptions.EXPERIMENTAL_useProjectService` to `parserOptions.projectService` - **v8**: Rename `parserOptions.project` to something like `parserOptions.DEPRECATED_legacyProjectProgram` and rename `parserOptions.projectService` to `parserOptions.project` -- **v9**: Remove `parserOptions.DEPRECATED_legacyProjectProgram` Our plan is to always recommend setting `parserOptions.project` in the docs. What that refers to will intentionally switch from the legacy program API to the new Project Service API in v8. From 31cbb5d1f17f2a4e8e3334986722ba022ad03669 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 24 Dec 2023 00:59:22 -0500 Subject: [PATCH 04/27] Account for solo and project references at each scale --- ...24-03-01-parser-options-project-service.md | 190 ++++++++++++++++-- 1 file changed, 174 insertions(+), 16 deletions(-) diff --git a/packages/website/blog/2024-03-01-parser-options-project-service.md b/packages/website/blog/2024-03-01-parser-options-project-service.md index 8920f888d718..75a5ee5b5924 100644 --- a/packages/website/blog/2024-03-01-parser-options-project-service.md +++ b/packages/website/blog/2024-03-01-parser-options-project-service.md @@ -218,7 +218,12 @@ We measured several common project scales: - 👶 Linting a "tiny" project (~dozen files) - 🧒 Linting a "small" project (~100 files) - 🧑 Linting a "medium" project (~1,000 files) -- 🧓 Linting a "large" project (~5,000 files, with project references) +- 🧓 Linting a "large" project (~5,000 files) + +For each of those project scales, we measured two variations: + +- "Solo" TypeScript: using a single TSConfig +- Project references: multiple, composite TSConfigs We also measured across different linting scenarios: @@ -232,31 +237,184 @@ See [typescript-eslint/project-apis-performance-comparison](https://github.com/t These measurements were taken by running `npm run lint` on the respective projects. -| API | 👶 Tiny | 🧒 Small | 🧑 Medium | 🧓 Large | -| ------------------ | ------- | -------- | --------- | -------- | -| Legacy Program | | | | | -| 🆕 Project Service | | | | | -| 𝚫 Delta | | | | | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
API👶 Tiny🧒 Small🧑 Medium🧓 Large
SoloProjectsSoloProjectsSoloProjectsSoloProjects
Legacy Program
🆕 Project Service
𝚫 Delta
### Partial Cold Start These measurements were taken by recording restarting VS Code on the respective projects with a single file open. -| API | 👶 Tiny | 🧒 Small | 🧑 Medium | 🧓 Large | -| ------------------ | ------- | -------- | --------- | -------- | -| Legacy Program | | | | | -| 🆕 Project Service | | | | | -| 𝚫 Delta | | | | | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
API👶 Tiny🧒 Small🧑 Medium🧓 Large
SoloProjectsSoloProjectsSoloProjectsSoloProjects
Legacy Program
🆕 Project Service
𝚫 Delta
### Warm Start These measurements were taken by recording VS Code on the respective projects with a single file open and modifying that file. -| API | 👶 Tiny | 🧒 Small | 🧑 Medium | 🧓 Large | -| ------------------ | ------- | -------- | --------- | -------- | -| Legacy Program | | | | | -| 🆕 Project Service | | | | | -| 𝚫 Delta | | | | | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
API👶 Tiny🧒 Small🧑 Medium🧓 Large
SoloProjectsSoloProjectsSoloProjectsSoloProjects
Legacy Program
🆕 Project Service
𝚫 Delta
## Next Steps From c0b6d903684f04e31587bb97702827cacfc574b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Wed, 27 Dec 2023 13:22:05 -0500 Subject: [PATCH 05/27] Apply suggestions from code review Co-authored-by: Joshua Chen --- .../website/blog/2024-03-01-parser-options-project-service.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/website/blog/2024-03-01-parser-options-project-service.md b/packages/website/blog/2024-03-01-parser-options-project-service.md index 75a5ee5b5924..d3f748bf1f24 100644 --- a/packages/website/blog/2024-03-01-parser-options-project-service.md +++ b/packages/website/blog/2024-03-01-parser-options-project-service.md @@ -16,7 +16,7 @@ But typed linting hasn't always been straightforward to configure or performant With typescript-eslint 7.0, we're marking as stable a `parserOptions.projectService` option that uses more powerful TypeScript APIs than previous typed linting implementations. We've found it to bring the following benefits: -- ✍️ **Configuration**: simpler ESLint configs for typed linting and no ancillary `tsconfig.eslint.json` +- ✍️ **Configuration**: simpler ESLint configs for typed linting and no separate `tsconfig.eslint.json` - ⚡️ **Performance**: faster linting both in CLIs and in editors - 🧠 **Predictability**: more closely aligns type information for lint rules to what editors produce @@ -41,7 +41,7 @@ Back in [Relative TSConfig Projects with `parserOptions.project = true` > Projec > 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`). > > We hope this option will eventually become the standard way to enable typed linting. -> However, because it's so new and untested, we're keeping it under the `EXPERIMENTAL_` prefix for all of the `6.??` versions. +> However, because it's so new and untested, we're keeping it under the `EXPERIMENTAL_` prefix for all of the `6.x` versions. Following several months of discussion and testing, we believe the new Project Service API is ready to be used by real-world projects. We've found them to be generally faster at runtime and more straightforward to configure. From a7097acd246b93e3edbd8a8e0e82cc39e30d6fd6 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 11 May 2024 07:50:23 -0700 Subject: [PATCH 06/27] Adjusted vision section --- .../2024-03-01-parser-options-project-service.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/website/blog/2024-03-01-parser-options-project-service.md b/packages/website/blog/2024-03-01-parser-options-project-service.md index 77441adc8128..3a2221173f20 100644 --- a/packages/website/blog/2024-03-01-parser-options-project-service.md +++ b/packages/website/blog/2024-03-01-parser-options-project-service.md @@ -430,19 +430,12 @@ We'd be happy to help you try out `parserOptions.projectService`. ### Long Term Vision Our hope is that the Project Service API becomes the standard way to work with typed linting over the next few major versions. - -Our intent is to roll it out according to the following rough timeline: - -- **v7**: Rename `parserOptions.EXPERIMENTAL_useProjectService` to `parserOptions.projectService` -- **v8**: Rename `parserOptions.project` to something like `parserOptions.DEPRECATED_legacyProjectProgram` and rename `parserOptions.projectService` to `parserOptions.project` - -Our plan is to always recommend setting `parserOptions.project` in the docs. -What that refers to will intentionally switch from the legacy program API to the new Project Service API in v8. - -That timeline is a rough prediction that may change as users try out the new API. Our priority will be to improve the new Project Service API so that it works in all places the legacy project program behavior does. We won't remove the legacy project program behavior unless and until the new Project Service API is able to fully replace it. +So, please, try out the new Project Service API. +It should help make your typed linting faster and simpler to configure. 💜 + ## Supporting typescript-eslint If you enjoyed this blog post and/or use typescript-eslint, please consider [supporting us on Open Collective](https://opencollective.com/typescript-eslint). From 3ca6cbccc20e30427ec4256a962694335fa75ea4 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Thu, 18 Jul 2024 17:49:12 -0400 Subject: [PATCH 07/27] Refreshed blog post for latest versions and names --- ...24-03-01-parser-options-project-service.md | 443 ------------------ ...4-08-15-parser-options-project-service.mdx | 260 ++++++++++ 2 files changed, 260 insertions(+), 443 deletions(-) delete mode 100644 packages/website/blog/2024-03-01-parser-options-project-service.md create mode 100644 packages/website/blog/2024-08-15-parser-options-project-service.mdx diff --git a/packages/website/blog/2024-03-01-parser-options-project-service.md b/packages/website/blog/2024-03-01-parser-options-project-service.md deleted file mode 100644 index 3a2221173f20..000000000000 --- a/packages/website/blog/2024-03-01-parser-options-project-service.md +++ /dev/null @@ -1,443 +0,0 @@ ---- -authors: - - image_url: https://www.joshuakgoldberg.com/img/josh.jpg - name: Josh Goldberg - title: typescript-eslint Maintainer - url: https://github.com/JoshuaKGoldberg -description: Using a faster and more convenient "Project Service" API for configuring typed linting. -slug: parser-options-project-service -tags: [parser, parser options, project, project service, tsconfig] -title: Straightforward Typed Linting with `parserOptions.projectService` ---- - -["Typed linting"](/linting/typed-linting), or enabling ESLint rules to tap into the power of the TypeScript type checker, is one of the best parts of typescript-eslint. -But typed linting hasn't always been straightforward to configure or performant at runtime. - -With typescript-eslint 7.0, we're marking as stable a `parserOptions.projectService` option that uses more powerful TypeScript APIs than previous typed linting implementations. -We've found it to bring the following benefits: - -- ✍️ **Configuration**: simpler ESLint configs for typed linting and no separate `tsconfig.eslint.json` -- ⚡️ **Performance**: faster linting both in CLIs and in editors -- 🧠 **Predictability**: more closely aligns type information for lint rules to what editors produce - -This blog post will cover how `parserOptions.projectService` improves performance, simplifies configurations, and brings linting type information much closer to what editors such as VS Code run with. - - - -## Introducing the Project Service - -Back in [Relative TSConfig Projects with `parserOptions.project = true` > Project Services](https://typescript-eslint.io/blog/parser-options-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. -> Manual Program creation logic comes with a few issues: -> -> - Complex project setups can be difficult to get right. -> - For example, [typescript-eslint does not yet support Project References](https://github.com/typescript-eslint/typescript-eslint/issues/2094). -> - The TypeScript compiler options used in the user's editor might differ from the compiler options in the TSConfigs they specified on disk. -> - Files not included in created Programs can't be linted with type information, even though editors still typically surface type information when editing those files. -> - Most commonly, `.eslintrc.(c)js` files can be tricky to lint, resulting in the dreaded [_TSConfig does not include this file_ error](/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file). -> -> 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`). -> -> We hope this option will eventually become the standard way to enable typed linting. -> However, because it's so new and untested, we're keeping it under the `EXPERIMENTAL_` prefix for all of the `6.x` versions. - -Following several months of discussion and testing, we believe the new Project Service API is ready to be used by real-world projects. -We've found them to be generally faster at runtime and more straightforward to configure. - -We're therefore promoting the `parserOptions.EXPERIMENTAL_useProjectService` option to the stable **`parserOptions.projectService`** in typescript-eslint v7. - -## Onboarding to the Project Service - -You can change over to the new Project Service API by replacing `project` with `projectService` in your ESLint configuration: - - - -### Flat ESLint Config - -```js title="eslint.config.js" -export default tseslint.flatConfig({ - // ... - languageOptions: { - parser: tseslint.parser, - parserOptions: { - // Remove this line - project: true, - // Add this line - projectService: true, - tsconfigRootDir: __dirname, - }, - }, - // ... -}); -``` - -### Legacy ESLint Config - -```js title=".eslintrc.cjs" -module.exports = { - // ... - parser: '@typescript-eslint/parser', - parserOptions: { - // Remove this line - project: true, - // Add this line - projectService: true, - tsconfigRootDir: __dirname, - }, - // ... -}; -``` - - - -Everything else around linting, including running ESLint and configuring rules, should work the same. - -### Including Additional Files - -One long-standing pain point of typed linting is enabling type information for files not included in the project's `tsconfig.json`, such as an `eslint.config.js` or `vitest.config.ts`. -Common solutions in the legacy Program API were to either skip type checking for those files or to create a separate `tsconfig.eslint.json` that enabled `compilerOptions.allowJs = true`. - -Now, the new Project Service API allows for a configuration object specifying any number of `additionalFiles` globs. -Those globs allowlist files that should have type information despite not being included in the project's TSConfig. - -For example, specifying `parserOptions.projectService.additionalFiles: ['./*']` would solve the common case of projects that have root-level files such as `eslint.config.js` and `vitest.config.ts`: - - - -### Flat ESLint Config - -```js title="eslint.config.js" -export default tseslint.flatConfig({ - // ... - languageOptions: { - parser: tseslint.parser, - parserOptions: { - projectService: { - additionalFiles: ['./*'], - }, - tsconfigRootDir: __dirname, - }, - }, - // ... -}); -``` - -### Legacy ESLint Config - -```js title=".eslintrc.cjs" -module.exports = { - // ... - parser: '@typescript-eslint/parser', - parserOptions: { - projectService: { - additionalFiles: ['./*'], - }, - tsconfigRootDir: __dirname, - }, - // ... -}; -``` - - - -:::tip -This means you should be able to remove any lint-only `tsconfig.eslint.json` files! -🥳 -::: - -Note that `projectService.additionalFiles` should only include files that aren't included in the project's `tsconfig.json`. -Creating fallback type information for additional files is slower than including those files in the `tsconfig.json`. -If a file is included in both the `tsconfig.json` and `projectService.additionalFiles`, typescript-eslint will report a parsing error: - -```plaintext -${filePath} was included by projectService.additionalFiles but also was found in the project service. Consider removing it from projectService.additionalFiles. -``` - -See [feat(typescript-estree): add allowDefaultProjectForFiles project service allowlist option](https://github.com/typescript-eslint/typescript-eslint/pull/7752) for more details on the API and [chore: add performance package with a project service hyperfine comparison (#7870)](https://github.com/typescript-eslint/typescript-eslint/pull/7870) for more details on performance. - -### New Starter Configs - -The following ESLint configs are starting recommendations for typed linting using the project service: - - - -## Flat ESLint Config - -```js title="eslint.config.js" -import tseslint from '@typescript-eslint/core'; -import js from '@eslint/js'; - -export default tseslint.flatConfig({ - extends: [js.configs.recommended, tseslint.configs.recommendedTypeChecked], - plugins: { - '@typescript-eslint': tseslint.plugin, - }, - languageOptions: { - parser: tseslint.parser, - parserOptions: { - projectService: { - additionalFiles: ['./*'], - }, - tsconfigRootDir: __dirname, - }, - }, -}); -``` - -## Legacy ESLint Config - -```js title=".eslintrc.cjs" -module.exports = { - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended-type-checked', - ], - parser: '@typescript-eslint/parser', - parserOptions: { - projectService: { - additionalFiles: ['./*'], - }, - tsconfigRootDir: __dirname, - }, - plugins: ['@typescript-eslint'], - root: true, -}; -``` - - - -See [typescript-eslint/examples](https://github.com/typescript-eslint/examples) for more references of how to configure linting in different projects. - -## Performance Comparisons - -In addition to simplifying configuration, we have also found the new Project Service API to result in faster lint times than the legacy program APIs. -We measured several common project scales: - -- 👶 Linting a "tiny" project (~dozen files) -- 🧒 Linting a "small" project (~100 files) -- 🧑 Linting a "medium" project (~1,000 files) -- 🧓 Linting a "large" project (~5,000 files) - -For each of those project scales, we measured two variations: - -- "Solo" TypeScript: using a single TSConfig -- Project references: multiple, composite TSConfigs - -We also measured across different linting scenarios: - -- "Full cold start" of linting the entire project -- "Partial cold start" of seeing editor suggestions after opening the project in VS Code -- "Warm start" of seeing editor suggestions after changing a file in VS Code - -See [typescript-eslint/project-apis-performance-comparison](https://github.com/typescript-eslint/project-apis-performance-comparison) for details on the performance measurements. - -### Full Cold Start - -These measurements were taken by running `npm run lint` on the respective projects. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
API👶 Tiny🧒 Small🧑 Medium🧓 Large
SoloProjectsSoloProjectsSoloProjectsSoloProjects
Legacy Program
🆕 Project Service
𝚫 Delta
- -### Partial Cold Start - -These measurements were taken by recording restarting VS Code on the respective projects with a single file open. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
API👶 Tiny🧒 Small🧑 Medium🧓 Large
SoloProjectsSoloProjectsSoloProjectsSoloProjects
Legacy Program
🆕 Project Service
𝚫 Delta
- -### Warm Start - -These measurements were taken by recording VS Code on the respective projects with a single file open and modifying that file. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
API👶 Tiny🧒 Small🧑 Medium🧓 Large
SoloProjectsSoloProjectsSoloProjectsSoloProjects
Legacy Program
🆕 Project Service
𝚫 Delta
- -## Next Steps - -### Giving Feedback - -We'd love to hear from you on how this option works for you. -Does it live up to what we've promised, and/or does it have bugs we haven't fixed yet? -Please do post in the [Community Feedback: Project Service APIs](https://github.com/typescript-eslint/typescript-eslint/discussions/8030) GitHub Discussions post on how it goes for you. - -For support in setting up the new APIs, feel free to ask on [the typescript-eslint Discord](https://discord.gg/FSxKq8Tdyg)'s `#project-service` channel. -We'd be happy to help you try out `parserOptions.projectService`. - -### Long Term Vision - -Our hope is that the Project Service API becomes the standard way to work with typed linting over the next few major versions. -Our priority will be to improve the new Project Service API so that it works in all places the legacy project program behavior does. -We won't remove the legacy project program behavior unless and until the new Project Service API is able to fully replace it. - -So, please, try out the new Project Service API. -It should help make your typed linting faster and simpler to configure. 💜 - -## Supporting typescript-eslint - -If you enjoyed this blog post and/or use typescript-eslint, please consider [supporting us on Open Collective](https://opencollective.com/typescript-eslint). -We're a small volunteer team and could use your support to make the ESLint experience on TypeScript great. -Thanks! 💖 diff --git a/packages/website/blog/2024-08-15-parser-options-project-service.mdx b/packages/website/blog/2024-08-15-parser-options-project-service.mdx new file mode 100644 index 000000000000..205afd377dda --- /dev/null +++ b/packages/website/blog/2024-08-15-parser-options-project-service.mdx @@ -0,0 +1,260 @@ +--- +authors: + - image_url: https://www.joshuakgoldberg.com/img/josh.jpg + name: Josh Goldberg + title: typescript-eslint Maintainer + url: https://github.com/JoshuaKGoldberg +description: Using a faster and more convenient "Project Service" API for configuring typed linting. +slug: parser-options-project-service +tags: [parser, parser options, project, project service, tsconfig] +title: Stable Typed Linting with `parserOptions.projectService` +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +["Typed linting"](/linting/typed-linting), or enabling ESLint rules to tap into the power of the TypeScript type checker, is one of the best parts of typescript-eslint. +It enables a slew of more powerful lint rules that check for nuanced bugs, best practice violations, and other code issues. +But typed linting hasn't always been straightforward to configure or performant at runtime. + +With typescript-eslint 8.0, we're marking as stable a **`parserOptions.projectService`** option that uses more powerful TypeScript APIs than previous typed linting implementations. +We've found it to bring the following benefits: + +- ✍️ **Configuration**: simpler ESLint configs for typed linting and no ESLint-specific TSConfig file +- 🧠 **Predictability**: uses the same type information services as editors, including more reliability +- ⚡️ **Speed**: faster linting times out-of-the-box both in CLIs and in editors + +This blog post will cover how `parserOptions.projectService` improves performance, simplifies configurations, and brings linting type information much closer to what editors such as VS Code run with. + +// + +## Introducing the Project Service + +Back in [Relative TSConfig Projects with `parserOptions.project = true` > Project Services](2023-09-18-parser-options-project-true.md#project-services), we'd mentioned 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. +> Manual Program creation logic comes with a few issues: +> +> - Complex project setups can be difficult to get right. +> - For example, [typescript-eslint does not yet support Project References](https://github.com/typescript-eslint/typescript-eslint/issues/2094). +> - The TypeScript compiler options used in the user's editor might differ from the compiler options in the TSConfigs they specified on disk. +> - Files not included in created Programs can't be linted with type information, even though editors still typically surface type information when editing those files. +> - Most commonly, `.eslintrc.(c)js` files can be tricky to lint, resulting in the dreaded [_TSConfig does not include this file_ error](/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file). +> +> 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`). + +Following a year of discussion and testing, we believe the new Project Service API is ready to be used by real-world projects. +We've found it to be generally faster at runtime and more straightforward to configure. + +We're therefore promoting the `parserOptions.EXPERIMENTAL_useProjectService` option to the stable name **`parserOptions.projectService`** in typescript-eslint v8. + +## ✍️ Onboarding to the Project Service + +You can change over to the new Project Service API by replacing `project` with `projectService` in your ESLint configuration: + + + + +```js title="eslint.config.js" +export default tseslint.config({ + // ... + languageOptions: { + parserOptions: { + // Remove this line + project: true, + // Add this line + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + // ... +}); +``` + +:::note +[`import.meta.dirname`](https://nodejs.org/api/esm.html#importmetadirname) is only present for ESM files in Node.js >=20.11.0 / >= 21.2.0.
+For CommonJS modules and/or older versions of Node.js, [use `__dirname` or an alternative](https://stackoverflow.com/questions/46745014/alternative-for-dirname-in-node-js-when-using-es6-modules). +::: + +
+ + +```js title=".eslintrc.cjs" +module.exports = { + // ... + parser: '@typescript-eslint/parser', + parserOptions: { + // Remove this line + project: true, + // Add this line + projectService: true, + tsconfigRootDir: __dirname, + }, + // ... +}; +``` + + +
+ +Other settings, including how you run ESLint and configure rules, should work the same. + +### Including Additional Files + +One long-standing pain point of typed linting is enabling type information for files not included in the project's `tsconfig.json`. +Common solutions in the legacy Program API were to either skip type checking for those files or to create a separate `tsconfig.eslint.json` that enabled `compilerOptions.allowJs = true`. + +Now, the new Project Service API allows for a configuration object specifying: + +- `allowDefaultProjectForFiles`: a glob of "out-of-project" files to lint with type information +- `defaultProject`: path to a TSConfig to use for out-of-project file type information + +For example, the following config solves the common case of projects that have root-level files like `eslint.config.js` and `vitest.config.ts`: + + + + +```js title="eslint.config.js" +export default tseslint.config({ + // ... + languageOptions: { + parserOptions: { + projectService: { + allowDefaultProjectForFiles: ['./*'], + defaultProject: './tsconfig.json', + }, + tsconfigRootDir: import.meta.dirname, + }, + }, + // ... +}); +``` + + + + +```js title=".eslintrc.cjs" +module.exports = { + // ... + parser: '@typescript-eslint/parser', + parserOptions: { + projectService: { + allowDefaultProjectForFiles: ['./*'], + defaultProject: './tsconfig.json', + }, + tsconfigRootDir: __dirname, + }, + // ... +}; +``` + + + + +:::tip +This means you should be able to remove any lint-only `tsconfig.eslint.json` files! +🥳 +::: + +## 🧠 Predictability and Reliability + +One of the challenges of typed linting is using TypeScript APIs designed for TypeScript projects in ESLint's runtime. +[ESLint does not provide parsers session information](https://github.com/eslint/rfcs/pull/102). +As a result, `@typescript-eslint/parser` has to use approximations to guess whether it was in the more performance-friendly "single-run" mode (rather than in ESLint's multi-pass `--fix` mode or a long-lived editor session). +Single-run mode comes with several bugs. +For example: + +- ESLint's `--fix` mode breaks type information after the first run ([#9577](https://github.com/typescript-eslint/typescript-eslint/pull/9577)) +- Extra file extensions, such as those used by `.svelte` and `.vue` fileSync, are not supported ([#9504](https://github.com/typescript-eslint/typescript-eslint/issues/9504)) + +typescript-eslint's single-run inference enables uses common heuristics such as checking for `'--fix'` in `process.argv`, the presence of `process.env.CI`, and the presence of `parserOptions.extraFileExtensions`. +It can be disabled with `parserOptions.disallowSingleRunInference`. + +Enabling single-run mode generally improves performance by 10-20%. +typescript-eslint@v8 enables inference of single-run mode by default. +If your project is stuck on `parserOptions.project`, we recommend keeping single-run inference on if possible. + +`parserOptions.projectService` does not suffer from the same bugs as `parserOptions.project` with single-run mode. +It supports extra file extensions out-of-the-box and does not slow down when used with ESLint's `--fix`. + +We recommend switching to `parserOptions.projectService` if possible. + +## ⚡️ Performance Comparisons + +In addition to simplifying configuration, we have also found the new Project Service API to result in _roughly equivalent or faster_ lint times compared to the equivalent legacy program APIs. + +We measured two variations of typed linting on a simulated project of roughly 1,024 files: + +- ◀️ Legacy Projects: using `parserOptions.project` _(without single-run inference)_ +- 🆕 Project Service: using `parserOptions.projectService` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
API🧒 Small🧑 Medium🧓 Large
SoloProjectsSoloProjectsSoloProjects
◀️ Legacy Projects
🆕 Project Service
𝚫 Delta
+ +See [typescript-eslint/performance](https://github.com/typescript-eslint/performance) for details on the performance measurements. + +## Next Steps + +### Giving Feedback + +We'd love to hear from you on how this option works for you. +Does it live up to what we've promised, and/or does it have bugs we haven't fixed yet? +Please do post in the [Community Feedback: Project Service APIs](https://github.com/typescript-eslint/typescript-eslint/discussions/8030) GitHub Discussions post on how it goes for you. + +For support in setting up the new APIs, feel free to ask on [the typescript-eslint Discord](https://discord.gg/FSxKq8Tdyg)'s `#project-service` channel. +We'd be happy to help you try out `parserOptions.projectService`. + +### Long Term Vision + +Our hope is that the Project Service API becomes the standard way to work with typed linting over the next few major versions. +Our priority will be to improve the new Project Service API so that it works in all places the legacy project program behavior does. +We won't remove the legacy project program behavior unless and until the new Project Service API is able to fully replace it. + +So, please, try out the new Project Service API. +It should help make your typed linting faster and more straightforward to configure. 💜 From 2cead5798ca7e0bed44d53fc7be2002e0d3ec820 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 19 Aug 2024 07:16:01 -0400 Subject: [PATCH 08/27] Real world examples --- docs/packages/Parser.mdx | 20 +++- .../tests/lib/createProjectService.test.ts | 6 +- ...7-announcing-typescript-eslint-v8-beta.mdx | 2 +- ...4-07-31-announcing-typescript-eslint-v8.md | 2 +- ...-08-25-parser-options-project-service.mdx} | 108 +++++++++--------- 5 files changed, 80 insertions(+), 58 deletions(-) rename packages/website/blog/{2024-08-15-parser-options-project-service.mdx => 2024-08-25-parser-options-project-service.mdx} (73%) diff --git a/docs/packages/Parser.mdx b/docs/packages/Parser.mdx index c75ca14a8855..e57dac319e2c 100644 --- a/docs/packages/Parser.mdx +++ b/docs/packages/Parser.mdx @@ -285,6 +285,8 @@ For an option that allows linting files outside of your TSConfig file(s), see [` Specifies using TypeScript APIs to generate type information for rules. It 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`). +See [Typed Linting with `parserOptions.projectService`](/blog/parser-options-project-service) for more context. + @@ -321,7 +323,6 @@ This option brings two main benefits over the older `project`: - Simpler configurations: most projects shouldn't need to explicitly configure `project` paths or create `tsconfig.eslint.json`s - Improved performance: this API is optimized on the TypeScript side for speed - - Initial versions of this option demonstrated performance changes in subsets of the typescript-eslint monorepo ranging from 11% slower to 70% faster For more information, see: @@ -333,11 +334,12 @@ For more information, see: The behavior of `parserOptions.projectService` can be customized by setting it to an object. ```js -module.exports = { +{ parser: '@typescript-eslint/parser', parserOptions: { projectService: { allowDefaultProject: ['*.js'], + defaultProject: "tsconfig.json" }, }, }; @@ -346,10 +348,24 @@ module.exports = { ##### `allowDefaultProject` Globs of files to allow running with the default project compiler options despite not being matched by the project service. +It takes in an array of string paths that will be resolved relative to the [`tsconfigRootDir`](#tsconfigrootdir). +When set, [`projectService.defaultProject`](#defaultproject) must be set as well. + +This is intended to produce type information for in root-level config files such as `eslint.config.js` that aren't included in their sibling `tsconfig.json`. +Every file with type information retrieved from the default project incurs a non-trivial performance overhead to linting. +Use this option sparingly. + +There are several restrictions on this option to prevent it from being overused: + +- `**` is not allowed in globs passed to it +- Files that match `allowDefaultProject` may not also be included in their nearest `tsconfig.json` ##### `defaultProject` Path to a TSConfig to use instead of TypeScript's default project configuration. +It takes in an array of string paths that will be resolved relative to the [`tsconfigRootDir`](#tsconfigrootdir). + +This is required to specify which TSConfig file on disk will be used for [`projectService.allowDefaultProject`](#allowdefaultproject). ##### `maximumDefaultProjectFileMatchCount_THIS_WILL_SLOW_DOWN_LINTING` diff --git a/packages/typescript-estree/tests/lib/createProjectService.test.ts b/packages/typescript-estree/tests/lib/createProjectService.test.ts index cbf3d72dab9c..bf90317c3300 100644 --- a/packages/typescript-estree/tests/lib/createProjectService.test.ts +++ b/packages/typescript-estree/tests/lib/createProjectService.test.ts @@ -66,7 +66,7 @@ describe('createProjectService', () => { createProjectService( { allowDefaultProject: ['file.js'], - defaultProject: './tsconfig.json', + defaultProject: 'tsconfig.json', }, undefined, ), @@ -86,7 +86,7 @@ describe('createProjectService', () => { createProjectService( { allowDefaultProject: ['file.js'], - defaultProject: './tsconfig.json', + defaultProject: 'tsconfig.json', }, undefined, ), @@ -102,7 +102,7 @@ describe('createProjectService', () => { const { service } = createProjectService( { allowDefaultProject: ['file.js'], - defaultProject: './tsconfig.json', + defaultProject: 'tsconfig.json', }, undefined, ); diff --git a/packages/website/blog/2024-05-27-announcing-typescript-eslint-v8-beta.mdx b/packages/website/blog/2024-05-27-announcing-typescript-eslint-v8-beta.mdx index 52d2de3c52b8..69189cb2ab01 100644 --- a/packages/website/blog/2024-05-27-announcing-typescript-eslint-v8-beta.mdx +++ b/packages/website/blog/2024-05-27-announcing-typescript-eslint-v8-beta.mdx @@ -133,7 +133,7 @@ export default tseslint.config( // Added lines start projectService: { allowDefaultProject: ['*.js'], - defaultProject: './tsconfig.json', + defaultProject: 'tsconfig.json', }, // Added lines end tsconfigRootDir: import.meta.dirname, diff --git a/packages/website/blog/2024-07-31-announcing-typescript-eslint-v8.md b/packages/website/blog/2024-07-31-announcing-typescript-eslint-v8.md index 97d6ec32fd70..40f5b5f68a1a 100644 --- a/packages/website/blog/2024-07-31-announcing-typescript-eslint-v8.md +++ b/packages/website/blog/2024-07-31-announcing-typescript-eslint-v8.md @@ -121,7 +121,7 @@ export default tseslint.config( // Added lines start projectService: { allowDefaultProject: ['*.js'], - defaultProject: './tsconfig.json', + defaultProject: 'tsconfig.json', }, // Added lines end tsconfigRootDir: import.meta.dirname, diff --git a/packages/website/blog/2024-08-15-parser-options-project-service.mdx b/packages/website/blog/2024-08-25-parser-options-project-service.mdx similarity index 73% rename from packages/website/blog/2024-08-15-parser-options-project-service.mdx rename to packages/website/blog/2024-08-25-parser-options-project-service.mdx index 205afd377dda..0c4fc7901cd0 100644 --- a/packages/website/blog/2024-08-15-parser-options-project-service.mdx +++ b/packages/website/blog/2024-08-25-parser-options-project-service.mdx @@ -7,7 +7,7 @@ authors: description: Using a faster and more convenient "Project Service" API for configuring typed linting. slug: parser-options-project-service tags: [parser, parser options, project, project service, tsconfig] -title: Stable Typed Linting with `parserOptions.projectService` +title: Typed Linting with `parserOptions.projectService` --- import Tabs from '@theme/Tabs'; @@ -17,16 +17,16 @@ import TabItem from '@theme/TabItem'; It enables a slew of more powerful lint rules that check for nuanced bugs, best practice violations, and other code issues. But typed linting hasn't always been straightforward to configure or performant at runtime. -With typescript-eslint 8.0, we're marking as stable a **`parserOptions.projectService`** option that uses more powerful TypeScript APIs than previous typed linting implementations. +With typescript-eslint 8.0, we marked as stable a **`parserOptions.projectService`** option that uses more powerful TypeScript APIs than previous typed linting implementations. We've found it to bring the following benefits: - ✍️ **Configuration**: simpler ESLint configs for typed linting and no ESLint-specific TSConfig file - 🧠 **Predictability**: uses the same type information services as editors, including more reliability -- ⚡️ **Speed**: faster linting times out-of-the-box both in CLIs and in editors +- ⚡️ **Speed**: faster linting times out-of-the-box both in CLIs and in editors in many cases -This blog post will cover how `parserOptions.projectService` improves performance, simplifies configurations, and brings linting type information much closer to what editors such as VS Code run with. +This blog post will cover how `parserOptions.projectService` simplifies configurations and brings linting type information much closer to what editors such as VS Code run with. -// + ## Introducing the Project Service @@ -100,10 +100,12 @@ module.exports = { Other settings, including how you run ESLint and configure rules, should work the same. +See [Packages > Parser > `projectService`](/packages/parser#projectservice) for more details. + ### Including Additional Files One long-standing pain point of typed linting is enabling type information for files not included in the project's `tsconfig.json`. -Common solutions in the legacy Program API were to either skip type checking for those files or to create a separate `tsconfig.eslint.json` that enabled `compilerOptions.allowJs = true`. +Common solutions in the traditional Program API were to either skip type checking for those files or to create a separate `tsconfig.eslint.json` that enabled `compilerOptions.allowJs = true`. Now, the new Project Service API allows for a configuration object specifying: @@ -121,8 +123,8 @@ export default tseslint.config({ languageOptions: { parserOptions: { projectService: { - allowDefaultProjectForFiles: ['./*'], - defaultProject: './tsconfig.json', + allowDefaultProjectForFiles: ['*.js'], + defaultProject: 'tsconfig.json', }, tsconfigRootDir: import.meta.dirname, }, @@ -140,8 +142,8 @@ module.exports = { parser: '@typescript-eslint/parser', parserOptions: { projectService: { - allowDefaultProjectForFiles: ['./*'], - defaultProject: './tsconfig.json', + allowDefaultProjectForFiles: ['*.js'], + defaultProject: 'tsconfig.json', }, tsconfigRootDir: __dirname, }, @@ -153,10 +155,12 @@ module.exports = { :::tip -This means you should be able to remove any lint-only `tsconfig.eslint.json` files! +This means most projects should be able to remove lint-only `tsconfig.eslint.json` files! 🥳 ::: +See [Packages > Parser > `projectService` > `ProjectServiceOptions`](/packages/parser#projectserviceOptions) for more details. + ## 🧠 Predictability and Reliability One of the challenges of typed linting is using TypeScript APIs designed for TypeScript projects in ESLint's runtime. @@ -166,10 +170,10 @@ Single-run mode comes with several bugs. For example: - ESLint's `--fix` mode breaks type information after the first run ([#9577](https://github.com/typescript-eslint/typescript-eslint/pull/9577)) -- Extra file extensions, such as those used by `.svelte` and `.vue` fileSync, are not supported ([#9504](https://github.com/typescript-eslint/typescript-eslint/issues/9504)) +- Extra file extensions, such as those used by `.svelte` and `.vue`, are not supported ([#9504](https://github.com/typescript-eslint/typescript-eslint/issues/9504)) typescript-eslint's single-run inference enables uses common heuristics such as checking for `'--fix'` in `process.argv`, the presence of `process.env.CI`, and the presence of `parserOptions.extraFileExtensions`. -It can be disabled with `parserOptions.disallowSingleRunInference`. +It can be disabled with [`parserOptions.disallowAutomaticSingleRunInference`](/packages/parser#disallowautomaticsingleruninference). Enabling single-run mode generally improves performance by 10-20%. typescript-eslint@v8 enables inference of single-run mode by default. @@ -182,62 +186,60 @@ We recommend switching to `parserOptions.projectService` if possible. ## ⚡️ Performance Comparisons -In addition to simplifying configuration, we have also found the new Project Service API to result in _roughly equivalent or faster_ lint times compared to the equivalent legacy program APIs. +In addition to simplifying configuration, we have also found the new Project Service API to result in _roughly equivalent or faster_ lint times compared to the equivalent traditional program APIs. -We measured two variations of typed linting on a simulated project of roughly 1,024 files: +To prove this, we measured the average of 10 lint times on three real repositories: -- ◀️ Legacy Projects: using `parserOptions.project` _(without single-run inference)_ -- 🆕 Project Service: using `parserOptions.projectService` +- [Babel](https://github.com/babel/babel): +- [create-t3-app](https://github.com/t3-oss/create-t3-app): +- [tRPC](https://github.com/trpc/trpc): + +> TODO: Reproduce these findings locally! - - - - - - - - - - - - + + + + + - - - - - - - + + + + + - - - - - - - + + + + + - - - - + +
API🧒 Small🧑 Medium🧓 Large
SoloProjectsSoloProjectsSoloProjectsRepository📜 Traditional Projects🆕 Project Service𝚫 DeltaSource
◀️ Legacy ProjectsBabel~38 seconds~36 seconds~5% faster[^1]
🆕 Project Servicecreate-t3-app6.662 s ± 0.235 s5.344 s ± 0.126 s20% faster[^2]
𝚫 DeltatRPC [^3]
-See [typescript-eslint/performance](https://github.com/typescript-eslint/performance) for details on the performance measurements. +In summary, we've found linting _some_ large real-world repositories **can be 5% to of 20% faster**. 🚀 + +### Performance Caveats + +Note that using Project Service APIs for linting is still a relatively new use case. +Neither TypeScript nor typescript-eslint has had much time to optimize internally for the new usage. +Some repositories structured differently from the ones we've tested on may be slower. + +See [typescript-eslint/performance](https://github.com/typescript-eslint/performance) for details on cases where the project service is slower than traditional projects. ## Next Steps @@ -245,7 +247,7 @@ See [typescript-eslint/performance](https://github.com/typescript-eslint/perform We'd love to hear from you on how this option works for you. Does it live up to what we've promised, and/or does it have bugs we haven't fixed yet? -Please do post in the [Community Feedback: Project Service APIs](https://github.com/typescript-eslint/typescript-eslint/discussions/8030) GitHub Discussions post on how it goes for you. +Please do post in the [Community Feedback: Project Service APIs](https://github.com/typescript-eslint/typescript-eslint/discussions/8030) GitHub Discussion on how it goes for you. For support in setting up the new APIs, feel free to ask on [the typescript-eslint Discord](https://discord.gg/FSxKq8Tdyg)'s `#project-service` channel. We'd be happy to help you try out `parserOptions.projectService`. @@ -253,8 +255,12 @@ We'd be happy to help you try out `parserOptions.projectService`. ### Long Term Vision Our hope is that the Project Service API becomes the standard way to work with typed linting over the next few major versions. -Our priority will be to improve the new Project Service API so that it works in all places the legacy project program behavior does. -We won't remove the legacy project program behavior unless and until the new Project Service API is able to fully replace it. +Our priority will be to improve the new Project Service API so that it works in all places the traditional project program behavior does. +We won't remove the traditional project program behavior unless and until the new Project Service API is able to fully replace it. So, please, try out the new Project Service API. It should help make your typed linting faster and more straightforward to configure. 💜 + +[^1]: https://github.com/babel/babel/pull/16192#issue-2054613116 + +[^2]: https://github.com/t3-oss/create-t3-app/pull/1936/#discussion_r1667389041 From 7c9152ad20dd2e8e8871a00f49e46ff0c446a4c4 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 08:26:11 -0400 Subject: [PATCH 09/27] git checkout main -- packages/website/blog/2023-09-18-parser-options-project-true.md --- .../blog/2023-09-18-parser-options-project-true.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/website/blog/2023-09-18-parser-options-project-true.md b/packages/website/blog/2023-09-18-parser-options-project-true.md index b1d0c683bff3..389331a7ff13 100644 --- a/packages/website/blog/2023-09-18-parser-options-project-true.md +++ b/packages/website/blog/2023-09-18-parser-options-project-true.md @@ -1,15 +1,6 @@ --- -<<<<<<< HEAD -authors: - - image_url: /img/team/joshuakgoldberg.jpg - name: Josh Goldberg - title: typescript-eslint Maintainer - url: https://github.com/JoshuaKGoldberg -description: Simplifying how many projects resolve their TSConfigs for typed linting. -======= authors: joshuakgoldberg description: Simplifying how many projects resolve their ->>>>>>> main slug: parser-options-project-true tags: [parser, parser options, project, tsconfig] title: Relative TSConfig Projects with `parserOptions.project = true` From de68287bd66c4f44bf09b108bc373af12d83f7ae Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 08:44:12 -0400 Subject: [PATCH 10/27] git checkout main -- docs/packages/Parser.mdx --- docs/packages/Parser.mdx | 23 +++---------------- ...ice.mdx => 2025-05-26-project-service.mdx} | 10 ++++---- 2 files changed, 9 insertions(+), 24 deletions(-) rename packages/website/blog/{2024-08-25-parser-options-project-service.mdx => 2025-05-26-project-service.mdx} (96%) diff --git a/docs/packages/Parser.mdx b/docs/packages/Parser.mdx index df9f997e93fe..ecf132f7869d 100644 --- a/docs/packages/Parser.mdx +++ b/docs/packages/Parser.mdx @@ -294,7 +294,7 @@ Specifies using TypeScript APIs to generate type information for rules. It will automatically use the nearest `tsconfig.json` for each file (like `project: true`). It can also be configured to also allow type information to be computed for JavaScript files without the `allowJs` compiler option (unlike `project: true`). -See [Typed Linting with `parserOptions.projectService`](/blog/parser-options-project-service) for more context. +See [Typed Linting with `parserOptions.projectService`](/blog/project-service) for more context. @@ -331,10 +331,7 @@ module.exports = { This option brings two main benefits over the older `project`: - Simpler configurations: most projects shouldn't need to explicitly configure `project` paths or create `tsconfig.eslint.json`s - <<<<<<< HEAD -- # Improved performance: this API is optimized on the TypeScript side for speed - Predictability: it uses the same type information services as editors, giving better consistency with the types seen in editors - > > > > > > > main See [FAQs > Typed Linting > Project Service Issues](../troubleshooting/typed-linting/index.mdx#project-service-issues) for help on working with the project service. @@ -348,7 +345,6 @@ The behavior of `parserOptions.projectService` can be customized by setting it t parserOptions: { projectService: { allowDefaultProject: ['*.js'], - defaultProject: "tsconfig.json" }, }, }; @@ -364,16 +360,10 @@ See [Troubleshooting & FAQs > Typed Linting > Project Service Issues](../trouble Globs of files to allow running with the default project compiler options despite not being matched by the project service. It takes in an array of string paths that will be resolved relative to the [`tsconfigRootDir`](#tsconfigrootdir). -<<<<<<< HEAD -When set, [`projectService.defaultProject`](#defaultproject) must be set as well. - -# This is intended to produce type information for in root-level config files such as `eslint.config.js` that aren't included in their sibling `tsconfig.json`. This is intended to produce type information for config files such as `eslint.config.js` that aren't included in their sibling `tsconfig.json`. - -> > > > > > > main -> > > > > > > Every file with type information retrieved from the default project incurs a non-trivial performance overhead to linting. -> > > > > > > Use this option sparingly. +Every file with type information retrieved from the default project incurs a non-trivial performance overhead to linting. +Use this option sparingly. There are several restrictions on this option to prevent it from being overused: @@ -385,11 +375,6 @@ There are several restrictions on this option to prevent it from being overused: > Default `'tsconfig.json'` Path to a TSConfig to use instead of TypeScript's default project configuration. -<<<<<<< HEAD -It takes in an array of string paths that will be resolved relative to the [`tsconfigRootDir`](#tsconfigrootdir). - -# This is required to specify which TSConfig file on disk will be used for [`projectService.allowDefaultProject`](#allowdefaultproject). - It takes in a string path that will be resolved relative to the [`tsconfigRootDir`](#tsconfigrootdir). `projectService.defaultProject` only impacts the "out-of-project" files included by [`allowDefaultProject`](#allowdefaultproject). @@ -412,8 +397,6 @@ parserOptions: { } ``` -> > > > > > > main - ##### `maximumDefaultProjectFileMatchCount_THIS_WILL_SLOW_DOWN_LINTING` > Default: `8`. diff --git a/packages/website/blog/2024-08-25-parser-options-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx similarity index 96% rename from packages/website/blog/2024-08-25-parser-options-project-service.mdx rename to packages/website/blog/2025-05-26-project-service.mdx index 0c4fc7901cd0..f989db9eaafe 100644 --- a/packages/website/blog/2024-08-25-parser-options-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -4,8 +4,8 @@ authors: name: Josh Goldberg title: typescript-eslint Maintainer url: https://github.com/JoshuaKGoldberg -description: Using a faster and more convenient "Project Service" API for configuring typed linting. -slug: parser-options-project-service +description: How typescript-eslint's new "Project Service" makes typed linting easier to configure, especially for large projects. +slug: project-service tags: [parser, parser options, project, project service, tsconfig] title: Typed Linting with `parserOptions.projectService` --- @@ -13,9 +13,11 @@ title: Typed Linting with `parserOptions.projectService` import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -["Typed linting"](/linting/typed-linting), or enabling ESLint rules to tap into the power of the TypeScript type checker, is one of the best parts of typescript-eslint. +["Typed linting"](/linting/typed-linting), or enabling ESLint rules to understand TypeScript types, is one of the best parts of typescript-eslint. It enables a slew of more powerful lint rules that check for nuanced bugs, best practice violations, and other code issues. -But typed linting hasn't always been straightforward to configure or performant at runtime. + +Typed linting hasn't always been straightforward to configure or performant at runtime. +ESLint and TypeScript were not originally designed to work together, and older APIs With typescript-eslint 8.0, we marked as stable a **`parserOptions.projectService`** option that uses more powerful TypeScript APIs than previous typed linting implementations. We've found it to bring the following benefits: From 60ac709c9263a61e11df6def5d78cdbc4c871c54 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 09:39:30 -0400 Subject: [PATCH 11/27] Lots of touchups --- .../blog/2025-05-26-project-service.mdx | 169 ++++++------------ 1 file changed, 56 insertions(+), 113 deletions(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index f989db9eaafe..3eec675b1260 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -7,7 +7,7 @@ authors: description: How typescript-eslint's new "Project Service" makes typed linting easier to configure, especially for large projects. slug: project-service tags: [parser, parser options, project, project service, tsconfig] -title: Typed Linting with `parserOptions.projectService` +title: Typed Linting with Project Service --- import Tabs from '@theme/Tabs'; @@ -17,14 +17,14 @@ import TabItem from '@theme/TabItem'; It enables a slew of more powerful lint rules that check for nuanced bugs, best practice violations, and other code issues. Typed linting hasn't always been straightforward to configure or performant at runtime. -ESLint and TypeScript were not originally designed to work together, and older APIs +Some projects in previously had to even use separate `tsconfig.eslint.json` files to enable type information for linting. -With typescript-eslint 8.0, we marked as stable a **`parserOptions.projectService`** option that uses more powerful TypeScript APIs than previous typed linting implementations. -We've found it to bring the following benefits: +In typescript-eslint 8.0, we stabilized a **`parserOptions.projectService`** option that uses more powerful, streamlined TypeScript APIs than before. +The "Project Service" brings several benefits: - ✍️ **Configuration**: simpler ESLint configs for typed linting and no ESLint-specific TSConfig file - 🧠 **Predictability**: uses the same type information services as editors, including more reliability -- ⚡️ **Speed**: faster linting times out-of-the-box both in CLIs and in editors in many cases +- 🚀 **Scalability**: supporting TypeScript project references for larger repositories (i.e. monorepos) This blog post will cover how `parserOptions.projectService` simplifies configurations and brings linting type information much closer to what editors such as VS Code run with. @@ -35,21 +35,13 @@ This blog post will cover how `parserOptions.projectService` simplifies configur Back in [Relative TSConfig Projects with `parserOptions.project = true` > Project Services](2023-09-18-parser-options-project-true.md#project-services), we'd mentioned 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. -> Manual Program creation logic comes with a few issues: -> -> - Complex project setups can be difficult to get right. -> - For example, [typescript-eslint does not yet support Project References](https://github.com/typescript-eslint/typescript-eslint/issues/2094). -> - The TypeScript compiler options used in the user's editor might differ from the compiler options in the TSConfigs they specified on disk. -> - Files not included in created Programs can't be linted with type information, even though editors still typically surface type information when editing those files. -> - Most commonly, `.eslintrc.(c)js` files can be tricky to lint, resulting in the dreaded [_TSConfig does not include this file_ error](/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file). +> Manual Program creation logic comes with a few issues: ... > > 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`). -Following a year of discussion and testing, we believe the new Project Service API is ready to be used by real-world projects. -We've found it to be generally faster at runtime and more straightforward to configure. - -We're therefore promoting the `parserOptions.EXPERIMENTAL_useProjectService` option to the stable name **`parserOptions.projectService`** in typescript-eslint v8. +Following a year of discussion and beta testing in typescript-eslint v6 and v7, we believe the new Project Service API is ready to be used by real-world projects. +We therefore promoted the `parserOptions.EXPERIMENTAL_useProjectService` option to the stable name **`parserOptions.projectService`** in typescript-eslint v8. ## ✍️ Onboarding to the Project Service @@ -67,18 +59,12 @@ export default tseslint.config({ project: true, // Add this line projectService: true, - tsconfigRootDir: import.meta.dirname, }, }, // ... }); ``` -:::note -[`import.meta.dirname`](https://nodejs.org/api/esm.html#importmetadirname) is only present for ESM files in Node.js >=20.11.0 / >= 21.2.0.
-For CommonJS modules and/or older versions of Node.js, [use `__dirname` or an alternative](https://stackoverflow.com/questions/46745014/alternative-for-dirname-in-node-js-when-using-es6-modules). -::: -
@@ -91,7 +77,6 @@ module.exports = { project: true, // Add this line projectService: true, - tsconfigRootDir: __dirname, }, // ... }; @@ -100,19 +85,18 @@ module.exports = {
-Other settings, including how you run ESLint and configure rules, should work the same. +That's it! -See [Packages > Parser > `projectService`](/packages/parser#projectservice) for more details. +Other settings, including how you run ESLint and configure rules, should work the same. +[Packages > Parser > `projectService`](/packages/parser#projectservice) has more details on granular configuration options. ### Including Additional Files -One long-standing pain point of typed linting is enabling type information for files not included in the project's `tsconfig.json`. -Common solutions in the traditional Program API were to either skip type checking for those files or to create a separate `tsconfig.eslint.json` that enabled `compilerOptions.allowJs = true`. +One long-standing pain point of typed linting was enabling typed linting for files not included in the project's `tsconfig.json`. +Common solutions in the traditional Program API were to either skip typed linting for those files or to create a `tsconfig.eslint.json` enabling the `allowJs` compiler option. -Now, the new Project Service API allows for a configuration object specifying: - -- `allowDefaultProjectForFiles`: a glob of "out-of-project" files to lint with type information -- `defaultProject`: path to a TSConfig to use for out-of-project file type information +The new Project Service API allows for a configuration object specifying `allowDefaultProjectForFiles`: a glob of "out-of-project" files to lint with type information. +That means you can lint those files without any new configuration files or TypeScript compiler options! For example, the following config solves the common case of projects that have root-level files like `eslint.config.js` and `vitest.config.ts`: @@ -126,9 +110,7 @@ export default tseslint.config({ parserOptions: { projectService: { allowDefaultProjectForFiles: ['*.js'], - defaultProject: 'tsconfig.json', }, - tsconfigRootDir: import.meta.dirname, }, }, // ... @@ -145,9 +127,7 @@ module.exports = { parserOptions: { projectService: { allowDefaultProjectForFiles: ['*.js'], - defaultProject: 'tsconfig.json', }, - tsconfigRootDir: __dirname, }, // ... }; @@ -156,112 +136,75 @@ module.exports = { -:::tip This means most projects should be able to remove lint-only `tsconfig.eslint.json` files! 🥳 -::: -See [Packages > Parser > `projectService` > `ProjectServiceOptions`](/packages/parser#projectserviceOptions) for more details. +[Packages > Parser > `projectService` > `ProjectServiceOptions`](/packages/parser#projectserviceOptions) has more details on out-of-project files and other granular configuration options. ## 🧠 Predictability and Reliability -One of the challenges of typed linting is using TypeScript APIs designed for TypeScript projects in ESLint's runtime. -[ESLint does not provide parsers session information](https://github.com/eslint/rfcs/pull/102). -As a result, `@typescript-eslint/parser` has to use approximations to guess whether it was in the more performance-friendly "single-run" mode (rather than in ESLint's multi-pass `--fix` mode or a long-lived editor session). -Single-run mode comes with several bugs. -For example: - -- ESLint's `--fix` mode breaks type information after the first run ([#9577](https://github.com/typescript-eslint/typescript-eslint/pull/9577)) -- Extra file extensions, such as those used by `.svelte` and `.vue`, are not supported ([#9504](https://github.com/typescript-eslint/typescript-eslint/issues/9504)) +Without separate `tsconfig.eslint.json` files, you can be more confident that the type information used for linting is the same as the one used for type checking. +We've round this to be a common source of confusion for users, as well as an annoying extra file to maintain. -typescript-eslint's single-run inference enables uses common heuristics such as checking for `'--fix'` in `process.argv`, the presence of `process.env.CI`, and the presence of `parserOptions.extraFileExtensions`. -It can be disabled with [`parserOptions.disallowAutomaticSingleRunInference`](/packages/parser#disallowautomaticsingleruninference). +Another benefit of using the new Project Service API is that typed linting requires no additional configuration to work with extensions to TypeScript. +Many use cases couldn't be supported by the traditional Program API while still optimizing for performance. +For example: -Enabling single-run mode generally improves performance by 10-20%. -typescript-eslint@v8 enables inference of single-run mode by default. -If your project is stuck on `parserOptions.project`, we recommend keeping single-run inference on if possible. +- ESLint's `--fix` mode would lose type information after the first run ([#9577](https://github.com/typescript-eslint/typescript-eslint/pull/9577)) +- Extra file extensions, such as those used by `.svelte` and `.vue`, were not supported ([#9504](https://github.com/typescript-eslint/typescript-eslint/issues/9504)) `parserOptions.projectService` does not suffer from the same bugs as `parserOptions.project` with single-run mode. It supports extra file extensions out-of-the-box and does not slow down when used with ESLint's `--fix`. -We recommend switching to `parserOptions.projectService` if possible. - -## ⚡️ Performance Comparisons - -In addition to simplifying configuration, we have also found the new Project Service API to result in _roughly equivalent or faster_ lint times compared to the equivalent traditional program APIs. - -To prove this, we measured the average of 10 lint times on three real repositories: - -- [Babel](https://github.com/babel/babel): -- [create-t3-app](https://github.com/t3-oss/create-t3-app): -- [tRPC](https://github.com/trpc/trpc): - -> TODO: Reproduce these findings locally! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Repository📜 Traditional Projects🆕 Project Service𝚫 DeltaSource
Babel~38 seconds~36 seconds~5% faster[^1]
create-t3-app6.662 s ± 0.235 s5.344 s ± 0.126 s20% faster[^2]
tRPC[^3]
- -In summary, we've found linting _some_ large real-world repositories **can be 5% to of 20% faster**. 🚀 - -### Performance Caveats - -Note that using Project Service APIs for linting is still a relatively new use case. -Neither TypeScript nor typescript-eslint has had much time to optimize internally for the new usage. -Some repositories structured differently from the ones we've tested on may be slower. - -See [typescript-eslint/performance](https://github.com/typescript-eslint/performance) for details on cases where the project service is slower than traditional projects. +## 🚀 Scalability with Project References + +[TypeScript's project references](https://www.typescriptlang.org/docs/handbook/project-references.html) are how many larger projects, such as monorepos, are able to scale TypeScript type checking. +They allow delineating discrete projects with their own `tsconfig.json` files and annotating which projects depend on which other projects. +Project references allow TypeScript to cache type information and only recheck projects that have changed in builds. + +The traditional `parserOptions.project` API did not support project references for typed linting. +We had experimented with adding support, but using the manual built-in TypeScript APIs would have been a significant maintenance investment with an unclear payoff. + +The new Project Service API does support project references out-of-the-box. +This is a huge win for monorepos, as it means you can lint all of your projects with type information without needing to create a separate `tsconfig.eslint.json` file for each project. + +## Performance + +Supporting project references allows the new Project Service API to be significantly faster than the traditional `parserOptions.project` API in many monorepo cases. +We've observed improvements for typed linting speed in real-world repositories [^1] [^2]. +For smaller projects, the performance of the new Project Service API is similar to the traditional `parserOptions.project` API. + +When we first started working with the new project service API, it outperformed equivalent `parserOptions.project` setups by ~10-15%. +Since then, we have observed regressions in performance that have brought it down in some cases to be slightly slower. + +We believe the new Project Service API should be faster than the traditional API, and are treating the lack of significant improvement as a bug. +See [⚡ Performance: parserOptions.projectService sometimes no longer outperforms parserOptions.project](https://github.com/typescript-eslint/typescript-eslint/issues/9571) for more information. ## Next Steps +The new Project Service API is available in typescript-eslint v8.0.0 and later. +We've been using it in our own monorepo for over a year, and have been thrilled to see many community repositories adopt it as well. + ### Giving Feedback We'd love to hear from you on how this option works for you. Does it live up to what we've promised, and/or does it have bugs we haven't fixed yet? -Please do post in the [Community Feedback: Project Service APIs](https://github.com/typescript-eslint/typescript-eslint/discussions/8030) GitHub Discussion on how it goes for you. +Please do post in the [Community Feedback: Project Service API](https://github.com/typescript-eslint/typescript-eslint/discussions/8030) GitHub Discussion on how it goes for you. For support in setting up the new APIs, feel free to ask on [the typescript-eslint Discord](https://discord.gg/FSxKq8Tdyg)'s `#project-service` channel. We'd be happy to help you try out `parserOptions.projectService`. ### Long Term Vision -Our hope is that the Project Service API becomes the standard way to work with typed linting over the next few major versions. -Our priority will be to improve the new Project Service API so that it works in all places the traditional project program behavior does. +The new Project Service API is a great step towards making typed linting easier and more straightforward to configure. +Our priority for the next year will be to improve the new Project Service API so that it works in all places the traditional Program API does. We won't remove the traditional project program behavior unless and until the new Project Service API is able to fully replace it. +We're also looking forward to investigating support for [TypeScript's 10x faster Go port (tsgo / typescript-go)](https://github.com/typescript-eslint/typescript-eslint/issues/10940). +Abstracting configuration details into the Project Service API means it will be much easier for us to support typed linting with Go without any additional user configuration. + So, please, try out the new Project Service API. -It should help make your typed linting faster and more straightforward to configure. 💜 +We're excited to hear how it works for you and what we can do to improve it. 💜 [^1]: https://github.com/babel/babel/pull/16192#issue-2054613116 From 025bf33da0ae0bfbeb278c805a3c7e5870b9b649 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 11:09:39 -0400 Subject: [PATCH 12/27] More tweaks --- .../blog/2025-05-26-project-service.mdx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index 3eec675b1260..77e409336567 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -1,9 +1,5 @@ --- -authors: - - image_url: https://www.joshuakgoldberg.com/img/josh.jpg - name: Josh Goldberg - title: typescript-eslint Maintainer - url: https://github.com/JoshuaKGoldberg +authors: joshuakgoldberg description: How typescript-eslint's new "Project Service" makes typed linting easier to configure, especially for large projects. slug: project-service tags: [parser, parser options, project, project service, tsconfig] @@ -22,12 +18,16 @@ Some projects in previously had to even use separate `tsconfig.eslint.json` file In typescript-eslint 8.0, we stabilized a **`parserOptions.projectService`** option that uses more powerful, streamlined TypeScript APIs than before. The "Project Service" brings several benefits: -- ✍️ **Configuration**: simpler ESLint configs for typed linting and no ESLint-specific TSConfig file +- ✍️ **Configuration**: simpler ESLint configs for typed linting with no ESLint-specific TSConfig files - 🧠 **Predictability**: uses the same type information services as editors, including more reliability - 🚀 **Scalability**: supporting TypeScript project references for larger repositories (i.e. monorepos) This blog post will cover how `parserOptions.projectService` simplifies configurations and brings linting type information much closer to what editors such as VS Code run with. +:::tip +See [Getting Started](/getting-started) to learn how to lint JavaScript and TypeScript code with typescript-eslint, then [Linting with Type Information](/linting/typed-linting) to onboard to typed linting. +::: + ## Introducing the Project Service @@ -43,7 +43,7 @@ Back in [Relative TSConfig Projects with `parserOptions.project = true` > Projec Following a year of discussion and beta testing in typescript-eslint v6 and v7, we believe the new Project Service API is ready to be used by real-world projects. We therefore promoted the `parserOptions.EXPERIMENTAL_useProjectService` option to the stable name **`parserOptions.projectService`** in typescript-eslint v8. -## ✍️ Onboarding to the Project Service +## Configuration You can change over to the new Project Service API by replacing `project` with `projectService` in your ESLint configuration: @@ -90,7 +90,7 @@ That's it! Other settings, including how you run ESLint and configure rules, should work the same. [Packages > Parser > `projectService`](/packages/parser#projectservice) has more details on granular configuration options. -### Including Additional Files +### Additional Files One long-standing pain point of typed linting was enabling typed linting for files not included in the project's `tsconfig.json`. Common solutions in the traditional Program API were to either skip typed linting for those files or to create a `tsconfig.eslint.json` enabling the `allowJs` compiler option. @@ -141,7 +141,7 @@ This means most projects should be able to remove lint-only `tsconfig.eslint.jso [Packages > Parser > `projectService` > `ProjectServiceOptions`](/packages/parser#projectserviceOptions) has more details on out-of-project files and other granular configuration options. -## 🧠 Predictability and Reliability +## Predictability Without separate `tsconfig.eslint.json` files, you can be more confident that the type information used for linting is the same as the one used for type checking. We've round this to be a common source of confusion for users, as well as an annoying extra file to maintain. @@ -156,7 +156,7 @@ For example: `parserOptions.projectService` does not suffer from the same bugs as `parserOptions.project` with single-run mode. It supports extra file extensions out-of-the-box and does not slow down when used with ESLint's `--fix`. -## 🚀 Scalability with Project References +## Scalability [TypeScript's project references](https://www.typescriptlang.org/docs/handbook/project-references.html) are how many larger projects, such as monorepos, are able to scale TypeScript type checking. They allow delineating discrete projects with their own `tsconfig.json` files and annotating which projects depend on which other projects. From 6295c5f1f255b136877be14a5851224f5abb6b45 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 11:17:51 -0400 Subject: [PATCH 13/27] Back-links galore --- docs/packages/Project_Service.mdx | 2 +- packages/website/blog/2025-05-26-project-service.mdx | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/packages/Project_Service.mdx b/docs/packages/Project_Service.mdx index 7f841f3560b1..7cfe65b67d93 100644 --- a/docs/packages/Project_Service.mdx +++ b/docs/packages/Project_Service.mdx @@ -16,7 +16,7 @@ The typescript-eslint Project Service is a wrapper around TypeScript's "project These APIs are what editors such as VS Code use to programmatically "open" files and generate TypeScript programs for type information. :::note -See [Announcing typescript-eslint v8 > Project Service](/blog/announcing-typescript-eslint-v8#project-service) for more details on how lint users interact with the Project Service. +See [Blog > Typed Linting with Project Service](/blog/project-service) for more details on how lint users interact with the Project Service. ::: ```ts diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index 77e409336567..2135e98b0640 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -43,6 +43,10 @@ Back in [Relative TSConfig Projects with `parserOptions.project = true` > Projec Following a year of discussion and beta testing in typescript-eslint v6 and v7, we believe the new Project Service API is ready to be used by real-world projects. We therefore promoted the `parserOptions.EXPERIMENTAL_useProjectService` option to the stable name **`parserOptions.projectService`** in typescript-eslint v8. +:::note +See [Announcing typescript-eslint v8 > Project Service](/blog/announcing-typescript-eslint-v8#project-service) for the original announcement. +::: + ## Configuration You can change over to the new Project Service API by replacing `project` with `projectService` in your ESLint configuration: @@ -200,7 +204,12 @@ The new Project Service API is a great step towards making typed linting easier Our priority for the next year will be to improve the new Project Service API so that it works in all places the traditional Program API does. We won't remove the traditional project program behavior unless and until the new Project Service API is able to fully replace it. -We're also looking forward to investigating support for [TypeScript's 10x faster Go port (tsgo / typescript-go)](https://github.com/typescript-eslint/typescript-eslint/issues/10940). +typescript-eslint@8.33.0 also provides a standalone [`@typescript-eslint/project-service`](/packages/project-service) package. +It contains the the wrappers around TypeScript's Project Service APIs that typescript-eslint uses for typed linting. +If you're working on your own linter, you can use this package to set up typed linting without taking a dependency on ESLint. +See [Packages > Project Service](/packages/project-service) for more details. + +We're also looking forward to investigating support for [TypeScript's 10x faster Go port](https://github.com/typescript-eslint/typescript-eslint/issues/10940). Abstracting configuration details into the Project Service API means it will be much easier for us to support typed linting with Go without any additional user configuration. So, please, try out the new Project Service API. From e5491c47f98349c0e6cccf6557dd5f7e37bec695 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 11:23:09 -0400 Subject: [PATCH 14/27] nit: line length 'own' --- packages/website/blog/2025-05-26-project-service.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index 2135e98b0640..c6e644abb750 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -187,7 +187,7 @@ See [⚡ Performance: parserOptions.projectService sometimes no longer outperfor ## Next Steps The new Project Service API is available in typescript-eslint v8.0.0 and later. -We've been using it in our own monorepo for over a year, and have been thrilled to see many community repositories adopt it as well. +We've been using it in our monorepo for over a year and have been thrilled to see many community repositories adopt it as well. ### Giving Feedback From 5b379fcf8fdc8ccd23521725ec1b6dc5326ddf6e Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 11:24:46 -0400 Subject: [PATCH 15/27] allowDefaultProject rename --- packages/website/blog/2025-05-26-project-service.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index c6e644abb750..dd39f80881b9 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -99,7 +99,7 @@ Other settings, including how you run ESLint and configure rules, should work th One long-standing pain point of typed linting was enabling typed linting for files not included in the project's `tsconfig.json`. Common solutions in the traditional Program API were to either skip typed linting for those files or to create a `tsconfig.eslint.json` enabling the `allowJs` compiler option. -The new Project Service API allows for a configuration object specifying `allowDefaultProjectForFiles`: a glob of "out-of-project" files to lint with type information. +The new Project Service API allows for a configuration object specifying `allowDefaultProject`: a glob of "out-of-project" files to lint with type information. That means you can lint those files without any new configuration files or TypeScript compiler options! For example, the following config solves the common case of projects that have root-level files like `eslint.config.js` and `vitest.config.ts`: @@ -113,7 +113,7 @@ export default tseslint.config({ languageOptions: { parserOptions: { projectService: { - allowDefaultProjectForFiles: ['*.js'], + allowDefaultProject: ['*.js'], }, }, }, @@ -130,7 +130,7 @@ module.exports = { parser: '@typescript-eslint/parser', parserOptions: { projectService: { - allowDefaultProjectForFiles: ['*.js'], + allowDefaultProject: ['*.js'], }, }, // ... From 6b7d8dd34b494377dc3cce52d013a7e64eba0792 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 11:25:41 -0400 Subject: [PATCH 16/27] touch up project refs --- packages/website/blog/2025-05-26-project-service.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index dd39f80881b9..4c1f73715350 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -162,9 +162,9 @@ It supports extra file extensions out-of-the-box and does not slow down when use ## Scalability -[TypeScript's project references](https://www.typescriptlang.org/docs/handbook/project-references.html) are how many larger projects, such as monorepos, are able to scale TypeScript type checking. +[TypeScript's project references](https://www.typescriptlang.org/docs/handbook/project-references.html) are how many larger projects, in particular monorepos, scale TypeScript type checking. They allow delineating discrete projects with their own `tsconfig.json` files and annotating which projects depend on which other projects. -Project references allow TypeScript to cache type information and only recheck projects that have changed in builds. +TypeScript is able to cache type information and only recheck projects that have changed in builds based on those project references. The traditional `parserOptions.project` API did not support project references for typed linting. We had experimented with adding support, but using the manual built-in TypeScript APIs would have been a significant maintenance investment with an unclear payoff. From aabd0645bdf5e83eb6b4d07793fddf1c29ed16f0 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 11:29:14 -0400 Subject: [PATCH 17/27] Touch up feedback --- packages/website/blog/2025-05-26-project-service.mdx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index 4c1f73715350..f5b14294a6aa 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -193,10 +193,11 @@ We've been using it in our monorepo for over a year and have been thrilled to se We'd love to hear from you on how this option works for you. Does it live up to what we've promised, and/or does it have bugs we haven't fixed yet? -Please do post in the [Community Feedback: Project Service API](https://github.com/typescript-eslint/typescript-eslint/discussions/8030) GitHub Discussion on how it goes for you. +Please do send us GitHub issues for any bugs you encounter or suggestions for how to improve the API. -For support in setting up the new APIs, feel free to ask on [the typescript-eslint Discord](https://discord.gg/FSxKq8Tdyg)'s `#project-service` channel. -We'd be happy to help you try out `parserOptions.projectService`. +The [typescript-eslint Discord](https://discord.gg/FSxKq8Tdyg) is a great place to ask questions and engage with us more casually. +For support in onboarding, feel free to ask in its `#help` channel. +We'd be happy to help you try out `parserOptions.projectService` and learn more about how you use typescript-eslint. ### Long Term Vision From 052ef50f10bc4e17933b151197140551640fc867 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 11:34:13 -0400 Subject: [PATCH 18/27] More proofreading --- .cspell.json | 1 + packages/website/blog/2025-05-26-project-service.mdx | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.cspell.json b/.cspell.json index ec67987a3b5a..e064c26d4d53 100644 --- a/.cspell.json +++ b/.cspell.json @@ -196,6 +196,7 @@ "tsconfigrootdir", "tsconfigs", "tseslint", + "tsgo", "tsvfs", "typedef", "typedefs", diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index f5b14294a6aa..36a09192985d 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -13,7 +13,8 @@ import TabItem from '@theme/TabItem'; It enables a slew of more powerful lint rules that check for nuanced bugs, best practice violations, and other code issues. Typed linting hasn't always been straightforward to configure or performant at runtime. -Some projects in previously had to even use separate `tsconfig.eslint.json` files to enable type information for linting. +We've seen user have to manage separate `tsconfig.eslint.json` files to enable typed linting — sometimes with different compiler options than the rest of the project. +Not ideal. In typescript-eslint 8.0, we stabilized a **`parserOptions.projectService`** option that uses more powerful, streamlined TypeScript APIs than before. The "Project Service" brings several benefits: @@ -22,7 +23,7 @@ The "Project Service" brings several benefits: - 🧠 **Predictability**: uses the same type information services as editors, including more reliability - 🚀 **Scalability**: supporting TypeScript project references for larger repositories (i.e. monorepos) -This blog post will cover how `parserOptions.projectService` simplifies configurations and brings linting type information much closer to what editors such as VS Code run with. +This blog post will cover how `parserOptions.projectService` simplifies configurations and aligns linting type information to what editors such as VS Code run with. :::tip See [Getting Started](/getting-started) to learn how to lint JavaScript and TypeScript code with typescript-eslint, then [Linting with Type Information](/linting/typed-linting) to onboard to typed linting. @@ -205,13 +206,12 @@ The new Project Service API is a great step towards making typed linting easier Our priority for the next year will be to improve the new Project Service API so that it works in all places the traditional Program API does. We won't remove the traditional project program behavior unless and until the new Project Service API is able to fully replace it. -typescript-eslint@8.33.0 also provides a standalone [`@typescript-eslint/project-service`](/packages/project-service) package. -It contains the the wrappers around TypeScript's Project Service APIs that typescript-eslint uses for typed linting. -If you're working on your own linter, you can use this package to set up typed linting without taking a dependency on ESLint. +As of typescript-eslint@8.33.0, we've also extracted most of the Project Service code into a standalone [`@typescript-eslint/project-service`](/packages/project-service) package. +It has no dependencies on ESLint or typescript-eslint, and is designed to be used by other projects that want to use TypeScript's Project Service API for typed linting. See [Packages > Project Service](/packages/project-service) for more details. We're also looking forward to investigating support for [TypeScript's 10x faster Go port](https://github.com/typescript-eslint/typescript-eslint/issues/10940). -Abstracting configuration details into the Project Service API means it will be much easier for us to support typed linting with Go without any additional user configuration. +Abstracting configuration details into the Project Service API means it will be much easier for typescript-eslint to support typed linting using "tsgo" without any additional user configuration. So, please, try out the new Project Service API. We're excited to hear how it works for you and what we can do to improve it. 💜 From 0cfdefd96b3c137f0330eb7aa5ace86b5deaac86 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 11:37:08 -0400 Subject: [PATCH 19/27] More proofreading --- .../website/blog/2025-05-26-project-service.mdx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index 36a09192985d..bde403cc14cf 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -9,11 +9,11 @@ title: Typed Linting with Project Service import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -["Typed linting"](/linting/typed-linting), or enabling ESLint rules to understand TypeScript types, is one of the best parts of typescript-eslint. -It enables a slew of more powerful lint rules that check for nuanced bugs, best practice violations, and other code issues. +["Typed linting"](/blog/typed-linting), or enabling ESLint rules to understand TypeScript types, is one of the best parts of typescript-eslint. +It enables a slew of more powerful lint rules that check for nuanced bugs, best practice violations, and other code issues that can only be detected using type information. Typed linting hasn't always been straightforward to configure or performant at runtime. -We've seen user have to manage separate `tsconfig.eslint.json` files to enable typed linting — sometimes with different compiler options than the rest of the project. +We've seen users have to manage separate `tsconfig.eslint.json` files to enable typed linting — sometimes with different compiler options than the rest of the project. Not ideal. In typescript-eslint 8.0, we stabilized a **`parserOptions.projectService`** option that uses more powerful, streamlined TypeScript APIs than before. @@ -93,7 +93,10 @@ module.exports = { That's it! Other settings, including how you run ESLint and configure rules, should work the same. -[Packages > Parser > `projectService`](/packages/parser#projectservice) has more details on granular configuration options. + +:::tip +See [Packages > Parser > `projectService`](/packages/parser#projectservice) for more details on granular configuration options. +::: ### Additional Files @@ -144,7 +147,9 @@ module.exports = { This means most projects should be able to remove lint-only `tsconfig.eslint.json` files! 🥳 -[Packages > Parser > `projectService` > `ProjectServiceOptions`](/packages/parser#projectserviceOptions) has more details on out-of-project files and other granular configuration options. +:::tip +See [Packages > Parser > `projectService` > `ProjectServiceOptions`](/packages/parser#projectserviceOptions) for more details on out-of-project files and other granular configuration options. +::: ## Predictability From 598ab00d565e9b405f2f89b855a97af2117d36c4 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 11:47:55 -0400 Subject: [PATCH 20/27] More proofreading --- .../blog/2025-05-26-project-service.mdx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index bde403cc14cf..e2f19a39dd2f 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -106,7 +106,7 @@ Common solutions in the traditional Program API were to either skip typed lintin The new Project Service API allows for a configuration object specifying `allowDefaultProject`: a glob of "out-of-project" files to lint with type information. That means you can lint those files without any new configuration files or TypeScript compiler options! -For example, the following config solves the common case of projects that have root-level files like `eslint.config.js` and `vitest.config.ts`: +For example, the following config solves the common case of projects that have root-level files like `eslint.config.js` or `vitest.config.ts`: @@ -144,7 +144,7 @@ module.exports = { -This means most projects should be able to remove lint-only `tsconfig.eslint.json` files! +This means most projects should be able to remove all `tsconfig.eslint.json` files! 🥳 :::tip @@ -153,17 +153,17 @@ See [Packages > Parser > `projectService` > `ProjectServiceOptions`](/packages/p ## Predictability -Without separate `tsconfig.eslint.json` files, you can be more confident that the type information used for linting is the same as the one used for type checking. -We've round this to be a common source of confusion for users, as well as an annoying extra file to maintain. +We've found configuring `tsconfig.eslint.json` to be a common source of confusion for users of the traditional Program APIs, as well as an annoying extra file to maintain. +They can easily result in the type information used for _linting_ accidentally being different from the type information used for _type checking_. +Unifying TypeScript configurations to the same `tsconfig.json` file(s) altogether avoids potential divergent types. -Another benefit of using the new Project Service API is that typed linting requires no additional configuration to work with extensions to TypeScript. -Many use cases couldn't be supported by the traditional Program API while still optimizing for performance. -For example: +Another benefit of using the new Project Service API is that typed linting requires no additional work in typescript-eslint for more difficult uses of ESLint and/or TypeScript. +We sometimes had to de-optimize the traditional Program API to support use cases: -- ESLint's `--fix` mode would lose type information after the first run ([#9577](https://github.com/typescript-eslint/typescript-eslint/pull/9577)) -- Extra file extensions, such as those used by `.svelte` and `.vue`, were not supported ([#9504](https://github.com/typescript-eslint/typescript-eslint/issues/9504)) +- CLI `--fix` mode would lose type information after the first pass ([#9577](https://github.com/typescript-eslint/typescript-eslint/pull/9577)) +- Extra file extensions such as `.svelte` and `.vue` were not supported at all ([#9504](https://github.com/typescript-eslint/typescript-eslint/issues/9504)) -`parserOptions.projectService` does not suffer from the same bugs as `parserOptions.project` with single-run mode. +The new Project Service API does not suffer from these issues. It supports extra file extensions out-of-the-box and does not slow down when used with ESLint's `--fix`. ## Scalability From ce6e9af0521844afdde311916e09c4c48366920a Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 11:51:27 -0400 Subject: [PATCH 21/27] More proofreading --- packages/website/blog/2025-05-26-project-service.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index e2f19a39dd2f..281f9f2622e2 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -212,7 +212,7 @@ Our priority for the next year will be to improve the new Project Service API so We won't remove the traditional project program behavior unless and until the new Project Service API is able to fully replace it. As of typescript-eslint@8.33.0, we've also extracted most of the Project Service code into a standalone [`@typescript-eslint/project-service`](/packages/project-service) package. -It has no dependencies on ESLint or typescript-eslint, and is designed to be used by other projects that want to use TypeScript's Project Service API for typed linting. +It has no dependencies on ESLint and is designed to be usable for any linter to enable TypeScript's Project Service API for typed linting. See [Packages > Project Service](/packages/project-service) for more details. We're also looking forward to investigating support for [TypeScript's 10x faster Go port](https://github.com/typescript-eslint/typescript-eslint/issues/10940). From 48bedd51cceebad0e2e9833d96e1007fb52122ae Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 11:55:03 -0400 Subject: [PATCH 22/27] More proofreading --- packages/website/blog/2025-05-26-project-service.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index 281f9f2622e2..7e0cc2d8ec78 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -153,8 +153,8 @@ See [Packages > Parser > `projectService` > `ProjectServiceOptions`](/packages/p ## Predictability -We've found configuring `tsconfig.eslint.json` to be a common source of confusion for users of the traditional Program APIs, as well as an annoying extra file to maintain. -They can easily result in the type information used for _linting_ accidentally being different from the type information used for _type checking_. +We've found configuring `tsconfig.eslint.json` files to be a common source of confusion with the traditional Program APIs. +They can result in the type information used for _linting_ accidentally being different from the type information used for _type checking_. Unifying TypeScript configurations to the same `tsconfig.json` file(s) altogether avoids potential divergent types. Another benefit of using the new Project Service API is that typed linting requires no additional work in typescript-eslint for more difficult uses of ESLint and/or TypeScript. From f08e16f95de7246b6d7406f1fc78c6527e81e1db Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 21 May 2025 12:00:12 -0400 Subject: [PATCH 23/27] fix broken link --- packages/website/blog/2025-05-26-project-service.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index 7e0cc2d8ec78..d40b78ab9a6e 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -26,7 +26,7 @@ The "Project Service" brings several benefits: This blog post will cover how `parserOptions.projectService` simplifies configurations and aligns linting type information to what editors such as VS Code run with. :::tip -See [Getting Started](/getting-started) to learn how to lint JavaScript and TypeScript code with typescript-eslint, then [Linting with Type Information](/linting/typed-linting) to onboard to typed linting. +See [Getting Started](/getting-started) to learn how to lint JavaScript and TypeScript code with typescript-eslint, then [Linting with Type Information](/getting-started/typed-linting) to onboard to typed linting. ::: From e791a1fa45c1d8a9a1f3a3ccc44df421353c6787 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Thu, 29 May 2025 15:52:53 +0300 Subject: [PATCH 24/27] Shoutout Benn and SvelteKit --- packages/website/blog/2025-05-26-project-service.mdx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index d40b78ab9a6e..962f6e919902 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -181,7 +181,7 @@ This is a huge win for monorepos, as it means you can lint all of your projects ## Performance Supporting project references allows the new Project Service API to be significantly faster than the traditional `parserOptions.project` API in many monorepo cases. -We've observed improvements for typed linting speed in real-world repositories [^1] [^2]. +We've observed improvements for typed linting speed in real-world repositories [^babel-conversion] [^create-t3-app-conversion] [^sveltekit-conversion]. For smaller projects, the performance of the new Project Service API is similar to the traditional `parserOptions.project` API. When we first started working with the new project service API, it outperformed equivalent `parserOptions.project` setups by ~10-15%. @@ -221,6 +221,8 @@ Abstracting configuration details into the Project Service API means it will be So, please, try out the new Project Service API. We're excited to hear how it works for you and what we can do to improve it. 💜 -[^1]: https://github.com/babel/babel/pull/16192#issue-2054613116 +[^babel-conversion]: https://github.com/babel/babel/pull/16192#issue-2054613116 -[^2]: https://github.com/t3-oss/create-t3-app/pull/1936/#discussion_r1667389041 +[^create-t3-app-conversion]: https://github.com/t3-oss/create-t3-app/pull/1936/#discussion_r1667389041 + +[^sveltekit-conversion]: https://github.com/sveltejs/kit/pull/13839 From b97b547ee0e1063d4fec4eef8f78242458377bca Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Thu, 29 May 2025 15:56:20 +0300 Subject: [PATCH 25/27] Add back tsconfigRootDir --- packages/website/blog/2025-05-26-project-service.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index 962f6e919902..df4ef29a0673 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -64,6 +64,7 @@ export default tseslint.config({ project: true, // Add this line projectService: true, + tsconfigRootDir: import.meta.dirname, }, }, // ... @@ -82,6 +83,7 @@ module.exports = { project: true, // Add this line projectService: true, + tsconfigRootDir: __dirname, }, // ... }; @@ -119,6 +121,7 @@ export default tseslint.config({ projectService: { allowDefaultProject: ['*.js'], }, + tsconfigRootDir: import.meta.dirname, }, }, // ... @@ -135,6 +138,7 @@ module.exports = { parserOptions: { projectService: { allowDefaultProject: ['*.js'], + tsconfigRootDir: __dirname, }, }, // ... From 15d054acaca445a82c96276211dcf70c4cdbe3bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Thu, 29 May 2025 15:56:55 +0300 Subject: [PATCH 26/27] Apply suggestions from code review Co-authored-by: Ronen Amiel --- packages/website/blog/2025-05-26-project-service.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index df4ef29a0673..230ddd87a57d 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -10,7 +10,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ["Typed linting"](/blog/typed-linting), or enabling ESLint rules to understand TypeScript types, is one of the best parts of typescript-eslint. -It enables a slew of more powerful lint rules that check for nuanced bugs, best practice violations, and other code issues that can only be detected using type information. +It enables a slew of [more powerful lint rules](/rules/?=recommended-typeInformation) that check for nuanced bugs, best practice violations, and other code issues that can only be detected using type information. Typed linting hasn't always been straightforward to configure or performant at runtime. We've seen users have to manage separate `tsconfig.eslint.json` files to enable typed linting — sometimes with different compiler options than the rest of the project. From d598bb98b7900324fc904f543737e95a830421fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Thu, 29 May 2025 15:57:02 +0300 Subject: [PATCH 27/27] Update packages/website/blog/2025-05-26-project-service.mdx Co-authored-by: Ronen Amiel --- packages/website/blog/2025-05-26-project-service.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/blog/2025-05-26-project-service.mdx b/packages/website/blog/2025-05-26-project-service.mdx index 230ddd87a57d..eb64c4fc6754 100644 --- a/packages/website/blog/2025-05-26-project-service.mdx +++ b/packages/website/blog/2025-05-26-project-service.mdx @@ -152,7 +152,7 @@ This means most projects should be able to remove all `tsconfig.eslint.json` fil 🥳 :::tip -See [Packages > Parser > `projectService` > `ProjectServiceOptions`](/packages/parser#projectserviceOptions) for more details on out-of-project files and other granular configuration options. +See [Packages > Parser > `projectService` > `ProjectServiceOptions`](/packages/parser#projectserviceoptions) for more details on out-of-project files and other granular configuration options. ::: ## Predictability