diff --git a/docs/packages/Parser.mdx b/docs/packages/Parser.mdx index c75ca14a8855..a75c1ca8429b 100644 --- a/docs/packages/Parser.mdx +++ b/docs/packages/Parser.mdx @@ -283,7 +283,8 @@ For an option that allows linting files outside of your TSConfig file(s), see [` > Default `false`. 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`). +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`). @@ -320,24 +321,21 @@ 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 -- 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 +- Predictability: it uses the same type information services as editors, giving better consistency with the types seen in editors -For more information, see: - -- [feat(typescript-estree): add EXPERIMENTAL_useProjectService option to use TypeScript project service](https://github.com/typescript-eslint/typescript-eslint/pull/6754) -- [docs: blog post on parserOptions.projectService](https://github.com/typescript-eslint/typescript-eslint/pull/8031) +See [FAQs > Typed Linting > Project Service Issues](../troubleshooting/typed-linting/index.mdx#project-service-issues) for help on working with the project service. #### `ProjectServiceOptions` 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 +344,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 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 a string path 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/docs/troubleshooting/typed-linting/index.mdx b/docs/troubleshooting/typed-linting/index.mdx index f1453b63f972..1ac1623e145a 100644 --- a/docs/troubleshooting/typed-linting/index.mdx +++ b/docs/troubleshooting/typed-linting/index.mdx @@ -84,18 +84,21 @@ If the IDE provides different type information from typescript-eslint's report, ## Are TypeScript project references supported? -Yes, but only with [`EXPERIMENTAL_useProjectService`](../../packages/Parser.mdx#experimental_useprojectservice). +Yes, but only with [`parserOptions.projectService`](../../packages/Parser.mdx#projectservice). See [issue #2094 discussing project references](https://github.com/typescript-eslint/typescript-eslint/issues/2094) for more details. ## Project Service Issues - - +[`parserOptions.projectService`](../../packages/Parser.mdx#projectservice) is the recommended parser option to enable typed linting as of typescript-eslint v8. +It enforces projects generate type information for typed linting from the same `tsconfig.json` files used by editors such as VS Code. -### I get errors telling me "Having many files run with the default project is known to cause performance issues and slow down linting." +### I get errors telling me "... was not found by the project service. Consider either including it in the tsconfig.json or including it in allowDefaultProject" + +These errors are caused by attempting to use the project service to lint a file not explicitly included in its nearest `tsconfig.json`. -These errors are caused by attempting to use the [`projectService`](../../packages/Parser.mdx#projectservice) to lint a file not explicitly included in a `tsconfig.json`. +The project service will attempt to build type information for each file being linted using the nearest `tsconfig.json` on disk to that file. +If that `tsconfig.json` does not include the file, and the file isn't allowlisted in [`allowDefaultProject`](../../packages/parser#allowdefaultproject), then the project service will throw this error. For each file being reported: @@ -111,13 +114,61 @@ For each file being reported: + "*.js" ] ``` - 2. Otherwise, consider setting [`parserOptions.createProjectService.allowDefaultProject`](../../packages/parser#allowdefaultproject). + 2. Otherwise, if you have a small number of "out of project" files, try setting [`projectService.allowDefaultProject`](../../packages/parser#allowdefaultproject). + 3. If not, you can switch to [`parserOptions.project`](../../packages/Parser.mdx#project) for more fine-grained control of projects. + +Note also: + +- TSConfigs don't include `.js` files by default. + Enabling [`allowJs`](https://www.typescriptlang.org/tsconfig/#allowJs) or [`checkJs`](https://www.typescriptlang.org/tsconfig/#checkJs) is required to do so. +- The project service _only_ looks at `tsconfig.json` files. + It does not look at `tsconfig.eslint.json` or other coincidentally-similarly-named files. + +If these steps don't work for you, please [file an issue on typescript-eslint's typescript-estree package](https://github.com/typescript-eslint/typescript-eslint/issues/new?assignees=&labels=enhancement%2Ctriage&projects=&template=07-enhancement-other.yaml&title=Enhancement%3A+%3Ca+short+description+of+my+proposal%3E) telling us your use case and why you need more out-of-project files linted. +Be sure to include a minimal reproduction we can work with to understand your use case! + + + +### I get errors telling me "Having many files run with the default project is known to cause performance issues and slow down linting." + +These errors are caused by attempting to use the project service to lint too many files not explicitly included in a `tsconfig.json` with its [`allowDefaultProject`](../../packages/parser#allowdefaultproject) option. typescript-eslint allows up to 8 "out of project" files by default. Each file causes a new TypeScript "program" to be built for each file it includes, which incurs a performance overhead _for each file_. -If you cannot do this, please [file an issue on typescript-eslint's typescript-estree package](https://github.com/typescript-eslint/typescript-eslint/issues/new?assignees=&labels=enhancement%2Ctriage&projects=&template=07-enhancement-other.yaml&title=Enhancement%3A+%3Ca+short+description+of+my+proposal%3E) telling us your use case and why you need more out-of-project files linted. -Be sure to include a minimal reproduction we can work with to understand your use case! +For each file being reported: + +- If you **do not** want to lint the file: + - Use [one of the options ESLint offers to ignore files](https://eslint.org/docs/latest/user-guide/configuring/ignoring-code), such an `ignores` config key. +- If you **do** want to lint the file: + - If you **do not** want to lint the file with [type-aware linting](../../getting-started/Typed_Linting.mdx): [disable type-checked linting for that file](#how-do-i-disable-type-checked-linting-for-a-file). + - If you **do** want to lint the file with [type-aware linting](../../getting-started/Typed_Linting.mdx): + 1. If possible, add the file to the closest `tsconfig.json`'s `include` instead of adding it to `allowDefaultProject`. For example, allowing `.js` files: + ```diff title="tsconfig.json" + "include": [ + "src", + + "*.js" + ] + ``` + 2. If not, you can switch to [`parserOptions.project`](../../packages/Parser.mdx#project) for more fine-grained control of projects. + +### I'd like to use TSConfigs other than `tsconfig.json`s for project service type information + +Only the TSConfig path used for "out of project" files in [`allowDefaultProject`](../../packages/Parser.mdx#allowdefaultproject) can be customized. +Otherwise, only `tsconfig.json` files on disk will be read. + +For example, instead of: + +- `tsconfig.json`s for building (and, coincidentally, type information in editors) +- Separate TSConfig(s) like `tsconfig.eslint.json` for linting + +Consider using: + +- `tsconfig.json`s for linting (and, intentionally, the same type information in editors) +- Separate TSConfig(s) like `tsconfig.build.json` for building + +The project service uses the same underlying TypeScript logic as editors such as VS Code. +Using only `tsconfig.json` for typed linting enforces that the types seen in your editor match what's used for linting. ## Traditional Project Issues diff --git a/packages/typescript-estree/tests/lib/createProjectService.test.ts b/packages/typescript-estree/tests/lib/createProjectService.test.ts index b708188722e1..16b801c007a6 100644 --- a/packages/typescript-estree/tests/lib/createProjectService.test.ts +++ b/packages/typescript-estree/tests/lib/createProjectService.test.ts @@ -61,7 +61,7 @@ describe('createProjectService', () => { expect(settings.allowDefaultProject).toBeUndefined(); }); - it('throws an error when options.defaultProject is set and getParsedConfigFile throws a diagnostic error', () => { + it('throws an error with a relative path when options.defaultProject is set to a relative path and getParsedConfigFile throws a diagnostic error', () => { mockGetParsedConfigFile.mockImplementation(() => { throw new Error('./tsconfig.json(1,1): error TS1234: Oh no!'); }); @@ -80,6 +80,25 @@ describe('createProjectService', () => { ); }); + it('throws an error with a local path when options.defaultProject is set to a local path and getParsedConfigFile throws a diagnostic error', () => { + mockGetParsedConfigFile.mockImplementation(() => { + throw new Error('./tsconfig.json(1,1): error TS1234: Oh no!'); + }); + + expect(() => + createProjectService( + { + allowDefaultProject: ['file.js'], + defaultProject: 'tsconfig.json', + }, + undefined, + undefined, + ), + ).toThrow( + /Could not read default project 'tsconfig.json': .+ error TS1234: Oh no!/, + ); + }); + it('throws an error when options.defaultProject is set and getParsedConfigFile throws an environment error', () => { mockGetParsedConfigFile.mockImplementation(() => { throw new Error( @@ -91,13 +110,13 @@ describe('createProjectService', () => { createProjectService( { allowDefaultProject: ['file.js'], - defaultProject: './tsconfig.json', + defaultProject: 'tsconfig.json', }, undefined, undefined, ), ).toThrow( - "Could not read default project './tsconfig.json': `getParsedConfigFile` is only supported in a Node-like environment.", + "Could not read default project 'tsconfig.json': `getParsedConfigFile` is only supported in a Node-like environment.", ); }); @@ -108,7 +127,7 @@ describe('createProjectService', () => { const { service } = createProjectService( { allowDefaultProject: ['file.js'], - defaultProject: './tsconfig.json', + defaultProject: 'tsconfig.json', }, undefined, undefined, @@ -127,7 +146,7 @@ describe('createProjectService', () => { const { service } = createProjectService( { allowDefaultProject: ['file.js'], - defaultProject: './tsconfig.json', + defaultProject: 'tsconfig.json', }, undefined, tsconfigRootDir, @@ -139,7 +158,7 @@ describe('createProjectService', () => { expect(mockGetParsedConfigFile).toHaveBeenCalledWith( // eslint-disable-next-line @typescript-eslint/no-require-imports require('typescript/lib/tsserverlibrary'), - './tsconfig.json', + 'tsconfig.json', tsconfigRootDir, ); }); 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 141b7b39fc94..4e5f8aa3f2f2 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/src/css/custom.css b/packages/website/src/css/custom.css index 825b824a9b6a..1fe7a2879640 100644 --- a/packages/website/src/css/custom.css +++ b/packages/website/src/css/custom.css @@ -211,3 +211,7 @@ h6 { td > p:last-child { margin-bottom: 0; } + +h5 { + font-weight: bold; +}