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 9 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
40 changes: 36 additions & 4 deletions docs/linting/TROUBLESHOOTING.md
Expand Up @@ -28,12 +28,44 @@ 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

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)

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:

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.
- 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`.

- 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)

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
105 changes: 61 additions & 44 deletions packages/typescript-estree/src/create-program/createProjectProgram.ts
Expand Up @@ -34,65 +34,82 @@ 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 =>
path.relative(extra.tsconfigRootDir || process.cwd(), filePath);
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved

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 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 {
const extraFileExtensions = extra.extraFileExtensions || [];

extraFileExtensions.forEach(extraExtension => {
if (!extraExtension.startsWith('.')) {
errorLines.push(
`Found unexpected extension "${extraExtension}" specified with the "extraFileExtensions" option. Did you mean ".${extraExtension}"?`,
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
);
}
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.`,
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
);
}
});

const fileExtension = path.extname(extra.filePath);
if (!DEFAULT_EXTRA_FILE_EXTENSIONS.includes(fileExtension)) {
const nonStandardExt = `The extension for the file (${fileExtension}) is non-standard`;
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
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".`,
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
);
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.`,
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
);
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#`,
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
);
}

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

export { createProjectProgram };
@@ -0,0 +1 @@
{}
@@ -1,59 +1,85 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`parseAndGenerateServices invalid file error messages "parserOptions.extraFileExtensions" is empty the extension does not match 1`] = `
""parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: other/unknownFileType.unknown.
"ESLint was configured to run on \`other/unknownFileType.unknown\` using \`parserOptions.project\`: tsconfig.json
The extension for the file (.unknown) is non-standard. You should add "parserOptions.extraFileExtensions" to your config."
`;

exports[`parseAndGenerateServices invalid file error messages "parserOptions.extraFileExtensions" is non-empty invalid extension 1`] = `
""parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: other/unknownFileType.unknown.
"ESLint was configured to run on \`other/unknownFileType.unknown\` using \`parserOptions.project\`: tsconfig.json
Found unexpected extension "unknown" specified with the "extraFileExtensions" option. Did you mean ".unknown"?
The extension for the file (.unknown) is non-standard. It should be added to your existing "parserOptions.extraFileExtensions"."
`;

exports[`parseAndGenerateServices invalid file error messages "parserOptions.extraFileExtensions" is non-empty the extension does not match 1`] = `
""parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: other/unknownFileType.unknown.
"ESLint was configured to run on \`other/unknownFileType.unknown\` using \`parserOptions.project\`: tsconfig.json
The extension for the file (.unknown) is non-standard. It should be added to your existing "parserOptions.extraFileExtensions"."
`;

exports[`parseAndGenerateServices invalid file error messages "parserOptions.extraFileExtensions" is non-empty the extension matches duplicate extension 1`] = `
""parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: ts/notIncluded.ts.
"ESLint was configured to run on \`ts/notIncluded.ts\` using \`parserOptions.project\`: tsconfig.json
You unnecessarily included the extension ".ts" with the "extraFileExtensions" option. This extension is already handled by the parser by default.
The file must be included in at least one of the projects provided."
However, that TSConfig does not include this file. Either:
- Change ESLint's list of included files to not include this file
- Change that TSConfig 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#"
`;

exports[`parseAndGenerateServices invalid file error messages "parserOptions.extraFileExtensions" is non-empty the extension matches the file isn't included 1`] = `
""parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: other/notIncluded.vue.
The file must be included in at least one of the projects provided."
"ESLint was configured to run on \`other/notIncluded.vue\` using \`parserOptions.project\`: tsconfig.json
However, that TSConfig does not include this file. Either:
- Change ESLint's list of included files to not include this file
- Change that TSConfig 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#"
`;

exports[`parseAndGenerateServices invalid file error messages project includes errors for not included files 1`] = `
""parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: ts/notIncluded0j1.ts.
The file must be included in at least one of the projects provided."
"ESLint was configured to run on \`ts/notIncluded0j1.ts\` using \`parserOptions.project\`: tsconfig.json
However, that TSConfig does not include this file. Either:
- Change ESLint's list of included files to not include this file
- Change that TSConfig 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#"
`;

exports[`parseAndGenerateServices invalid file error messages project includes errors for not included files 2`] = `
""parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: ts/notIncluded02.tsx.
The file must be included in at least one of the projects provided."
"ESLint was configured to run on \`ts/notIncluded02.tsx\` using \`parserOptions.project\`: tsconfig.json
However, that TSConfig does not include this file. Either:
- Change ESLint's list of included files to not include this file
- Change that TSConfig 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#"
`;

exports[`parseAndGenerateServices invalid file error messages project includes errors for not included files 3`] = `
""parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: js/notIncluded01.js.
The file must be included in at least one of the projects provided."
"ESLint was configured to run on \`js/notIncluded01.js\` using \`parserOptions.project\`: tsconfig.json
However, that TSConfig does not include this file. Either:
- Change ESLint's list of included files to not include this file
- Change that TSConfig 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#"
`;

exports[`parseAndGenerateServices invalid file error messages project includes errors for not included files 4`] = `
""parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: js/notIncluded02.jsx.
The file must be included in at least one of the projects provided."
"ESLint was configured to run on \`js/notIncluded02.jsx\` using \`parserOptions.project\`: tsconfig.json
However, that TSConfig does not include this file. Either:
- Change ESLint's list of included files to not include this file
- Change that TSConfig 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#"
`;

exports[`parseAndGenerateServices invalid project error messages throws when non of multiple projects include the file 1`] = `
"ESLint was configured to run on \`ts/notIncluded0j1.ts\` using \`parserOptions.project\`:
- tsconfig.json
- tsconfig.extra.json
However, none of those TSConfigs include this file. Either:
- Change ESLint's list of included files to not include this file
- Change one of those TSConfigs 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#"
`;

exports[`parseAndGenerateServices isolated parsing should parse .js file - with JSX content - parserOptions.jsx = false 1`] = `
Expand Down