Skip to content

Dont open composite projects to determine if script info is part of project #59688

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 11 commits into from
Sep 18, 2024
  •  
  •  
  •  
19 changes: 16 additions & 3 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3250,13 +3250,25 @@ function shouldReportNoInputFiles(fileNames: string[], canJsonReportNoInutFiles:
return fileNames.length === 0 && canJsonReportNoInutFiles && (!resolutionStack || resolutionStack.length === 0);
}

/** @internal */
export function isSolutionConfig(config: ParsedCommandLine): boolean {
return !config.fileNames.length &&
hasProperty(config.raw, "references");
}

/** @internal */
export function canJsonReportNoInputFiles(raw: any): boolean {
return !hasProperty(raw, "files") && !hasProperty(raw, "references");
}

/** @internal */
export function updateErrorForNoInputFiles(fileNames: string[], configFileName: string, configFileSpecs: ConfigFileSpecs, configParseDiagnostics: Diagnostic[], canJsonReportNoInutFiles: boolean): boolean {
export function updateErrorForNoInputFiles(
fileNames: string[],
configFileName: string,
configFileSpecs: ConfigFileSpecs,
configParseDiagnostics: Diagnostic[],
canJsonReportNoInutFiles: boolean,
): boolean {
const existingErrors = configParseDiagnostics.length;
if (shouldReportNoInputFiles(fileNames, canJsonReportNoInutFiles)) {
configParseDiagnostics.push(getErrorForNoInputFiles(configFileSpecs, configFileName));
Expand Down Expand Up @@ -3960,13 +3972,14 @@ export function matchesExclude(
);
}

function matchesExcludeWorker(
/** @internal */
export function matchesExcludeWorker(
pathToCheck: string,
excludeSpecs: readonly string[] | undefined,
useCaseSensitiveFileNames: boolean,
currentDirectory: string,
basePath?: string,
) {
): boolean {
const excludePattern = getRegularExpressionForWildcard(excludeSpecs, combinePaths(normalizePath(currentDirectory), basePath), "exclude");
const excludeRegex = excludePattern && getRegexFromPattern(excludePattern, useCaseSensitiveFileNames);
if (!excludeRegex) return false;
Expand Down
104 changes: 66 additions & 38 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,11 @@ export function forEachResolvedProjectReference<T>(
resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined,
cb: (resolvedProjectReference: ResolvedProjectReference, parent: ResolvedProjectReference | undefined) => T | undefined,
): T | undefined {
return forEachProjectReference(/*projectReferences*/ undefined, resolvedProjectReferences, (resolvedRef, parent) => resolvedRef && cb(resolvedRef, parent));
return forEachProjectReference(
/*projectReferences*/ undefined,
resolvedProjectReferences,
(resolvedRef, parent) => resolvedRef && cb(resolvedRef, parent),
);
}

function forEachProjectReference<T>(
Expand All @@ -1160,7 +1164,6 @@ function forEachProjectReference<T>(
cbRef?: (projectReferences: readonly ProjectReference[] | undefined, parent: ResolvedProjectReference | undefined) => T | undefined,
): T | undefined {
let seenResolvedRefs: Set<Path> | undefined;

return worker(projectReferences, resolvedProjectReferences, /*parent*/ undefined);

function worker(
Expand All @@ -1173,19 +1176,26 @@ function forEachProjectReference<T>(
const result = cbRef(projectReferences, parent);
if (result) return result;
}

return forEach(resolvedProjectReferences, (resolvedRef, index) => {
if (resolvedRef && seenResolvedRefs?.has(resolvedRef.sourceFile.path)) {
// ignore recursives
return undefined;
}

const result = cbResolvedRef(resolvedRef, parent, index);
if (result || !resolvedRef) return result;

(seenResolvedRefs ||= new Set()).add(resolvedRef.sourceFile.path);
return worker(resolvedRef.commandLine.projectReferences, resolvedRef.references, resolvedRef);
});
let skipChildren: Set<ResolvedProjectReference> | undefined;
return forEach(
resolvedProjectReferences,
(resolvedRef, index) => {
if (resolvedRef && seenResolvedRefs?.has(resolvedRef.sourceFile.path)) {
(skipChildren ??= new Set()).add(resolvedRef);
// ignore recursives
return undefined;
}
const result = cbResolvedRef(resolvedRef, parent, index);
if (result || !resolvedRef) return result;
(seenResolvedRefs ||= new Set()).add(resolvedRef.sourceFile.path);
},
) || forEach(
resolvedProjectReferences,
resolvedRef =>
resolvedRef && !skipChildren?.has(resolvedRef) ?
worker(resolvedRef.commandLine.projectReferences, resolvedRef.references, resolvedRef) :
undefined,
);
}
}

Expand Down Expand Up @@ -1367,7 +1377,14 @@ export function isProgramUptoDate(
(seenResolvedRefs || (seenResolvedRefs = [])).push(oldResolvedRef);

// If child project references are upto date, this project reference is uptodate
return !forEach(oldResolvedRef.references, (childResolvedRef, index) => !resolvedProjectReferenceUptoDate(childResolvedRef, oldResolvedRef.commandLine.projectReferences![index]));
return !forEach(
oldResolvedRef.references,
(childResolvedRef, index) =>
!resolvedProjectReferenceUptoDate(
childResolvedRef,
oldResolvedRef.commandLine.projectReferences![index],
),
);
}

// In old program, not able to resolve project reference path,
Expand Down Expand Up @@ -4905,7 +4922,14 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
case FileIncludeKind.SourceFromProjectReference:
case FileIncludeKind.OutputFromProjectReference:
const referencedResolvedRef = Debug.checkDefined(resolvedProjectReferences?.[reason.index]);
const referenceInfo = forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) => resolvedRef === referencedResolvedRef ? { sourceFile: parent?.sourceFile || options.configFile!, index } : undefined);
const referenceInfo = forEachProjectReference(
projectReferences,
resolvedProjectReferences,
(resolvedRef, parent, index) =>
resolvedRef === referencedResolvedRef ?
{ sourceFile: parent?.sourceFile || options.configFile!, index } :
undefined,
);
if (!referenceInfo) return undefined;
const { sourceFile, index } = referenceInfo;
const referencesSyntax = forEachTsConfigPropArray(sourceFile as TsConfigSourceFile, "references", property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined);
Expand Down Expand Up @@ -4945,28 +4969,32 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg

function verifyProjectReferences() {
const buildInfoPath = !options.suppressOutputPathCheck ? getTsBuildInfoEmitOutputFilePath(options) : undefined;
forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) => {
const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
const parentFile = parent && parent.sourceFile as JsonSourceFile;
verifyDeprecatedProjectReference(ref, parentFile, index);
if (!resolvedRef) {
createDiagnosticForReference(parentFile, index, Diagnostics.File_0_not_found, ref.path);
return;
}
const options = resolvedRef.commandLine.options;
if (!options.composite || options.noEmit) {
// ok to not have composite if the current program is container only
const inputs = parent ? parent.commandLine.fileNames : rootNames;
if (inputs.length) {
if (!options.composite) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path);
if (options.noEmit) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_may_not_disable_emit, ref.path);
forEachProjectReference(
projectReferences,
resolvedProjectReferences,
(resolvedRef, parent, index) => {
const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
const parentFile = parent && parent.sourceFile as JsonSourceFile;
verifyDeprecatedProjectReference(ref, parentFile, index);
if (!resolvedRef) {
createDiagnosticForReference(parentFile, index, Diagnostics.File_0_not_found, ref.path);
return;
}
}
if (!parent && buildInfoPath && buildInfoPath === getTsBuildInfoEmitOutputFilePath(options)) {
createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, buildInfoPath, ref.path);
hasEmitBlockingDiagnostics.set(toPath(buildInfoPath), true);
}
});
const options = resolvedRef.commandLine.options;
if (!options.composite || options.noEmit) {
// ok to not have composite if the current program is container only
const inputs = parent ? parent.commandLine.fileNames : rootNames;
if (inputs.length) {
if (!options.composite) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path);
if (options.noEmit) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_may_not_disable_emit, ref.path);
}
}
if (!parent && buildInfoPath && buildInfoPath === getTsBuildInfoEmitOutputFilePath(options)) {
createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, buildInfoPath, ref.path);
hasEmitBlockingDiagnostics.set(toPath(buildInfoPath), true);
}
},
);
}

