Skip to content

Commit 7fc062a

Browse files
authored
feat: remove partial type-information program (#6066)
1 parent dde6861 commit 7fc062a

32 files changed

+329
-3087
lines changed

packages/eslint-plugin/src/rules/consistent-type-exports.ts

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
import type {
2-
ParserServices,
3-
TSESLint,
4-
TSESTree,
5-
} from '@typescript-eslint/utils';
1+
import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
62
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
73
import { SymbolFlags } from 'typescript';
84

@@ -75,6 +71,28 @@ export default util.createRule<Options, MessageIds>({
7571
const sourceExportsMap: { [key: string]: SourceExports } = {};
7672
const parserServices = util.getParserServices(context);
7773

74+
/**
75+
* Helper for identifying if an export specifier resolves to a
76+
* JavaScript value or a TypeScript type.
77+
*
78+
* @returns True/false if is a type or not, or undefined if the specifier
79+
* can't be resolved.
80+
*/
81+
function isSpecifierTypeBased(
82+
specifier: TSESTree.ExportSpecifier,
83+
): boolean | undefined {
84+
const checker = parserServices.program.getTypeChecker();
85+
const node = parserServices.esTreeNodeToTSNodeMap.get(specifier.exported);
86+
const symbol = checker.getSymbolAtLocation(node);
87+
const aliasedSymbol = checker.getAliasedSymbol(symbol!);
88+
89+
if (!aliasedSymbol || aliasedSymbol.escapedName === 'unknown') {
90+
return undefined;
91+
}
92+
93+
return !(aliasedSymbol.flags & SymbolFlags.Value);
94+
}
95+
7896
return {
7997
ExportNamedDeclaration(node: TSESTree.ExportNamedDeclaration): void {
8098
// Coerce the source into a string for use as a lookup entry.
@@ -112,7 +130,7 @@ export default util.createRule<Options, MessageIds>({
112130
continue;
113131
}
114132

115-
const isTypeBased = isSpecifierTypeBased(parserServices, specifier);
133+
const isTypeBased = isSpecifierTypeBased(specifier);
116134

117135
if (isTypeBased === true) {
118136
typeBasedSpecifiers.push(specifier);
@@ -199,29 +217,6 @@ export default util.createRule<Options, MessageIds>({
199217
},
200218
});
201219

202-
/**
203-
* Helper for identifying if an export specifier resolves to a
204-
* JavaScript value or a TypeScript type.
205-
*
206-
* @returns True/false if is a type or not, or undefined if the specifier
207-
* can't be resolved.
208-
*/
209-
function isSpecifierTypeBased(
210-
parserServices: ParserServices,
211-
specifier: TSESTree.ExportSpecifier,
212-
): boolean | undefined {
213-
const checker = parserServices.program.getTypeChecker();
214-
const node = parserServices.esTreeNodeToTSNodeMap.get(specifier.exported);
215-
const symbol = checker.getSymbolAtLocation(node);
216-
const aliasedSymbol = checker.getAliasedSymbol(symbol!);
217-
218-
if (!aliasedSymbol || aliasedSymbol.escapedName === 'unknown') {
219-
return undefined;
220-
}
221-
222-
return !(aliasedSymbol.flags & SymbolFlags.Value);
223-
}
224-
225220
/**
226221
* Inserts "type" into an export.
227222
*

packages/eslint-plugin/src/rules/naming-convention.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,8 @@ export default util.createRule<Options, MessageIds>({
9090

9191
const validators = parseOptions(context);
9292

93-
// getParserServices(context, false) -- dirty hack to work around the docs checker test...
94-
const compilerOptions = util
95-
.getParserServices(context, true)
96-
.program.getCompilerOptions();
93+
const compilerOptions =
94+
util.getParserServices(context, true).program?.getCompilerOptions() ?? {};
9795
function handleMember(
9896
validator: ValidatorFunction | null,
9997
node:

packages/eslint-plugin/tests/docs.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ describe('Validating rule docs', () => {
120120
});
121121

122122
describe('Validating rule metadata', () => {
123+
const rulesThatRequireTypeInformationInAWayThatsHardToDetect = new Set([
124+
// the core rule file doesn't use type information, instead it's used in `src/rules/naming-convention-utils/validator.ts`
125+
'naming-convention',
126+
]);
123127
function requiresFullTypeInformation(content: string): boolean {
124128
return /getParserServices(\(\s*[^,\s)]+)\s*(,\s*false\s*)?\)/.test(content);
125129
}
@@ -135,6 +139,13 @@ describe('Validating rule metadata', () => {
135139
});
136140

137141
it('`requiresTypeChecking` should be set if the rule uses type information', () => {
142+
if (
143+
rulesThatRequireTypeInformationInAWayThatsHardToDetect.has(ruleName)
144+
) {
145+
expect(true).toEqual(rule.meta.docs?.requiresTypeChecking ?? false);
146+
return;
147+
}
148+
138149
// quick-and-dirty check to see if it uses parserServices
139150
// not perfect but should be good enough
140151
const ruleFileContents = fs.readFileSync(

packages/parser/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
export { parse, parseForESLint, ParserOptions } from './parser';
22
export {
33
ParserServices,
4+
ParserServicesWithTypeInformation,
5+
ParserServicesWithoutTypeInformation,
46
clearCaches,
57
createProgram,
68
} from '@typescript-eslint/typescript-estree';

packages/parser/src/parser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ function parseForESLint(
128128
ast.sourceType = options.sourceType;
129129

130130
let emitDecoratorMetadata = options.emitDecoratorMetadata === true;
131-
if (services.hasFullTypeInformation) {
131+
if (services.program) {
132132
// automatically apply the options configured for the program
133133
const compilerOptions = services.program.getCompilerOptions();
134134
if (analyzeOptions.lib == null) {

packages/type-utils/tests/isTypeReadonly.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
type ReadonlynessOptions,
88
isTypeReadonly,
99
} from '../src/isTypeReadonly';
10+
import { expectToHaveParserServices } from './test-utils/expectToHaveParserServices';
1011

1112
describe('isTypeReadonly', () => {
1213
const rootDir = path.join(__dirname, 'fixtures');
@@ -21,6 +22,7 @@ describe('isTypeReadonly', () => {
2122
filePath: path.join(rootDir, 'file.ts'),
2223
tsconfigRootDir: rootDir,
2324
});
25+
expectToHaveParserServices(services);
2426
const checker = services.program.getTypeChecker();
2527
const esTreeNodeToTSNodeMap = services.esTreeNodeToTSNodeMap;
2628

packages/type-utils/tests/isUnsafeAssignment.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import path from 'path';
44
import type * as ts from 'typescript';
55

66
import { isUnsafeAssignment } from '../src/isUnsafeAssignment';
7+
import { expectToHaveParserServices } from './test-utils/expectToHaveParserServices';
78

89
describe('isUnsafeAssignment', () => {
910
const rootDir = path.join(__dirname, 'fixtures');
@@ -19,6 +20,7 @@ describe('isUnsafeAssignment', () => {
1920
filePath: path.join(rootDir, 'file.ts'),
2021
tsconfigRootDir: rootDir,
2122
});
23+
expectToHaveParserServices(services);
2224
const checker = services.program.getTypeChecker();
2325
const esTreeNodeToTSNodeMap = services.esTreeNodeToTSNodeMap;
2426

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type {
2+
ParserServices,
3+
ParserServicesWithTypeInformation,
4+
} from '@typescript-eslint/typescript-estree';
5+
6+
export function expectToHaveParserServices(
7+
services: ParserServices | null | undefined,
8+
): asserts services is ParserServicesWithTypeInformation {
9+
expect(services?.program).toBeDefined();
10+
expect(services?.esTreeNodeToTSNodeMap).toBeDefined();
11+
expect(services?.tsNodeToESTreeNodeMap).toBeDefined();
12+
}

packages/typescript-estree/jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
module.exports = {
66
...require('../../jest.config.base.js'),
77
testRegex: [
8-
'./tests/lib/.*\\.ts$',
8+
'./tests/lib/.*\\.test\\.ts$',
99
'./tests/ast-alignment/spec\\.ts$',
1010
'./tests/[^/]+\\.test\\.ts$',
1111
],

packages/typescript-estree/src/create-program/createDefaultProgram.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from 'path';
33
import * as ts from 'typescript';
44

55
import type { ParseSettings } from '../parseSettings';
6-
import type { ASTAndProgram } from './shared';
6+
import type { ASTAndDefiniteProgram } from './shared';
77
import {
88
createDefaultCompilerOptionsFromExtra,
99
getModuleResolver,
@@ -20,7 +20,7 @@ const log = debug('typescript-eslint:typescript-estree:createDefaultProgram');
2020
*/
2121
function createDefaultProgram(
2222
parseSettings: ParseSettings,
23-
): ASTAndProgram | undefined {
23+
): ASTAndDefiniteProgram | undefined {
2424
log(
2525
'Getting default program for: %s',
2626
parseSettings.filePath || 'unnamed file',

0 commit comments

Comments
 (0)