Skip to content

Commit 360dc91

Browse files
authored
Merge pull request #17602 from Microsoft/multiInferredProjects
Adds support for inferred project isolation by projectRootPath
2 parents 95ee9ae + c272c3c commit 360dc91

10 files changed

+298
-66
lines changed

src/harness/harnessLanguageService.ts

+1
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,7 @@ namespace Harness.LanguageService {
825825
host: serverHost,
826826
cancellationToken: ts.server.nullCancellationToken,
827827
useSingleInferredProject: false,
828+
useInferredProjectPerProjectRoot: false,
828829
typingsInstaller: undefined,
829830
byteLength: Utils.byteLength,
830831
hrtime: process.hrtime,

src/harness/unittests/cachingInServerLSHost.ts

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ namespace ts {
5757
logger: projectSystem.nullLogger,
5858
cancellationToken: { isCancellationRequested: () => false },
5959
useSingleInferredProject: false,
60+
useInferredProjectPerProjectRoot: false,
6061
typingsInstaller: undefined
6162
};
6263
const projectService = new server.ProjectService(svcOpts);

src/harness/unittests/compileOnSave.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ namespace ts.projectSystem {
3636
host,
3737
cancellationToken: nullCancellationToken,
3838
useSingleInferredProject: false,
39+
useInferredProjectPerProjectRoot: false,
3940
typingsInstaller: typingsInstaller || server.nullTypingsInstaller,
4041
byteLength: Utils.byteLength,
4142
hrtime: process.hrtime,
@@ -552,7 +553,7 @@ namespace ts.projectSystem {
552553
};
553554
const host = createServerHost([file1, file2, configFile, libFile], { newLine: "\r\n" });
554555
const typingsInstaller = createTestTypingsInstaller(host);
555-
const session = createSession(host, typingsInstaller);
556+
const session = createSession(host, { typingsInstaller });
556557

557558
openFilesForSession([file1, file2], session);
558559
const compileFileRequest = makeSessionRequest<server.protocol.CompileOnSaveEmitFileRequestArgs>(CommandNames.CompileOnSaveEmitFile, { file: file1.path, projectFileName: configFile.path });

src/harness/unittests/session.ts

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ namespace ts.server {
4343
host: mockHost,
4444
cancellationToken: nullCancellationToken,
4545
useSingleInferredProject: false,
46+
useInferredProjectPerProjectRoot: false,
4647
typingsInstaller: undefined,
4748
byteLength: Utils.byteLength,
4849
hrtime: process.hrtime,
@@ -394,6 +395,7 @@ namespace ts.server {
394395
host: mockHost,
395396
cancellationToken: nullCancellationToken,
396397
useSingleInferredProject: false,
398+
useInferredProjectPerProjectRoot: false,
397399
typingsInstaller: undefined,
398400
byteLength: Utils.byteLength,
399401
hrtime: process.hrtime,
@@ -461,6 +463,7 @@ namespace ts.server {
461463
host: mockHost,
462464
cancellationToken: nullCancellationToken,
463465
useSingleInferredProject: false,
466+
useInferredProjectPerProjectRoot: false,
464467
typingsInstaller: undefined,
465468
byteLength: Utils.byteLength,
466469
hrtime: process.hrtime,

src/harness/unittests/tsserverProjectSystem.ts

+153-30
Original file line numberDiff line numberDiff line change
@@ -185,23 +185,28 @@ namespace ts.projectSystem {
185185
}
186186
}
187187

188-
export function createSession(host: server.ServerHost, typingsInstaller?: server.ITypingsInstaller, projectServiceEventHandler?: server.ProjectServiceEventHandler, cancellationToken?: server.ServerCancellationToken, throttleWaitMilliseconds?: number) {
189-
if (typingsInstaller === undefined) {
190-
typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host);
188+
export function createSession(host: server.ServerHost, opts: Partial<server.SessionOptions> = {}) {
189+
if (opts.typingsInstaller === undefined) {
190+
opts.typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/ 5, host);
191191
}
192-
const opts: server.SessionOptions = {
192+
193+
if (opts.eventHandler !== undefined) {
194+
opts.canUseEvents = true;
195+
}
196+
197+
const sessionOptions: server.SessionOptions = {
193198
host,
194-
cancellationToken: cancellationToken || server.nullCancellationToken,
199+
cancellationToken: server.nullCancellationToken,
195200
useSingleInferredProject: false,
196-
typingsInstaller,
201+
useInferredProjectPerProjectRoot: false,
202+
typingsInstaller: undefined,
197203
byteLength: Utils.byteLength,
198204
hrtime: process.hrtime,
199205
logger: nullLogger,
200-
canUseEvents: projectServiceEventHandler !== undefined,
201-
eventHandler: projectServiceEventHandler,
202-
throttleWaitMilliseconds
206+
canUseEvents: false
203207
};
204-
return new TestSession(opts);
208+
209+
return new TestSession({ ...sessionOptions, ...opts });
205210
}
206211

207212
interface CreateProjectServiceParameters {
@@ -215,9 +220,16 @@ namespace ts.projectSystem {
215220

216221
export class TestProjectService extends server.ProjectService {
217222
constructor(host: server.ServerHost, logger: server.Logger, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean,
218-
typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler) {
223+
typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler, opts: Partial<server.ProjectServiceOptions> = {}) {
219224
super({
220-
host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, eventHandler
225+
host,
226+
logger,
227+
cancellationToken,
228+
useSingleInferredProject,
229+
useInferredProjectPerProjectRoot: false,
230+
typingsInstaller,
231+
eventHandler,
232+
...opts
221233
});
222234
}
223235

@@ -631,7 +643,7 @@ namespace ts.projectSystem {
631643
}
632644
}
633645

634-
describe("tsserver-project-system", () => {
646+
describe("tsserverProjectSystem", () => {
635647
const commonFile1: FileOrFolder = {
636648
path: "/a/b/commonFile1.ts",
637649
content: "let x = 1"
@@ -2230,13 +2242,16 @@ namespace ts.projectSystem {
22302242
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
22312243

22322244
let lastEvent: server.ProjectLanguageServiceStateEvent;
2233-
const session = createSession(host, /*typingsInstaller*/ undefined, e => {
2234-
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ContextEvent || e.eventName === server.ProjectInfoTelemetryEvent) {
2235-
return;
2245+
const session = createSession(host, {
2246+
canUseEvents: true,
2247+
eventHandler: e => {
2248+
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ContextEvent || e.eventName === server.ProjectInfoTelemetryEvent) {
2249+
return;
2250+
}
2251+
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
2252+
assert.equal(e.data.project.getProjectName(), config.path, "project name");
2253+
lastEvent = <server.ProjectLanguageServiceStateEvent>e;
22362254
}
2237-
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
2238-
assert.equal(e.data.project.getProjectName(), config.path, "project name");
2239-
lastEvent = <server.ProjectLanguageServiceStateEvent>e;
22402255
});
22412256
session.executeCommand(<protocol.OpenRequest>{
22422257
seq: 0,
@@ -2280,12 +2295,15 @@ namespace ts.projectSystem {
22802295
host.getFileSize = (filePath: string) =>
22812296
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
22822297
let lastEvent: server.ProjectLanguageServiceStateEvent;
2283-
const session = createSession(host, /*typingsInstaller*/ undefined, e => {
2284-
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectInfoTelemetryEvent) {
2285-
return;
2298+
const session = createSession(host, {
2299+
canUseEvents: true,
2300+
eventHandler: e => {
2301+
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectInfoTelemetryEvent) {
2302+
return;
2303+
}
2304+
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
2305+
lastEvent = <server.ProjectLanguageServiceStateEvent>e;
22862306
}
2287-
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
2288-
lastEvent = <server.ProjectLanguageServiceStateEvent>e;
22892307
});
22902308
session.executeCommand(<protocol.OpenRequest>{
22912309
seq: 0,
@@ -3069,7 +3087,10 @@ namespace ts.projectSystem {
30693087
};
30703088

30713089
const host = createServerHost([file, configFile]);
3072-
const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler);
3090+
const session = createSession(host, {
3091+
canUseEvents: true,
3092+
eventHandler: serverEventManager.handler
3093+
});
30733094
openFilesForSession([file], session);
30743095
serverEventManager.checkEventCountOfType("configFileDiag", 1);
30753096

@@ -3096,7 +3117,10 @@ namespace ts.projectSystem {
30963117
};
30973118

30983119
const host = createServerHost([file, configFile]);
3099-
const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler);
3120+
const session = createSession(host, {
3121+
canUseEvents: true,
3122+
eventHandler: serverEventManager.handler
3123+
});
31003124
openFilesForSession([file], session);
31013125
serverEventManager.checkEventCountOfType("configFileDiag", 1);
31023126
});
@@ -3115,7 +3139,10 @@ namespace ts.projectSystem {
31153139
};
31163140

31173141
const host = createServerHost([file, configFile]);
3118-
const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler);
3142+
const session = createSession(host, {
3143+
canUseEvents: true,
3144+
eventHandler: serverEventManager.handler
3145+
});
31193146
openFilesForSession([file], session);
31203147
serverEventManager.checkEventCountOfType("configFileDiag", 1);
31213148

@@ -3504,6 +3531,93 @@ namespace ts.projectSystem {
35043531
checkNumberOfProjects(projectService, { inferredProjects: 1 });
35053532
checkProjectActualFiles(projectService.inferredProjects[0], [f.path]);
35063533
});
3534+
3535+
it("inferred projects per project root", () => {
3536+
const file1 = { path: "/a/file1.ts", content: "let x = 1;", projectRootPath: "/a" };
3537+
const file2 = { path: "/a/file2.ts", content: "let y = 2;", projectRootPath: "/a" };
3538+
const file3 = { path: "/b/file2.ts", content: "let x = 3;", projectRootPath: "/b" };
3539+
const file4 = { path: "/c/file3.ts", content: "let z = 4;" };
3540+
const host = createServerHost([file1, file2, file3, file4]);
3541+
const session = createSession(host, {
3542+
useSingleInferredProject: true,
3543+
useInferredProjectPerProjectRoot: true
3544+
});
3545+
session.executeCommand(<server.protocol.SetCompilerOptionsForInferredProjectsRequest>{
3546+
seq: 1,
3547+
type: "request",
3548+
command: CommandNames.CompilerOptionsForInferredProjects,
3549+
arguments: {
3550+
options: {
3551+
allowJs: true,
3552+
target: ScriptTarget.ESNext
3553+
}
3554+
}
3555+
});
3556+
session.executeCommand(<server.protocol.SetCompilerOptionsForInferredProjectsRequest>{
3557+
seq: 2,
3558+
type: "request",
3559+
command: CommandNames.CompilerOptionsForInferredProjects,
3560+
arguments: {
3561+
options: {
3562+
allowJs: true,
3563+
target: ScriptTarget.ES2015
3564+
},
3565+
projectRootPath: "/b"
3566+
}
3567+
});
3568+
session.executeCommand(<server.protocol.OpenRequest>{
3569+
seq: 3,
3570+
type: "request",
3571+
command: CommandNames.Open,
3572+
arguments: {
3573+
file: file1.path,
3574+
fileContent: file1.content,
3575+
scriptKindName: "JS",
3576+
projectRootPath: file1.projectRootPath
3577+
}
3578+
});
3579+
session.executeCommand(<server.protocol.OpenRequest>{
3580+
seq: 4,
3581+
type: "request",
3582+
command: CommandNames.Open,
3583+
arguments: {
3584+
file: file2.path,
3585+
fileContent: file2.content,
3586+
scriptKindName: "JS",
3587+
projectRootPath: file2.projectRootPath
3588+
}
3589+
});
3590+
session.executeCommand(<server.protocol.OpenRequest>{
3591+
seq: 5,
3592+
type: "request",
3593+
command: CommandNames.Open,
3594+
arguments: {
3595+
file: file3.path,
3596+
fileContent: file3.content,
3597+
scriptKindName: "JS",
3598+
projectRootPath: file3.projectRootPath
3599+
}
3600+
});
3601+
session.executeCommand(<server.protocol.OpenRequest>{
3602+
seq: 6,
3603+
type: "request",
3604+
command: CommandNames.Open,
3605+
arguments: {
3606+
file: file4.path,
3607+
fileContent: file4.content,
3608+
scriptKindName: "JS"
3609+
}
3610+
});
3611+
3612+
const projectService = session.getProjectService();
3613+
checkNumberOfProjects(projectService, { inferredProjects: 3 });
3614+
checkProjectActualFiles(projectService.inferredProjects[0], [file4.path]);
3615+
checkProjectActualFiles(projectService.inferredProjects[1], [file1.path, file2.path]);
3616+
checkProjectActualFiles(projectService.inferredProjects[2], [file3.path]);
3617+
assert.equal(projectService.inferredProjects[0].getCompilerOptions().target, ScriptTarget.ESNext);
3618+
assert.equal(projectService.inferredProjects[1].getCompilerOptions().target, ScriptTarget.ESNext);
3619+
assert.equal(projectService.inferredProjects[2].getCompilerOptions().target, ScriptTarget.ES2015);
3620+
});
35073621
});
35083622

35093623
describe("No overwrite emit error", () => {
@@ -3697,7 +3811,7 @@ namespace ts.projectSystem {
36973811
resetRequest: noop
36983812
};
36993813

3700-
const session = createSession(host, /*typingsInstaller*/ undefined, /*projectServiceEventHandler*/ undefined, cancellationToken);
3814+
const session = createSession(host, { cancellationToken });
37013815

37023816
expectedRequestId = session.getNextSeq();
37033817
session.executeCommandSeq(<server.protocol.OpenRequest>{
@@ -3737,7 +3851,11 @@ namespace ts.projectSystem {
37373851

37383852
const cancellationToken = new TestServerCancellationToken();
37393853
const host = createServerHost([f1, config]);
3740-
const session = createSession(host, /*typingsInstaller*/ undefined, () => { }, cancellationToken);
3854+
const session = createSession(host, {
3855+
canUseEvents: true,
3856+
eventHandler: () => { },
3857+
cancellationToken
3858+
});
37413859
{
37423860
session.executeCommandSeq(<protocol.OpenRequest>{
37433861
command: "open",
@@ -3870,7 +3988,12 @@ namespace ts.projectSystem {
38703988
};
38713989
const cancellationToken = new TestServerCancellationToken(/*cancelAfterRequest*/ 3);
38723990
const host = createServerHost([f1, config]);
3873-
const session = createSession(host, /*typingsInstaller*/ undefined, () => { }, cancellationToken, /*throttleWaitMilliseconds*/ 0);
3991+
const session = createSession(host, {
3992+
canUseEvents: true,
3993+
eventHandler: () => { },
3994+
cancellationToken,
3995+
throttleWaitMilliseconds: 0
3996+
});
38743997
{
38753998
session.executeCommandSeq(<protocol.OpenRequest>{
38763999
command: "open",

0 commit comments

Comments
 (0)