function createDiagnosticForOptionPathKeyValue(key: string, valueIndex: number, message: DiagnosticMessage, ...args: DiagnosticArguments) {
Expand Down
15 changes: 9 additions & 6 deletions src/compiler/tsbuildPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import {
isIncrementalBuildInfo,
isIncrementalCompilation,
isPackageJsonInfo,
isSolutionConfig,
loadWithModeAwareCache,
maybeBind,
missingFileModifiedTime,
Expand Down Expand Up @@ -1236,7 +1237,13 @@ function getNextInvalidatedProjectCreateInfo<T extends BuilderProgram>(
else if (updateLevel === ProgramUpdateLevel.RootNamesAndUpdate) {
// Update file names
config.fileNames = getFileNamesFromConfigSpecs(config.options.configFile!.configFileSpecs!, getDirectoryPath(project), config.options, state.parseConfigFileHost);
updateErrorForNoInputFiles(config.fileNames, project, config.options.configFile!.configFileSpecs!, config.errors, canJsonReportNoInputFiles(config.raw));
updateErrorForNoInputFiles(
config.fileNames,
project,
config.options.configFile!.configFileSpecs!,
config.errors,
canJsonReportNoInputFiles(config.raw),
);
watchInputFiles(state, project, projectPath, config);
watchPackageJsonFiles(state, project, projectPath, config);
}
Expand Down Expand Up @@ -1461,11 +1468,7 @@ function checkConfigFileUpToDateStatus<T extends BuilderProgram>(state: Solution

function getUpToDateStatusWorker<T extends BuilderProgram>(state: SolutionBuilderState<T>, project: ParsedCommandLine, resolvedPath: ResolvedConfigFilePath): UpToDateStatus {
// Container if no files are specified in the project
if (!project.fileNames.length && !canJsonReportNoInputFiles(project.raw)) {
return {
type: UpToDateStatusType.ContainerOnly,
};
}
if (isSolutionConfig(project)) return { type: UpToDateStatusType.ContainerOnly };

// Fast check to see if reference projects are upto date and error free
let referenceStatuses;
Expand Down
10 changes: 9 additions & 1 deletion src/compiler/watchPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,15 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler

updateLevel = ProgramUpdateLevel.Update;
rootFileNames = getFileNamesFromConfigSpecs(compilerOptions.configFile!.configFileSpecs!, getNormalizedAbsolutePath(getDirectoryPath(configFileName), currentDirectory), compilerOptions, parseConfigFileHost, extraFileExtensions);
if (updateErrorForNoInputFiles(rootFileNames, getNormalizedAbsolutePath(configFileName, currentDirectory), compilerOptions.configFile!.configFileSpecs!, configFileParsingDiagnostics!, canConfigFileJsonReportNoInputFiles)) {
if (
updateErrorForNoInputFiles(
rootFileNames,
getNormalizedAbsolutePath(configFileName, currentDirectory),
compilerOptions.configFile!.configFileSpecs!,
configFileParsingDiagnostics!,
canConfigFileJsonReportNoInputFiles,
)
) {
hasChangedConfigFileParsingErrors = true;
}

Expand Down
2 changes: 2 additions & 0 deletions src/harness/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import { protocol } from "./_namespaces/ts.server.js";

export interface SessionClientHost extends LanguageServiceHost {
writeMessage(message: string): void;
openFile(fileName: string): void;
}

interface RenameEntry {
Expand Down Expand Up @@ -480,6 +481,7 @@ export class SessionClient implements LanguageService {
}

getFileReferences(fileName: string): ReferenceEntry[] {
this.host.openFile(fileName);
const request = this.processRequest<protocol.FileReferencesRequest>(protocol.CommandTypes.FileReferences, { file: fileName });
const response = this.processResponse<protocol.FileReferencesResponse>(request);

Expand Down
3 changes: 3 additions & 0 deletions src/harness/projectServiceStateLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ interface ProjectData {
projectStateVersion: Project["projectStateVersion"];
projectProgramVersion: Project["projectProgramVersion"];
dirty: Project["dirty"];
initialLoadPending: Project["initialLoadPending"];
isClosed: ReturnType<Project["isClosed"]>;
isOrphan: ReturnType<Project["isOrphan"]>;
noOpenRef: boolean;
Expand Down Expand Up @@ -123,6 +124,7 @@ export function patchServiceForStateBaseline(service: ProjectService): void {
projectDiff = printProperty(PrintPropertyWhen.Always, data, "projectStateVersion", project.projectStateVersion, projectDiff, projectPropertyLogs);
projectDiff = printProperty(PrintPropertyWhen.Always, data, "projectProgramVersion", project.projectProgramVersion, projectDiff, projectPropertyLogs);
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "dirty", project.dirty, projectDiff, projectPropertyLogs);
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "initialLoadPending", project.initialLoadPending, projectDiff, projectPropertyLogs);
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "isClosed", project.isClosed(), projectDiff, projectPropertyLogs);
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "isOrphan", !isBackgroundProject(project) && project.isOrphan(), projectDiff, projectPropertyLogs);
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "noOpenRef", noOpenRef(project), projectDiff, projectPropertyLogs);
Expand Down Expand Up @@ -154,6 +156,7 @@ export function patchServiceForStateBaseline(service: ProjectService): void {
projectStateVersion: project.projectStateVersion,
projectProgramVersion: project.projectProgramVersion,
dirty: project.dirty,
initialLoadPending: project.initialLoadPending,
isClosed: project.isClosed(),
isOrphan: !isBackgroundProject(project) && project.isOrphan(),
noOpenRef: noOpenRef(project),
Expand Down
Loading