Skip to content

Commit

Permalink
fix(utils): removed TRuleListener generic from the createRule (#5036
Browse files Browse the repository at this point in the history
)

* refactor(utils)!: removed `TRuleListener` generic from the `createRule`

* refactor!: removed `TRuleListener` generic from the `CLIEngine` and `RuleCreateFunction`

* chore: document and refactor 'extra' to 'parserSettings' (#5834)

* chore(website): fix renamed Sponsorship docs link (#5882)

* docs: Mention wide globs performance implications in monorepos docs and parser README (#5864)

* docs: Mention wide globs performance implications in monorepos docs and parser readme

* Update docs/linting/typed-linting/MONOREPOS.md

Co-authored-by: Josh Goldberg <git@joshuakgoldberg.com>

Co-authored-by: Josh Goldberg <git@joshuakgoldberg.com>
Co-authored-by: Adnan Hashmi <56730784+adnanhashmi09@users.noreply.github.com>
  • Loading branch information
3 people committed Oct 25, 2022
1 parent c4e0d86 commit 0414e4d
Show file tree
Hide file tree
Showing 22 changed files with 616 additions and 612 deletions.
27 changes: 27 additions & 0 deletions docs/linting/typed-linting/MONOREPOS.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,33 @@ module.exports = {
};
```

### Wide globs in `parserOptions.project`

Using wide globs `**` in your `parserOptions.project` may degrade linting performance.
Instead of globs that use `**` to recursively check all folders, prefer paths that use a single `*` at a time.

```js title=".eslintrc.js"
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
],
parser: '@typescript-eslint/parser',
parserOptions: {
tsconfigRootDir: __dirname,
// Remove this line
project: ['./tsconfig.eslint.json', './**/tsconfig.json'],
// Add this line
project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'],
},
plugins: ['@typescript-eslint'],
root: true,
};
```

See [Glob pattern in parser's option "project" slows down linting](https://github.com/typescript-eslint/typescript-eslint/issues/2611) for more details.

### Important note regarding large (> 10) multi-package monorepos

We've had reports that for sufficiently large and/or interdependent projects, you may run into OOMs using this approach.
Expand Down
2 changes: 2 additions & 0 deletions packages/parser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ This option allows you to provide a path to your project's `tsconfig.json`. **Th

- If you use project references, TypeScript will not automatically use project references to resolve files. This means that you will have to add each referenced tsconfig to the `project` field either separately, or via a glob.

- Note that using wide globs `**` in your `parserOptions.project` may cause performance implications. Instead of globs that use `**` to recursively check all folders, prefer paths that use a single `*` at a time. For more info see [#2611](https://github.com/typescript-eslint/typescript-eslint/issues/2611).

- TypeScript will ignore files with duplicate filenames in the same folder (for example, `src/file.ts` and `src/file.js`). TypeScript purposely ignore all but one of the files, only keeping the one file with the highest priority extension (the extension priority order (from highest to lowest) is `.ts`, `.tsx`, `.d.ts`, `.js`, `.jsx`). For more info see #955.

- Note that if this setting is specified and `createDefaultProgram` is not, you must only lint files that are included in the projects as defined by the provided `tsconfig.json` files. If your existing configuration does not include all of the files you would like to lint, you can create a separate `tsconfig.eslint.json` as follows:
Expand Down
18 changes: 9 additions & 9 deletions packages/typescript-estree/src/ast-converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import type { ASTMaps } from './convert';
import { Converter, convertError } from './convert';
import { convertComments } from './convert-comments';
import { convertTokens } from './node-utils';
import type { Extra } from './parser-options';
import type { ParseSettings } from './parseSettings';
import { simpleTraverse } from './simple-traverse';
import type { TSESTree } from './ts-estree';

export function astConverter(
ast: SourceFile,
extra: Extra,
parseSettings: ParseSettings,
shouldPreserveNodeMaps: boolean,
): { estree: TSESTree.Program; astMaps: ASTMaps } {
/**
Expand All @@ -26,7 +26,7 @@ export function astConverter(
* Recursively convert the TypeScript AST into an ESTree-compatible AST
*/
const instance = new Converter(ast, {
errorOnUnknownASTType: extra.errorOnUnknownASTType || false,
errorOnUnknownASTType: parseSettings.errorOnUnknownASTType || false,
shouldPreserveNodeMaps,
});

Expand All @@ -35,15 +35,15 @@ export function astConverter(
/**
* Optionally remove range and loc if specified
*/
if (!extra.range || !extra.loc) {
if (!parseSettings.range || !parseSettings.loc) {
simpleTraverse(estree, {
enter: node => {
if (!extra.range) {
if (!parseSettings.range) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment -- TS 4.0 made this an error because the types aren't optional
// @ts-expect-error
delete node.range;
}
if (!extra.loc) {
if (!parseSettings.loc) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment -- TS 4.0 made this an error because the types aren't optional
// @ts-expect-error
delete node.loc;
Expand All @@ -55,15 +55,15 @@ export function astConverter(
/**
* Optionally convert and include all tokens in the AST
*/
if (extra.tokens) {
if (parseSettings.tokens) {
estree.tokens = convertTokens(ast);
}

/**
* Optionally convert and include all comments in the AST
*/
if (extra.comment) {
estree.comments = convertComments(ast, extra.code);
if (parseSettings.comment) {
estree.comments = convertComments(ast, parseSettings.code);
}

const astMaps = instance.getASTMaps();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import debug from 'debug';
import path from 'path';
import * as ts from 'typescript';

import type { Extra } from '../parser-options';
import type { ASTAndProgram, CanonicalPath } from './shared';
import type { ParseSettings } from '../parseSettings';
import type { ASTAndProgram } from './shared';
import {
createDefaultCompilerOptionsFromExtra,
getModuleResolver,
Expand All @@ -12,27 +12,26 @@ import {
const log = debug('typescript-eslint:typescript-estree:createDefaultProgram');

/**
* @param code The code of the file being linted
* @param extra The config object
* @param extra.tsconfigRootDir The root directory for relative tsconfig paths
* @param extra.projects Provided tsconfig paths
* @param parseSettings Internal settings for parsing the file
* @returns If found, returns the source file corresponding to the code and the containing program
*/
function createDefaultProgram(
code: string,
extra: Extra,
parseSettings: ParseSettings,
): ASTAndProgram | undefined {
log('Getting default program for: %s', extra.filePath || 'unnamed file');
log(
'Getting default program for: %s',
parseSettings.filePath || 'unnamed file',
);

if (!extra.projects || extra.projects.length !== 1) {
if (parseSettings.projects?.length !== 1) {
return undefined;
}

const tsconfigPath: CanonicalPath = extra.projects[0];
const tsconfigPath = parseSettings.projects[0];

const commandLine = ts.getParsedCommandLineOfConfigFile(
tsconfigPath,
createDefaultCompilerOptionsFromExtra(extra),
createDefaultCompilerOptionsFromExtra(parseSettings),
{ ...ts.sys, onUnRecoverableConfigFileDiagnostic: () => {} },
);

Expand All @@ -45,24 +44,24 @@ function createDefaultProgram(
/* setParentNodes */ true,
);

if (extra.moduleResolver) {
if (parseSettings.moduleResolver) {
compilerHost.resolveModuleNames = getModuleResolver(
extra.moduleResolver,
parseSettings.moduleResolver,
).resolveModuleNames;
}

const oldReadFile = compilerHost.readFile;
compilerHost.readFile = (fileName: string): string | undefined =>
path.normalize(fileName) === path.normalize(extra.filePath)
? code
path.normalize(fileName) === path.normalize(parseSettings.filePath)
? parseSettings.code
: oldReadFile(fileName);

const program = ts.createProgram(
[extra.filePath],
[parseSettings.filePath],
commandLine.options,
compilerHost,
);
const ast = program.getSourceFile(extra.filePath);
const ast = program.getSourceFile(parseSettings.filePath);

return ast && { ast, program };
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import debug from 'debug';
import * as ts from 'typescript';

import type { Extra } from '../parser-options';
import type { ParseSettings } from '../parseSettings';
import { getScriptKind } from './getScriptKind';
import type { ASTAndProgram } from './shared';
import { createDefaultCompilerOptionsFromExtra } from './shared';
Expand All @@ -12,19 +12,19 @@ const log = debug('typescript-eslint:typescript-estree:createIsolatedProgram');
* @param code The code of the file being linted
* @returns Returns a new source file and program corresponding to the linted code
*/
function createIsolatedProgram(code: string, extra: Extra): ASTAndProgram {
function createIsolatedProgram(parseSettings: ParseSettings): ASTAndProgram {
log(
'Getting isolated program in %s mode for: %s',
extra.jsx ? 'TSX' : 'TS',
extra.filePath,
parseSettings.jsx ? 'TSX' : 'TS',
parseSettings.filePath,
);

const compilerHost: ts.CompilerHost = {
fileExists() {
return true;
},
getCanonicalFileName() {
return extra.filePath;
return parseSettings.filePath;
},
getCurrentDirectory() {
return '';
Expand All @@ -43,10 +43,10 @@ function createIsolatedProgram(code: string, extra: Extra): ASTAndProgram {
getSourceFile(filename: string) {
return ts.createSourceFile(
filename,
code,
parseSettings.code,
ts.ScriptTarget.Latest,
/* setParentNodes */ true,
getScriptKind(extra.filePath, extra.jsx),
getScriptKind(parseSettings.filePath, parseSettings.jsx),
);
},
readFile() {
Expand All @@ -61,17 +61,17 @@ function createIsolatedProgram(code: string, extra: Extra): ASTAndProgram {
};

const program = ts.createProgram(
[extra.filePath],
[parseSettings.filePath],
{
noResolve: true,
target: ts.ScriptTarget.Latest,
jsx: extra.jsx ? ts.JsxEmit.Preserve : undefined,
...createDefaultCompilerOptionsFromExtra(extra),
jsx: parseSettings.jsx ? ts.JsxEmit.Preserve : undefined,
...createDefaultCompilerOptionsFromExtra(parseSettings),
},
compilerHost,
);

const ast = program.getSourceFile(extra.filePath);
const ast = program.getSourceFile(parseSettings.filePath);
if (!ast) {
throw new Error(
'Expected an ast to be returned for the single-file isolated program.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path from 'path';
import * as ts from 'typescript';

import { firstDefined } from '../node-utils';
import type { Extra } from '../parser-options';
import type { ParseSettings } from '../parseSettings';
import { getProgramsForProjects } from './createWatchProgram';
import type { ASTAndProgram } from './shared';
import { getAstFromProgram } from './shared';
Expand All @@ -22,45 +22,37 @@ const DEFAULT_EXTRA_FILE_EXTENSIONS = [
] as readonly string[];

/**
* @param code The code of the file being linted
* @param createDefaultProgram True if the default program should be created
* @param extra The config object
* @returns If found, returns the source file corresponding to the code and the containing program
* @param parseSettings Internal settings for parsing the file
* @returns If found, the source file corresponding to the code and the containing program
*/
function createProjectProgram(
code: string,
createDefaultProgram: boolean,
extra: Extra,
parseSettings: ParseSettings,
): ASTAndProgram | undefined {
log('Creating project program for: %s', extra.filePath);
log('Creating project program for: %s', parseSettings.filePath);

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

// The file was either matched within the tsconfig, or we allow creating a default program
if (astAndProgram || createDefaultProgram) {
if (astAndProgram || parseSettings.createDefaultProgram) {
return astAndProgram;
}

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

const describedFilePath = describeFilePath(extra.filePath);
const relativeProjects = extra.projects.map(describeFilePath);
const describedFilePath = describeFilePath(parseSettings.filePath);
const relativeProjects = parseSettings.projects.map(describeFilePath);
const describedPrograms =
relativeProjects.length === 1
? relativeProjects[0]
Expand All @@ -70,7 +62,7 @@ function createProjectProgram(
];
let hasMatchedAnError = false;

const extraFileExtensions = extra.extraFileExtensions || [];
const extraFileExtensions = parseSettings.extraFileExtensions || [];

extraFileExtensions.forEach(extraExtension => {
if (!extraExtension.startsWith('.')) {
Expand All @@ -85,7 +77,7 @@ function createProjectProgram(
}
});

const fileExtension = path.extname(extra.filePath);
const fileExtension = path.extname(parseSettings.filePath);
if (!DEFAULT_EXTRA_FILE_EXTENSIONS.includes(fileExtension)) {
const nonStandardExt = `The extension for the file (\`${fileExtension}\`) is non-standard`;
if (extraFileExtensions.length > 0) {
Expand All @@ -105,7 +97,7 @@ function createProjectProgram(

if (!hasMatchedAnError) {
const [describedInclusions, describedSpecifiers] =
extra.projects.length === 1
parseSettings.projects.length === 1
? ['that TSConfig does not', 'that TSConfig']
: ['none of those TSConfigs', 'one of those TSConfigs'];
errorLines.push(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import debug from 'debug';
import * as ts from 'typescript';

import type { Extra } from '../parser-options';
import type { ParseSettings } from '../parseSettings';
import { getScriptKind } from './getScriptKind';

const log = debug('typescript-eslint:typescript-estree:createSourceFile');

function createSourceFile(code: string, extra: Extra): ts.SourceFile {
function createSourceFile(parseSettings: ParseSettings): ts.SourceFile {
log(
'Getting AST without type information in %s mode for: %s',
extra.jsx ? 'TSX' : 'TS',
extra.filePath,
parseSettings.jsx ? 'TSX' : 'TS',
parseSettings.filePath,
);

return ts.createSourceFile(
extra.filePath,
code,
parseSettings.filePath,
parseSettings.code,
ts.ScriptTarget.Latest,
/* setParentNodes */ true,
getScriptKind(extra.filePath, extra.jsx),
getScriptKind(parseSettings.filePath, parseSettings.jsx),
);
}

Expand Down

0 comments on commit 0414e4d

Please sign in to comment.