From 5cd681c67fb37320a10574cbc06d7f0e7481d985 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Thu, 7 Mar 2024 17:18:24 -0500 Subject: [PATCH 1/4] feat!(parser): always enable comment, loc, range, tokens --- packages/parser/src/parser.ts | 72 +++++++++------- packages/types/src/parser-options.ts | 4 - .../src/parseSettings/createParseSettings.ts | 85 ++++++++++--------- packages/typescript-estree/src/parser.ts | 27 +++--- 4 files changed, 98 insertions(+), 90 deletions(-) diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts index 17c5a2ff61b9..8eb50b2b12e3 100644 --- a/packages/parser/src/parser.ts +++ b/packages/parser/src/parser.ts @@ -6,6 +6,7 @@ import { analyze } from '@typescript-eslint/scope-manager'; import type { Lib, TSESTree } from '@typescript-eslint/types'; import { ParserOptions } from '@typescript-eslint/types'; import type { + AST, ParserServices, TSESTreeOptions, } from '@typescript-eslint/typescript-estree'; @@ -18,12 +19,14 @@ import { ScriptTarget } from 'typescript'; const log = debug('typescript-eslint:parser:parser'); +interface ESLintProgram extends AST<{ comment: true; tokens: true }> { + comments: TSESTree.Comment[]; + range: [number, number]; + tokens: TSESTree.Token[]; +} + interface ParseForESLintResult { - ast: TSESTree.Program & { - range?: [number, number]; - tokens?: TSESTree.Token[]; - comments?: TSESTree.Comment[]; - }; + ast: ESLintProgram; services: ParserServices; visitorKeys: VisitorKeys; scopeManager: ScopeManager; @@ -87,50 +90,55 @@ function parse( function parseForESLint( code: ts.SourceFile | string, - options?: ParserOptions | null, + parserOptions?: ParserOptions | null, ): ParseForESLintResult { - if (!options || typeof options !== 'object') { - options = {}; + if (!parserOptions || typeof parserOptions !== 'object') { + parserOptions = {}; } else { - options = { ...options }; + parserOptions = { ...parserOptions }; } // https://eslint.org/docs/user-guide/configuring#specifying-parser-options // if sourceType is not provided by default eslint expect that it will be set to "script" - if (options.sourceType !== 'module' && options.sourceType !== 'script') { - options.sourceType = 'script'; + if ( + parserOptions.sourceType !== 'module' && + parserOptions.sourceType !== 'script' + ) { + parserOptions.sourceType = 'script'; } - if (typeof options.ecmaFeatures !== 'object') { - options.ecmaFeatures = {}; + if (typeof parserOptions.ecmaFeatures !== 'object') { + parserOptions.ecmaFeatures = {}; } - const parserOptions: TSESTreeOptions = {}; - Object.assign(parserOptions, options, { - jsx: validateBoolean(options.ecmaFeatures.jsx), - }); - const analyzeOptions: AnalyzeOptions = { - globalReturn: options.ecmaFeatures.globalReturn, - jsxPragma: options.jsxPragma, - jsxFragmentName: options.jsxFragmentName, - lib: options.lib, - sourceType: options.sourceType, - }; - /** * Allow the user to suppress the warning from typescript-estree if they are using an unsupported * version of TypeScript */ const warnOnUnsupportedTypeScriptVersion = validateBoolean( - options.warnOnUnsupportedTypeScriptVersion, + parserOptions.warnOnUnsupportedTypeScriptVersion, true, ); - if (!warnOnUnsupportedTypeScriptVersion) { - parserOptions.loggerFn = false; - } - const { ast, services } = parseAndGenerateServices(code, parserOptions); - ast.sourceType = options.sourceType; + const tsestreeOptions = { + comment: true, + jsx: validateBoolean(parserOptions.ecmaFeatures.jsx), + loc: true, + range: true, + tokens: true, + ...(warnOnUnsupportedTypeScriptVersion && { loggerFn: false }), + } satisfies TSESTreeOptions; + Object.assign(tsestreeOptions, parserOptions); // this is fine + const analyzeOptions: AnalyzeOptions = { + globalReturn: parserOptions.ecmaFeatures.globalReturn, + jsxPragma: parserOptions.jsxPragma, + jsxFragmentName: parserOptions.jsxFragmentName, + lib: parserOptions.lib, + sourceType: parserOptions.sourceType, + }; + + const { ast, services } = parseAndGenerateServices(code, tsestreeOptions); + ast.sourceType = parserOptions.sourceType; - let emitDecoratorMetadata = options.emitDecoratorMetadata === true; + let emitDecoratorMetadata = parserOptions.emitDecoratorMetadata === true; if (services.program) { // automatically apply the options configured for the program const compilerOptions = services.program.getCompilerOptions(); diff --git a/packages/types/src/parser-options.ts b/packages/types/src/parser-options.ts index 477acb50cacb..fdbe0d27c9d0 100644 --- a/packages/types/src/parser-options.ts +++ b/packages/types/src/parser-options.ts @@ -53,7 +53,6 @@ interface ParserOptions { emitDecoratorMetadata?: boolean; // typescript-estree specific - comment?: boolean; debugLevel?: DebugLevel; errorOnTypeScriptSyntacticAndSemanticIssues?: boolean; errorOnUnknownASTType?: boolean; @@ -62,13 +61,10 @@ interface ParserOptions { extraFileExtensions?: string[]; filePath?: string; jsDocParsingMode?: JSDocParsingMode; - loc?: boolean; programs?: Program[] | null; project?: string[] | string | boolean | null; projectFolderIgnoreList?: (RegExp | string)[]; - range?: boolean; sourceType?: SourceType; - tokens?: boolean; tsconfigRootDir?: string; warnOnUnsupportedTypeScriptVersion?: boolean; cacheLifetime?: { diff --git a/packages/typescript-estree/src/parseSettings/createParseSettings.ts b/packages/typescript-estree/src/parseSettings/createParseSettings.ts index 49d048391d8f..f53c401c9f07 100644 --- a/packages/typescript-estree/src/parseSettings/createParseSettings.ts +++ b/packages/typescript-estree/src/parseSettings/createParseSettings.ts @@ -37,17 +37,17 @@ const JSDocParsingMode = { export function createParseSettings( code: ts.SourceFile | string, - options: Partial = {}, + tsestreeOptions: Partial = {}, ): MutableParseSettings { const codeFullText = enforceCodeString(code); - const singleRun = inferSingleRun(options); + const singleRun = inferSingleRun(tsestreeOptions); const tsconfigRootDir = - typeof options.tsconfigRootDir === 'string' - ? options.tsconfigRootDir + typeof tsestreeOptions.tsconfigRootDir === 'string' + ? tsestreeOptions.tsconfigRootDir : process.cwd(); - const passedLoggerFn = typeof options.loggerFn === 'function'; + const passedLoggerFn = typeof tsestreeOptions.loggerFn === 'function'; const jsDocParsingMode = ((): ts.JSDocParsingMode => { - switch (options.jsDocParsingMode) { + switch (tsestreeOptions.jsDocParsingMode) { case 'all': return JSDocParsingMode.ParseAll; @@ -63,67 +63,70 @@ export function createParseSettings( })(); const parseSettings: MutableParseSettings = { - allowInvalidAST: options.allowInvalidAST === true, + allowInvalidAST: tsestreeOptions.allowInvalidAST === true, code, codeFullText, - comment: options.comment === true, + comment: tsestreeOptions.comment === true, comments: [], DEPRECATED__createDefaultProgram: // eslint-disable-next-line deprecation/deprecation -- will be cleaned up with the next major - options.DEPRECATED__createDefaultProgram === true, + tsestreeOptions.DEPRECATED__createDefaultProgram === true, debugLevel: - options.debugLevel === true + tsestreeOptions.debugLevel === true ? new Set(['typescript-eslint']) - : Array.isArray(options.debugLevel) - ? new Set(options.debugLevel) + : Array.isArray(tsestreeOptions.debugLevel) + ? new Set(tsestreeOptions.debugLevel) : new Set(), errorOnTypeScriptSyntacticAndSemanticIssues: false, - errorOnUnknownASTType: options.errorOnUnknownASTType === true, + errorOnUnknownASTType: tsestreeOptions.errorOnUnknownASTType === true, EXPERIMENTAL_projectService: - options.EXPERIMENTAL_useProjectService || - (options.project && - options.EXPERIMENTAL_useProjectService !== false && + tsestreeOptions.EXPERIMENTAL_useProjectService || + (tsestreeOptions.project && + tsestreeOptions.EXPERIMENTAL_useProjectService !== false && process.env.TYPESCRIPT_ESLINT_EXPERIMENTAL_TSSERVER === 'true') ? (TSSERVER_PROJECT_SERVICE ??= createProjectService( - options.EXPERIMENTAL_useProjectService, + tsestreeOptions.EXPERIMENTAL_useProjectService, jsDocParsingMode, )) : undefined, EXPERIMENTAL_useSourceOfProjectReferenceRedirect: - options.EXPERIMENTAL_useSourceOfProjectReferenceRedirect === true, + tsestreeOptions.EXPERIMENTAL_useSourceOfProjectReferenceRedirect === true, extraFileExtensions: - Array.isArray(options.extraFileExtensions) && - options.extraFileExtensions.every(ext => typeof ext === 'string') - ? options.extraFileExtensions + Array.isArray(tsestreeOptions.extraFileExtensions) && + tsestreeOptions.extraFileExtensions.every(ext => typeof ext === 'string') + ? tsestreeOptions.extraFileExtensions : [], filePath: ensureAbsolutePath( - typeof options.filePath === 'string' && options.filePath !== '' - ? options.filePath - : getFileName(options.jsx), + typeof tsestreeOptions.filePath === 'string' && + tsestreeOptions.filePath !== '' + ? tsestreeOptions.filePath + : getFileName(tsestreeOptions.jsx), tsconfigRootDir, ), jsDocParsingMode, - jsx: options.jsx === true, - loc: options.loc === true, + jsx: tsestreeOptions.jsx === true, + loc: tsestreeOptions.loc === true, log: - typeof options.loggerFn === 'function' - ? options.loggerFn - : options.loggerFn === false + typeof tsestreeOptions.loggerFn === 'function' + ? tsestreeOptions.loggerFn + : tsestreeOptions.loggerFn === false ? (): void => {} // eslint-disable-line @typescript-eslint/no-empty-function : console.log, // eslint-disable-line no-console - preserveNodeMaps: options.preserveNodeMaps !== false, - programs: Array.isArray(options.programs) ? options.programs : null, + preserveNodeMaps: tsestreeOptions.preserveNodeMaps !== false, + programs: Array.isArray(tsestreeOptions.programs) + ? tsestreeOptions.programs + : null, projects: [], - range: options.range === true, + range: tsestreeOptions.range === true, singleRun, suppressDeprecatedPropertyWarnings: - options.suppressDeprecatedPropertyWarnings ?? + tsestreeOptions.suppressDeprecatedPropertyWarnings ?? process.env.NODE_ENV !== 'test', - tokens: options.tokens === true ? [] : null, + tokens: tsestreeOptions.tokens === true ? [] : null, tsconfigMatchCache: (TSCONFIG_MATCH_CACHE ??= new ExpiringCache( singleRun ? 'Infinity' - : options.cacheLifetime?.glob ?? + : tsestreeOptions.cacheLifetime?.glob ?? DEFAULT_TSCONFIG_CACHE_DURATION_SECONDS, )), tsconfigRootDir, @@ -146,8 +149,8 @@ export function createParseSettings( debug.enable(namespaces.join(',')); } - if (Array.isArray(options.programs)) { - if (!options.programs.length) { + if (Array.isArray(tsestreeOptions.programs)) { + if (!tsestreeOptions.programs.length) { throw new Error( `You have set parserOptions.programs to an empty array. This will cause all files to not be found in existing programs. Either provide one or more existing TypeScript Program instances in the array, or remove the parserOptions.programs setting.`, ); @@ -160,9 +163,9 @@ export function createParseSettings( // Providing a program or project service overrides project resolution if (!parseSettings.programs && !parseSettings.EXPERIMENTAL_projectService) { parseSettings.projects = resolveProjectList({ - cacheLifetime: options.cacheLifetime, - project: getProjectConfigFiles(parseSettings, options.project), - projectFolderIgnoreList: options.projectFolderIgnoreList, + cacheLifetime: tsestreeOptions.cacheLifetime, + project: getProjectConfigFiles(parseSettings, tsestreeOptions.project), + projectFolderIgnoreList: tsestreeOptions.projectFolderIgnoreList, singleRun: parseSettings.singleRun, tsconfigRootDir: tsconfigRootDir, }); @@ -171,7 +174,7 @@ export function createParseSettings( // No type-aware linting which means that cross-file (or even same-file) JSDoc is useless // So in this specific case we default to 'none' if no value was provided if ( - options.jsDocParsingMode == null && + tsestreeOptions.jsDocParsingMode == null && parseSettings.projects.length === 0 && parseSettings.programs == null && parseSettings.EXPERIMENTAL_projectService == null diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index ffa0c4212295..ef5fdfcfe09f 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -96,11 +96,11 @@ function getProgramAndAST( return createIsolatedProgram(parseSettings); } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -interface EmptyObject {} +/* eslint-disable @typescript-eslint/ban-types */ type AST = TSESTree.Program & - (T['comment'] extends true ? { comments: TSESTree.Comment[] } : EmptyObject) & - (T['tokens'] extends true ? { tokens: TSESTree.Token[] } : EmptyObject); + (T['comment'] extends true ? { comments: TSESTree.Comment[] } : {}) & + (T['tokens'] extends true ? { tokens: TSESTree.Token[] } : {}); +/* eslint-enable @typescript-eslint/ban-types */ interface ParseAndGenerateServicesResult { ast: AST; @@ -167,12 +167,12 @@ function clearParseAndGenerateServicesCalls(): void { function parseAndGenerateServices( code: ts.SourceFile | string, - options: T, + tsestreeOptions: T, ): ParseAndGenerateServicesResult { /** * Reset the parse configuration */ - const parseSettings = createParseSettings(code, options); + const parseSettings = createParseSettings(code, tsestreeOptions); /** * If this is a single run in which the user has not provided any existing programs but there @@ -211,8 +211,9 @@ function parseAndGenerateServices( parseSettings.programs != null || parseSettings.projects.length > 0; if ( - typeof options.errorOnTypeScriptSyntacticAndSemanticIssues === 'boolean' && - options.errorOnTypeScriptSyntacticAndSemanticIssues + typeof tsestreeOptions.errorOnTypeScriptSyntacticAndSemanticIssues === + 'boolean' && + tsestreeOptions.errorOnTypeScriptSyntacticAndSemanticIssues ) { parseSettings.errorOnTypeScriptSyntacticAndSemanticIssues = true; } @@ -234,15 +235,15 @@ function parseAndGenerateServices( * In this scenario we cannot rely upon the singleRun AOT compiled programs because the SourceFiles will not contain the source * with the latest fixes applied. Therefore we fallback to creating the quickest possible isolated program from the updated source. */ - if (parseSettings.singleRun && options.filePath) { - parseAndGenerateServicesCalls[options.filePath] = - (parseAndGenerateServicesCalls[options.filePath] || 0) + 1; + if (parseSettings.singleRun && tsestreeOptions.filePath) { + parseAndGenerateServicesCalls[tsestreeOptions.filePath] = + (parseAndGenerateServicesCalls[tsestreeOptions.filePath] || 0) + 1; } const { ast, program } = parseSettings.singleRun && - options.filePath && - parseAndGenerateServicesCalls[options.filePath] > 1 + tsestreeOptions.filePath && + parseAndGenerateServicesCalls[tsestreeOptions.filePath] > 1 ? createIsolatedProgram(parseSettings) : getProgramAndAST(parseSettings, hasFullTypeInformation); From f384f64f7c1d4e9157cf9002735d4a249b4a059c Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Fri, 8 Mar 2024 08:14:12 -0500 Subject: [PATCH 2/4] fix: tests --- packages/parser/src/parser.ts | 2 +- packages/parser/tests/lib/parser.test.ts | 36 +++++++++++++++--------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts index 8eb50b2b12e3..8d6944836429 100644 --- a/packages/parser/src/parser.ts +++ b/packages/parser/src/parser.ts @@ -124,7 +124,7 @@ function parseForESLint( loc: true, range: true, tokens: true, - ...(warnOnUnsupportedTypeScriptVersion && { loggerFn: false }), + ...(!warnOnUnsupportedTypeScriptVersion && { loggerFn: false }), } satisfies TSESTreeOptions; Object.assign(tsestreeOptions, parserOptions); // this is fine const analyzeOptions: AnalyzeOptions = { diff --git a/packages/parser/tests/lib/parser.test.ts b/packages/parser/tests/lib/parser.test.ts index e6bd731db075..119ea5362002 100644 --- a/packages/parser/tests/lib/parser.test.ts +++ b/packages/parser/tests/lib/parser.test.ts @@ -24,10 +24,6 @@ describe('parser', () => { const code = 'const valid = true;'; const spy = jest.spyOn(typescriptESTree, 'parseAndGenerateServices'); const config: ParserOptions = { - loc: false, - comment: false, - range: false, - tokens: false, sourceType: 'module' as const, ecmaFeatures: { globalReturn: false, @@ -43,7 +39,11 @@ describe('parser', () => { parseForESLint(code, config); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenLastCalledWith(code, { + comment: true, jsx: false, + loc: true, + range: true, + tokens: true, ...config, }); }); @@ -51,21 +51,33 @@ describe('parser', () => { it('`warnOnUnsupportedTypeScriptVersion: false` should set `loggerFn: false` on typescript-estree', () => { const code = 'const valid = true;'; const spy = jest.spyOn(typescriptESTree, 'parseAndGenerateServices'); - parseForESLint(code, { warnOnUnsupportedTypeScriptVersion: true }); + parseForESLint(code, { warnOnUnsupportedTypeScriptVersion: false }); expect(spy).toHaveBeenCalledWith(code, { + comment: true, ecmaFeatures: {}, jsx: false, + loc: true, + loggerFn: false, + range: true, sourceType: 'script', - warnOnUnsupportedTypeScriptVersion: true, + tokens: true, + warnOnUnsupportedTypeScriptVersion: false, }); - spy.mockClear(); - parseForESLint(code, { warnOnUnsupportedTypeScriptVersion: false }); + }); + + it('`warnOnUnsupportedTypeScriptVersion: true` should not set `loggerFn: false` on typescript-estree', () => { + const code = 'const valid = true;'; + const spy = jest.spyOn(typescriptESTree, 'parseAndGenerateServices'); + parseForESLint(code, { warnOnUnsupportedTypeScriptVersion: true }); expect(spy).toHaveBeenCalledWith(code, { + comment: true, ecmaFeatures: {}, jsx: false, + loc: true, + range: true, sourceType: 'script', - loggerFn: false, - warnOnUnsupportedTypeScriptVersion: false, + tokens: true, + warnOnUnsupportedTypeScriptVersion: true, }); }); @@ -73,10 +85,6 @@ describe('parser', () => { const code = 'const valid = true;'; const spy = jest.spyOn(scopeManager, 'analyze'); const config: ParserOptions = { - loc: false, - comment: false, - range: false, - tokens: false, sourceType: 'module' as const, ecmaFeatures: { globalReturn: false, From 8f7e22686f66875cbaae626d3a428a18a5de3e58 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Fri, 8 Mar 2024 08:19:45 -0500 Subject: [PATCH 3/4] Smooth out the Object.assign --- packages/parser/src/parser.ts | 3 ++- packages/types/src/parser-options.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts index 8d6944836429..b211bae5c280 100644 --- a/packages/parser/src/parser.ts +++ b/packages/parser/src/parser.ts @@ -125,8 +125,9 @@ function parseForESLint( range: true, tokens: true, ...(!warnOnUnsupportedTypeScriptVersion && { loggerFn: false }), + ...parserOptions, } satisfies TSESTreeOptions; - Object.assign(tsestreeOptions, parserOptions); // this is fine + const analyzeOptions: AnalyzeOptions = { globalReturn: parserOptions.ecmaFeatures.globalReturn, jsxPragma: parserOptions.jsxPragma, diff --git a/packages/types/src/parser-options.ts b/packages/types/src/parser-options.ts index fdbe0d27c9d0..211678c1c379 100644 --- a/packages/types/src/parser-options.ts +++ b/packages/types/src/parser-options.ts @@ -63,7 +63,7 @@ interface ParserOptions { jsDocParsingMode?: JSDocParsingMode; programs?: Program[] | null; project?: string[] | string | boolean | null; - projectFolderIgnoreList?: (RegExp | string)[]; + projectFolderIgnoreList?: string[]; sourceType?: SourceType; tsconfigRootDir?: string; warnOnUnsupportedTypeScriptVersion?: boolean; From 4332b9cdcb8ab00fefc1966a6130774095aefece Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Fri, 8 Mar 2024 11:12:10 -0500 Subject: [PATCH 4/4] test: added some more for parser, why not --- packages/parser/tests/lib/parser.test.ts | 77 +++++++++++++++++++++++- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/packages/parser/tests/lib/parser.test.ts b/packages/parser/tests/lib/parser.test.ts index 119ea5362002..6aff5656133e 100644 --- a/packages/parser/tests/lib/parser.test.ts +++ b/packages/parser/tests/lib/parser.test.ts @@ -2,6 +2,7 @@ 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 { ScriptTarget } from 'typescript'; import { parse, parseForESLint } from '../../src/parser'; @@ -48,7 +49,7 @@ describe('parser', () => { }); }); - it('`warnOnUnsupportedTypeScriptVersion: false` should set `loggerFn: false` on typescript-estree', () => { + it('sets `loggerFn: false` on typescript-estree when provided `warnOnUnsupportedTypeScriptVersion: false`', () => { const code = 'const valid = true;'; const spy = jest.spyOn(typescriptESTree, 'parseAndGenerateServices'); parseForESLint(code, { warnOnUnsupportedTypeScriptVersion: false }); @@ -65,7 +66,7 @@ describe('parser', () => { }); }); - it('`warnOnUnsupportedTypeScriptVersion: true` should not set `loggerFn: false` on typescript-estree', () => { + it('sets `loggerFn: false` on typescript-estree when provided `warnOnUnsupportedTypeScriptVersion: true`', () => { const code = 'const valid = true;'; const spy = jest.spyOn(typescriptESTree, 'parseAndGenerateServices'); parseForESLint(code, { warnOnUnsupportedTypeScriptVersion: true }); @@ -81,7 +82,75 @@ describe('parser', () => { }); }); - it('analyze() should be called with options', () => { + it('should call analyze() with inferred analyze options when no analyze options are provided', () => { + const code = 'const valid = true;'; + const spy = jest.spyOn(scopeManager, 'analyze'); + const config: ParserOptions = { + errorOnTypeScriptSyntacticAndSemanticIssues: false, + filePath: 'isolated-file.src.ts', + project: 'tsconfig.json', + tsconfigRootDir: path.join(__dirname, '../fixtures/services'), + }; + + parseForESLint(code, config); + + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenLastCalledWith(expect.anything(), { + globalReturn: undefined, + jsxFragmentName: undefined, + jsxPragma: undefined, + lib: ['lib'], + sourceType: 'script', + }); + }); + + it.each([ + ['esnext.full', ScriptTarget.ESNext], + ['es2022.full', ScriptTarget.ES2022], + ['es2021.full', ScriptTarget.ES2021], + ['es2020.full', ScriptTarget.ES2020], + ['es2019.full', ScriptTarget.ES2019], + ['es2018.full', ScriptTarget.ES2018], + ['es2017.full', ScriptTarget.ES2017], + ['es2016.full', ScriptTarget.ES2016], + ['es6', ScriptTarget.ES2015], + ['lib', ScriptTarget.ES5], + ['lib', undefined], + ])( + 'calls analyze() with `lib: [%s]` when the compiler options target is %s', + (lib, target) => { + const code = 'const valid = true;'; + const spy = jest.spyOn(scopeManager, 'analyze'); + const config: ParserOptions = { + filePath: 'isolated-file.src.ts', + project: 'tsconfig.json', + tsconfigRootDir: path.join(__dirname, '../fixtures/services'), + }; + + jest + .spyOn(typescriptESTree, 'parseAndGenerateServices') + .mockReturnValueOnce({ + ast: {}, + services: { + program: { + getCompilerOptions: () => ({ target }), + }, + }, + } as typescriptESTree.ParseAndGenerateServicesResult); + + parseForESLint(code, config); + + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenLastCalledWith( + expect.anything(), + expect.objectContaining({ + lib: [lib], + }), + ); + }, + ); + + it('calls analyze() with the provided analyze options when analyze options are provided', () => { const code = 'const valid = true;'; const spy = jest.spyOn(scopeManager, 'analyze'); const config: ParserOptions = { @@ -101,7 +170,9 @@ describe('parser', () => { tsconfigRootDir: path.join(__dirname, '../fixtures/services'), extraFileExtensions: ['.foo'], }; + parseForESLint(code, config); + expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenLastCalledWith(expect.anything(), { globalReturn: false,