Skip to content

Commit 29ed92e

Browse files
authored
Merge pull request microsoft#19118 from Microsoft/caseSensitivityInferredProjectRoot
Handles case sensitivity of project root with respect to inferred projects
2 parents c2150f4 + 6254864 commit 29ed92e

File tree

4 files changed

+124
-11
lines changed

4 files changed

+124
-11
lines changed

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,11 +281,11 @@ namespace ts.projectSystem {
281281
checkNumberOfProjects(this, count);
282282
}
283283
}
284-
export function createProjectService(host: server.ServerHost, parameters: CreateProjectServiceParameters = {}) {
284+
export function createProjectService(host: server.ServerHost, parameters: CreateProjectServiceParameters = {}, options?: Partial<server.ProjectServiceOptions>) {
285285
const cancellationToken = parameters.cancellationToken || server.nullCancellationToken;
286286
const logger = parameters.logger || nullLogger;
287287
const useSingleInferredProject = parameters.useSingleInferredProject !== undefined ? parameters.useSingleInferredProject : false;
288-
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, parameters.typingsInstaller, parameters.eventHandler);
288+
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, parameters.typingsInstaller, parameters.eventHandler, options);
289289
}
290290

291291
export function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) {
@@ -3784,6 +3784,113 @@ namespace ts.projectSystem {
37843784
assert.equal(projectService.inferredProjects[1].getCompilationSettings().target, ScriptTarget.ESNext);
37853785
assert.equal(projectService.inferredProjects[2].getCompilationSettings().target, ScriptTarget.ES2015);
37863786
});
3787+
3788+
function checkInferredProject(inferredProject: server.InferredProject, actualFiles: FileOrFolder[], target: ScriptTarget) {
3789+
checkProjectActualFiles(inferredProject, actualFiles.map(f => f.path));
3790+
assert.equal(inferredProject.getCompilationSettings().target, target);
3791+
}
3792+
3793+
function verifyProjectRootWithCaseSensitivity(useCaseSensitiveFileNames: boolean) {
3794+
const files: [FileOrFolder, FileOrFolder, FileOrFolder, FileOrFolder] = [
3795+
{ path: "/a/file1.ts", content: "let x = 1;" },
3796+
{ path: "/A/file2.ts", content: "let y = 2;" },
3797+
{ path: "/b/file2.ts", content: "let x = 3;" },
3798+
{ path: "/c/file3.ts", content: "let z = 4;" }
3799+
];
3800+
const host = createServerHost(files, { useCaseSensitiveFileNames });
3801+
const projectService = createProjectService(host, { useSingleInferredProject: true, }, { useInferredProjectPerProjectRoot: true });
3802+
projectService.setCompilerOptionsForInferredProjects({
3803+
allowJs: true,
3804+
target: ScriptTarget.ESNext
3805+
});
3806+
projectService.setCompilerOptionsForInferredProjects({
3807+
allowJs: true,
3808+
target: ScriptTarget.ES2015
3809+
}, "/a");
3810+
3811+
openClientFiles(["/a", "/a", "/b", undefined]);
3812+
verifyInferredProjectsState([
3813+
[[files[3]], ScriptTarget.ESNext],
3814+
[[files[0], files[1]], ScriptTarget.ES2015],
3815+
[[files[2]], ScriptTarget.ESNext]
3816+
]);
3817+
closeClientFiles();
3818+
3819+
openClientFiles(["/a", "/A", "/b", undefined]);
3820+
if (useCaseSensitiveFileNames) {
3821+
verifyInferredProjectsState([
3822+
[[files[3]], ScriptTarget.ESNext],
3823+
[[files[0]], ScriptTarget.ES2015],
3824+
[[files[1]], ScriptTarget.ESNext],
3825+
[[files[2]], ScriptTarget.ESNext]
3826+
]);
3827+
}
3828+
else {
3829+
verifyInferredProjectsState([
3830+
[[files[3]], ScriptTarget.ESNext],
3831+
[[files[0], files[1]], ScriptTarget.ES2015],
3832+
[[files[2]], ScriptTarget.ESNext]
3833+
]);
3834+
}
3835+
closeClientFiles();
3836+
3837+
projectService.setCompilerOptionsForInferredProjects({
3838+
allowJs: true,
3839+
target: ScriptTarget.ES2017
3840+
}, "/A");
3841+
3842+
openClientFiles(["/a", "/a", "/b", undefined]);
3843+
verifyInferredProjectsState([
3844+
[[files[3]], ScriptTarget.ESNext],
3845+
[[files[0], files[1]], useCaseSensitiveFileNames ? ScriptTarget.ES2015 : ScriptTarget.ES2017],
3846+
[[files[2]], ScriptTarget.ESNext]
3847+
]);
3848+
closeClientFiles();
3849+
3850+
openClientFiles(["/a", "/A", "/b", undefined]);
3851+
if (useCaseSensitiveFileNames) {
3852+
verifyInferredProjectsState([
3853+
[[files[3]], ScriptTarget.ESNext],
3854+
[[files[0]], ScriptTarget.ES2015],
3855+
[[files[1]], ScriptTarget.ES2017],
3856+
[[files[2]], ScriptTarget.ESNext]
3857+
]);
3858+
}
3859+
else {
3860+
verifyInferredProjectsState([
3861+
[[files[3]], ScriptTarget.ESNext],
3862+
[[files[0], files[1]], ScriptTarget.ES2017],
3863+
[[files[2]], ScriptTarget.ESNext]
3864+
]);
3865+
}
3866+
closeClientFiles();
3867+
3868+
function openClientFiles(projectRoots: [string | undefined, string | undefined, string | undefined, string | undefined]) {
3869+
files.forEach((file, index) => {
3870+
projectService.openClientFile(file.path, file.content, ScriptKind.JS, projectRoots[index]);
3871+
});
3872+
}
3873+
3874+
function closeClientFiles() {
3875+
files.forEach(file => projectService.closeClientFile(file.path));
3876+
}
3877+
3878+
function verifyInferredProjectsState(expected: [FileOrFolder[], ScriptTarget][]) {
3879+
checkNumberOfProjects(projectService, { inferredProjects: expected.length });
3880+
projectService.inferredProjects.forEach((p, index) => {
3881+
const [actualFiles, target] = expected[index];
3882+
checkInferredProject(p, actualFiles, target);
3883+
});
3884+
}
3885+
}
3886+
3887+
it("inferred projects per project root with case sensitive system", () => {
3888+
verifyProjectRootWithCaseSensitivity(/*useCaseSensitiveFileNames*/ true);
3889+
});
3890+
3891+
it("inferred projects per project root with case insensitive system", () => {
3892+
verifyProjectRootWithCaseSensitivity(/*useCaseSensitiveFileNames*/ false);
3893+
});
37873894
});
37883895

