Skip to content

Commit 6d3d162

Browse files
feat(typescript-estree): add EXPERIMENTAL_useProjectService option to use TypeScript project service (typescript-eslint#6754)
* WIP: createProjectService * Collected updates: reuse program; lean more into tsserver * chore: fix website config.ts type checking * Spell checking * remove .only * Cleaned up todo comments in code and properly restrict new option * Added separate test run in CI for experimental option * fix: no node-version * Remove process.env manual set * Fix linter config.ts * Tweaked project creation to try to explicitly set cwd * Progress on updating unit tests for absolute paths * fix: add missing clearTSServerProjectService * Add more path relativity fixes * Lint fixes and watch program relativity * No, not always true * Fix around semanticInfo.test.ts * Switch snapshot to inline * perf: only openExternalProject once per project * Revert "perf: only openExternalProject once per project" This reverts commit a9aec01. * Reverted changes to allow alternate TSConfig names * Remove project existence checking * Add linting from root * Refactor CI naming a bit, and bumping to MacOS image... * Alas, linting from root style runs out of memory * Added a test, why not * fix: don't fall back to default program/project creation * Fixed up more test exclusions * Move tsserver import to a require * rename: tsserverType -> ts * Use path.resolve, and then simplify parserSettings usage * Remove unneeded typingsInstaller
1 parent 606a52c commit 6d3d162

37 files changed

+809
-556
lines changed

.github/workflows/ci.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,38 @@ jobs:
193193
# Sadly 1 day is the minimum
194194
retention-days: 1
195195

196+
unit_tests_tsserver:
197+
name: Run Unit Tests with Experimental TSServer
198+
needs: [build]
199+
runs-on: ubuntu-latest
200+
strategy:
201+
matrix:
202+
package:
203+
[
204+
'eslint-plugin',
205+
'eslint-plugin-internal',
206+
'eslint-plugin-tslint',
207+
'typescript-estree',
208+
]
209+
env:
210+
COLLECT_COVERAGE: false
211+
steps:
212+
- name: Checkout
213+
uses: actions/checkout@v3
214+
with:
215+
fetch-depth: 2
216+
- name: Install
217+
uses: ./.github/actions/prepare-install
218+
with:
219+
node-version: 18
220+
- name: Build
221+
uses: ./.github/actions/prepare-build
222+
- name: Run unit tests for ${{ matrix.package }}
223+
run: npx nx test ${{ matrix.package }} --coverage=false
224+
env:
225+
CI: true
226+
TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER: true
227+
196228
website_tests:
197229
# The NETLIFY_TOKEN secret will not be available on forks
198230
if: github.repository_owner == 'typescript-eslint'

.vscode/launch.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,40 @@
141141
"${workspaceFolder}/packages/scope-manager/dist/index.js",
142142
],
143143
},
144+
{
145+
"type": "node",
146+
"request": "launch",
147+
"name": "Jest Test Current eslint-plugin-tslint Rule",
148+
"cwd": "${workspaceFolder}/packages/eslint-plugin-tslint/",
149+
"program": "${workspaceFolder}/node_modules/jest/bin/jest.js",
150+
"args": [
151+
"--runInBand",
152+
"--no-cache",
153+
"--no-coverage",
154+
"${fileBasenameNoExtension}"
155+
],
156+
"sourceMaps": true,
157+
"console": "integratedTerminal",
158+
"internalConsoleOptions": "neverOpen",
159+
"skipFiles": [
160+
"${workspaceFolder}/packages/utils/src/index.ts",
161+
"${workspaceFolder}/packages/utils/dist/index.js",
162+
"${workspaceFolder}/packages/utils/src/ts-estree.ts",
163+
"${workspaceFolder}/packages/utils/dist/ts-estree.js",
164+
"${workspaceFolder}/packages/type-utils/src/index.ts",
165+
"${workspaceFolder}/packages/type-utils/dist/index.js",
166+
"${workspaceFolder}/packages/parser/src/index.ts",
167+
"${workspaceFolder}/packages/parser/dist/index.js",
168+
"${workspaceFolder}/packages/typescript-estree/src/index.ts",
169+
"${workspaceFolder}/packages/typescript-estree/dist/index.js",
170+
"${workspaceFolder}/packages/types/src/index.ts",
171+
"${workspaceFolder}/packages/types/dist/index.js",
172+
"${workspaceFolder}/packages/visitor-keys/src/index.ts",
173+
"${workspaceFolder}/packages/visitor-keys/dist/index.js",
174+
"${workspaceFolder}/packages/scope-manager/dist/index.js",
175+
"${workspaceFolder}/packages/scope-manager/dist/index.js",
176+
],
177+
},
144178
{
145179
"type": "node",
146180
"request": "launch",

docs/packages/TypeScript_ESTree.mdx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,15 @@ interface ParseAndGenerateServicesOptions extends ParseOptions {
147147
*/
148148
errorOnTypeScriptSyntacticAndSemanticIssues?: boolean;
149149

150+
/**
151+
* ***EXPERIMENTAL FLAG*** - Use this at your own risk.
152+
*
153+
* Whether to create a shared TypeScript server to power program creation.
154+
*
155+
* @see https://github.com/typescript-eslint/typescript-eslint/issues/6575
156+
*/
157+
EXPERIMENTAL_useProjectService?: boolean;
158+
150159
/**
151160
* ***EXPERIMENTAL FLAG*** - Use this at your own risk.
152161
*
@@ -155,7 +164,7 @@ interface ParseAndGenerateServicesOptions extends ParseOptions {
155164
*
156165
* This flag REQUIRES at least TS v3.9, otherwise it does nothing.
157166
*
158-
* See: https://github.com/typescript-eslint/typescript-eslint/issues/2094
167+
* @see https://github.com/typescript-eslint/typescript-eslint/issues/2094
159168
*/
160169
EXPERIMENTAL_useSourceOfProjectReferenceRedirect?: boolean;
161170

packages/eslint-plugin-tslint/src/rules/config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ESLintUtils } from '@typescript-eslint/utils';
2+
import path from 'path';
23
import type { RuleSeverity } from 'tslint';
34
import { Configuration } from 'tslint';
45

@@ -118,7 +119,7 @@ export default createRule<Options, MessageIds>({
118119
context,
119120
[{ rules: tslintRules, rulesDirectory: tslintRulesDirectory, lintFile }],
120121
) {
121-
const fileName = context.getFilename();
122+
const fileName = path.resolve(context.getCwd(), context.getFilename());
122123
const sourceCode = context.getSourceCode().text;
123124
const services = ESLintUtils.getParserServices(context);
124125
const program = services.program;

packages/eslint-plugin/tests/rules/consistent-type-imports.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const ruleTester = new RuleTester({
1616
});
1717

1818
const withMetaParserOptions = {
19+
EXPERIMENTAL_useProjectService: false,
1920
tsconfigRootDir: getFixturesRootDir(),
2021
project: './tsconfig-withmeta.json',
2122
};

packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,7 @@ function getElem(dict: Record<string, { foo: string }>, key: string) {
595595
}
596596
`,
597597
parserOptions: {
598+
EXPERIMENTAL_useProjectService: false,
598599
tsconfigRootDir: getFixturesRootDir(),
599600
project: './tsconfig.noUncheckedIndexedAccess.json',
600601
},

packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ function assignmentTest(
6565
const ruleTester = new RuleTester({
6666
parser: '@typescript-eslint/parser',
6767
parserOptions: {
68+
EXPERIMENTAL_useProjectService: false,
6869
project: './tsconfig.noImplicitThis.json',
6970
tsconfigRootDir: getFixturesRootDir(),
7071
},

packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getFixturesRootDir } from '../RuleTester';
66
const ruleTester = new RuleTester({
77
parser: '@typescript-eslint/parser',
88
parserOptions: {
9+
EXPERIMENTAL_useProjectService: false,
910
project: './tsconfig.noImplicitThis.json',
1011
tsconfigRootDir: getFixturesRootDir(),
1112
},

packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getFixturesRootDir } from '../RuleTester';
66
const ruleTester = new RuleTester({
77
parser: '@typescript-eslint/parser',
88
parserOptions: {
9+
EXPERIMENTAL_useProjectService: false,
910
project: './tsconfig.noImplicitThis.json',
1011
tsconfigRootDir: getFixturesRootDir(),
1112
},

packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getFixturesRootDir } from '../RuleTester';
66
const ruleTester = new RuleTester({
77
parser: '@typescript-eslint/parser',
88
parserOptions: {
9+
EXPERIMENTAL_useProjectService: false,
910
project: './tsconfig.noImplicitThis.json',
1011
tsconfigRootDir: getFixturesRootDir(),
1112
},

packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const ruleTester = new RuleTester({
1414
});
1515

1616
const withMetaParserOptions = {
17+
EXPERIMENTAL_useProjectService: false,
1718
tsconfigRootDir: getFixturesRootDir(),
1819
project: './tsconfig-withmeta.json',
1920
};

packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ const y = x!;
204204

205205
const ruleTesterWithNoUncheckedIndexAccess = new RuleTester({
206206
parserOptions: {
207+
EXPERIMENTAL_useProjectService: false,
207208
sourceType: 'module',
208209
tsconfigRootDir: getFixturesRootDir(),
209210
project: './tsconfig.noUncheckedIndexedAccess.json',

packages/parser/tests/lib/parser.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as scopeManager from '@typescript-eslint/scope-manager';
22
import type { ParserOptions } from '@typescript-eslint/types';
33
import * as typescriptESTree from '@typescript-eslint/typescript-estree';
4+
import path from 'path';
45

56
import { parse, parseForESLint } from '../../src/parser';
67

@@ -33,10 +34,10 @@ describe('parser', () => {
3334
jsx: false,
3435
},
3536
// ts-estree specific
36-
filePath: 'isolated-file.src.ts',
37+
filePath: './isolated-file.src.ts',
3738
project: 'tsconfig.json',
3839
errorOnTypeScriptSyntacticAndSemanticIssues: false,
39-
tsconfigRootDir: 'tests/fixtures/services',
40+
tsconfigRootDir: path.resolve(__dirname, '../fixtures/services'),
4041
extraFileExtensions: ['.foo'],
4142
};
4243
parseForESLint(code, config);
@@ -89,7 +90,7 @@ describe('parser', () => {
8990
filePath: 'isolated-file.src.ts',
9091
project: 'tsconfig.json',
9192
errorOnTypeScriptSyntacticAndSemanticIssues: false,
92-
tsconfigRootDir: 'tests/fixtures/services',
93+
tsconfigRootDir: path.join(__dirname, '../fixtures/services'),
9394
extraFileExtensions: ['.foo'],
9495
};
9596
parseForESLint(code, config);

packages/rule-tester/tests/RuleTester.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ describe('RuleTester', () => {
169169
{
170170
code: 'type-aware parser options should override the constructor config',
171171
parserOptions: {
172+
EXPERIMENTAL_useProjectService: false,
172173
project: 'tsconfig.test-specific.json',
173174
tsconfigRootDir: '/set/in/the/test/',
174175
},
@@ -209,6 +210,7 @@ describe('RuleTester', () => {
209210
"code": "type-aware parser options should override the constructor config",
210211
"filename": "/set/in/the/test/file.ts",
211212
"parserOptions": {
213+
"EXPERIMENTAL_useProjectService": false,
212214
"project": "tsconfig.test-specific.json",
213215
"tsconfigRootDir": "/set/in/the/test/",
214216
},

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -196,42 +196,42 @@ describe('TypeOrValueSpecifier', () => {
196196
],
197197
[
198198
'interface Foo {prop: string}; type Test = Foo;',
199-
{ from: 'file', name: 'Foo', path: 'tests/fixtures/file.ts' },
199+
{ from: 'file', name: 'Foo', path: 'file.ts' },
200200
],
201201
[
202202
'type Foo = {prop: string}; type Test = Foo;',
203-
{ from: 'file', name: 'Foo', path: 'tests/fixtures/file.ts' },
203+
{ from: 'file', name: 'Foo', path: 'file.ts' },
204204
],
205205
[
206206
'interface Foo {prop: string}; type Test = Foo;',
207207
{
208208
from: 'file',
209209
name: 'Foo',
210-
path: 'tests/../tests/fixtures/////file.ts',
210+
path: './////file.ts',
211211
},
212212
],
213213
[
214214
'type Foo = {prop: string}; type Test = Foo;',
215215
{
216216
from: 'file',
217217
name: 'Foo',
218-
path: 'tests/../tests/fixtures/////file.ts',
218+
path: './////file.ts',
219219
},
220220
],
221221
[
222222
'interface Foo {prop: string}; type Test = Foo;',
223223
{
224224
from: 'file',
225225
name: ['Foo', 'Bar'],
226-
path: 'tests/fixtures/file.ts',
226+
path: 'file.ts',
227227
},
228228
],
229229
[
230230
'type Foo = {prop: string}; type Test = Foo;',
231231
{
232232
from: 'file',
233233
name: ['Foo', 'Bar'],
234-
path: 'tests/fixtures/file.ts',
234+
path: 'file.ts',
235235
},
236236
],
237237
])('matches a matching file specifier: %s', runTestPositive);
@@ -247,14 +247,14 @@ describe('TypeOrValueSpecifier', () => {
247247
],
248248
[
249249
'interface Foo {prop: string}; type Test = Foo;',
250-
{ from: 'file', name: 'Foo', path: 'tests/fixtures/wrong-file.ts' },
250+
{ from: 'file', name: 'Foo', path: 'wrong-file.ts' },
251251
],
252252
[
253253
'interface Foo {prop: string}; type Test = Foo;',
254254
{
255255
from: 'file',
256256
name: ['Foo', 'Bar'],
257-
path: 'tests/fixtures/wrong-file.ts',
257+
path: 'wrong-file.ts',
258258
},
259259
],
260260
])("doesn't match a mismatched file specifier: %s", runTestNegative);
@@ -399,14 +399,14 @@ describe('TypeOrValueSpecifier', () => {
399399
['type Test = RegExp;', { from: 'file', name: ['RegExp', 'BigInt'] }],
400400
[
401401
'type Test = RegExp;',
402-
{ from: 'file', name: 'RegExp', path: 'tests/fixtures/file.ts' },
402+
{ from: 'file', name: 'RegExp', path: 'file.ts' },
403403
],
404404
[
405405
'type Test = RegExp;',
406406
{
407407
from: 'file',
408408
name: ['RegExp', 'BigInt'],
409-
path: 'tests/fixtures/file.ts',
409+
path: 'file.ts',
410410
},
411411
],
412412
[

packages/types/src/parser-options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ interface ParserOptions {
4747
debugLevel?: DebugLevel;
4848
errorOnTypeScriptSyntacticAndSemanticIssues?: boolean;
4949
errorOnUnknownASTType?: boolean;
50+
EXPERIMENTAL_useProjectService?: boolean; // purposely undocumented for now
5051
EXPERIMENTAL_useSourceOfProjectReferenceRedirect?: boolean; // purposely undocumented for now
5152
extraFileExtensions?: string[];
5253
filePath?: string;

packages/typescript-estree/src/clear-caches.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { clearWatchCaches } from './create-program/getWatchProgramsForProjects';
22
import { clearProgramCache as clearProgramCacheOriginal } from './parser';
3-
import { clearTSConfigMatchCache } from './parseSettings/createParseSettings';
3+
import {
4+
clearTSConfigMatchCache,
5+
clearTSServerProjectService,
6+
} from './parseSettings/createParseSettings';
47
import { clearGlobCache } from './parseSettings/resolveProjectList';
58

69
/**
@@ -14,6 +17,7 @@ export function clearCaches(): void {
1417
clearProgramCacheOriginal();
1518
clearWatchCaches();
1619
clearTSConfigMatchCache();
20+
clearTSServerProjectService();
1721
clearGlobCache();
1822
}
1923

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import * as ts from 'typescript';
55
import { firstDefined } from '../node-utils';
66
import type { ParseSettings } from '../parseSettings';
77
import { describeFilePath } from './describeFilePath';
8-
import { getWatchProgramsForProjects } from './getWatchProgramsForProjects';
98
import type { ASTAndDefiniteProgram } from './shared';
109
import { getAstFromProgram } from './shared';
1110

@@ -28,12 +27,12 @@ const DEFAULT_EXTRA_FILE_EXTENSIONS = [
2827
*/
2928
function createProjectProgram(
3029
parseSettings: ParseSettings,
30+
programsForProjects: readonly ts.Program[],
3131
): ASTAndDefiniteProgram | undefined {
3232
log('Creating project program for: %s', parseSettings.filePath);
3333

34-
const programsForProjects = getWatchProgramsForProjects(parseSettings);
3534
const astAndProgram = firstDefined(programsForProjects, currentProgram =>
36-
getAstFromProgram(currentProgram, parseSettings),
35+
getAstFromProgram(currentProgram, parseSettings.filePath),
3736
);
3837

3938
// The file was either matched within the tsconfig, or we allow creating a default program

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy