Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(typescript-estree): clarify docs and error for program project without matching TSConfig #5762

Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 40 additions & 4 deletions docs/linting/TROUBLESHOOTING.md
Expand Up @@ -28,12 +28,48 @@ If you don't find an existing extension rule, or the extension rule doesn't work
> We release a new version our tooling every week.
> _Please_ ensure that you [check our the latest list of "extension" rules](https://typescript-eslint.io/rules/#extension-rules) **_before_** filing an issue.

## I get errors telling me "The file must be included in at least one of the projects provided"
## I get errors telling me "ESLint was configured to run ... However, that TSConfig does not / none of those TSConfigs include this file"
bradzacher marked this conversation as resolved.
Show resolved Hide resolved

### Fixing the Error
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I split this section into these two h3s. Re-reading the un-split explanation felt like a tl;dr. My instinct is users just want to know how to fix this ASAP.


- If you **do not** want to lint the file:
- Use [one of the options ESLint offers](https://eslint.org/docs/latest/user-guide/configuring/ignoring-code) to ignore files, namely a `.eslintignore` file, or `ignorePatterns` config.
- If you **do** want to lint the file:
- If you **do not** want to lint the file with [type-aware linting](./TYPED_LINTING.md):
- Use [ESLint's `overrides` configuration](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#configuration-based-on-glob-patterns) to configure the file to not be parsed with type information.
- A popular setup is to omit the above additions from top-level configuration and only apply them to TypeScript files via an override.
- Alternatively, you can add `parserOptions: { project: null }` to an override for the files you wish to exclude. Note that `{ project: undefined }` will not work.
- If you **do** want to lint the file with [type-aware linting](./TYPED_LINTING.md):
- Check the `include` option of each of the tsconfigs that you provide to `parserOptions.project` - you must ensure that all files match an `include` glob, or else our tooling will not be able to find it.
- If your file shouldn't be a part of one of your existing tsconfigs (for example, it is a script/tool local to the repo), then consider creating a new tsconfig (we advise calling it `tsconfig.eslint.json`) in your project root which lists this file in its `include`. For an example of this, you can check out the configuration we use in this repo:
- [`tsconfig.eslint.json`](https://github.com/typescript-eslint/typescript-eslint/blob/main/tsconfig.eslint.json)
- [`.eslintrc.js`](https://github.com/typescript-eslint/typescript-eslint/blob/main/.eslintrc.js)

### More Details

This error may appear from the combination of two things:

- The ESLint configuration for the source file specifies at least one TSConfig file in `parserOptions.project`
- None of those TSConfig files includes the source file being linted
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
- Note that files with the same name and different extension may not be recognized by TypeScript: see [`parserOptions.project` docs](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/parser#parseroptionsproject)

This error means that the file that's being linted is not included in any of the TSConfig files you provided us.
This happens when users have test files, config files, or similar that are not included.
When TSConfig files are specified for parsing a source file, `@typescript-eslint/parser` will use the first TSConfig that is able to include that source file (per [aka.ms/tsconfig#include](https://www.typescriptlang.org/tsconfig#include)) to generate type information.
However, if no specified TSConfig includes the source file, the parser won't be able to generate type information.

This error most commonly happens on config files or similar that are not included in their project TSConfig(s).
For example, many projects have files like:

- An `.eslintrc.cjs` with `parserOptions.project: ["./tsconfig.json"]`
- A `tsconfig.json` with `include: ["src"]`

In that case, viewing the `.eslintrc.cjs` in an IDE with the ESLint extension will show the error notice that the file couldn't be linted because it isn't included in `tsconfig.json`.

See our docs on [type aware linting](./TYPED_LINTING.md) for more information.

## I get errors telling me "The file must be included in at least one of the projects provided"

See our docs on [type aware linting](./TYPED_LINTING.md#i-get-errors-telling-me-the-file-must-be-included-in-at-least-one-of-the-projects-provided) for solutions.
You're using an outdated version of `@typescript-eslint/parser`.
Update to the latest version to see a more informative version of this error message, explained [above](#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file).

## I use a framework (like Vue) that requires custom file extensions, and I get errors like "You should add `parserOptions.extraFileExtensions` to your config"

Expand Down
19 changes: 2 additions & 17 deletions docs/linting/TYPED_LINTING.md
Expand Up @@ -52,23 +52,8 @@ This means that generally they usually only run a complete lint before a push, o

### I get errors telling me "The file must be included in at least one of the projects provided"

This error means that the file that's being linted is not included in any of the tsconfig files you provided us.
A lot of the time this happens when users have test files or similar that are not included in their normal tsconfigs.

Depending on what you want to achieve:

- If you **do not** want to lint the file:
- Use [one of the options ESLint offers](https://eslint.org/docs/user-guide/configuring#ignoring-files-and-directories) to ignore files, like a `.eslintignore` file, or `ignorePatterns` config.
- If you **do** want to lint the file:
- If you **do not** want to lint the file with [type-aware linting](./TYPED_LINTING.md):
- Use [ESLint's `overrides` configuration](https://eslint.org/docs/user-guide/configuring#configuration-based-on-glob-patterns) to configure the file to not be parsed with type information.
- A popular setup is to omit the above additions from top-level configuration and only apply them to TypeScript files via an override.
- Alternatively, you can add `parserOptions: { project: null }` to an override for the files you wish to exclude. Note that `{ project: undefined }` will not work.
- If you **do** want to lint the file with [type-aware linting](./TYPED_LINTING.md):
- Check the `include` option of each of the tsconfigs that you provide to `parserOptions.project` - you must ensure that all files match an `include` glob, or else our tooling will not be able to find it.
- If your file shouldn't be a part of one of your existing tsconfigs (for example, it is a script/tool local to the repo), then consider creating a new tsconfig (we advise calling it `tsconfig.eslint.json`) in your project root which lists this file in its `include`. For an example of this, you can check out the configuration we use in this repo:
- [`tsconfig.eslint.json`](https://github.com/typescript-eslint/typescript-eslint/blob/main/tsconfig.eslint.json)
- [`.eslintrc.js`](https://github.com/typescript-eslint/typescript-eslint/blob/main/.eslintrc.js)
You're using an outdated version of `@typescript-eslint/parser`.
Update to the latest version to see a more informative version of this error message, explained [Troubleshooting and FAQs](./TROUBLESHOOTING.md##i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file).

## Troubleshooting

Expand Down
113 changes: 69 additions & 44 deletions packages/typescript-estree/src/create-program/createProjectProgram.ts
Expand Up @@ -34,65 +34,90 @@ function createProjectProgram(
): ASTAndProgram | undefined {
log('Creating project program for: %s', extra.filePath);

const astAndProgram = firstDefined(
getProgramsForProjects(code, extra.filePath, extra),
currentProgram => getAstFromProgram(currentProgram, extra),
const programsForProjects = getProgramsForProjects(
code,
extra.filePath,
extra,
);
const astAndProgram = firstDefined(programsForProjects, currentProgram =>
getAstFromProgram(currentProgram, extra),
);

if (!astAndProgram && !createDefaultProgram) {
// the file was either not matched within the tsconfig, or the extension wasn't expected
const errorLines = [
'"parserOptions.project" has been set for @typescript-eslint/parser.',
`The file does not match your project config: ${path.relative(
extra.tsconfigRootDir || process.cwd(),
extra.filePath,
)}.`,
];
let hasMatchedAnError = false;
// The file was either matched within the tsconfig, or we allow creating a default program
if (astAndProgram || createDefaultProgram) {
return astAndProgram;
}

const extraFileExtensions = extra.extraFileExtensions || [];
const describeFilePath = (filePath: string): string => {
const relative = path.relative(
extra.tsconfigRootDir || process.cwd(),
filePath,
);
if (extra.tsconfigRootDir) {
return `<tsconfigRootDir>/${relative}`;
}
return `<cwd>/${relative}`;
};

extraFileExtensions.forEach(extraExtension => {
if (!extraExtension.startsWith('.')) {
errorLines.push(
`Found unexpected extension "${extraExtension}" specified with the "extraFileExtensions" option. Did you mean ".${extraExtension}"?`,
);
}
if (DEFAULT_EXTRA_FILE_EXTENSIONS.includes(extraExtension)) {
errorLines.push(
`You unnecessarily included the extension "${extraExtension}" with the "extraFileExtensions" option. This extension is already handled by the parser by default.`,
);
}
});
const describedFilePath = describeFilePath(extra.filePath);
const relativeProjects = extra.projects.map(describeFilePath);
const describedPrograms =
relativeProjects.length === 1
? relativeProjects[0]
: `\n${relativeProjects.map(project => `- ${project}`).join('\n')}`;
bradzacher marked this conversation as resolved.
Show resolved Hide resolved
const errorLines = [
`ESLint was configured to run on \`${describedFilePath}\` using \`parserOptions.project\`: ${describedPrograms}`,
];
let hasMatchedAnError = false;

const extraFileExtensions = extra.extraFileExtensions || [];

const fileExtension = path.extname(extra.filePath);
if (!DEFAULT_EXTRA_FILE_EXTENSIONS.includes(fileExtension)) {
const nonStandardExt = `The extension for the file (${fileExtension}) is non-standard`;
if (extraFileExtensions.length > 0) {
if (!extraFileExtensions.includes(fileExtension)) {
errorLines.push(
`${nonStandardExt}. It should be added to your existing "parserOptions.extraFileExtensions".`,
);
hasMatchedAnError = true;
}
} else {
extraFileExtensions.forEach(extraExtension => {
if (!extraExtension.startsWith('.')) {
errorLines.push(
`Found unexpected extension \`${extraExtension}\` specified with the \`parserOptions.extraFileExtensions\` option. Did you mean \`.${extraExtension}\`?`,
);
}
if (DEFAULT_EXTRA_FILE_EXTENSIONS.includes(extraExtension)) {
errorLines.push(
`You unnecessarily included the extension \`${extraExtension}\` with the \`parserOptions.extraFileExtensions\` option. This extension is already handled by the parser by default.`,
);
}
});

const fileExtension = path.extname(extra.filePath);
if (!DEFAULT_EXTRA_FILE_EXTENSIONS.includes(fileExtension)) {
const nonStandardExt = `The extension for the file (\`${fileExtension}\`) is non-standard`;
if (extraFileExtensions.length > 0) {
if (!extraFileExtensions.includes(fileExtension)) {
errorLines.push(
`${nonStandardExt}. You should add "parserOptions.extraFileExtensions" to your config.`,
`${nonStandardExt}. It should be added to your existing \`parserOptions.extraFileExtensions\`.`,
);
hasMatchedAnError = true;
}
}

if (!hasMatchedAnError) {
} else {
errorLines.push(
'The file must be included in at least one of the projects provided.',
`${nonStandardExt}. You should add \`parserOptions.extraFileExtensions\` to your config.`,
);
hasMatchedAnError = true;
}
}

throw new Error(errorLines.join('\n'));
if (!hasMatchedAnError) {
const [describedInclusions, describedSpecifiers] =
extra.projects.length === 1
? ['that TSConfig does not', 'that TSConfig']
: ['none of those TSConfigs', 'one of those TSConfigs'];
errorLines.push(
`However, ${describedInclusions} include this file. Either:`,
`- Change ESLint's list of included files to not include this file`,
`- Change ${describedSpecifiers} to include this file`,
`- Create a new TSConfig that includes this file and include it in your parserOptions.project`,
`See the TypeScript ESLint docs for more info: https://typescript-eslint.io/docs/linting/troubleshooting##i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file`,
);
}

return astAndProgram;
throw new Error(errorLines.join('\n'));
}

export { createProjectProgram };
@@ -0,0 +1 @@
{}