diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 47da35c1741f..5091d2438954 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -192,38 +192,6 @@ jobs: # Sadly 1 day is the minimum retention-days: 1 - unit_tests_tsserver: - name: Run Unit Tests with Experimental TSServer - needs: [build] - runs-on: ubuntu-latest - strategy: - matrix: - package: - [ - 'eslint-plugin', - 'eslint-plugin-internal', - 'eslint-plugin-tslint', - 'typescript-estree', - ] - env: - COLLECT_COVERAGE: false - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 2 - - name: Install - uses: ./.github/actions/prepare-install - with: - node-version: 18 - - name: Build - uses: ./.github/actions/prepare-build - - name: Run unit tests for ${{ matrix.package }} - run: npx nx test ${{ matrix.package }} --coverage=false - env: - CI: true - TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER: true - website_tests: # The NETLIFY_TOKEN secret will not be available on forks if: github.repository_owner == 'typescript-eslint' diff --git a/.vscode/launch.json b/.vscode/launch.json index 82c02f90b2e5..4cee04bec4a0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -141,40 +141,6 @@ "${workspaceFolder}/packages/scope-manager/dist/index.js", ], }, - { - "type": "node", - "request": "launch", - "name": "Jest Test Current eslint-plugin-tslint Rule", - "cwd": "${workspaceFolder}/packages/eslint-plugin-tslint/", - "program": "${workspaceFolder}/node_modules/jest/bin/jest.js", - "args": [ - "--runInBand", - "--no-cache", - "--no-coverage", - "${fileBasenameNoExtension}" - ], - "sourceMaps": true, - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "skipFiles": [ - "${workspaceFolder}/packages/utils/src/index.ts", - "${workspaceFolder}/packages/utils/dist/index.js", - "${workspaceFolder}/packages/utils/src/ts-estree.ts", - "${workspaceFolder}/packages/utils/dist/ts-estree.js", - "${workspaceFolder}/packages/type-utils/src/index.ts", - "${workspaceFolder}/packages/type-utils/dist/index.js", - "${workspaceFolder}/packages/parser/src/index.ts", - "${workspaceFolder}/packages/parser/dist/index.js", - "${workspaceFolder}/packages/typescript-estree/src/index.ts", - "${workspaceFolder}/packages/typescript-estree/dist/index.js", - "${workspaceFolder}/packages/types/src/index.ts", - "${workspaceFolder}/packages/types/dist/index.js", - "${workspaceFolder}/packages/visitor-keys/src/index.ts", - "${workspaceFolder}/packages/visitor-keys/dist/index.js", - "${workspaceFolder}/packages/scope-manager/dist/index.js", - "${workspaceFolder}/packages/scope-manager/dist/index.js", - ], - }, { "type": "node", "request": "launch", diff --git a/docs/packages/TypeScript_ESTree.mdx b/docs/packages/TypeScript_ESTree.mdx index 6354565e8282..68e2706ca9a1 100644 --- a/docs/packages/TypeScript_ESTree.mdx +++ b/docs/packages/TypeScript_ESTree.mdx @@ -147,15 +147,6 @@ interface ParseAndGenerateServicesOptions extends ParseOptions { */ errorOnTypeScriptSyntacticAndSemanticIssues?: boolean; - /** - * ***EXPERIMENTAL FLAG*** - Use this at your own risk. - * - * Whether to create a shared TypeScript server to power program creation. - * - * @see https://github.com/typescript-eslint/typescript-eslint/issues/6575 - */ - EXPERIMENTAL_useProjectService?: boolean; - /** * ***EXPERIMENTAL FLAG*** - Use this at your own risk. * @@ -164,7 +155,7 @@ interface ParseAndGenerateServicesOptions extends ParseOptions { * * This flag REQUIRES at least TS v3.9, otherwise it does nothing. * - * @see https://github.com/typescript-eslint/typescript-eslint/issues/2094 + * See: https://github.com/typescript-eslint/typescript-eslint/issues/2094 */ EXPERIMENTAL_useSourceOfProjectReferenceRedirect?: boolean; diff --git a/packages/eslint-plugin-tslint/src/rules/config.ts b/packages/eslint-plugin-tslint/src/rules/config.ts index 23b7558b80fb..e7918218a905 100644 --- a/packages/eslint-plugin-tslint/src/rules/config.ts +++ b/packages/eslint-plugin-tslint/src/rules/config.ts @@ -1,5 +1,4 @@ import { ESLintUtils } from '@typescript-eslint/utils'; -import path from 'path'; import type { RuleSeverity } from 'tslint'; import { Configuration } from 'tslint'; @@ -119,7 +118,7 @@ export default createRule({ context, [{ rules: tslintRules, rulesDirectory: tslintRulesDirectory, lintFile }], ) { - const fileName = path.resolve(context.getCwd(), context.getFilename()); + const fileName = context.getFilename(); const sourceCode = context.getSourceCode().text; const services = ESLintUtils.getParserServices(context); const program = services.program; diff --git a/packages/eslint-plugin/tests/rules/consistent-type-imports.test.ts b/packages/eslint-plugin/tests/rules/consistent-type-imports.test.ts index 1910b1986eeb..3c94e99ff141 100644 --- a/packages/eslint-plugin/tests/rules/consistent-type-imports.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-type-imports.test.ts @@ -16,7 +16,6 @@ const ruleTester = new RuleTester({ }); const withMetaParserOptions = { - EXPERIMENTAL_useProjectService: false, tsconfigRootDir: getFixturesRootDir(), project: './tsconfig-withmeta.json', }; diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts index c68c735ae11e..397d4b507ccb 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -595,7 +595,6 @@ function getElem(dict: Record, key: string) { } `, parserOptions: { - EXPERIMENTAL_useProjectService: false, tsconfigRootDir: getFixturesRootDir(), project: './tsconfig.noUncheckedIndexedAccess.json', }, diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts index fb2d713e647f..27028234b316 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts @@ -65,7 +65,6 @@ function assignmentTest( const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', parserOptions: { - EXPERIMENTAL_useProjectService: false, project: './tsconfig.noImplicitThis.json', tsconfigRootDir: getFixturesRootDir(), }, diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts index bb844011fd7b..b91b2ec6273d 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts @@ -6,7 +6,6 @@ import { getFixturesRootDir } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', parserOptions: { - EXPERIMENTAL_useProjectService: false, project: './tsconfig.noImplicitThis.json', tsconfigRootDir: getFixturesRootDir(), }, diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts index 8298cec6ceba..b66b96d4663c 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts @@ -6,7 +6,6 @@ import { getFixturesRootDir } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', parserOptions: { - EXPERIMENTAL_useProjectService: false, project: './tsconfig.noImplicitThis.json', tsconfigRootDir: getFixturesRootDir(), }, diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts index b75cdfb28c05..f0cbd8b25352 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts @@ -6,7 +6,6 @@ import { getFixturesRootDir } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', parserOptions: { - EXPERIMENTAL_useProjectService: false, project: './tsconfig.noImplicitThis.json', tsconfigRootDir: getFixturesRootDir(), }, diff --git a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts index 2646f3ce55bc..ea681ffd46e5 100644 --- a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts @@ -14,7 +14,6 @@ const ruleTester = new RuleTester({ }); const withMetaParserOptions = { - EXPERIMENTAL_useProjectService: false, tsconfigRootDir: getFixturesRootDir(), project: './tsconfig-withmeta.json', }; diff --git a/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts b/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts index 8b86634d2797..2f5d7163de34 100644 --- a/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts +++ b/packages/eslint-plugin/tests/rules/non-nullable-type-assertion-style.test.ts @@ -204,7 +204,6 @@ const y = x!; const ruleTesterWithNoUncheckedIndexAccess = new RuleTester({ parserOptions: { - EXPERIMENTAL_useProjectService: false, sourceType: 'module', tsconfigRootDir: getFixturesRootDir(), project: './tsconfig.noUncheckedIndexedAccess.json', diff --git a/packages/parser/tests/lib/parser.ts b/packages/parser/tests/lib/parser.ts index e6bd731db075..952e388eb8cf 100644 --- a/packages/parser/tests/lib/parser.ts +++ b/packages/parser/tests/lib/parser.ts @@ -1,7 +1,6 @@ import * as scopeManager from '@typescript-eslint/scope-manager'; import type { ParserOptions } from '@typescript-eslint/types'; import * as typescriptESTree from '@typescript-eslint/typescript-estree'; -import path from 'path'; import { parse, parseForESLint } from '../../src/parser'; @@ -34,10 +33,10 @@ describe('parser', () => { jsx: false, }, // ts-estree specific - filePath: './isolated-file.src.ts', + filePath: 'isolated-file.src.ts', project: 'tsconfig.json', errorOnTypeScriptSyntacticAndSemanticIssues: false, - tsconfigRootDir: path.resolve(__dirname, '../fixtures/services'), + tsconfigRootDir: 'tests/fixtures/services', extraFileExtensions: ['.foo'], }; parseForESLint(code, config); @@ -90,7 +89,7 @@ describe('parser', () => { filePath: 'isolated-file.src.ts', project: 'tsconfig.json', errorOnTypeScriptSyntacticAndSemanticIssues: false, - tsconfigRootDir: path.join(__dirname, '../fixtures/services'), + tsconfigRootDir: 'tests/fixtures/services', extraFileExtensions: ['.foo'], }; parseForESLint(code, config); diff --git a/packages/rule-tester/tests/RuleTester.test.ts b/packages/rule-tester/tests/RuleTester.test.ts index 25b6aa0888ed..93f9f6d35d24 100644 --- a/packages/rule-tester/tests/RuleTester.test.ts +++ b/packages/rule-tester/tests/RuleTester.test.ts @@ -169,7 +169,6 @@ describe('RuleTester', () => { { code: 'type-aware parser options should override the constructor config', parserOptions: { - EXPERIMENTAL_useProjectService: false, project: 'tsconfig.test-specific.json', tsconfigRootDir: '/set/in/the/test/', }, @@ -210,7 +209,6 @@ describe('RuleTester', () => { "code": "type-aware parser options should override the constructor config", "filename": "/set/in/the/test/file.ts", "parserOptions": { - "EXPERIMENTAL_useProjectService": false, "project": "tsconfig.test-specific.json", "tsconfigRootDir": "/set/in/the/test/", }, diff --git a/packages/type-utils/tests/TypeOrValueSpecifier.test.ts b/packages/type-utils/tests/TypeOrValueSpecifier.test.ts index a91358b4b172..8ddca54d3b34 100644 --- a/packages/type-utils/tests/TypeOrValueSpecifier.test.ts +++ b/packages/type-utils/tests/TypeOrValueSpecifier.test.ts @@ -196,18 +196,18 @@ describe('TypeOrValueSpecifier', () => { ], [ 'interface Foo {prop: string}; type Test = Foo;', - { from: 'file', name: 'Foo', path: 'file.ts' }, + { from: 'file', name: 'Foo', path: 'tests/fixtures/file.ts' }, ], [ 'type Foo = {prop: string}; type Test = Foo;', - { from: 'file', name: 'Foo', path: 'file.ts' }, + { from: 'file', name: 'Foo', path: 'tests/fixtures/file.ts' }, ], [ 'interface Foo {prop: string}; type Test = Foo;', { from: 'file', name: 'Foo', - path: './////file.ts', + path: 'tests/../tests/fixtures/////file.ts', }, ], [ @@ -215,7 +215,7 @@ describe('TypeOrValueSpecifier', () => { { from: 'file', name: 'Foo', - path: './////file.ts', + path: 'tests/../tests/fixtures/////file.ts', }, ], [ @@ -223,7 +223,7 @@ describe('TypeOrValueSpecifier', () => { { from: 'file', name: ['Foo', 'Bar'], - path: 'file.ts', + path: 'tests/fixtures/file.ts', }, ], [ @@ -231,7 +231,7 @@ describe('TypeOrValueSpecifier', () => { { from: 'file', name: ['Foo', 'Bar'], - path: 'file.ts', + path: 'tests/fixtures/file.ts', }, ], ])('matches a matching file specifier: %s', runTestPositive); @@ -247,14 +247,14 @@ describe('TypeOrValueSpecifier', () => { ], [ 'interface Foo {prop: string}; type Test = Foo;', - { from: 'file', name: 'Foo', path: 'wrong-file.ts' }, + { from: 'file', name: 'Foo', path: 'tests/fixtures/wrong-file.ts' }, ], [ 'interface Foo {prop: string}; type Test = Foo;', { from: 'file', name: ['Foo', 'Bar'], - path: 'wrong-file.ts', + path: 'tests/fixtures/wrong-file.ts', }, ], ])("doesn't match a mismatched file specifier: %s", runTestNegative); @@ -399,14 +399,14 @@ describe('TypeOrValueSpecifier', () => { ['type Test = RegExp;', { from: 'file', name: ['RegExp', 'BigInt'] }], [ 'type Test = RegExp;', - { from: 'file', name: 'RegExp', path: 'file.ts' }, + { from: 'file', name: 'RegExp', path: 'tests/fixtures/file.ts' }, ], [ 'type Test = RegExp;', { from: 'file', name: ['RegExp', 'BigInt'], - path: 'file.ts', + path: 'tests/fixtures/file.ts', }, ], [ diff --git a/packages/types/src/parser-options.ts b/packages/types/src/parser-options.ts index 5389efba75eb..4e8bb90dfae1 100644 --- a/packages/types/src/parser-options.ts +++ b/packages/types/src/parser-options.ts @@ -47,7 +47,6 @@ interface ParserOptions { debugLevel?: DebugLevel; errorOnTypeScriptSyntacticAndSemanticIssues?: boolean; errorOnUnknownASTType?: boolean; - EXPERIMENTAL_useProjectService?: boolean; // purposely undocumented for now EXPERIMENTAL_useSourceOfProjectReferenceRedirect?: boolean; // purposely undocumented for now extraFileExtensions?: string[]; filePath?: string; diff --git a/packages/typescript-estree/src/clear-caches.ts b/packages/typescript-estree/src/clear-caches.ts index 015fd18e29c8..aea4d6cf845c 100644 --- a/packages/typescript-estree/src/clear-caches.ts +++ b/packages/typescript-estree/src/clear-caches.ts @@ -1,9 +1,6 @@ import { clearWatchCaches } from './create-program/getWatchProgramsForProjects'; import { clearProgramCache as clearProgramCacheOriginal } from './parser'; -import { - clearTSConfigMatchCache, - clearTSServerProjectService, -} from './parseSettings/createParseSettings'; +import { clearTSConfigMatchCache } from './parseSettings/createParseSettings'; import { clearGlobCache } from './parseSettings/resolveProjectList'; /** @@ -17,7 +14,6 @@ export function clearCaches(): void { clearProgramCacheOriginal(); clearWatchCaches(); clearTSConfigMatchCache(); - clearTSServerProjectService(); clearGlobCache(); } diff --git a/packages/typescript-estree/src/create-program/createProjectProgram.ts b/packages/typescript-estree/src/create-program/createProjectProgram.ts index edfe00992c19..51a2ebdfdfc6 100644 --- a/packages/typescript-estree/src/create-program/createProjectProgram.ts +++ b/packages/typescript-estree/src/create-program/createProjectProgram.ts @@ -5,6 +5,7 @@ import * as ts from 'typescript'; import { firstDefined } from '../node-utils'; import type { ParseSettings } from '../parseSettings'; import { describeFilePath } from './describeFilePath'; +import { getWatchProgramsForProjects } from './getWatchProgramsForProjects'; import type { ASTAndDefiniteProgram } from './shared'; import { getAstFromProgram } from './shared'; @@ -27,12 +28,12 @@ const DEFAULT_EXTRA_FILE_EXTENSIONS = [ */ function createProjectProgram( parseSettings: ParseSettings, - programsForProjects: readonly ts.Program[], ): ASTAndDefiniteProgram | undefined { log('Creating project program for: %s', parseSettings.filePath); + const programsForProjects = getWatchProgramsForProjects(parseSettings); const astAndProgram = firstDefined(programsForProjects, currentProgram => - getAstFromProgram(currentProgram, parseSettings.filePath), + getAstFromProgram(currentProgram, parseSettings), ); // The file was either matched within the tsconfig, or we allow creating a default program diff --git a/packages/typescript-estree/src/create-program/createProjectService.ts b/packages/typescript-estree/src/create-program/createProjectService.ts deleted file mode 100644 index 333d221f85b1..000000000000 --- a/packages/typescript-estree/src/create-program/createProjectService.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* eslint-disable @typescript-eslint/no-empty-function -- for TypeScript APIs*/ -import type * as ts from 'typescript/lib/tsserverlibrary'; - -const doNothing = (): void => {}; - -const createStubFileWatcher = (): ts.FileWatcher => ({ - close: doNothing, -}); - -export type TypeScriptProjectService = ts.server.ProjectService; - -export function createProjectService(): TypeScriptProjectService { - // We import this lazily to avoid its cost for users who don't use the service - const tsserver = require('typescript/lib/tsserverlibrary') as typeof ts; - - // TODO: see getWatchProgramsForProjects - // We don't watch the disk, we just refer to these when ESLint calls us - // there's a whole separate update pass in maybeInvalidateProgram at the bottom of getWatchProgramsForProjects - // (this "goes nuclear on TypeScript") - const system: ts.server.ServerHost = { - ...tsserver.sys, - clearImmediate, - clearTimeout, - setImmediate, - setTimeout, - watchDirectory: createStubFileWatcher, - watchFile: createStubFileWatcher, - }; - - return new tsserver.server.ProjectService({ - host: system, - cancellationToken: { isCancellationRequested: (): boolean => false }, - useSingleInferredProject: false, - useInferredProjectPerProjectRoot: false, - logger: { - close: doNothing, - endGroup: doNothing, - getLogFileName: () => undefined, - hasLevel: () => false, - info: doNothing, - loggingEnabled: () => false, - msg: doNothing, - perftrc: doNothing, - startGroup: doNothing, - }, - session: undefined, - }); -} -/* eslint-enable @typescript-eslint/no-empty-function */ diff --git a/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts b/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts index e5e4b70f7d0a..2ec2b4ce35ae 100644 --- a/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts +++ b/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts @@ -261,10 +261,7 @@ function createWatchProgram( const watchCompilerHost = ts.createWatchCompilerHost( tsconfigPath, createDefaultCompilerOptionsFromExtra(parseSettings), - { - ...ts.sys, - getCurrentDirectory: () => parseSettings.tsconfigRootDir, - }, + ts.sys, ts.createAbstractBuilder, diagnosticReporter, // TODO: file issue on TypeScript to suggest making optional? diff --git a/packages/typescript-estree/src/create-program/shared.ts b/packages/typescript-estree/src/create-program/shared.ts index 8966093372ed..b0e39d19e7e6 100644 --- a/packages/typescript-estree/src/create-program/shared.ts +++ b/packages/typescript-estree/src/create-program/shared.ts @@ -97,12 +97,12 @@ function getExtension(fileName: string | undefined): string | null { function getAstFromProgram( currentProgram: Program, - filePath: string, + parseSettings: ParseSettings, ): ASTAndDefiniteProgram | undefined { - const ast = currentProgram.getSourceFile(filePath); + const ast = currentProgram.getSourceFile(parseSettings.filePath); // working around https://github.com/typescript-eslint/typescript-eslint/issues/1573 - const expectedExt = getExtension(filePath); + const expectedExt = getExtension(parseSettings.filePath); const returnedExt = getExtension(ast?.fileName); if (expectedExt !== returnedExt) { return undefined; diff --git a/packages/typescript-estree/src/create-program/useProvidedPrograms.ts b/packages/typescript-estree/src/create-program/useProvidedPrograms.ts index c2b67e795750..96093e9a3afa 100644 --- a/packages/typescript-estree/src/create-program/useProvidedPrograms.ts +++ b/packages/typescript-estree/src/create-program/useProvidedPrograms.ts @@ -3,25 +3,24 @@ import * as fs from 'fs'; import * as path from 'path'; import * as ts from 'typescript'; +import type { ParseSettings } from '../parseSettings'; import type { ASTAndDefiniteProgram } from './shared'; import { CORE_COMPILER_OPTIONS, getAstFromProgram } from './shared'; const log = debug('typescript-eslint:typescript-estree:useProvidedProgram'); -export interface ProvidedProgramsSettings { - filePath: string; - tsconfigRootDir: string; -} - function useProvidedPrograms( programInstances: Iterable, - { filePath, tsconfigRootDir }: ProvidedProgramsSettings, + parseSettings: ParseSettings, ): ASTAndDefiniteProgram | undefined { - log('Retrieving ast for %s from provided program instance(s)', filePath); + log( + 'Retrieving ast for %s from provided program instance(s)', + parseSettings.filePath, + ); let astAndProgram: ASTAndDefiniteProgram | undefined; for (const programInstance of programInstances) { - astAndProgram = getAstFromProgram(programInstance, filePath); + astAndProgram = getAstFromProgram(programInstance, parseSettings); // Stop at the first applicable program instance if (astAndProgram) { break; @@ -30,8 +29,8 @@ function useProvidedPrograms( if (!astAndProgram) { const relativeFilePath = path.relative( - tsconfigRootDir || process.cwd(), - filePath, + parseSettings.tsconfigRootDir || process.cwd(), + parseSettings.filePath, ); const errorLines = [ '"parserOptions.programs" has been provided for @typescript-eslint/parser.', diff --git a/packages/typescript-estree/src/parseSettings/createParseSettings.ts b/packages/typescript-estree/src/parseSettings/createParseSettings.ts index 3062a6164b32..b26e00376977 100644 --- a/packages/typescript-estree/src/parseSettings/createParseSettings.ts +++ b/packages/typescript-estree/src/parseSettings/createParseSettings.ts @@ -1,8 +1,6 @@ import debug from 'debug'; import type * as ts from 'typescript'; -import type { TypeScriptProjectService } from '../create-program/createProjectService'; -import { createProjectService } from '../create-program/createProjectService'; import { ensureAbsolutePath } from '../create-program/shared'; import type { TSESTreeOptions } from '../parser-options'; import { isSourceFile } from '../source-files'; @@ -21,7 +19,6 @@ const log = debug( ); let TSCONFIG_MATCH_CACHE: ExpiringCache | null; -let TSSERVER_PROJECT_SERVICE: TypeScriptProjectService | null = null; export function createParseSettings( code: ts.SourceFile | string, @@ -50,13 +47,6 @@ export function createParseSettings( : new Set(), errorOnTypeScriptSyntacticAndSemanticIssues: false, errorOnUnknownASTType: options.errorOnUnknownASTType === true, - EXPERIMENTAL_projectService: - (options.EXPERIMENTAL_useProjectService === true && - process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER !== 'false') || - (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER === 'true' && - options.EXPERIMENTAL_useProjectService !== false) - ? (TSSERVER_PROJECT_SERVICE ??= createProjectService()) - : undefined, EXPERIMENTAL_useSourceOfProjectReferenceRedirect: options.EXPERIMENTAL_useSourceOfProjectReferenceRedirect === true, extraFileExtensions: @@ -124,8 +114,8 @@ export function createParseSettings( ); } - // Providing a program or project service overrides project resolution - if (!parseSettings.programs && !parseSettings.EXPERIMENTAL_projectService) { + // Providing a program overrides project resolution + if (!parseSettings.programs) { parseSettings.projects = resolveProjectList({ cacheLifetime: options.cacheLifetime, project: getProjectConfigFiles(parseSettings, options.project), @@ -144,10 +134,6 @@ export function clearTSConfigMatchCache(): void { TSCONFIG_MATCH_CACHE?.clear(); } -export function clearTSServerProjectService(): void { - TSSERVER_PROJECT_SERVICE = null; -} - /** * Ensures source code is a string. */ diff --git a/packages/typescript-estree/src/parseSettings/index.ts b/packages/typescript-estree/src/parseSettings/index.ts index da093dedfed9..3cf3e2a6f692 100644 --- a/packages/typescript-estree/src/parseSettings/index.ts +++ b/packages/typescript-estree/src/parseSettings/index.ts @@ -1,5 +1,4 @@ import type * as ts from 'typescript'; -import type * as tsserverlibrary from 'typescript/lib/tsserverlibrary'; import type { CanonicalPath } from '../create-program/shared'; import type { TSESTree } from '../ts-estree'; @@ -58,13 +57,6 @@ export interface MutableParseSettings { */ errorOnUnknownASTType: boolean; - /** - * Experimental: TypeScript server to power program creation. - */ - EXPERIMENTAL_projectService: - | tsserverlibrary.server.ProjectService - | undefined; - /** * Whether TS should use the source files for referenced projects instead of the compiled .d.ts files. * diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index 711ef4bca34b..b867f32e63b7 100644 --- a/packages/typescript-estree/src/parser-options.ts +++ b/packages/typescript-estree/src/parser-options.ts @@ -94,15 +94,6 @@ interface ParseAndGenerateServicesOptions extends ParseOptions { */ errorOnTypeScriptSyntacticAndSemanticIssues?: boolean; - /** - * ***EXPERIMENTAL FLAG*** - Use this at your own risk. - * - * Whether to create a shared TypeScript server to power program creation. - * - * @see https://github.com/typescript-eslint/typescript-eslint/issues/6575 - */ - EXPERIMENTAL_useProjectService?: boolean; - /** * ***EXPERIMENTAL FLAG*** - Use this at your own risk. * @@ -111,7 +102,7 @@ interface ParseAndGenerateServicesOptions extends ParseOptions { * * This flag REQUIRES at least TS v3.9, otherwise it does nothing. * - * @see https://github.com/typescript-eslint/typescript-eslint/issues/2094 + * See: https://github.com/typescript-eslint/typescript-eslint/issues/2094 */ EXPERIMENTAL_useSourceOfProjectReferenceRedirect?: boolean; diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 4bf5dec26bd9..63723c6dd628 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -10,7 +10,6 @@ import { createNoProgram, createSourceFile, } from './create-program/createSourceFile'; -import { getWatchProgramsForProjects } from './create-program/getWatchProgramsForProjects'; import type { ASTAndProgram, CanonicalPath } from './create-program/shared'; import { createProgramFromConfigFile, @@ -26,7 +25,6 @@ import type { ParseSettings } from './parseSettings'; import { createParseSettings } from './parseSettings/createParseSettings'; import { getFirstSemanticOrSyntacticError } from './semantic-or-syntactic-errors'; import type { TSESTree } from './ts-estree'; -import { useProgramFromProjectService } from './useProgramFromProjectService'; const log = debug('typescript-eslint:typescript-estree:parser'); @@ -49,16 +47,6 @@ function getProgramAndAST( parseSettings: ParseSettings, hasFullTypeInformation: boolean, ): ASTAndProgram { - if (parseSettings.EXPERIMENTAL_projectService) { - const fromProjectService = useProgramFromProjectService( - parseSettings.EXPERIMENTAL_projectService, - parseSettings, - ); - if (fromProjectService) { - return fromProjectService; - } - } - if (parseSettings.programs) { const fromProvidedPrograms = useProvidedPrograms( parseSettings.programs, @@ -69,30 +57,27 @@ function getProgramAndAST( } } - // no need to waste time creating a program as the caller didn't want parser services - // so we can save time and just create a lonesome source file - if (!hasFullTypeInformation) { - return createNoProgram(parseSettings); - } - - const fromProjectProgram = createProjectProgram( - parseSettings, - getWatchProgramsForProjects(parseSettings), - ); - if (fromProjectProgram) { - return fromProjectProgram; - } + if (hasFullTypeInformation) { + const fromProjectProgram = createProjectProgram(parseSettings); + if (fromProjectProgram) { + return fromProjectProgram; + } - // eslint-disable-next-line deprecation/deprecation -- will be cleaned up with the next major - if (parseSettings.DEPRECATED__createDefaultProgram) { // eslint-disable-next-line deprecation/deprecation -- will be cleaned up with the next major - const fromDefaultProgram = createDefaultProgram(parseSettings); - if (fromDefaultProgram) { - return fromDefaultProgram; + if (parseSettings.DEPRECATED__createDefaultProgram) { + // eslint-disable-next-line deprecation/deprecation -- will be cleaned up with the next major + const fromDefaultProgram = createDefaultProgram(parseSettings); + if (fromDefaultProgram) { + return fromDefaultProgram; + } } + + return createIsolatedProgram(parseSettings); } - return createIsolatedProgram(parseSettings); + // no need to waste time creating a program as the caller didn't want parser services + // so we can save time and just create a lonesome source file + return createNoProgram(parseSettings); } // eslint-disable-next-line @typescript-eslint/no-empty-interface diff --git a/packages/typescript-estree/src/useProgramFromProjectService.ts b/packages/typescript-estree/src/useProgramFromProjectService.ts deleted file mode 100644 index 16a7933a671c..000000000000 --- a/packages/typescript-estree/src/useProgramFromProjectService.ts +++ /dev/null @@ -1,40 +0,0 @@ -import path from 'path'; -import type * as ts from 'typescript'; -import type { server } from 'typescript/lib/tsserverlibrary'; - -import { createProjectProgram } from './create-program/createProjectProgram'; -import { type ASTAndDefiniteProgram } from './create-program/shared'; -import type { MutableParseSettings } from './parseSettings'; - -export function useProgramFromProjectService( - projectService: server.ProjectService, - parseSettings: Readonly, -): ASTAndDefiniteProgram | undefined { - const opened = projectService.openClientFile( - absolutify(parseSettings.filePath), - parseSettings.codeFullText, - /* scriptKind */ undefined, - parseSettings.tsconfigRootDir, - ); - if (!opened.configFileName) { - return undefined; - } - - const scriptInfo = projectService.getScriptInfo(parseSettings.filePath); - const program = projectService - .getDefaultProjectForFile(scriptInfo!.fileName, true)! - .getLanguageService(/*ensureSynchronized*/ true) - .getProgram(); - - if (!program) { - return undefined; - } - - return createProjectProgram(parseSettings, [program as ts.Program]); - - function absolutify(filePath: string): string { - return path.isAbsolute(filePath) - ? filePath - : path.join(projectService.host.getCurrentDirectory(), filePath); - } -} diff --git a/packages/typescript-estree/tests/lib/__snapshots__/parse.test.ts.snap b/packages/typescript-estree/tests/lib/__snapshots__/parse.test.ts.snap index c4182c42113a..0f777af2df28 100644 --- a/packages/typescript-estree/tests/lib/__snapshots__/parse.test.ts.snap +++ b/packages/typescript-estree/tests/lib/__snapshots__/parse.test.ts.snap @@ -1,5 +1,87 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`parseAndGenerateServices invalid file error messages "parserOptions.extraFileExtensions" is empty the extension does not match 1`] = ` +"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`] = ` +"ESLint was configured to run on \`/other/unknownFileType.unknown\` using \`parserOptions.project\`: /tsconfig.json +Found unexpected extension \`unknown\` specified with the \`parserOptions.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`] = ` +"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`] = ` +"ESLint was configured to run on \`/ts/notIncluded.ts\` using \`parserOptions.project\`: /tsconfig.json +You unnecessarily included the extension \`.ts\` with the \`parserOptions.extraFileExtensions\` option. This extension is already handled by the parser by default. +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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" +`; + +exports[`parseAndGenerateServices invalid file error messages "parserOptions.extraFileExtensions" is non-empty the extension matches the file isn't included 1`] = ` +"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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" +`; + +exports[`parseAndGenerateServices invalid file error messages project includes errors for not included files 1`] = ` +"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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" +`; + +exports[`parseAndGenerateServices invalid file error messages project includes errors for not included files 2`] = ` +"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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" +`; + +exports[`parseAndGenerateServices invalid file error messages project includes errors for not included files 3`] = ` +"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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" +`; + +exports[`parseAndGenerateServices invalid file error messages project includes errors for not included files 4`] = ` +"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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" +`; + +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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" +`; + exports[`parseAndGenerateServices isolated parsing should parse .js file - with JSX content - parserOptions.jsx = false 1`] = ` { "ast": { diff --git a/packages/typescript-estree/tests/lib/__snapshots__/semanticInfo.test.ts.snap b/packages/typescript-estree/tests/lib/__snapshots__/semanticInfo.test.ts.snap index 79772deb70e0..0d94ba3a46b8 100644 --- a/packages/typescript-estree/tests/lib/__snapshots__/semanticInfo.test.ts.snap +++ b/packages/typescript-estree/tests/lib/__snapshots__/semanticInfo.test.ts.snap @@ -1734,3 +1734,5 @@ exports[`semanticInfo fixtures/non-existent-estree-nodes.src 1`] = ` "type": "Program", } `; + +exports[`semanticInfo malformed project file 1`] = `"Compiler option 'compileOnSave' requires a value of type boolean."`; diff --git a/packages/typescript-estree/tests/lib/createProjectService.test.ts b/packages/typescript-estree/tests/lib/createProjectService.test.ts deleted file mode 100644 index f6f6d117e3bc..000000000000 --- a/packages/typescript-estree/tests/lib/createProjectService.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createProjectService } from '../../src/create-program/createProjectService'; - -describe('createProjectService', () => { - it('does not crash', () => { - createProjectService(); - }); -}); diff --git a/packages/typescript-estree/tests/lib/parse.project-true.test.ts b/packages/typescript-estree/tests/lib/parse.project-true.test.ts index ca81ab99f506..ff2ea0d0e3fb 100644 --- a/packages/typescript-estree/tests/lib/parse.project-true.test.ts +++ b/packages/typescript-estree/tests/lib/parse.project-true.test.ts @@ -35,17 +35,15 @@ describe('parseAndGenerateServices', () => { }); }); - if (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER !== 'true') { - it('throws an error when a parent project does not exist', () => { - expect(() => - parser.parseAndGenerateServices('const a = true', { - ...config, - filePath: join(PROJECT_DIR, 'notIncluded.ts'), - }), - ).toThrow( - /project was set to `true` but couldn't find any tsconfig.json relative to '.+[/\\]tests[/\\]fixtures[/\\]projectTrue[/\\]notIncluded.ts' within '.+[/\\]tests[/\\]fixtures[/\\]projectTrue'./, - ); - }); - } + it('throws an error when a parent project does not exist', () => { + expect(() => + parser.parseAndGenerateServices('const a = true', { + ...config, + filePath: join(PROJECT_DIR, 'notIncluded.ts'), + }), + ).toThrow( + /project was set to `true` but couldn't find any tsconfig.json relative to '.+[/\\]tests[/\\]fixtures[/\\]projectTrue[/\\]notIncluded.ts' within '.+[/\\]tests[/\\]fixtures[/\\]projectTrue'./, + ); + }); }); }); diff --git a/packages/typescript-estree/tests/lib/parse.test.ts b/packages/typescript-estree/tests/lib/parse.test.ts index 658493c9150c..02bacdfadf3a 100644 --- a/packages/typescript-estree/tests/lib/parse.test.ts +++ b/packages/typescript-estree/tests/lib/parse.test.ts @@ -166,7 +166,6 @@ describe('parseAndGenerateServices', () => { describe('isolated parsing', () => { const config: TSESTreeOptions = { - EXPERIMENTAL_useProjectService: false, comment: true, tokens: true, range: true, @@ -340,184 +339,124 @@ describe('parseAndGenerateServices', () => { }); }); - if (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER !== 'true') { - describe('invalid file error messages', () => { - const PROJECT_DIR = resolve(FIXTURES_DIR, '../invalidFileErrors'); - const code = 'var a = true'; - const config: TSESTreeOptions = { - comment: true, - tokens: true, - range: true, - loc: true, - tsconfigRootDir: PROJECT_DIR, - project: './tsconfig.json', + describe('invalid file error messages', () => { + const PROJECT_DIR = resolve(FIXTURES_DIR, '../invalidFileErrors'); + const code = 'var a = true'; + const config: TSESTreeOptions = { + comment: true, + tokens: true, + range: true, + loc: true, + tsconfigRootDir: PROJECT_DIR, + project: './tsconfig.json', + }; + const testParse = + (filePath: string, extraFileExtensions: string[] = ['.vue']) => + (): void => { + try { + parser.parseAndGenerateServices(code, { + ...config, + extraFileExtensions, + filePath: join(PROJECT_DIR, filePath), + }); + } catch (error) { + throw alignErrorPath(error as Error); + } }; - const testParse = - (filePath: string, extraFileExtensions: string[] = ['.vue']) => - (): void => { - try { - parser.parseAndGenerateServices(code, { - ...config, - extraFileExtensions, - filePath: join(PROJECT_DIR, filePath), - }); - } catch (error) { - throw alignErrorPath(error as Error); - } - }; - - describe('project includes', () => { - it("doesn't error for matched files", () => { - expect(testParse('ts/included01.ts')).not.toThrow(); - expect(testParse('ts/included02.tsx')).not.toThrow(); - expect(testParse('js/included01.js')).not.toThrow(); - expect(testParse('js/included02.jsx')).not.toThrow(); - }); - it('errors for not included files', () => { - expect(testParse('ts/notIncluded0j1.ts')) - .toThrowErrorMatchingInlineSnapshot(` - "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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" - `); - expect(testParse('ts/notIncluded02.tsx')) - .toThrowErrorMatchingInlineSnapshot(` - "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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" - `); - expect(testParse('js/notIncluded01.js')) - .toThrowErrorMatchingInlineSnapshot(` - "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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" - `); - expect(testParse('js/notIncluded02.jsx')) - .toThrowErrorMatchingInlineSnapshot(` - "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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" - `); - }); + describe('project includes', () => { + it("doesn't error for matched files", () => { + expect(testParse('ts/included01.ts')).not.toThrow(); + expect(testParse('ts/included02.tsx')).not.toThrow(); + expect(testParse('js/included01.js')).not.toThrow(); + expect(testParse('js/included02.jsx')).not.toThrow(); }); - describe('"parserOptions.extraFileExtensions" is empty', () => { - it('should not error', () => { - expect(testParse('ts/included01.ts', [])).not.toThrow(); - }); - - it('the extension does not match', () => { - expect(testParse('other/unknownFileType.unknown', [])) - .toThrowErrorMatchingInlineSnapshot(` - "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." - `); - }); + it('errors for not included files', () => { + expect( + testParse('ts/notIncluded0j1.ts'), + ).toThrowErrorMatchingSnapshot(); + expect( + testParse('ts/notIncluded02.tsx'), + ).toThrowErrorMatchingSnapshot(); + expect(testParse('js/notIncluded01.js')).toThrowErrorMatchingSnapshot(); + expect( + testParse('js/notIncluded02.jsx'), + ).toThrowErrorMatchingSnapshot(); }); + }); - describe('"parserOptions.extraFileExtensions" is non-empty', () => { - describe('the extension matches', () => { - it('the file is included', () => { - expect(testParse('other/included.vue')).not.toThrow(); - }); + describe('"parserOptions.extraFileExtensions" is empty', () => { + it('should not error', () => { + expect(testParse('ts/included01.ts', [])).not.toThrow(); + }); - it("the file isn't included", () => { - expect(testParse('other/notIncluded.vue')) - .toThrowErrorMatchingInlineSnapshot(` - "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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" - `); - }); + it('the extension does not match', () => { + expect( + testParse('other/unknownFileType.unknown', []), + ).toThrowErrorMatchingSnapshot(); + }); + }); - it('duplicate extension', () => { - expect(testParse('ts/notIncluded.ts', ['.ts'])) - .toThrowErrorMatchingInlineSnapshot(` - "ESLint was configured to run on \`/ts/notIncluded.ts\` using \`parserOptions.project\`: /tsconfig.json - You unnecessarily included the extension \`.ts\` with the \`parserOptions.extraFileExtensions\` option. This extension is already handled by the parser by default. - 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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" - `); - }); + describe('"parserOptions.extraFileExtensions" is non-empty', () => { + describe('the extension matches', () => { + it('the file is included', () => { + expect(testParse('other/included.vue')).not.toThrow(); }); - it('invalid extension', () => { - expect(testParse('other/unknownFileType.unknown', ['unknown'])) - .toThrowErrorMatchingInlineSnapshot(` - "ESLint was configured to run on \`/other/unknownFileType.unknown\` using \`parserOptions.project\`: /tsconfig.json - Found unexpected extension \`unknown\` specified with the \`parserOptions.extraFileExtensions\` option. Did you mean \`.unknown\`? - The extension for the file (\`.unknown\`) is non-standard. It should be added to your existing \`parserOptions.extraFileExtensions\`." - `); + it("the file isn't included", () => { + expect( + testParse('other/notIncluded.vue'), + ).toThrowErrorMatchingSnapshot(); }); - it('the extension does not match', () => { - expect(testParse('other/unknownFileType.unknown')) - .toThrowErrorMatchingInlineSnapshot(` - "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\`." - `); + it('duplicate extension', () => { + expect( + testParse('ts/notIncluded.ts', ['.ts']), + ).toThrowErrorMatchingSnapshot(); }); }); + + it('invalid extension', () => { + expect( + testParse('other/unknownFileType.unknown', ['unknown']), + ).toThrowErrorMatchingSnapshot(); + }); + + it('the extension does not match', () => { + expect( + testParse('other/unknownFileType.unknown'), + ).toThrowErrorMatchingSnapshot(); + }); }); + }); - describe('invalid project error messages', () => { - if (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER !== 'true') { - it('throws when none of multiple projects include the file', () => { - const PROJECT_DIR = resolve(FIXTURES_DIR, '../invalidFileErrors'); - const code = 'var a = true'; - const config: TSESTreeOptions = { - comment: true, - tokens: true, - range: true, - loc: true, - tsconfigRootDir: PROJECT_DIR, - project: ['./**/tsconfig.json', './**/tsconfig.extra.json'], - }; - const testParse = (filePath: string) => (): void => { - try { - parser.parseAndGenerateServices(code, { - ...config, - filePath: join(PROJECT_DIR, filePath), - }); - } catch (error) { - throw alignErrorPath(error as Error); - } - }; - - expect(testParse('ts/notIncluded0j1.ts')) - .toThrowErrorMatchingInlineSnapshot(` - "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/linting/troubleshooting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file" - `); - }); - } + describe('invalid project error messages', () => { + it('throws when non of multiple projects include the file', () => { + const PROJECT_DIR = resolve(FIXTURES_DIR, '../invalidFileErrors'); + const code = 'var a = true'; + const config: TSESTreeOptions = { + comment: true, + tokens: true, + range: true, + loc: true, + tsconfigRootDir: PROJECT_DIR, + project: ['./**/tsconfig.json', './**/tsconfig.extra.json'], + }; + const testParse = (filePath: string) => (): void => { + try { + parser.parseAndGenerateServices(code, { + ...config, + filePath: join(PROJECT_DIR, filePath), + }); + } catch (error) { + throw alignErrorPath(error as Error); + } + }; + + expect(testParse('ts/notIncluded0j1.ts')).toThrowErrorMatchingSnapshot(); }); - } + }); describe('debug options', () => { const debugEnable = jest.fn(); @@ -560,127 +499,123 @@ describe('parseAndGenerateServices', () => { ); }); - if (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER !== 'true') { - it('should turn on typescript debugger', () => { - expect(() => - parser.parseAndGenerateServices('const x = 1;', { - debugLevel: ['typescript'], - filePath: './path-that-doesnt-exist.ts', - project: ['./tsconfig-that-doesnt-exist.json'], - }), - ) // should throw because the file and tsconfig don't exist - .toThrow(); - expect(createDefaultCompilerOptionsFromExtra).toHaveBeenCalled(); - expect(createDefaultCompilerOptionsFromExtra).toHaveReturnedWith( - expect.objectContaining({ - extendedDiagnostics: true, - }), - ); - }); - } + it('should turn on typescript debugger', () => { + expect(() => + parser.parseAndGenerateServices('const x = 1;', { + debugLevel: ['typescript'], + filePath: './path-that-doesnt-exist.ts', + project: ['./tsconfig-that-doesnt-exist.json'], + }), + ) // should throw because the file and tsconfig don't exist + .toThrow(); + expect(createDefaultCompilerOptionsFromExtra).toHaveBeenCalled(); + expect(createDefaultCompilerOptionsFromExtra).toHaveReturnedWith( + expect.objectContaining({ + extendedDiagnostics: true, + }), + ); + }); }); - if (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER !== 'true') { - describe('projectFolderIgnoreList', () => { - beforeEach(() => { - parser.clearCaches(); - }); - - const PROJECT_DIR = resolve(FIXTURES_DIR, '../projectFolderIgnoreList'); - const code = 'var a = true'; - const config: TSESTreeOptions = { - comment: true, - tokens: true, - range: true, - loc: true, - tsconfigRootDir: PROJECT_DIR, - project: './**/tsconfig.json', - }; + describe('projectFolderIgnoreList', () => { + beforeEach(() => { + parser.clearCaches(); + }); - const testParse = - ( - filePath: 'ignoreme' | 'includeme', - projectFolderIgnoreList?: TSESTreeOptions['projectFolderIgnoreList'], - ) => - (): void => { - parser.parseAndGenerateServices(code, { - ...config, - projectFolderIgnoreList, - filePath: join(PROJECT_DIR, filePath, './file.ts'), - }); - }; + const PROJECT_DIR = resolve(FIXTURES_DIR, '../projectFolderIgnoreList'); + const code = 'var a = true'; + const config: TSESTreeOptions = { + comment: true, + tokens: true, + range: true, + loc: true, + tsconfigRootDir: PROJECT_DIR, + project: './**/tsconfig.json', + }; - it('ignores nothing when given nothing', () => { - expect(testParse('ignoreme')).not.toThrow(); - expect(testParse('includeme')).not.toThrow(); - }); + const testParse = + ( + filePath: 'ignoreme' | 'includeme', + projectFolderIgnoreList?: TSESTreeOptions['projectFolderIgnoreList'], + ) => + (): void => { + parser.parseAndGenerateServices(code, { + ...config, + projectFolderIgnoreList, + filePath: join(PROJECT_DIR, filePath, './file.ts'), + }); + }; - it('ignores a folder when given a string glob', () => { - const ignore = ['**/ignoreme/**']; - // cspell:disable-next-line - expect(testParse('ignoreme', ignore)).toThrow(); - // cspell:disable-next-line - expect(testParse('includeme', ignore)).not.toThrow(); - }); + it('ignores nothing when given nothing', () => { + expect(testParse('ignoreme')).not.toThrow(); + expect(testParse('includeme')).not.toThrow(); }); - describe('cacheLifetime', () => { - describe('glob', () => { - function doParse(lifetime: CacheDurationSeconds): void { - parser.parseAndGenerateServices('const x = 1', { - cacheLifetime: { - glob: lifetime, - }, - filePath: join(FIXTURES_DIR, 'file.ts'), - tsconfigRootDir: FIXTURES_DIR, - project: ['./**/tsconfig.json', './**/tsconfig.extra.json'], - }); - } + it('ignores a folder when given a string glob', () => { + const ignore = ['**/ignoreme/**']; + // cspell:disable-next-line + expect(testParse('ignoreme', ignore)).toThrow(); + // cspell:disable-next-line + expect(testParse('includeme', ignore)).not.toThrow(); + }); + }); - it('should cache globs if the lifetime is non-zero', () => { - doParse(30); - expect(globbySyncMock).toHaveBeenCalledTimes(1); - doParse(30); - // shouldn't call globby again due to the caching - expect(globbySyncMock).toHaveBeenCalledTimes(1); + describe('cacheLifetime', () => { + describe('glob', () => { + function doParse(lifetime: CacheDurationSeconds): void { + parser.parseAndGenerateServices('const x = 1', { + cacheLifetime: { + glob: lifetime, + }, + filePath: join(FIXTURES_DIR, 'file.ts'), + tsconfigRootDir: FIXTURES_DIR, + project: ['./**/tsconfig.json', './**/tsconfig.extra.json'], }); + } - it('should not cache globs if the lifetime is zero', () => { - doParse(0); - expect(globbySyncMock).toHaveBeenCalledTimes(1); - doParse(0); - // should call globby again because we specified immediate cache expiry - expect(globbySyncMock).toHaveBeenCalledTimes(2); - }); + it('should cache globs if the lifetime is non-zero', () => { + doParse(30); + expect(globbySyncMock).toHaveBeenCalledTimes(1); + doParse(30); + // shouldn't call globby again due to the caching + expect(globbySyncMock).toHaveBeenCalledTimes(1); + }); - it('should evict the cache if the entry expires', () => { - hrtimeSpy.mockReturnValueOnce([1, 0]); + it('should not cache globs if the lifetime is zero', () => { + doParse(0); + expect(globbySyncMock).toHaveBeenCalledTimes(1); + doParse(0); + // should call globby again because we specified immediate cache expiry + expect(globbySyncMock).toHaveBeenCalledTimes(2); + }); - doParse(30); - expect(globbySyncMock).toHaveBeenCalledTimes(1); + it('should evict the cache if the entry expires', () => { + hrtimeSpy.mockReturnValueOnce([1, 0]); - // wow so much time has passed - hrtimeSpy.mockReturnValueOnce([Number.MAX_VALUE, 0]); + doParse(30); + expect(globbySyncMock).toHaveBeenCalledTimes(1); - doParse(30); - // shouldn't call globby again due to the caching - expect(globbySyncMock).toHaveBeenCalledTimes(2); - }); + // wow so much time has passed + hrtimeSpy.mockReturnValueOnce([Number.MAX_VALUE, 0]); + + doParse(30); + // shouldn't call globby again due to the caching + expect(globbySyncMock).toHaveBeenCalledTimes(2); + }); - it('should infinitely cache if passed Infinity', () => { - hrtimeSpy.mockReturnValueOnce([1, 0]); + it('should infinitely cache if passed Infinity', () => { + hrtimeSpy.mockReturnValueOnce([1, 0]); - doParse('Infinity'); - expect(globbySyncMock).toHaveBeenCalledTimes(1); + doParse('Infinity'); + expect(globbySyncMock).toHaveBeenCalledTimes(1); - // wow so much time has passed - hrtimeSpy.mockReturnValueOnce([Number.MAX_VALUE, 0]); + // wow so much time has passed + hrtimeSpy.mockReturnValueOnce([Number.MAX_VALUE, 0]); - doParse('Infinity'); - // shouldn't call globby again due to the caching - expect(globbySyncMock).toHaveBeenCalledTimes(1); - }); + doParse('Infinity'); + // shouldn't call globby again due to the caching + expect(globbySyncMock).toHaveBeenCalledTimes(1); }); }); - } + }); }); diff --git a/packages/typescript-estree/tests/lib/persistentParse.test.ts b/packages/typescript-estree/tests/lib/persistentParse.test.ts index 710b9c54ab5a..63e81d7e260a 100644 --- a/packages/typescript-estree/tests/lib/persistentParse.test.ts +++ b/packages/typescript-estree/tests/lib/persistentParse.test.ts @@ -2,7 +2,6 @@ import fs from 'fs'; import path from 'path'; import tmp from 'tmp'; -import { clearCaches } from '../../src/clear-caches'; import { clearWatchCaches } from '../../src/create-program/getWatchProgramsForProjects'; import { parseAndGenerateServices } from '../../src/parser'; @@ -87,11 +86,6 @@ function baseTests( tsConfigExcludeBar: Record, tsConfigIncludeAll: Record, ): void { - // The experimental project server creates a default project for files - if (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER === 'true') { - return; - } - it('parses both files successfully when included', () => { const PROJECT_DIR = setup(tsConfigIncludeAll); @@ -183,7 +177,6 @@ function baseTests( // change the config file so it now includes all files writeTSConfig(PROJECT_DIR, tsConfigIncludeAll); - clearCaches(); expect(() => parseFile('foo', PROJECT_DIR)).not.toThrow(); expect(() => parseFile('bar', PROJECT_DIR)).not.toThrow(); @@ -264,48 +257,43 @@ describe('persistent parse', () => { /* If there is no includes, then typescript will ask for a slightly different set of watchers. */ + describe('tsconfig with no includes / files', () => { + const tsConfigExcludeBar = { + exclude: ['./src/bar.ts'], + }; + const tsConfigIncludeAll = {}; - if (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER !== 'true') { - describe('tsconfig with no includes / files', () => { - const tsConfigExcludeBar = { - exclude: ['./src/bar.ts'], - }; - const tsConfigIncludeAll = {}; - - baseTests(tsConfigExcludeBar, tsConfigIncludeAll); + baseTests(tsConfigExcludeBar, tsConfigIncludeAll); - it('handles tsconfigs with no includes/excludes (single level)', () => { - const PROJECT_DIR = setup({}, false); + it('handles tsconfigs with no includes/excludes (single level)', () => { + const PROJECT_DIR = setup({}, false); - // parse once to: assert the config as correct, and to make sure the program is setup - expect(() => parseFile('foo', PROJECT_DIR)).not.toThrow(); - expect(() => parseFile('bar', PROJECT_DIR)).toThrow(); + // parse once to: assert the config as correct, and to make sure the program is setup + expect(() => parseFile('foo', PROJECT_DIR)).not.toThrow(); + expect(() => parseFile('bar', PROJECT_DIR)).toThrow(); - // write a new file and attempt to parse it - writeFile(PROJECT_DIR, 'bar'); - clearCaches(); + // write a new file and attempt to parse it + writeFile(PROJECT_DIR, 'bar'); - expect(() => parseFile('foo', PROJECT_DIR)).not.toThrow(); - expect(() => parseFile('bar', PROJECT_DIR)).not.toThrow(); - }); + expect(() => parseFile('foo', PROJECT_DIR)).not.toThrow(); + expect(() => parseFile('bar', PROJECT_DIR)).not.toThrow(); + }); - it('handles tsconfigs with no includes/excludes (nested)', () => { - const PROJECT_DIR = setup({}, false); - const bazSlashBar = 'baz/bar' as const; + it('handles tsconfigs with no includes/excludes (nested)', () => { + const PROJECT_DIR = setup({}, false); + const bazSlashBar = 'baz/bar' as const; - // parse once to: assert the config as correct, and to make sure the program is setup - expect(() => parseFile('foo', PROJECT_DIR)).not.toThrow(); - expect(() => parseFile(bazSlashBar, PROJECT_DIR)).toThrow(); + // parse once to: assert the config as correct, and to make sure the program is setup + expect(() => parseFile('foo', PROJECT_DIR)).not.toThrow(); + expect(() => parseFile(bazSlashBar, PROJECT_DIR)).toThrow(); - // write a new file and attempt to parse it - writeFile(PROJECT_DIR, bazSlashBar); - clearCaches(); + // write a new file and attempt to parse it + writeFile(PROJECT_DIR, bazSlashBar); - expect(() => parseFile('foo', PROJECT_DIR)).not.toThrow(); - expect(() => parseFile(bazSlashBar, PROJECT_DIR)).not.toThrow(); - }); + expect(() => parseFile('foo', PROJECT_DIR)).not.toThrow(); + expect(() => parseFile(bazSlashBar, PROJECT_DIR)).not.toThrow(); }); - } + }); /* If there is no includes, then typescript will ask for a slightly different set of watchers. diff --git a/packages/typescript-estree/tests/lib/semanticInfo-singleRun.test.ts b/packages/typescript-estree/tests/lib/semanticInfo-singleRun.test.ts index 09daae795d61..725195111f14 100644 --- a/packages/typescript-estree/tests/lib/semanticInfo-singleRun.test.ts +++ b/packages/typescript-estree/tests/lib/semanticInfo-singleRun.test.ts @@ -135,132 +135,124 @@ describe('semanticInfo - singleRun', () => { process.env.CI = originalEnvCI; }); - if (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER !== 'true') { - it('should lazily create the required program out of the provided "parserOptions.project" one time when TSESTREE_SINGLE_RUN=true', () => { - /** - * Single run because of explicit environment variable TSESTREE_SINGLE_RUN - */ - const originalTSESTreeSingleRun = process.env.TSESTREE_SINGLE_RUN; - process.env.TSESTREE_SINGLE_RUN = 'true'; - - const resultProgram = parseAndGenerateServices(code, options).services - .program; - expect(resultProgram).toEqual(mockProgram); - - // Call parseAndGenerateServices() again to ensure caching of Programs is working correctly... - parseAndGenerateServices(code, options); - // ...by asserting this was only called once per project - expect(createProgramFromConfigFile).toHaveBeenCalledTimes( - tsconfigs.length, - ); - - expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( - 1, - resolvedProject(tsconfigs[0]), - ); - expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( - 2, - resolvedProject(tsconfigs[1]), - ); - - // Restore process data - process.env.TSESTREE_SINGLE_RUN = originalTSESTreeSingleRun; - }); - - it('should lazily create the required program out of the provided "parserOptions.project" one time when singleRun is inferred from CI=true', () => { - /** - * Single run because of CI=true (we need to make sure we respect the original value - * so that we won't interfere with our own usage of the variable) - */ - const originalEnvCI = process.env.CI; - process.env.CI = 'true'; - - const resultProgram = parseAndGenerateServices(code, options).services - .program; - expect(resultProgram).toEqual(mockProgram); - - // Call parseAndGenerateServices() again to ensure caching of Programs is working correctly... - parseAndGenerateServices(code, options); - // ...by asserting this was only called once per project - expect(createProgramFromConfigFile).toHaveBeenCalledTimes( - tsconfigs.length, - ); - - expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( - 1, - resolvedProject(tsconfigs[0]), - ); - expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( - 2, - resolvedProject(tsconfigs[1]), - ); - - // Restore process data - process.env.CI = originalEnvCI; - }); - - it('should lazily create the required program out of the provided "parserOptions.project" one time when singleRun is inferred from process.argv', () => { - /** - * Single run because of process.argv - */ - const originalProcessArgv = process.argv; - process.argv = ['', path.normalize('node_modules/.bin/eslint'), '']; - - const resultProgram = parseAndGenerateServices(code, options).services - .program; - expect(resultProgram).toEqual(mockProgram); - - // Call parseAndGenerateServices() again to ensure caching of Programs is working correctly... - parseAndGenerateServices(code, options); - // ...by asserting this was only called once per project - expect(createProgramFromConfigFile).toHaveBeenCalledTimes( - tsconfigs.length, - ); - - expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( - 1, - resolvedProject(tsconfigs[0]), - ); - expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( - 2, - resolvedProject(tsconfigs[1]), - ); - - // Restore process data - process.argv = originalProcessArgv; - }); - - it('should stop iterating through and lazily creating programs for the given "parserOptions.project" once a matching one has been found', () => { - /** - * Single run because of explicit environment variable TSESTREE_SINGLE_RUN - */ - const originalTSESTreeSingleRun = process.env.TSESTREE_SINGLE_RUN; - process.env.TSESTREE_SINGLE_RUN = 'true'; - - const optionsWithReversedTsconfigs = { - ...options, - // Now the matching tsconfig comes first - project: [...options.project].reverse(), - }; - - const resultProgram = parseAndGenerateServices( - code, - optionsWithReversedTsconfigs, - ).services.program; - expect(resultProgram).toEqual(mockProgram); - - // Call parseAndGenerateServices() again to ensure caching of Programs is working correctly... - parseAndGenerateServices(code, options); - // ...by asserting this was only called only once - expect(createProgramFromConfigFile).toHaveBeenCalledTimes(1); - - expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( - 1, - resolvedProject(tsconfigs[1]), - ); - - // Restore process data - process.env.TSESTREE_SINGLE_RUN = originalTSESTreeSingleRun; - }); - } + it('should lazily create the required program out of the provided "parserOptions.project" one time when TSESTREE_SINGLE_RUN=true', () => { + /** + * Single run because of explicit environment variable TSESTREE_SINGLE_RUN + */ + const originalTSESTreeSingleRun = process.env.TSESTREE_SINGLE_RUN; + process.env.TSESTREE_SINGLE_RUN = 'true'; + + const resultProgram = parseAndGenerateServices(code, options).services + .program; + expect(resultProgram).toEqual(mockProgram); + + // Call parseAndGenerateServices() again to ensure caching of Programs is working correctly... + parseAndGenerateServices(code, options); + // ...by asserting this was only called once per project + expect(createProgramFromConfigFile).toHaveBeenCalledTimes(tsconfigs.length); + + expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( + 1, + resolvedProject(tsconfigs[0]), + ); + expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( + 2, + resolvedProject(tsconfigs[1]), + ); + + // Restore process data + process.env.TSESTREE_SINGLE_RUN = originalTSESTreeSingleRun; + }); + + it('should lazily create the required program out of the provided "parserOptions.project" one time when singleRun is inferred from CI=true', () => { + /** + * Single run because of CI=true (we need to make sure we respect the original value + * so that we won't interfere with our own usage of the variable) + */ + const originalEnvCI = process.env.CI; + process.env.CI = 'true'; + + const resultProgram = parseAndGenerateServices(code, options).services + .program; + expect(resultProgram).toEqual(mockProgram); + + // Call parseAndGenerateServices() again to ensure caching of Programs is working correctly... + parseAndGenerateServices(code, options); + // ...by asserting this was only called once per project + expect(createProgramFromConfigFile).toHaveBeenCalledTimes(tsconfigs.length); + + expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( + 1, + resolvedProject(tsconfigs[0]), + ); + expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( + 2, + resolvedProject(tsconfigs[1]), + ); + + // Restore process data + process.env.CI = originalEnvCI; + }); + + it('should lazily create the required program out of the provided "parserOptions.project" one time when singleRun is inferred from process.argv', () => { + /** + * Single run because of process.argv + */ + const originalProcessArgv = process.argv; + process.argv = ['', path.normalize('node_modules/.bin/eslint'), '']; + + const resultProgram = parseAndGenerateServices(code, options).services + .program; + expect(resultProgram).toEqual(mockProgram); + + // Call parseAndGenerateServices() again to ensure caching of Programs is working correctly... + parseAndGenerateServices(code, options); + // ...by asserting this was only called once per project + expect(createProgramFromConfigFile).toHaveBeenCalledTimes(tsconfigs.length); + + expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( + 1, + resolvedProject(tsconfigs[0]), + ); + expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( + 2, + resolvedProject(tsconfigs[1]), + ); + + // Restore process data + process.argv = originalProcessArgv; + }); + + it('should stop iterating through and lazily creating programs for the given "parserOptions.project" once a matching one has been found', () => { + /** + * Single run because of explicit environment variable TSESTREE_SINGLE_RUN + */ + const originalTSESTreeSingleRun = process.env.TSESTREE_SINGLE_RUN; + process.env.TSESTREE_SINGLE_RUN = 'true'; + + const optionsWithReversedTsconfigs = { + ...options, + // Now the matching tsconfig comes first + project: [...options.project].reverse(), + }; + + const resultProgram = parseAndGenerateServices( + code, + optionsWithReversedTsconfigs, + ).services.program; + expect(resultProgram).toEqual(mockProgram); + + // Call parseAndGenerateServices() again to ensure caching of Programs is working correctly... + parseAndGenerateServices(code, options); + // ...by asserting this was only called only once + expect(createProgramFromConfigFile).toHaveBeenCalledTimes(1); + + expect(createProgramFromConfigFile).toHaveBeenNthCalledWith( + 1, + resolvedProject(tsconfigs[1]), + ); + + // Restore process data + process.env.TSESTREE_SINGLE_RUN = originalTSESTreeSingleRun; + }); }); diff --git a/packages/typescript-estree/tests/lib/semanticInfo.test.ts b/packages/typescript-estree/tests/lib/semanticInfo.test.ts index 83569479085c..3ebb735eb7f6 100644 --- a/packages/typescript-estree/tests/lib/semanticInfo.test.ts +++ b/packages/typescript-estree/tests/lib/semanticInfo.test.ts @@ -3,7 +3,7 @@ import glob = require('glob'); import * as path from 'path'; import * as ts from 'typescript'; -import { clearCaches } from '../../src'; +import { clearWatchCaches } from '../../src/create-program/getWatchProgramsForProjects'; import { createProgramFromConfigFile as createProgram } from '../../src/create-program/useProvidedPrograms'; import type { ParseAndGenerateServicesResult } from '../../src/parser'; import { parseAndGenerateServices } from '../../src/parser'; @@ -37,7 +37,7 @@ function createOptions(fileName: string): TSESTreeOptions & { cwd?: string } { } // ensure tsconfig-parser watch caches are clean for each test -beforeEach(() => clearCaches()); +beforeEach(() => clearWatchCaches()); describe('semanticInfo', () => { // test all AST snapshots @@ -246,61 +246,55 @@ describe('semanticInfo', () => { expect(parseResult.services.program).toBeDefined(); }); - if (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER !== 'true') { - it(`non-existent file should throw error when project provided`, () => { - expect(() => - parseCodeAndGenerateServices( - `function M() { return Base }`, - createOptions(''), - ), - ).toThrow( - /ESLint was configured to run on `\/estree\.ts` using/, - ); - }); - } - - if (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER !== 'true') { - it('non-existent project file', () => { - const fileName = path.resolve(FIXTURES_DIR, 'isolated-file.src.ts'); - const badConfig = createOptions(fileName); - badConfig.project = './tsconfigs.json'; - expect(() => - parseCodeAndGenerateServices( - fs.readFileSync(fileName, 'utf8'), - badConfig, - ), - ).toThrow(/Cannot read file .+tsconfigs\.json'/); - }); + it(`non-existent file should throw error when project provided`, () => { + expect(() => + parseCodeAndGenerateServices( + `function M() { return Base }`, + createOptions(''), + ), + ).toThrow( + /ESLint was configured to run on `\/estree\.ts` using/, + ); + }); - it('fail to read project file', () => { - const fileName = path.resolve(FIXTURES_DIR, 'isolated-file.src.ts'); - const badConfig = createOptions(fileName); - badConfig.project = '.'; - expect(() => - parseCodeAndGenerateServices( - fs.readFileSync(fileName, 'utf8'), - badConfig, - ), - ).toThrow( - // case insensitive because unix based systems are case insensitive - /Cannot read file .+semanticInfo'/i, - ); - }); + it('non-existent project file', () => { + const fileName = path.resolve(FIXTURES_DIR, 'isolated-file.src.ts'); + const badConfig = createOptions(fileName); + badConfig.project = './tsconfigs.json'; + expect(() => + parseCodeAndGenerateServices( + fs.readFileSync(fileName, 'utf8'), + badConfig, + ), + ).toThrow(/Cannot read file .+tsconfigs\.json'/); + }); - it('malformed project file', () => { - const fileName = path.resolve(FIXTURES_DIR, 'isolated-file.src.ts'); - const badConfig = createOptions(fileName); - badConfig.project = './badTSConfig/tsconfig.json'; - expect(() => - parseCodeAndGenerateServices( - fs.readFileSync(fileName, 'utf8'), - badConfig, - ), - ).toThrowErrorMatchingInlineSnapshot( - `"Compiler option 'compileOnSave' requires a value of type boolean."`, - ); - }); - } + it('fail to read project file', () => { + const fileName = path.resolve(FIXTURES_DIR, 'isolated-file.src.ts'); + const badConfig = createOptions(fileName); + badConfig.project = '.'; + expect(() => + parseCodeAndGenerateServices( + fs.readFileSync(fileName, 'utf8'), + badConfig, + ), + ).toThrow( + // case insensitive because unix based systems are case insensitive + /Cannot read file .+semanticInfo'./i, + ); + }); + + it('malformed project file', () => { + const fileName = path.resolve(FIXTURES_DIR, 'isolated-file.src.ts'); + const badConfig = createOptions(fileName); + badConfig.project = './badTSConfig/tsconfig.json'; + expect(() => + parseCodeAndGenerateServices( + fs.readFileSync(fileName, 'utf8'), + badConfig, + ), + ).toThrowErrorMatchingSnapshot(); + }); it('default program produced with option', () => { const parseResult = parseCodeAndGenerateServices('var foo = 5;', { @@ -320,22 +314,20 @@ describe('semanticInfo', () => { ); }); - if (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER !== 'true') { - it(`first matching provided program instance is returned in result`, () => { - const filename = testFiles[0]; - const program1 = createProgram(path.join(FIXTURES_DIR, 'tsconfig.json')); - const program2 = createProgram(path.join(FIXTURES_DIR, 'tsconfig.json')); - const code = fs.readFileSync(path.join(FIXTURES_DIR, filename), 'utf8'); - const options = createOptions(filename); - const optionsProjectString = { - ...options, - programs: [program1, program2], - project: './tsconfig.json', - }; - const parseResult = parseAndGenerateServices(code, optionsProjectString); - expect(parseResult.services.program).toBe(program1); - }); - } + it(`first matching provided program instance is returned in result`, () => { + const filename = testFiles[0]; + const program1 = createProgram(path.join(FIXTURES_DIR, 'tsconfig.json')); + const program2 = createProgram(path.join(FIXTURES_DIR, 'tsconfig.json')); + const code = fs.readFileSync(path.join(FIXTURES_DIR, filename), 'utf8'); + const options = createOptions(filename); + const optionsProjectString = { + ...options, + programs: [program1, program2], + project: './tsconfig.json', + }; + const parseResult = parseAndGenerateServices(code, optionsProjectString); + expect(parseResult.services.program).toBe(program1); + }); it('file not in provided program instance(s)', () => { const filename = 'non-existent-file.ts'; @@ -377,10 +369,6 @@ describe('semanticInfo', () => { function testIsolatedFile( parseResult: ParseAndGenerateServicesResult, ): void { - if (process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER === 'true') { - return; - } - // get type checker expectToHaveParserServices(parseResult.services); const checker = parseResult.services.program.getTypeChecker(); diff --git a/packages/utils/src/ts-eslint/Rule.ts b/packages/utils/src/ts-eslint/Rule.ts index f15e92bdb38c..4ff0546bb65c 100644 --- a/packages/utils/src/ts-eslint/Rule.ts +++ b/packages/utils/src/ts-eslint/Rule.ts @@ -223,7 +223,7 @@ interface RuleContext< * It is a path to a directory that should be considered as the current working directory. * @since 6.6.0 */ - getCwd(): string; + getCwd?(): string; /** * Returns the filename associated with the source. diff --git a/packages/website/src/components/linter/config.ts b/packages/website/src/components/linter/config.ts index fc6389b4e1b2..f728dc1a6355 100644 --- a/packages/website/src/components/linter/config.ts +++ b/packages/website/src/components/linter/config.ts @@ -13,7 +13,6 @@ export const defaultParseSettings: ParseSettings = { DEPRECATED__createDefaultProgram: false, errorOnTypeScriptSyntacticAndSemanticIssues: false, errorOnUnknownASTType: false, - EXPERIMENTAL_projectService: undefined, EXPERIMENTAL_useSourceOfProjectReferenceRedirect: false, extraFileExtensions: [], filePath: '', 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