Skip to content

fix: do not pass tsconfig canonical file name to typescript API to get program details for config file #9042

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ function createDefaultProgram(
parseSettings.filePath || 'unnamed file',
);

if (parseSettings.projects.length !== 1) {
if (parseSettings.projects.size !== 1) {
return undefined;
}

const tsconfigPath = parseSettings.projects[0];
const tsconfigPath = Array.from(parseSettings.projects.values())[0];

const commandLine = ts.getParsedCommandLineOfConfigFile(
tsconfigPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ function createProjectProgram(
parseSettings.filePath,
parseSettings.tsconfigRootDir,
);
const relativeProjects = parseSettings.projects.map(describeProjectFilePath);
const relativeProjects = Array.from(parseSettings.projects.values()).map(
describeProjectFilePath,
);
const describedPrograms =
relativeProjects.length === 1
? relativeProjects[0]
Expand Down Expand Up @@ -93,7 +95,7 @@ function createProjectProgram(

if (!hasMatchedAnError) {
const [describedInclusions, describedSpecifiers] =
parseSettings.projects.length === 1
parseSettings.projects.size === 1
? ['that TSConfig does not', 'that TSConfig']
: ['none of those TSConfigs', 'one of those TSConfigs'];
errorLines.push(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ function getWatchProgramsForProjects(
);
}

const currentProjectsFromSettings = new Set(parseSettings.projects);
const currentProjectsFromSettings = new Map(parseSettings.projects);

/*
* before we go into the process of attempting to find and update every program
Expand Down Expand Up @@ -198,13 +198,13 @@ function getWatchProgramsForProjects(
* - the file is new/renamed, and the program hasn't been updated.
*/
for (const tsconfigPath of parseSettings.projects) {
const existingWatch = knownWatchProgramMap.get(tsconfigPath);
const existingWatch = knownWatchProgramMap.get(tsconfigPath[0]);

if (existingWatch) {
const updatedProgram = maybeInvalidateProgram(
existingWatch,
filePath,
tsconfigPath,
tsconfigPath[0],
);
if (!updatedProgram) {
continue;
Expand All @@ -215,7 +215,7 @@ function getWatchProgramsForProjects(

// cache and check the file list
const fileList = updateCachedFileList(
tsconfigPath,
tsconfigPath[0],
updatedProgram,
parseSettings,
);
Expand All @@ -229,15 +229,19 @@ function getWatchProgramsForProjects(
continue;
}

const programWatch = createWatchProgram(tsconfigPath, parseSettings);
knownWatchProgramMap.set(tsconfigPath, programWatch);
const programWatch = createWatchProgram(tsconfigPath[1], parseSettings);
knownWatchProgramMap.set(tsconfigPath[0], programWatch);

const program = programWatch.getProgram().getProgram();
// sets parent pointers in source files
program.getTypeChecker();

// cache and check the file list
const fileList = updateCachedFileList(tsconfigPath, program, parseSettings);
const fileList = updateCachedFileList(
tsconfigPath[0],
program,
parseSettings,
);
if (fileList.has(filePath)) {
log('Found program for file. %s', filePath);
// we can return early because we know this program contains the file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export function createParseSettings(
: console.log, // eslint-disable-line no-console
preserveNodeMaps: options.preserveNodeMaps !== false,
programs: Array.isArray(options.programs) ? options.programs : null,
projects: [],
projects: new Map(),
range: options.range === true,
singleRun,
suppressDeprecatedPropertyWarnings:
Expand Down Expand Up @@ -172,7 +172,7 @@ export function createParseSettings(
// So in this specific case we default to 'none' if no value was provided
if (
options.jsDocParsingMode == null &&
parseSettings.projects.length === 0 &&
parseSettings.projects.size === 0 &&
parseSettings.programs == null &&
parseSettings.EXPERIMENTAL_projectService == null
) {
Expand Down
2 changes: 1 addition & 1 deletion packages/typescript-estree/src/parseSettings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export interface MutableParseSettings {
/**
* Normalized paths to provided project paths.
*/
projects: readonly CanonicalPath[];
projects: ReadonlyMap<CanonicalPath, string>;

/**
* Whether to add the `range` property to AST nodes.
Expand Down
22 changes: 12 additions & 10 deletions packages/typescript-estree/src/parseSettings/resolveProjectList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ const log = debug(
'typescript-eslint:typescript-estree:parser:parseSettings:resolveProjectList',
);

let RESOLUTION_CACHE: ExpiringCache<string, readonly CanonicalPath[]> | null =
null;
let RESOLUTION_CACHE: ExpiringCache<
string,
ReadonlyMap<CanonicalPath, string>
> | null = null;

export function clearGlobCache(): void {
RESOLUTION_CACHE?.clear();
Expand All @@ -36,7 +38,7 @@ export function resolveProjectList(
singleRun: boolean;
tsconfigRootDir: string;
}>,
): readonly CanonicalPath[] {
): ReadonlyMap<CanonicalPath, string> {
const sanitizedProjects: string[] = [];

// Normalize and sanitize the project paths
Expand All @@ -49,7 +51,7 @@ export function resolveProjectList(
}

if (sanitizedProjects.length === 0) {
return [];
return new Map();
}

const projectFolderIgnoreList = (
Expand Down Expand Up @@ -91,7 +93,7 @@ export function resolveProjectList(
const nonGlobProjects = sanitizedProjects.filter(project => !isGlob(project));
const globProjects = sanitizedProjects.filter(project => isGlob(project));

const uniqueCanonicalProjectPaths = new Set(
const uniqueCanonicalProjectPaths = new Map(
nonGlobProjects
.concat(
globProjects.length === 0
Expand All @@ -100,21 +102,21 @@ export function resolveProjectList(
cwd: options.tsconfigRootDir,
}),
)
.map(project =>
.map(project => [
getCanonicalFileName(
ensureAbsolutePath(project, options.tsconfigRootDir),
),
),
ensureAbsolutePath(project, options.tsconfigRootDir),
]),
);

log(
'parserOptions.project (excluding ignored) matched projects: %s',
uniqueCanonicalProjectPaths,
);

const returnValue = Array.from(uniqueCanonicalProjectPaths);
RESOLUTION_CACHE.set(cacheKey, returnValue);
return returnValue;
RESOLUTION_CACHE.set(cacheKey, uniqueCanonicalProjectPaths);
return uniqueCanonicalProjectPaths;
}

function getHash({
Expand Down
10 changes: 5 additions & 5 deletions packages/typescript-estree/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,21 +188,21 @@ function parseAndGenerateServices<T extends TSESTreeOptions = TSESTreeOptions>(
if (
parseSettings.singleRun &&
!parseSettings.programs &&
parseSettings.projects.length > 0
parseSettings.projects.size > 0
) {
parseSettings.programs = {
*[Symbol.iterator](): Iterator<ts.Program> {
for (const configFile of parseSettings.projects) {
const existingProgram = existingPrograms.get(configFile);
const existingProgram = existingPrograms.get(configFile[0]);
if (existingProgram) {
yield existingProgram;
} else {
log(
'Detected single-run/CLI usage, creating Program once ahead of time for project: %s',
configFile,
);
const newProgram = createProgramFromConfigFile(configFile);
existingPrograms.set(configFile, newProgram);
const newProgram = createProgramFromConfigFile(configFile[1]);
existingPrograms.set(configFile[0], newProgram);
yield newProgram;
}
}
Expand All @@ -214,7 +214,7 @@ function parseAndGenerateServices<T extends TSESTreeOptions = TSESTreeOptions>(
* Generate a full ts.Program or offer provided instances in order to be able to provide parser services, such as type-checking
*/
const hasFullTypeInformation =
parseSettings.programs != null || parseSettings.projects.length > 0;
parseSettings.programs != null || parseSettings.projects.size > 0;

if (
typeof options.errorOnTypeScriptSyntacticAndSemanticIssues === 'boolean' &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import glob = require('glob');
import * as path from 'path';

import { getCanonicalFileName } from '../../src/create-program/shared';
import { createProgramFromConfigFile as createProgramFromConfigFileOriginal } from '../../src/create-program/useProvidedPrograms';
import {
clearParseAndGenerateServicesCalls,
Expand Down Expand Up @@ -94,7 +93,7 @@ const options = {
} as const;

const resolvedProject = (p: string): string =>
getCanonicalFileName(path.resolve(path.join(process.cwd(), FIXTURES_DIR), p));
path.resolve(path.join(process.cwd(), FIXTURES_DIR), p);

describe('semanticInfo - singleRun', () => {
beforeEach(() => {
Expand Down
2 changes: 1 addition & 1 deletion packages/website/src/components/linter/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const defaultParseSettings: ParseSettings = {
log: console.log,
preserveNodeMaps: true,
programs: null,
projects: [],
projects: new Map(),
range: true,
singleRun: false,
suppressDeprecatedPropertyWarnings: false,
Expand Down
Loading