|
| 1 | +--- |
| 2 | +authors: |
| 3 | + - image_url: https://www.joshuakgoldberg.com/img/josh.jpg |
| 4 | + name: Josh Goldberg |
| 5 | + title: typescript-eslint Maintainer |
| 6 | + url: https://github.com/JoshuaKGoldberg |
| 7 | +description: Simplifying how many projects resolve their |
| 8 | +slug: parser-options-project-true |
| 9 | +tags: [parser, parser options, project, tsconfig] |
| 10 | +title: Relative TSConfig Projects with `parserOptions.project = true` |
| 11 | +--- |
| 12 | + |
| 13 | +["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. |
| 14 | +But enabling the type checker in repositories with multiple `tsconfig.json` files can be annoying to set up. |
| 15 | +Even worse, specifying the wrong include paths could result in incorrect rule reports and/or unexpectedly slow lint times. |
| 16 | + |
| 17 | +Improving the setup experience for typed lint rules has been a long-standing goal for typescript-eslint. |
| 18 | +One long-standing feature request for that experience has been to support automatically detecting TSConfigs for developers. |
| 19 | +We're happy to say that we now support that by setting `parserOptions.project` equal to `true` in ESLint configurations. |
| 20 | + |
| 21 | +This post will explain what life was like before, what's changed, and what's coming next. 🎉 |
| 22 | + |
| 23 | +<!--truncate--> |
| 24 | + |
| 25 | +## The Problem With Projects |
| 26 | + |
| 27 | +The `@typescript-eslint/parser` package is what enables ESLint to parse TypeScript source files. |
| 28 | +It converts raw TypeScript code into an ["AST" format](./2022-12-05-asts-and-typescript-eslint.md). |
| 29 | +When [`parserOptions.project`](/packages/parser#project) is specified, it additionally sets up TypeScript programs that can be used by [typed rules](/developers/custom-rules#typed-rules). |
| 30 | + |
| 31 | +Many projects today start with ESLint configs that look something like: |
| 32 | + |
| 33 | +```js |
| 34 | +module.exports = { |
| 35 | + // ... |
| 36 | + parserOptions: { |
| 37 | + project: './tsconfig.json', |
| 38 | + tsconfigRootDir: __dirname, |
| 39 | + }, |
| 40 | + // ... |
| 41 | +}; |
| 42 | +``` |
| 43 | + |
| 44 | +In larger repos, `parserOptions.project` often ends up being one of the three traditionally allowed forms: |
| 45 | + |
| 46 | +- Path, such as `project: './tsconfig.json'` |
| 47 | +- Glob pattern, such as `project: './packages/**/tsconfig.json'` |
| 48 | +- Array of paths and/or glob patterns, such as `project: ['./packages/**/tsconfig.json', './separate-package/tsconfig.json']` |
| 49 | + |
| 50 | +Explicitly indicating which TSConfig files are used for typed linting can be useful. |
| 51 | +Developers like being given explicit control over their tooling. |
| 52 | +However, we've seen a few issues arise from this approach: |
| 53 | + |
| 54 | +- Particularly large repos can end up with so many TSConfig globs, they become confusing to developers or even cause [performance issues from overly permissive globs](/linting/troubleshooting/performance-troubleshooting#wide-includes-in-your-eslint-options) |
| 55 | +- Needing to change a template ESLint config every time it's used for a different repository structure is a pain |
| 56 | +- Using a TSConfig that's different from what your editor uses can result in different lint reports between the editor and the command-line |
| 57 | + |
| 58 | +Although developers may sometimes need exact control over their `parserOptions.project`, most of the time we just want to use the _nearest `tsconfig.json` to each linted file_, which is the TSConfig used by the editor by default. |
| 59 | + |
| 60 | +In other words, many developers want our [issue #101: Feature request: support looking up tsconfig.json relative to linted file](https://github.com/typescript-eslint/typescript-eslint/issues/101). |
| 61 | + |
| 62 | +## Introducing `true` |
| 63 | + |
| 64 | +As of typescript-eslint 5.52.0, we now support providing `true` for `parserOptions.project`: |
| 65 | + |
| 66 | +```js |
| 67 | +module.exports = { |
| 68 | + // ... |
| 69 | + parserOptions: { |
| 70 | + project: true, |
| 71 | + tsconfigRootDir: __dirname, |
| 72 | + }, |
| 73 | + // ... |
| 74 | +}; |
| 75 | +``` |
| 76 | + |
| 77 | +Doing so indicates that each source file being linted should use type information based on the nearest `tsconfig.json` in its directory. |
| 78 | +For each file, `@typescript-eslint/parser` will check that file's directory, then the parent directory, and so on - until a `tsconfig.json` file is found. |
| 79 | + |
| 80 | +:::tip |
| 81 | +We recommend setting the [`tsconfigRootDir`](/packages/parser#tsconfigrootdir) ESLint config to the project's root directory (most commonly, `__dirname`). |
| 82 | +That way, if you accidentally delete or rename the root `tsconfig.json` file, `@typescript-eslint/parser` won't search parent directories for higher `tsconfig.json` files. |
| 83 | +::: |
| 84 | + |
| 85 | +### Why Try `true` |
| 86 | + |
| 87 | +If your project uses typed linting and manually specifies `tsconfig.json` files, we'd highly recommend trying out `parserOptions.project: true`. |
| 88 | +We've seen it reduce lines of code in ESLint configurations in many early adopters. |
| 89 | +Sometimes, it even reduces time spent on typed linting by helping projects use a simpler set of TSConfigs. 🚀 |
| 90 | + |
| 91 | +In the long term, we're hoping to further improve the configuration and performance for typed linting (see _[Project Services](#project-services)_ below). |
| 92 | +Simplifying your configuration now will make it easier to onboard to our new options when they're available. |
| 93 | + |
| 94 | +### How It Works |
| 95 | + |
| 96 | +When `@typescript-eslint/parser` is configured to generate type information, it attaches a backing TypeScript "Program" for each file it parses. |
| 97 | +Those Programs provide type checking APIs used by lint rules. |
| 98 | +Each TSConfig file on disk is generally used to create exactly one Program, and files included by the same TSConfig file will reuse the same Program. |
| 99 | + |
| 100 | +Depending on how the ESLint config's `parserOptions.project` was specified, determining _which_ TSConfig file to use for each file can be different: |
| 101 | + |
| 102 | +- For a single path (e.g. `"tsconfig.json"`), only one Program will be created, and all linted files will reuse it. |
| 103 | +- For globs and/or arrays (e.g. `"./packages/*/tsconfig.json"`), each linted file will use the Program created by the _first_ matched TSConfig file. |
| 104 | + |
| 105 | +For `true`, each linted file will first try the `tsconfig.json` in its directory, then its parent directory, and so on until one is found on disk or the directory root (`parserOptions.tsconfigRootDir`) is reached. |
| 106 | + |
| 107 | +:::note |
| 108 | +`@typescript-eslint/parser` caches those directory `tsconfig.json` file lookups for a duration corresponding to [`parserOptions.cacheLifetime`](/packages/parser#cachelifetime). |
| 109 | +No potential TSConfig path should be checked more than once in a lint run. |
| 110 | +::: |
| 111 | + |
| 112 | +See [feat(typescript-estree): allow specifying project: true](https://github.com/typescript-eslint/typescript-eslint/pull/6084) for the backing code changes. |
| 113 | + |
| 114 | +## What's Next |
| 115 | + |
| 116 | +### Investigating Custom TSConfig Names |
| 117 | + |
| 118 | +Some projects use TSConfig files with names other than `tsconfig.json`: most commonly, `tsconfig.eslint.json`. |
| 119 | +`parserOptions.project: true` does not support specifying different name(s) to search for. |
| 120 | +We have two followup issues filed to investigate fleshing out that support: |
| 121 | + |
| 122 | +- [Enhancement: Allow altering the file names that project: true searches for](https://github.com/typescript-eslint/typescript-eslint/issues/7383) |
| 123 | +- [Enhancement: Allow parserOptions.project to be (true | string)[]?](https://github.com/typescript-eslint/typescript-eslint/issues/7384) |
| 124 | + |
| 125 | +If either of those two issues would benefit you, please 👍 react to them. |
| 126 | +And if your project has a use case not yet mentioned in their comments, please post that use case. |
| 127 | +We want to know what's important for users! |
| 128 | + |
| 129 | +### Project Services |
| 130 | + |
| 131 | +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. |
| 132 | +Manual Program creation logic comes with a few issues: |
| 133 | + |
| 134 | +- Complex project setups can be difficult to get right. |
| 135 | + - For example, [typescript-eslint does not yet support Project References](https://github.com/typescript-eslint/typescript-eslint/issues/2094). |
| 136 | +- The TypeScript compiler options used in the user's editor might differ from the compiler options in the TSConfigs they specified on disk. |
| 137 | +- 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. |
| 138 | + - 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). |
| 139 | + |
| 140 | +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. |
| 141 | +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`). |
| 142 | + |
| 143 | +We hope this option will eventually become the standard way to enable typed linting. |
| 144 | +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. |
| 145 | + |
| 146 | +See [Packages > Parser > `EXPERIMENTAL_useProjectService`](/packages/parser#EXPERIMENTAL_useProjectService) for more information. |
| 147 | + |
| 148 | +## Supporting typescript-eslint |
| 149 | + |
| 150 | +If you enjoyed this blog post and/or use typescript-eslint, please consider [supporting us on Open Collective](https://opencollective.com/typescript-eslint). |
| 151 | +We're a small volunteer team and could use your support to make the ESLint experience on TypeScript great. |
| 152 | +Thanks! 💖 |
0 commit comments