37893896
describe("No overwrite emit error", () => {

src/server/editorServices.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -590,9 +590,9 @@ namespace ts.server {
590590
// always set 'allowNonTsExtensions' for inferred projects since user cannot configure it from the outside
591591
// previously we did not expose a way for user to change these settings and this option was enabled by default
592592
compilerOptions.allowNonTsExtensions = true;
593-
594-
if (projectRootPath) {
595-
this.compilerOptionsForInferredProjectsPerProjectRoot.set(projectRootPath, compilerOptions);
593+
const canonicalProjectRootPath = projectRootPath && this.toCanonicalFileName(projectRootPath);
594+
if (canonicalProjectRootPath) {
595+
this.compilerOptionsForInferredProjectsPerProjectRoot.set(canonicalProjectRootPath, compilerOptions);
596596
}
597597
else {
598598
this.compilerOptionsForInferredProjects = compilerOptions;
@@ -608,9 +608,9 @@ namespace ts.server {
608608
// root path
609609
// - Inferred projects with a projectRootPath, if the new options apply to that
610610
// project root path.
611-
if (projectRootPath ?
612-
project.projectRootPath === projectRootPath :
613-
!project.projectRootPath || !this.compilerOptionsForInferredProjectsPerProjectRoot.has(project.projectRootPath)) {
611+
if (canonicalProjectRootPath ?
612+
project.projectRootPath === canonicalProjectRootPath :
613+
!project.projectRootPath || !this.compilerOptionsForInferredProjectsPerProjectRoot.has(project.projectRootPath)) {
614614
project.setCompilerOptions(compilerOptions);
615615
project.compileOnSaveEnabled = compilerOptions.compileOnSave;
616616
project.markAsDirty();
@@ -1599,9 +1599,10 @@ namespace ts.server {
15991599
}
16001600

16011601
if (projectRootPath) {
1602+
const canonicalProjectRootPath = this.toCanonicalFileName(projectRootPath);
16021603
// if we have an explicit project root path, find (or create) the matching inferred project.
16031604
for (const project of this.inferredProjects) {
1604-
if (project.projectRootPath === projectRootPath) {
1605+
if (project.projectRootPath === canonicalProjectRootPath) {
16051606
return project;
16061607
}
16071608
}

src/server/project.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1047,12 +1047,15 @@ namespace ts.server {
10471047
super.setCompilerOptions(newOptions);
10481048
}
10491049

1050+
/** this is canonical project root path */
1051+
readonly projectRootPath: string | undefined;
1052+
10501053
/*@internal*/
10511054
constructor(
10521055
projectService: ProjectService,
10531056
documentRegistry: DocumentRegistry,
10541057
compilerOptions: CompilerOptions,
1055-
readonly projectRootPath: string | undefined,
1058+
projectRootPath: string | undefined,
10561059
currentDirectory: string | undefined) {
10571060
super(InferredProject.newName(),
10581061
ProjectKind.Inferred,
@@ -1064,6 +1067,7 @@ namespace ts.server {
10641067
/*compileOnSaveEnabled*/ false,
10651068
projectService.host,
10661069
currentDirectory);
1070+
this.projectRootPath = projectRootPath && projectService.toCanonicalFileName(projectRootPath);
10671071
}
10681072

10691073
addRoot(info: ScriptInfo) {

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7188,11 +7188,12 @@ declare namespace ts.server {
71887188
* the file and its imports/references are put into an InferredProject.
71897189
*/
71907190
class InferredProject extends Project {
7191-
readonly projectRootPath: string | undefined;
71927191
private static readonly newName;
71937192
private _isJsInferredProject;
71947193
toggleJsInferredProject(isJsInferredProject: boolean): void;
71957194
setCompilerOptions(options?: CompilerOptions): void;
7195+
/** this is canonical project root path */
7196+
readonly projectRootPath: string | undefined;
71967197
addRoot(info: ScriptInfo): void;
71977198
removeRoot(info: ScriptInfo): void;
71987199
isProjectWithSingleRoot(): boolean;

0 commit comments

Comments
 (0)