Skip to content

Commit 99eca0d

Browse files
authored
fix(typescript-estree): ensure --fix works with singleRun mode (typescript-eslint#3655)
1 parent f0861e0 commit 99eca0d

File tree

3 files changed

+58
-8
lines changed

3 files changed

+58
-8
lines changed

packages/typescript-estree/src/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
export * from './parser';
1+
export {
2+
AST,
3+
parse,
4+
parseAndGenerateServices,
5+
parseWithNodeMaps,
6+
ParseAndGenerateServicesResult,
7+
ParseWithNodeMapsResult,
8+
clearProgramCache,
9+
} from './parser';
210
export { ParserServices, TSESTreeOptions } from './parser-options';
311
export { simpleTraverse } from './simple-traverse';
412
export * from './ts-estree';

packages/typescript-estree/src/parser.ts

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,12 @@ function parseWithNodeMaps<T extends TSESTreeOptions = TSESTreeOptions>(
502502
return parseWithNodeMapsInternal(code, options, true);
503503
}
504504

505+
let parseAndGenerateServicesCalls: { [fileName: string]: number } = {};
506+
// Privately exported utility intended for use in typescript-eslint unit tests only
507+
function clearParseAndGenerateServicesCalls(): void {
508+
parseAndGenerateServicesCalls = {};
509+
}
510+
505511
function parseAndGenerateServices<T extends TSESTreeOptions = TSESTreeOptions>(
506512
code: string,
507513
options: T,
@@ -572,12 +578,41 @@ function parseAndGenerateServices<T extends TSESTreeOptions = TSESTreeOptions>(
572578
*/
573579
const shouldProvideParserServices =
574580
extra.programs != null || (extra.projects && extra.projects.length > 0);
575-
const { ast, program } = getProgramAndAST(
576-
code,
577-
extra.programs,
578-
shouldProvideParserServices,
579-
extra.createDefaultProgram,
580-
)!;
581+
582+
/**
583+
* If we are in singleRun mode but the parseAndGenerateServices() function has been called more than once for the current file,
584+
* it must mean that we are in the middle of an ESLint automated fix cycle (in which parsing can be performed up to an additional
585+
* 10 times in order to apply all possible fixes for the file).
586+
*
587+
* In this scenario we cannot rely upon the singleRun AOT compiled programs because the SourceFiles will not contain the source
588+
* with the latest fixes applied. Therefore we fallback to creating the quickest possible isolated program from the updated source.
589+
*/
590+
let ast: ts.SourceFile;
591+
let program: ts.Program;
592+
593+
if (extra.singleRun && options.filePath) {
594+
parseAndGenerateServicesCalls[options.filePath] =
595+
(parseAndGenerateServicesCalls[options.filePath] || 0) + 1;
596+
}
597+
598+
if (
599+
extra.singleRun &&
600+
options.filePath &&
601+
parseAndGenerateServicesCalls[options.filePath] > 1
602+
) {
603+
const isolatedAstAndProgram = createIsolatedProgram(code, extra);
604+
ast = isolatedAstAndProgram.ast;
605+
program = isolatedAstAndProgram.program;
606+
} else {
607+
const astAndProgram = getProgramAndAST(
608+
code,
609+
extra.programs,
610+
shouldProvideParserServices,
611+
extra.createDefaultProgram,
612+
)!;
613+
ast = astAndProgram.ast;
614+
program = astAndProgram.program;
615+
}
581616

582617
/**
583618
* Convert the TypeScript AST to an ESTree-compatible one, and optionally preserve
@@ -620,4 +655,5 @@ export {
620655
ParseAndGenerateServicesResult,
621656
ParseWithNodeMapsResult,
622657
clearProgramCache,
658+
clearParseAndGenerateServicesCalls,
623659
};

packages/typescript-estree/tests/lib/semanticInfo-singleRun.test.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import glob from 'glob';
22
import * as path from 'path';
3-
import { clearProgramCache, parseAndGenerateServices } from '../../src';
3+
import {
4+
clearProgramCache,
5+
parseAndGenerateServices,
6+
clearParseAndGenerateServicesCalls,
7+
} from '../../src/parser';
48
import { getCanonicalFileName } from '../../src/create-program/shared';
59

610
const mockProgram = {
@@ -91,6 +95,8 @@ describe('semanticInfo - singleRun', () => {
9195
clearProgramCache();
9296
// ensure invocations of mock are clean for each test
9397
(createProgramFromConfigFile as jest.Mock).mockClear();
98+
// Do not track invocations per file across tests
99+
clearParseAndGenerateServicesCalls();
94100
});
95101

96102
it('should not create any programs ahead of time by default when there is no way to infer singleRun=true', () => {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy