• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts.projectSystem {
2    export function createHostWithSolutionBuild(files: readonly TestFSWithWatch.FileOrFolderOrSymLink[], rootNames: readonly string[]) {
3        const host = createServerHost(files);
4        // ts build should succeed
5        tscWatch.ensureErrorFreeBuild(host, rootNames);
6        return host;
7    }
8
9    describe("unittests:: tsserver:: with project references and tsbuild", () => {
10        describe("with container project", () => {
11            function getProjectFiles(project: string): [File, File] {
12                return [
13                    TestFSWithWatch.getTsBuildProjectFile(project, "tsconfig.json"),
14                    TestFSWithWatch.getTsBuildProjectFile(project, "index.ts"),
15                ];
16            }
17
18            const project = "container";
19            const containerLib = getProjectFiles("container/lib");
20            const containerExec = getProjectFiles("container/exec");
21            const containerCompositeExec = getProjectFiles("container/compositeExec");
22            const containerConfig = TestFSWithWatch.getTsBuildProjectFile(project, "tsconfig.json");
23            const files = [libFile, ...containerLib, ...containerExec, ...containerCompositeExec, containerConfig];
24
25            it("does not error on container only project", () => {
26                const host = createHostWithSolutionBuild(files, [containerConfig.path]);
27
28                // Open external project for the folder
29                const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
30                const service = session.getProjectService();
31                service.openExternalProjects([{
32                    projectFileName: TestFSWithWatch.getTsBuildProjectFilePath(project, project),
33                    rootFiles: files.map(f => ({ fileName: f.path })),
34                    options: {}
35                }]);
36                files.forEach(f => {
37                    const args: protocol.FileRequestArgs = {
38                        file: f.path,
39                        projectFileName: endsWith(f.path, "tsconfig.json") ? f.path : undefined
40                    };
41                    session.executeCommandSeq<protocol.SyntacticDiagnosticsSyncRequest>({
42                        command: protocol.CommandTypes.SyntacticDiagnosticsSync,
43                        arguments: args
44                    });
45                    session.executeCommandSeq<protocol.SemanticDiagnosticsSyncRequest>({
46                        command: protocol.CommandTypes.SemanticDiagnosticsSync,
47                        arguments: args
48                    });
49                });
50                const containerProject = service.configuredProjects.get(containerConfig.path)!;
51                session.executeCommandSeq<protocol.CompilerOptionsDiagnosticsRequest>({
52                    command: protocol.CommandTypes.CompilerOptionsDiagnosticsFull,
53                    arguments: { projectFileName: containerProject.projectName }
54                });
55                baselineTsserverLogs("projectReferences", `does not error on container only project`, session);
56            });
57
58            it("can successfully find references with --out options", () => {
59                const host = createHostWithSolutionBuild(files, [containerConfig.path]);
60                const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
61                openFilesForSession([containerCompositeExec[1]], session);
62                const myConstStart = protocolLocationFromSubstring(containerCompositeExec[1].content, "myConst");
63                session.executeCommandSeq<protocol.RenameRequest>({
64                    command: protocol.CommandTypes.Rename,
65                    arguments: { file: containerCompositeExec[1].path, ...myConstStart }
66                });
67
68                baselineTsserverLogs("projectReferences", `can successfully find references with out option`, session);
69            });
70
71            it("ancestor and project ref management", () => {
72                const tempFile: File = {
73                    path: `/user/username/projects/temp/temp.ts`,
74                    content: "let x = 10"
75                };
76                const host = createHostWithSolutionBuild(files.concat([tempFile]), [containerConfig.path]);
77                const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
78                openFilesForSession([containerCompositeExec[1]], session);
79                const service = session.getProjectService();
80
81                // Open temp file and verify all projects alive
82                openFilesForSession([tempFile], session);
83
84                // Ref projects are loaded after as part of this command
85                const locationOfMyConst = protocolLocationFromSubstring(containerCompositeExec[1].content, "myConst");
86                session.executeCommandSeq<protocol.RenameRequest>({
87                    command: protocol.CommandTypes.Rename,
88                    arguments: {
89                        file: containerCompositeExec[1].path,
90                        ...locationOfMyConst
91                    }
92                });
93
94                // Open temp file and verify all projects alive
95                service.closeClientFile(tempFile.path);
96                openFilesForSession([tempFile], session);
97
98                // Close all files and open temp file, only inferred project should be alive
99                service.closeClientFile(containerCompositeExec[1].path);
100                service.closeClientFile(tempFile.path);
101                openFilesForSession([tempFile], session);
102                baselineTsserverLogs("projectReferences", `ancestor and project ref management`, session);
103            });
104        });
105
106        describe("when root file is file from referenced project", () => {
107            function verify(disableSourceOfProjectReferenceRedirect: boolean) {
108                const projectLocation = `/user/username/projects/project`;
109                const commonConfig: File = {
110                    path: `${projectLocation}/src/common/tsconfig.json`,
111                    content: JSON.stringify({
112                        compilerOptions: {
113                            composite: true,
114                            declarationMap: true,
115                            outDir: "../../out",
116                            baseUrl: "..",
117                            disableSourceOfProjectReferenceRedirect
118                        },
119                        include: ["./**/*"]
120                    })
121                };
122                const keyboardTs: File = {
123                    path: `${projectLocation}/src/common/input/keyboard.ts`,
124                    content: `function bar() { return "just a random function so .d.ts location doesnt match"; }
125export function evaluateKeyboardEvent() { }`
126                };
127                const keyboardTestTs: File = {
128                    path: `${projectLocation}/src/common/input/keyboard.test.ts`,
129                    content: `import { evaluateKeyboardEvent } from 'common/input/keyboard';
130function testEvaluateKeyboardEvent() {
131    return evaluateKeyboardEvent();
132}
133`
134                };
135                const srcConfig: File = {
136                    path: `${projectLocation}/src/tsconfig.json`,
137                    content: JSON.stringify({
138                        compilerOptions: {
139                            composite: true,
140                            declarationMap: true,
141                            outDir: "../out",
142                            baseUrl: ".",
143                            paths: {
144                                "common/*": ["./common/*"],
145                            },
146                            tsBuildInfoFile: "../out/src.tsconfig.tsbuildinfo",
147                            disableSourceOfProjectReferenceRedirect
148                        },
149                        include: ["./**/*"],
150                        references: [
151                            { path: "./common" }
152                        ]
153                    })
154                };
155                const terminalTs: File = {
156                    path: `${projectLocation}/src/terminal.ts`,
157                    content: `import { evaluateKeyboardEvent } from 'common/input/keyboard';
158function foo() {
159    return evaluateKeyboardEvent();
160}
161`
162                };
163                const host = createHostWithSolutionBuild(
164                    [commonConfig, keyboardTs, keyboardTestTs, srcConfig, terminalTs, libFile],
165                    [srcConfig.path]
166                );
167                const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
168                openFilesForSession([keyboardTs, terminalTs], session);
169
170                const searchStr = "evaluateKeyboardEvent";
171                const importStr = `import { evaluateKeyboardEvent } from 'common/input/keyboard';`;
172                const result = session.executeCommandSeq<protocol.ReferencesRequest>({
173                    command: protocol.CommandTypes.References,
174                    arguments: protocolFileLocationFromSubstring(keyboardTs, searchStr)
175                }).response as protocol.ReferencesResponseBody;
176                assert.deepEqual(result, {
177                    refs: [
178                        makeReferenceItem({
179                            file: keyboardTs,
180                            text: searchStr,
181                            contextText: `export function evaluateKeyboardEvent() { }`,
182                            isDefinition: true,
183                            lineText: `export function evaluateKeyboardEvent() { }`
184                        }),
185                        makeReferenceItem({
186                            file: keyboardTestTs,
187                            text: searchStr,
188                            contextText: importStr,
189                            isDefinition: false,
190                            isWriteAccess: true,
191                            lineText: importStr
192                        }),
193                        makeReferenceItem({
194                            file: keyboardTestTs,
195                            text: searchStr,
196                            options: { index: 1 },
197                            isDefinition: false,
198                            lineText: `    return evaluateKeyboardEvent();`
199                        }),
200                        makeReferenceItem({
201                            file: terminalTs,
202                            text: searchStr,
203                            contextText: importStr,
204                            isDefinition: false,
205                            isWriteAccess: true,
206                            lineText: importStr
207                        }),
208                        makeReferenceItem({
209                            file: terminalTs,
210                            text: searchStr,
211                            options: { index: 1 },
212                            isDefinition: false,
213                            lineText: `    return evaluateKeyboardEvent();`
214                        }),
215                    ],
216                    symbolName: searchStr,
217                    symbolStartOffset: protocolLocationFromSubstring(keyboardTs.content, searchStr).offset,
218                    symbolDisplayString: "function evaluateKeyboardEvent(): void"
219                });
220                baselineTsserverLogs("projectReferences", `root file is file from referenced project${disableSourceOfProjectReferenceRedirect ? " and using declaration maps" : ""}`, session);
221            }
222
223            it(`when using declaration file maps to navigate between projects`, () => {
224                verify(/*disableSourceOfProjectReferenceRedirect*/ true);
225            });
226            it(`when using original source files in the project`, () => {
227                verify(/*disableSourceOfProjectReferenceRedirect*/ false);
228            });
229        });
230
231        it("reusing d.ts files from composite and non composite projects", () => {
232            const configA: File = {
233                path: `${tscWatch.projectRoot}/compositea/tsconfig.json`,
234                content: JSON.stringify({
235                    compilerOptions: {
236                        composite: true,
237                        outDir: "../dist/",
238                        rootDir: "../",
239                        baseUrl: "../",
240                        paths: { "@ref/*": ["./dist/*"] }
241                    }
242                })
243            };
244            const aTs: File = {
245                path: `${tscWatch.projectRoot}/compositea/a.ts`,
246                content: `import { b } from "@ref/compositeb/b";`
247            };
248            const a2Ts: File = {
249                path: `${tscWatch.projectRoot}/compositea/a2.ts`,
250                content: `export const x = 10;`
251            };
252            const configB: File = {
253                path: `${tscWatch.projectRoot}/compositeb/tsconfig.json`,
254                content: configA.content
255            };
256            const bTs: File = {
257                path: `${tscWatch.projectRoot}/compositeb/b.ts`,
258                content: "export function b() {}"
259            };
260            const bDts: File = {
261                path: `${tscWatch.projectRoot}/dist/compositeb/b.d.ts`,
262                content: "export declare function b(): void;"
263            };
264            const configC: File = {
265                path: `${tscWatch.projectRoot}/compositec/tsconfig.json`,
266                content: JSON.stringify({
267                    compilerOptions: {
268                        composite: true,
269                        outDir: "../dist/",
270                        rootDir: "../",
271                        baseUrl: "../",
272                        paths: { "@ref/*": ["./*"] }
273                    },
274                    references: [{ path: "../compositeb" }]
275                })
276            };
277            const cTs: File = {
278                path: `${tscWatch.projectRoot}/compositec/c.ts`,
279                content: aTs.content
280            };
281            const files = [libFile, aTs, a2Ts, configA, bDts, bTs, configB, cTs, configC];
282            const host = createServerHost(files);
283            const service = createProjectService(host);
284            service.openClientFile(aTs.path);
285            service.checkNumberOfProjects({ configuredProjects: 1 });
286
287            // project A referencing b.d.ts without project reference
288            const projectA = service.configuredProjects.get(configA.path)!;
289            assert.isDefined(projectA);
290            checkProjectActualFiles(projectA, [aTs.path, a2Ts.path, bDts.path, libFile.path, configA.path]);
291
292            // reuses b.d.ts but sets the path and resolved path since projectC has project references
293            // as the real resolution was to b.ts
294            service.openClientFile(cTs.path);
295            service.checkNumberOfProjects({ configuredProjects: 2 });
296            const projectC = service.configuredProjects.get(configC.path)!;
297            checkProjectActualFiles(projectC, [cTs.path, bTs.path, libFile.path, configC.path]);
298
299            // Now new project for project A tries to reuse b but there is no filesByName mapping for b's source location
300            host.writeFile(a2Ts.path, `${a2Ts.content}export const y = 30;`);
301            assert.isTrue(projectA.dirty);
302            projectA.updateGraph();
303        });
304
305        describe("when references are monorepo like with symlinks", () => {
306            interface Packages {
307                bPackageJson: File;
308                aTest: File;
309                bFoo: File;
310                bBar: File;
311                bSymlink: SymLink;
312            }
313            function verifySymlinkScenario(scenario: string, packages: () => Packages) {
314                describe(`${scenario}: when solution is not built`, () => {
315                    it("with preserveSymlinks turned off", () => {
316                        verifySession(scenario, packages(), /*alreadyBuilt*/ false, {});
317                    });
318
319                    it("with preserveSymlinks turned on", () => {
320                        verifySession(scenario, packages(), /*alreadyBuilt*/ false, { preserveSymlinks: true });
321                    });
322                });
323
324                describe(`${scenario}: when solution is already built`, () => {
325                    it("with preserveSymlinks turned off", () => {
326                        verifySession(scenario, packages(), /*alreadyBuilt*/ true, {});
327                    });
328
329                    it("with preserveSymlinks turned on", () => {
330                        verifySession(scenario, packages(), /*alreadyBuilt*/ true, { preserveSymlinks: true });
331                    });
332                });
333            }
334
335            function verifySession(scenario: string, { bPackageJson, aTest, bFoo, bBar, bSymlink }: Packages, alreadyBuilt: boolean, extraOptions: CompilerOptions) {
336                const aConfig = config("A", extraOptions, ["../B"]);
337                const bConfig = config("B", extraOptions);
338                const files = [libFile, bPackageJson, aConfig, bConfig, aTest, bFoo, bBar, bSymlink];
339                const host = alreadyBuilt ?
340                    createHostWithSolutionBuild(files, [aConfig.path]) :
341                    createServerHost(files);
342
343                // Create symlink in node module
344                const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
345                openFilesForSession([aTest], session);
346                verifyGetErrRequest({ session, host, files: [aTest] });
347                session.executeCommandSeq<protocol.UpdateOpenRequest>({
348                    command: protocol.CommandTypes.UpdateOpen,
349                    arguments: {
350                        changedFiles: [{
351                            fileName: aTest.path,
352                            textChanges: [{
353                                newText: "\n",
354                                start: { line: 5, offset: 1 },
355                                end: { line: 5, offset: 1 }
356                            }]
357                        }]
358                    }
359                });
360                verifyGetErrRequest({ session, host, files: [aTest] });
361                baselineTsserverLogs("projectReferences", `monorepo like with symlinks ${scenario} and solution is ${alreadyBuilt ? "built" : "not built"}${extraOptions.preserveSymlinks ? " with preserveSymlinks" : ""}`, session);
362            }
363
364            function config(packageName: string, extraOptions: CompilerOptions, references?: string[]): File {
365                return {
366                    path: `${tscWatch.projectRoot}/packages/${packageName}/tsconfig.json`,
367                    content: JSON.stringify({
368                        compilerOptions: {
369                            outDir: "lib",
370                            rootDir: "src",
371                            composite: true,
372                            ...extraOptions
373                        },
374                        include: ["src"],
375                        ...(references ? { references: references.map(path => ({ path })) } : {})
376                    })
377                };
378            }
379
380            function file(packageName: string, fileName: string, content: string): File {
381                return {
382                    path: `${tscWatch.projectRoot}/packages/${packageName}/src/${fileName}`,
383                    content
384                };
385            }
386
387            function verifyMonoRepoLike(scope = "") {
388                verifySymlinkScenario(`when packageJson has types field and has index.ts${scope ? " with scoped package" : ""}`, () => ({
389                    bPackageJson: {
390                        path: `${tscWatch.projectRoot}/packages/B/package.json`,
391                        content: JSON.stringify({
392                            main: "lib/index.js",
393                            types: "lib/index.d.ts"
394                        })
395                    },
396                    aTest: file("A", "index.ts", `import { foo } from '${scope}b';
397import { bar } from '${scope}b/lib/bar';
398foo();
399bar();
400`),
401                    bFoo: file("B", "index.ts", `export function foo() { }`),
402                    bBar: file("B", "bar.ts", `export function bar() { }`),
403                    bSymlink: {
404                        path: `${tscWatch.projectRoot}/node_modules/${scope}b`,
405                        symLink: `${tscWatch.projectRoot}/packages/B`
406                    }
407                }));
408
409                verifySymlinkScenario(`when referencing file from subFolder${scope ? " with scoped package" : ""}`, () => ({
410                    bPackageJson: {
411                        path: `${tscWatch.projectRoot}/packages/B/package.json`,
412                        content: "{}"
413                    },
414                    aTest: file("A", "test.ts", `import { foo } from '${scope}b/lib/foo';
415import { bar } from '${scope}b/lib/bar/foo';
416foo();
417bar();
418`),
419                    bFoo: file("B", "foo.ts", `export function foo() { }`),
420                    bBar: file("B", "bar/foo.ts", `export function bar() { }`),
421                    bSymlink: {
422                        path: `${tscWatch.projectRoot}/node_modules/${scope}b`,
423                        symLink: `${tscWatch.projectRoot}/packages/B`
424                    }
425                }));
426            }
427
428            describe("when package is not scoped", () => {
429                verifyMonoRepoLike();
430            });
431            describe("when package is scoped", () => {
432                verifyMonoRepoLike("@issue/");
433            });
434        });
435
436        it("when the referenced projects have allowJs and emitDeclarationOnly", () => {
437            const compositeConfig: File = {
438                path: `${tscWatch.projectRoot}/packages/emit-composite/tsconfig.json`,
439                content: JSON.stringify({
440                    compilerOptions: {
441                        composite: true,
442                        allowJs: true,
443                        emitDeclarationOnly: true,
444                        outDir: "lib",
445                        rootDir: "src"
446                    },
447                    include: ["src"]
448                })
449            };
450            const compositePackageJson: File = {
451                path: `${tscWatch.projectRoot}/packages/emit-composite/package.json`,
452                content: JSON.stringify({
453                    name: "emit-composite",
454                    version: "1.0.0",
455                    main: "src/index.js",
456                    typings: "lib/index.d.ts"
457                })
458            };
459            const compositeIndex: File = {
460                path: `${tscWatch.projectRoot}/packages/emit-composite/src/index.js`,
461                content: `const testModule = require('./testModule');
462module.exports = {
463    ...testModule
464}`
465            };
466            const compositeTestModule: File = {
467                path: `${tscWatch.projectRoot}/packages/emit-composite/src/testModule.js`,
468                content: `/**
469 * @param {string} arg
470 */
471 const testCompositeFunction = (arg) => {
472}
473module.exports = {
474    testCompositeFunction
475}`
476            };
477            const consumerConfig: File = {
478                path: `${tscWatch.projectRoot}/packages/consumer/tsconfig.json`,
479                content: JSON.stringify({
480                    include: ["src"],
481                    references: [{ path: "../emit-composite" }]
482                })
483            };
484            const consumerIndex: File = {
485                path: `${tscWatch.projectRoot}/packages/consumer/src/index.ts`,
486                content: `import { testCompositeFunction } from 'emit-composite';
487testCompositeFunction('why hello there');
488testCompositeFunction('why hello there', 42);`
489            };
490            const symlink: SymLink = {
491                path: `${tscWatch.projectRoot}/node_modules/emit-composite`,
492                symLink: `${tscWatch.projectRoot}/packages/emit-composite`
493            };
494            const host = createServerHost([libFile, compositeConfig, compositePackageJson, compositeIndex, compositeTestModule, consumerConfig, consumerIndex, symlink], { useCaseSensitiveFileNames: true });
495            const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
496            openFilesForSession([consumerIndex], session);
497            verifyGetErrRequest({ host, session, files: [consumerIndex] });
498            baselineTsserverLogs("projectReferences", `when the referenced projects have allowJs and emitDeclarationOnly`, session);
499        });
500
501        it("when finding local reference doesnt load ancestor/sibling projects", () => {
502            const solutionLocation = "/user/username/projects/solution";
503            const solution: File = {
504                path: `${solutionLocation}/tsconfig.json`,
505                content: JSON.stringify({
506                    files: [],
507                    include: [],
508                    references: [
509                        { path: "./compiler" },
510                        { path: "./services" },
511                    ]
512                })
513            };
514            const compilerConfig: File = {
515                path: `${solutionLocation}/compiler/tsconfig.json`,
516                content: JSON.stringify({
517                    compilerOptions: {
518                        composite: true,
519                        module: "none"
520                    },
521                    files: ["./types.ts", "./program.ts"]
522                })
523            };
524            const typesFile: File = {
525                path: `${solutionLocation}/compiler/types.ts`,
526                content: `
527                namespace ts {
528                    export interface Program {
529                        getSourceFiles(): string[];
530                    }
531                }`
532            };
533            const programFile: File = {
534                path: `${solutionLocation}/compiler/program.ts`,
535                content: `
536                namespace ts {
537                    export const program: Program = {
538                        getSourceFiles: () => [getSourceFile()]
539                    };
540                    function getSourceFile() { return "something"; }
541                }`
542            };
543            const servicesConfig: File = {
544                path: `${solutionLocation}/services/tsconfig.json`,
545                content: JSON.stringify({
546                    compilerOptions: {
547                        composite: true
548                    },
549                    files: ["./services.ts"],
550                    references: [
551                        { path: "../compiler" }
552                    ]
553                })
554            };
555            const servicesFile: File = {
556                path: `${solutionLocation}/services/services.ts`,
557                content: `
558                namespace ts {
559                    const result = program.getSourceFiles();
560                }`
561            };
562
563            const files = [libFile, solution, compilerConfig, typesFile, programFile, servicesConfig, servicesFile, libFile];
564            const host = createServerHost(files);
565            const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
566            openFilesForSession([programFile], session);
567
568            // Find all references for getSourceFile
569            // Shouldnt load more projects
570            session.executeCommandSeq<protocol.ReferencesRequest>({
571                command: protocol.CommandTypes.References,
572                arguments: protocolFileLocationFromSubstring(programFile, "getSourceFile", { index: 1 })
573            });
574
575            // Find all references for getSourceFiles
576            // Should load more projects
577            session.executeCommandSeq<protocol.ReferencesRequest>({
578                command: protocol.CommandTypes.References,
579                arguments: protocolFileLocationFromSubstring(programFile, "getSourceFiles")
580            });
581            baselineTsserverLogs("projectReferences", `finding local reference doesnt load ancestor/sibling projects`, session);
582        });
583
584        it("when finding references in overlapping projects", () => {
585            const solutionLocation = "/user/username/projects/solution";
586            const solutionConfig: File = {
587                path: `${solutionLocation}/tsconfig.json`,
588                content: JSON.stringify({
589                    files: [],
590                    include: [],
591                    references: [
592                        { path: "./a" },
593                        { path: "./b" },
594                        { path: "./c" },
595                        { path: "./d" },
596                    ]
597                })
598            };
599            const aConfig: File = {
600                path: `${solutionLocation}/a/tsconfig.json`,
601                content: JSON.stringify({
602                    compilerOptions: {
603                        composite: true,
604                        module: "none"
605                    },
606                    files: ["./index.ts"]
607                })
608            };
609            const aFile: File = {
610                path: `${solutionLocation}/a/index.ts`,
611                content: `
612                export interface I {
613                    M(): void;
614                }`
615            };
616
617            const bConfig: File = {
618                path: `${solutionLocation}/b/tsconfig.json`,
619                content: JSON.stringify({
620                    compilerOptions: {
621                        composite: true
622                    },
623                    files: ["./index.ts"],
624                    references: [
625                        { path: "../a" }
626                    ]
627                })
628            };
629            const bFile: File = {
630                path: `${solutionLocation}/b/index.ts`,
631                content: `
632                import { I } from "../a";
633
634                export class B implements I {
635                    M() {}
636                }`
637            };
638
639            const cConfig: File = {
640                path: `${solutionLocation}/c/tsconfig.json`,
641                content: JSON.stringify({
642                    compilerOptions: {
643                        composite: true
644                    },
645                    files: ["./index.ts"],
646                    references: [
647                        { path: "../b" }
648                    ]
649                })
650            };
651            const cFile: File = {
652                path: `${solutionLocation}/c/index.ts`,
653                content: `
654                import { I } from "../a";
655                import { B } from "../b";
656
657                export const C: I = new B();
658                `
659            };
660
661            const dConfig: File = {
662                path: `${solutionLocation}/d/tsconfig.json`,
663                content: JSON.stringify({
664                    compilerOptions: {
665                        composite: true
666                    },
667                    files: ["./index.ts"],
668                    references: [
669                        { path: "../c" }
670                    ]
671                })
672            };
673            const dFile: File = {
674                path: `${solutionLocation}/d/index.ts`,
675                content: `
676                import { I } from "../a";
677                import { C } from "../c";
678
679                export const D: I = C;
680                `
681            };
682
683            const files = [libFile, solutionConfig, aConfig, aFile, bConfig, bFile, cConfig, cFile, dConfig, dFile, libFile];
684            const host = createServerHost(files);
685            const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
686            openFilesForSession([bFile], session);
687
688            // The first search will trigger project loads
689            session.executeCommandSeq<protocol.ReferencesRequest>({
690                command: protocol.CommandTypes.References,
691                arguments: protocolFileLocationFromSubstring(bFile, "I", { index: 1 })
692            });
693
694            // The second search starts with the projects already loaded
695            // Formerly, this would search some projects multiple times
696            session.executeCommandSeq<protocol.ReferencesRequest>({
697                command: protocol.CommandTypes.References,
698                arguments: protocolFileLocationFromSubstring(bFile, "I", { index: 1 })
699            });
700
701            baselineTsserverLogs("projectReferences", `finding references in overlapping projects`, session);
702        });
703
704        describe("special handling of localness of the definitions for findAllRefs", () => {
705            function verify(scenario: string, definition: string, usage: string, referenceTerm: string) {
706                it(scenario, () => {
707                    const solutionLocation = "/user/username/projects/solution";
708                    const solution: File = {
709                        path: `${solutionLocation}/tsconfig.json`,
710                        content: JSON.stringify({
711                            files: [],
712                            references: [
713                                { path: "./api" },
714                                { path: "./app" },
715                            ]
716                        })
717                    };
718                    const apiConfig: File = {
719                        path: `${solutionLocation}/api/tsconfig.json`,
720                        content: JSON.stringify({
721                            compilerOptions: {
722                                composite: true,
723                                outDir: "dist",
724                                rootDir: "src",
725                            },
726                            include: ["src"],
727                            references: [{ path: "../shared" }]
728                        })
729                    };
730                    const apiFile: File = {
731                        path: `${solutionLocation}/api/src/server.ts`,
732                        content: `import * as shared from "../../shared/dist";
733${usage}`
734                    };
735                    const appConfig: File = {
736                        path: `${solutionLocation}/app/tsconfig.json`,
737                        content: apiConfig.content
738                    };
739                    const appFile: File = {
740                        path: `${solutionLocation}/app/src/app.ts`,
741                        content: apiFile.content
742                    };
743                    const sharedConfig: File = {
744                        path: `${solutionLocation}/shared/tsconfig.json`,
745                        content: JSON.stringify({
746                            compilerOptions: {
747                                composite: true,
748                                outDir: "dist",
749                                rootDir: "src",
750                            },
751                            include: ["src"]
752                        })
753                    };
754                    const sharedFile: File = {
755                        path: `${solutionLocation}/shared/src/index.ts`,
756                        content: definition
757                    };
758                    const host = createServerHost([libFile, solution, libFile, apiConfig, apiFile, appConfig, appFile, sharedConfig, sharedFile]);
759                    const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
760                    openFilesForSession([apiFile], session);
761
762                    // Find all references
763                    session.executeCommandSeq<protocol.ReferencesRequest>({
764                        command: protocol.CommandTypes.References,
765                        arguments: protocolFileLocationFromSubstring(apiFile, referenceTerm)
766                    });
767
768                    baselineTsserverLogs("projectReferences", `special handling of localness ${scenario}`, session);
769                });
770            }
771
772            verify(
773                "when using arrow function assignment",
774                `export const dog = () => { };`,
775                `shared.dog();`,
776                "dog"
777            );
778
779            verify(
780                "when using arrow function as object literal property types",
781                `export const foo = { bar: () => { } };`,
782                `shared.foo.bar();`,
783                "bar"
784            );
785
786            verify(
787                "when using object literal property",
788                `export const foo = {  baz: "BAZ" };`,
789                `shared.foo.baz;`,
790                "baz"
791            );
792
793            verify(
794                "when using method of class expression",
795                `export const foo = class { fly() {} };`,
796                `const instance = new shared.foo();
797instance.fly();`,
798                "fly"
799            );
800
801
802            verify(
803                // when using arrow function as object literal property is loaded through indirect assignment with original declaration local to project is treated as local
804                "when using arrow function as object literal property",
805                `const local = { bar: () => { } };
806export const foo = local;`,
807                `shared.foo.bar();`,
808                "bar"
809            );
810        });
811
812        it("when disableSolutionSearching is true, solution and siblings are not loaded", () => {
813            const solutionLocation = "/user/username/projects/solution";
814            const solution: File = {
815                path: `${solutionLocation}/tsconfig.json`,
816                content: JSON.stringify({
817                    files: [],
818                    include: [],
819                    references: [
820                        { path: "./compiler" },
821                        { path: "./services" },
822                    ]
823                })
824            };
825            const compilerConfig: File = {
826                path: `${solutionLocation}/compiler/tsconfig.json`,
827                content: JSON.stringify({
828                    compilerOptions: {
829                        composite: true,
830                        module: "none",
831                        disableSolutionSearching: true
832                    },
833                    files: ["./types.ts", "./program.ts"]
834                })
835            };
836            const typesFile: File = {
837                path: `${solutionLocation}/compiler/types.ts`,
838                content: `
839                namespace ts {
840                    export interface Program {
841                        getSourceFiles(): string[];
842                    }
843                }`
844            };
845            const programFile: File = {
846                path: `${solutionLocation}/compiler/program.ts`,
847                content: `
848                namespace ts {
849                    export const program: Program = {
850                        getSourceFiles: () => [getSourceFile()]
851                    };
852                    function getSourceFile() { return "something"; }
853                }`
854            };
855            const servicesConfig: File = {
856                path: `${solutionLocation}/services/tsconfig.json`,
857                content: JSON.stringify({
858                    compilerOptions: {
859                        composite: true
860                    },
861                    files: ["./services.ts"],
862                    references: [
863                        { path: "../compiler" }
864                    ]
865                })
866            };
867            const servicesFile: File = {
868                path: `${solutionLocation}/services/services.ts`,
869                content: `
870                namespace ts {
871                    const result = program.getSourceFiles();
872                }`
873            };
874
875            const files = [libFile, solution, compilerConfig, typesFile, programFile, servicesConfig, servicesFile, libFile];
876            const host = createServerHost(files);
877            const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
878            openFilesForSession([programFile], session);
879
880            // Find all references
881            // No new solutions/projects loaded
882            session.executeCommandSeq<protocol.ReferencesRequest>({
883                command: protocol.CommandTypes.References,
884                arguments: protocolFileLocationFromSubstring(programFile, "getSourceFiles")
885            });
886            baselineTsserverLogs("projectReferences", `with disableSolutionSearching solution and siblings are not loaded`, session);
887        });
888
889        describe("when default project is solution project", () => {
890            interface Setup {
891                scenario: string;
892                solutionOptions?: CompilerOptions;
893                solutionFiles?: string[];
894                configRefs: string[];
895                additionalFiles: readonly File[];
896            }
897            const main: File = {
898                path: `${tscWatch.projectRoot}/src/main.ts`,
899                content: `import { foo } from 'helpers/functions';
900export { foo };`
901            };
902            const helper: File = {
903                path: `${tscWatch.projectRoot}/src/helpers/functions.ts`,
904                content: `export const foo = 1;`
905            };
906            const mainDts: File = {
907                path: `${tscWatch.projectRoot}/target/src/main.d.ts`,
908                content: `import { foo } from 'helpers/functions';
909export { foo };
910//# sourceMappingURL=main.d.ts.map`
911            };
912            const mainDtsMap: File = {
913                path: `${tscWatch.projectRoot}/target/src/main.d.ts.map`,
914                content: `{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAExC,OAAO,EAAC,GAAG,EAAC,CAAC"}`
915            };
916            const helperDts: File = {
917                path: `${tscWatch.projectRoot}/target/src/helpers/functions.d.ts`,
918                content: `export declare const foo = 1;
919//# sourceMappingURL=functions.d.ts.map`
920            };
921            const helperDtsMap: File = {
922                path: `${tscWatch.projectRoot}/target/src/helpers/functions.d.ts.map`,
923                content: `{"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../../../src/helpers/functions.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,GAAG,IAAI,CAAC"}`
924            };
925            const tsconfigIndirect3: File = {
926                path: `${tscWatch.projectRoot}/indirect3/tsconfig.json`,
927                content: JSON.stringify({
928                    compilerOptions: {
929                        baseUrl: "../target/src/"
930                    },
931                })
932            };
933            const fileResolvingToMainDts: File = {
934                path: `${tscWatch.projectRoot}/indirect3/main.ts`,
935                content: `import { foo } from 'main';
936foo;
937export function bar() {}`
938            };
939            const tsconfigSrcPath = `${tscWatch.projectRoot}/tsconfig-src.json`;
940            const tsconfigPath = `${tscWatch.projectRoot}/tsconfig.json`;
941            const dummyFilePath = "/dummy/dummy.ts";
942            function setup({ solutionFiles, solutionOptions, configRefs, additionalFiles }: Setup) {
943                const tsconfigSrc: File = {
944                    path: tsconfigSrcPath,
945                    content: JSON.stringify({
946                        compilerOptions: {
947                            composite: true,
948                            outDir: "./target/",
949                            baseUrl: "./src/"
950                        },
951                        include: ["./src/**/*"]
952                    })
953                };
954                const tsconfig: File = {
955                    path: tsconfigPath,
956                    content: JSON.stringify({
957                        ... (solutionOptions ? { compilerOptions: solutionOptions } : {}),
958                        references: configRefs.map(path => ({ path })),
959                        files: solutionFiles || []
960                    })
961                };
962                const dummyFile: File = {
963                    path: dummyFilePath,
964                    content: "let a = 10;"
965                };
966                const host = createServerHost([
967                    tsconfigSrc, tsconfig, main, helper,
968                    libFile, dummyFile,
969                    mainDts, mainDtsMap, helperDts, helperDtsMap,
970                    tsconfigIndirect3, fileResolvingToMainDts,
971                    ...additionalFiles]);
972                const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
973                const service = session.getProjectService();
974                service.openClientFile(main.path);
975                return { session, service, host };
976            }
977
978            function verifySolutionScenario(input: Setup) {
979                const { session, service, host } = setup(input);
980
981                const info = service.getScriptInfoForPath(main.path as Path)!;
982                session.logger.startGroup();
983                session.logger.info(`getDefaultProject for ${main.path}: ${info.getDefaultProject().projectName}`);
984                session.logger.info(`findDefaultConfiguredProject for ${main.path}: ${service.findDefaultConfiguredProject(info)!.projectName}`);
985                session.logger.endGroup();
986
987                // Verify errors
988                verifyGetErrRequest({ session, host, files: [main] });
989
990                // Verify collection of script infos
991                service.openClientFile(dummyFilePath);
992
993                service.closeClientFile(main.path);
994                service.closeClientFile(dummyFilePath);
995                service.openClientFile(dummyFilePath);
996
997                service.openClientFile(main.path);
998                service.closeClientFile(dummyFilePath);
999                service.openClientFile(dummyFilePath);
1000
1001                // Verify Reload projects
1002                service.reloadProjects();
1003
1004                // Find all refs
1005                session.executeCommandSeq<protocol.ReferencesRequest>({
1006                    command: protocol.CommandTypes.References,
1007                    arguments: protocolFileLocationFromSubstring(main, "foo", { index: 1 })
1008                }).response as protocol.ReferencesResponseBody;
1009
1010                service.closeClientFile(main.path);
1011                service.closeClientFile(dummyFilePath);
1012
1013                // Verify when declaration map references the file
1014                service.openClientFile(fileResolvingToMainDts.path);
1015
1016                // Find all refs from dts include
1017                session.executeCommandSeq<protocol.ReferencesRequest>({
1018                    command: protocol.CommandTypes.References,
1019                    arguments: protocolFileLocationFromSubstring(fileResolvingToMainDts, "foo")
1020                }).response as protocol.ReferencesResponseBody;
1021                baselineTsserverLogs("projectReferences", input.scenario, session);
1022            }
1023
1024            function getIndirectProject(postfix: string, optionsToExtend?: CompilerOptions) {
1025                const tsconfigIndirect: File = {
1026                    path: `${tscWatch.projectRoot}/tsconfig-indirect${postfix}.json`,
1027                    content: JSON.stringify({
1028                        compilerOptions: {
1029                            composite: true,
1030                            outDir: "./target/",
1031                            baseUrl: "./src/",
1032                            ...optionsToExtend
1033                        },
1034                        files: [`./indirect${postfix}/main.ts`],
1035                        references: [{ path: "./tsconfig-src.json" }]
1036                    })
1037                };
1038                const indirect: File = {
1039                    path: `${tscWatch.projectRoot}/indirect${postfix}/main.ts`,
1040                    content: fileResolvingToMainDts.content
1041                };
1042                return { tsconfigIndirect, indirect };
1043            }
1044
1045            function verifyDisableReferencedProjectLoad(input: Setup) {
1046                const { session, service } = setup(input);
1047
1048                const info = service.getScriptInfoForPath(main.path as Path)!;
1049                session.logger.startGroup();
1050                session.logger.info(`getDefaultProject for ${main.path}: ${info.getDefaultProject().projectName}`);
1051                session.logger.info(`findDefaultConfiguredProject for ${main.path}: ${service.findDefaultConfiguredProject(info)?.projectName}`);
1052                session.logger.endGroup();
1053
1054                // Verify collection of script infos
1055                service.openClientFile(dummyFilePath);
1056
1057                service.closeClientFile(main.path);
1058                service.closeClientFile(dummyFilePath);
1059                service.openClientFile(dummyFilePath);
1060
1061                service.openClientFile(main.path);
1062
1063                // Verify Reload projects
1064                service.reloadProjects();
1065                baselineTsserverLogs("projectReferences", input.scenario, session);
1066            }
1067
1068            it("when project is directly referenced by solution", () => {
1069                verifySolutionScenario({
1070                    scenario: "project is directly referenced by solution",
1071                    configRefs: ["./tsconfig-src.json"],
1072                    additionalFiles: emptyArray,
1073                });
1074            });
1075
1076            it("when project is indirectly referenced by solution", () => {
1077                const { tsconfigIndirect, indirect } = getIndirectProject("1");
1078                const { tsconfigIndirect: tsconfigIndirect2, indirect: indirect2 } = getIndirectProject("2");
1079                verifySolutionScenario({
1080                    scenario: "project is indirectly referenced by solution",
1081                    configRefs: ["./tsconfig-indirect1.json", "./tsconfig-indirect2.json"],
1082                    additionalFiles: [tsconfigIndirect, indirect, tsconfigIndirect2, indirect2],
1083                });
1084            });
1085
1086            it("disables looking into the child project if disableReferencedProjectLoad is set", () => {
1087                verifyDisableReferencedProjectLoad({
1088                    scenario: "disables looking into the child project if disableReferencedProjectLoad is set",
1089                    solutionOptions: { disableReferencedProjectLoad: true },
1090                    configRefs: ["./tsconfig-src.json"],
1091                    additionalFiles: emptyArray,
1092                });
1093            });
1094
1095            it("disables looking into the child project if disableReferencedProjectLoad is set in indirect project", () => {
1096                const { tsconfigIndirect, indirect } = getIndirectProject("1", { disableReferencedProjectLoad: true });
1097                verifyDisableReferencedProjectLoad({
1098                    scenario: "disables looking into the child project if disableReferencedProjectLoad is set in indirect project",
1099                    configRefs: ["./tsconfig-indirect1.json"],
1100                    additionalFiles: [tsconfigIndirect, indirect],
1101                });
1102            });
1103
1104            it("disables looking into the child project if disableReferencedProjectLoad is set in first indirect project but not in another one", () => {
1105                const { tsconfigIndirect, indirect } = getIndirectProject("1", { disableReferencedProjectLoad: true });
1106                const { tsconfigIndirect: tsconfigIndirect2, indirect: indirect2 } = getIndirectProject("2");
1107                verifyDisableReferencedProjectLoad({
1108                    scenario: "disables looking into the child project if disableReferencedProjectLoad is set in first indirect project but not in another one",
1109                    configRefs: ["./tsconfig-indirect1.json", "./tsconfig-indirect2.json"],
1110                    additionalFiles: [tsconfigIndirect, indirect, tsconfigIndirect2, indirect2],
1111                });
1112            });
1113
1114            describe("when solution is project that contains its own files", () => {
1115                it("when the project found is not solution but references open file through project reference", () => {
1116                    const ownMain: File = {
1117                        path: `${tscWatch.projectRoot}/own/main.ts`,
1118                        content: fileResolvingToMainDts.content
1119                    };
1120                    verifySolutionScenario({
1121                        scenario: "solution with its own files and project found is not solution but references open file through project reference",
1122                        solutionFiles: [`./own/main.ts`],
1123                        solutionOptions: {
1124                            outDir: "./target/",
1125                            baseUrl: "./src/"
1126                        },
1127                        configRefs: ["./tsconfig-src.json"],
1128                        additionalFiles: [ownMain],
1129                    });
1130                });
1131
1132                it("when project is indirectly referenced by solution", () => {
1133                    const ownMain: File = {
1134                        path: `${tscWatch.projectRoot}/own/main.ts`,
1135                        content: `import { bar } from 'main';
1136bar;`
1137                    };
1138                    const { tsconfigIndirect, indirect } = getIndirectProject("1");
1139                    const { tsconfigIndirect: tsconfigIndirect2, indirect: indirect2 } = getIndirectProject("2");
1140                    verifySolutionScenario({
1141                        scenario: "solution with its own files and project is indirectly referenced by solution",
1142                        solutionFiles: [`./own/main.ts`],
1143                        solutionOptions: {
1144                            outDir: "./target/",
1145                            baseUrl: "./indirect1/"
1146                        },
1147                        configRefs: ["./tsconfig-indirect1.json", "./tsconfig-indirect2.json"],
1148                        additionalFiles: [tsconfigIndirect, indirect, tsconfigIndirect2, indirect2, ownMain],
1149                    });
1150                });
1151
1152                it("disables looking into the child project if disableReferencedProjectLoad is set", () => {
1153                    const ownMain: File = {
1154                        path: `${tscWatch.projectRoot}/own/main.ts`,
1155                        content: fileResolvingToMainDts.content
1156                    };
1157                    verifyDisableReferencedProjectLoad({
1158                        scenario: "solution with its own files and disables looking into the child project if disableReferencedProjectLoad is set",
1159                        solutionFiles: [`./own/main.ts`],
1160                        solutionOptions: {
1161                            outDir: "./target/",
1162                            baseUrl: "./src/",
1163                            disableReferencedProjectLoad: true
1164                        },
1165                        configRefs: ["./tsconfig-src.json"],
1166                        additionalFiles: [ownMain],
1167                    });
1168                });
1169
1170                it("disables looking into the child project if disableReferencedProjectLoad is set in indirect project", () => {
1171                    const ownMain: File = {
1172                        path: `${tscWatch.projectRoot}/own/main.ts`,
1173                        content: `import { bar } from 'main';
1174bar;`
1175                    };
1176                    const { tsconfigIndirect, indirect } = getIndirectProject("1", { disableReferencedProjectLoad: true });
1177                    verifyDisableReferencedProjectLoad({
1178                        scenario: "solution with its own files and disables looking into the child project if disableReferencedProjectLoad is set in indirect project",
1179                        solutionFiles: [`./own/main.ts`],
1180                        solutionOptions: {
1181                            outDir: "./target/",
1182                            baseUrl: "./indirect1/",
1183                        },
1184                        configRefs: ["./tsconfig-indirect1.json"],
1185                        additionalFiles: [tsconfigIndirect, indirect, ownMain],
1186                    });
1187                });
1188
1189                it("disables looking into the child project if disableReferencedProjectLoad is set in first indirect project but not in another one", () => {
1190                    const ownMain: File = {
1191                        path: `${tscWatch.projectRoot}/own/main.ts`,
1192                        content: `import { bar } from 'main';
1193bar;`
1194                    };
1195                    const { tsconfigIndirect, indirect } = getIndirectProject("1", { disableReferencedProjectLoad: true });
1196                    const { tsconfigIndirect: tsconfigIndirect2, indirect: indirect2 } = getIndirectProject("2");
1197                    verifyDisableReferencedProjectLoad({
1198                        scenario: "solution with its own files and disables looking into the child project if disableReferencedProjectLoad is set in first indirect project but not in another one",
1199                        solutionFiles: [`./own/main.ts`],
1200                        solutionOptions: {
1201                            outDir: "./target/",
1202                            baseUrl: "./indirect1/",
1203                        },
1204                        configRefs: ["./tsconfig-indirect1.json", "./tsconfig-indirect2.json"],
1205                        additionalFiles: [tsconfigIndirect, indirect, tsconfigIndirect2, indirect2, ownMain],
1206                    });
1207                });
1208            });
1209        });
1210
1211        describe("when new file is added to the referenced project", () => {
1212            function setup(extendOptionsProject2?: CompilerOptions) {
1213                const config1: File = {
1214                    path: `${tscWatch.projectRoot}/projects/project1/tsconfig.json`,
1215                    content: JSON.stringify({
1216                        compilerOptions: {
1217                            module: "none",
1218                            composite: true
1219                        },
1220                        exclude: ["temp"]
1221                    })
1222                };
1223                const class1: File = {
1224                    path: `${tscWatch.projectRoot}/projects/project1/class1.ts`,
1225                    content: `class class1 {}`
1226                };
1227                const class1Dts: File = {
1228                    path: `${tscWatch.projectRoot}/projects/project1/class1.d.ts`,
1229                    content: `declare class class1 {}`
1230                };
1231                const config2: File = {
1232                    path: `${tscWatch.projectRoot}/projects/project2/tsconfig.json`,
1233                    content: JSON.stringify({
1234                        compilerOptions: {
1235                            module: "none",
1236                            composite: true,
1237                            ...(extendOptionsProject2 || {})
1238                        },
1239                        references: [
1240                            { path: "../project1" }
1241                        ]
1242                    })
1243                };
1244                const class2: File = {
1245                    path: `${tscWatch.projectRoot}/projects/project2/class2.ts`,
1246                    content: `class class2 {}`
1247                };
1248                const host = createServerHost([config1, class1, class1Dts, config2, class2, libFile]);
1249                const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
1250                openFilesForSession([class2], session);
1251                return { host, session, class1 };
1252            }
1253
1254            it("when referenced project is not open", () => {
1255                const { host, session } = setup();
1256
1257                // Add new class to referenced project
1258                const class3 = `${tscWatch.projectRoot}/projects/project1/class3.ts`;
1259                host.writeFile(class3, `class class3 {}`);
1260                host.checkTimeoutQueueLengthAndRun(2);
1261
1262                // Add excluded file to referenced project
1263                host.ensureFileOrFolder({ path: `${tscWatch.projectRoot}/projects/project1/temp/file.d.ts`, content: `declare class file {}` });
1264                host.checkTimeoutQueueLengthAndRun(0);
1265
1266                // Add output from new class to referenced project
1267                const class3Dts = `${tscWatch.projectRoot}/projects/project1/class3.d.ts`;
1268                host.writeFile(class3Dts, `declare class class3 {}`);
1269                host.checkTimeoutQueueLengthAndRun(0);
1270                baselineTsserverLogs("projectReferences", `new file is added to the referenced project when referenced project is not open`, session);
1271            });
1272
1273            it("when referenced project is open", () => {
1274                const { host, session, class1 } = setup();
1275                openFilesForSession([class1], session);
1276
1277                // Add new class to referenced project
1278                const class3 = `${tscWatch.projectRoot}/projects/project1/class3.ts`;
1279                host.writeFile(class3, `class class3 {}`);
1280                host.checkTimeoutQueueLengthAndRun(3);
1281                // Add excluded file to referenced project
1282                host.ensureFileOrFolder({ path: `${tscWatch.projectRoot}/projects/project1/temp/file.d.ts`, content: `declare class file {}` });
1283                host.checkTimeoutQueueLengthAndRun(0);
1284                // Add output from new class to referenced project
1285                const class3Dts = `${tscWatch.projectRoot}/projects/project1/class3.d.ts`;
1286                host.writeFile(class3Dts, `declare class class3 {}`);
1287                host.checkTimeoutQueueLengthAndRun(0);
1288                baselineTsserverLogs("projectReferences", `new file is added to the referenced project when referenced project is open`, session);
1289            });
1290
1291            it("when referenced project is not open with disableSourceOfProjectReferenceRedirect", () => {
1292                const { host, session } = setup({ disableSourceOfProjectReferenceRedirect: true });
1293
1294                // Add new class to referenced project
1295                const class3 = `${tscWatch.projectRoot}/projects/project1/class3.ts`;
1296                host.writeFile(class3, `class class3 {}`);
1297                host.checkTimeoutQueueLengthAndRun(2);
1298                // Add output of new class to referenced project
1299                const class3Dts = `${tscWatch.projectRoot}/projects/project1/class3.d.ts`;
1300                host.writeFile(class3Dts, `declare class class3 {}`);
1301                host.checkTimeoutQueueLengthAndRun(2);
1302                // Add excluded file to referenced project
1303                host.ensureFileOrFolder({ path: `${tscWatch.projectRoot}/projects/project1/temp/file.d.ts`, content: `declare class file {}` });
1304                host.checkTimeoutQueueLengthAndRun(0);
1305                // Delete output from new class to referenced project
1306                host.deleteFile(class3Dts);
1307                host.checkTimeoutQueueLengthAndRun(2);
1308                // Write back output of new class to referenced project
1309                host.writeFile(class3Dts, `declare class class3 {}`);
1310                host.checkTimeoutQueueLengthAndRun(2);
1311                baselineTsserverLogs("projectReferences", `new file is added to the referenced project when referenced project is not open with disableSourceOfProjectReferenceRedirect`, session);
1312            });
1313
1314            it("when referenced project is open with disableSourceOfProjectReferenceRedirect", () => {
1315                const { host, session, class1 } = setup({ disableSourceOfProjectReferenceRedirect: true });
1316                openFilesForSession([class1], session);
1317
1318                // Add new class to referenced project
1319                const class3 = `${tscWatch.projectRoot}/projects/project1/class3.ts`;
1320                host.writeFile(class3, `class class3 {}`);
1321                host.checkTimeoutQueueLengthAndRun(3);
1322                // Add output of new class to referenced project
1323                const class3Dts = `${tscWatch.projectRoot}/projects/project1/class3.d.ts`;
1324                host.writeFile(class3Dts, `declare class class3 {}`);
1325                host.checkTimeoutQueueLengthAndRun(2);
1326                // Add excluded file to referenced project
1327                host.ensureFileOrFolder({ path: `${tscWatch.projectRoot}/projects/project1/temp/file.d.ts`, content: `declare class file {}` });
1328                host.checkTimeoutQueueLengthAndRun(0);
1329                // Delete output from new class to referenced project
1330                host.deleteFile(class3Dts);
1331                host.checkTimeoutQueueLengthAndRun(2);
1332                // Write back output of new class to referenced project
1333                host.writeFile(class3Dts, `declare class class3 {}`);
1334                host.checkTimeoutQueueLengthAndRun(2);
1335                baselineTsserverLogs("projectReferences", `new file is added to the referenced project when referenced project is open with disableSourceOfProjectReferenceRedirect`, session);
1336            });
1337        });
1338
1339        describe("auto import with referenced project", () => {
1340            function verifyAutoImport(built: boolean, disableSourceOfProjectReferenceRedirect?: boolean) {
1341                const solnConfig: File = {
1342                    path: `${tscWatch.projectRoot}/tsconfig.json`,
1343                    content: JSON.stringify({
1344                        files: [],
1345                        references: [
1346                            { path: "shared/src/library" },
1347                            { path: "app/src/program" }
1348                        ]
1349                    })
1350                };
1351                const sharedConfig: File = {
1352                    path: `${tscWatch.projectRoot}/shared/src/library/tsconfig.json`,
1353                    content: JSON.stringify({
1354                        compilerOptions: {
1355                            composite: true,
1356                            outDir: "../../bld/library"
1357                        }
1358                    })
1359                };
1360                const sharedIndex: File = {
1361                    path: `${tscWatch.projectRoot}/shared/src/library/index.ts`,
1362                    content: `export function foo() {}`
1363                };
1364                const sharedPackage: File = {
1365                    path: `${tscWatch.projectRoot}/shared/package.json`,
1366                    content: JSON.stringify({
1367                        name: "shared",
1368                        version: "1.0.0",
1369                        main: "bld/library/index.js",
1370                        types: "bld/library/index.d.ts"
1371                    })
1372                };
1373                const appConfig: File = {
1374                    path: `${tscWatch.projectRoot}/app/src/program/tsconfig.json`,
1375                    content: JSON.stringify({
1376                        compilerOptions: {
1377                            composite: true,
1378                            outDir: "../../bld/program",
1379                            disableSourceOfProjectReferenceRedirect
1380                        },
1381                        references: [
1382                            { path: "../../../shared/src/library" }
1383                        ]
1384                    })
1385                };
1386                const appBar: File = {
1387                    path: `${tscWatch.projectRoot}/app/src/program/bar.ts`,
1388                    content: `import {foo} from "shared";`
1389                };
1390                const appIndex: File = {
1391                    path: `${tscWatch.projectRoot}/app/src/program/index.ts`,
1392                    content: `foo`
1393                };
1394                const sharedSymlink: SymLink = {
1395                    path: `${tscWatch.projectRoot}/node_modules/shared`,
1396                    symLink: `${tscWatch.projectRoot}/shared`
1397                };
1398                const files = [solnConfig, sharedConfig, sharedIndex, sharedPackage, appConfig, appBar, appIndex, sharedSymlink, libFile];
1399                const host = createServerHost(files);
1400                if (built) {
1401                    tscWatch.solutionBuildWithBaseline(host, [solnConfig.path]);
1402                    host.clearOutput();
1403                }
1404                const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
1405                openFilesForSession([appIndex], session);
1406                session.executeCommandSeq<protocol.CodeFixRequest>({
1407                    command: protocol.CommandTypes.GetCodeFixes,
1408                    arguments: {
1409                        file: appIndex.path,
1410                        startLine: 1,
1411                        startOffset: 1,
1412                        endLine: 1,
1413                        endOffset: 4,
1414                        errorCodes: [Diagnostics.Cannot_find_name_0.code],
1415                    }
1416                });
1417                baselineTsserverLogs("projectReferences", `auto import with referenced project${built ? " when built" : ""}${disableSourceOfProjectReferenceRedirect ? " with disableSourceOfProjectReferenceRedirect": ""}`, session);
1418            }
1419
1420            it("when project is built", () => {
1421                verifyAutoImport(/*built*/ true);
1422            });
1423            it("when project is not built", () => {
1424                verifyAutoImport(/*built*/ false);
1425            });
1426            it("when disableSourceOfProjectReferenceRedirect is true", () => {
1427                verifyAutoImport(/*built*/ true, /*disableSourceOfProjectReferenceRedirect*/ true);
1428            });
1429        });
1430
1431        it("when files from two projects are open and one project references", () => {
1432            function getPackageAndFile(packageName: string, references?: string[], optionsToExtend?: CompilerOptions): [file: File, config: File] {
1433                const file: File = {
1434                    path: `${tscWatch.projectRoot}/${packageName}/src/file1.ts`,
1435                    content: `export const ${packageName}Const = 10;`
1436                };
1437                const config: File = {
1438                    path: `${tscWatch.projectRoot}/${packageName}/tsconfig.json`,
1439                    content: JSON.stringify({
1440                        compilerOptions: { composite: true, ...optionsToExtend || {} },
1441                        references: references?.map(path => ({ path: `../${path}` }))
1442                    })
1443                };
1444                return [file, config];
1445            }
1446            const [mainFile, mainConfig] = getPackageAndFile("main", ["core", "indirect", "noCoreRef1", "indirectDisabledChildLoad1", "indirectDisabledChildLoad2", "refToCoreRef3", "indirectNoCoreRef"]);
1447            const [coreFile, coreConfig] = getPackageAndFile("core");
1448            const [noCoreRef1File, noCoreRef1Config] = getPackageAndFile("noCoreRef1");
1449            const [indirectFile, indirectConfig] = getPackageAndFile("indirect", ["coreRef1"]);
1450            const [coreRef1File, coreRef1Config] = getPackageAndFile("coreRef1", ["core"]);
1451            const [indirectDisabledChildLoad1File, indirectDisabledChildLoad1Config] = getPackageAndFile("indirectDisabledChildLoad1", ["coreRef2"], { disableReferencedProjectLoad: true });
1452            const [coreRef2File, coreRef2Config] = getPackageAndFile("coreRef2", ["core"]);
1453            const [indirectDisabledChildLoad2File, indirectDisabledChildLoad2Config] = getPackageAndFile("indirectDisabledChildLoad2", ["coreRef3"], { disableReferencedProjectLoad: true });
1454            const [coreRef3File, coreRef3Config] = getPackageAndFile("coreRef3", ["core"]);
1455            const [refToCoreRef3File, refToCoreRef3Config] = getPackageAndFile("refToCoreRef3", ["coreRef3"]);
1456            const [indirectNoCoreRefFile, indirectNoCoreRefConfig] = getPackageAndFile("indirectNoCoreRef", ["noCoreRef2"]);
1457            const [noCoreRef2File, noCoreRef2Config] = getPackageAndFile("noCoreRef2");
1458
1459            const host = createServerHost([
1460                libFile, mainFile, mainConfig, coreFile, coreConfig, noCoreRef1File, noCoreRef1Config,
1461                indirectFile, indirectConfig, coreRef1File, coreRef1Config,
1462                indirectDisabledChildLoad1File, indirectDisabledChildLoad1Config, coreRef2File, coreRef2Config,
1463                indirectDisabledChildLoad2File, indirectDisabledChildLoad2Config, coreRef3File, coreRef3Config,
1464                refToCoreRef3File, refToCoreRef3Config,
1465                indirectNoCoreRefFile, indirectNoCoreRefConfig, noCoreRef2File, noCoreRef2Config
1466            ], { useCaseSensitiveFileNames: true });
1467            const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
1468            openFilesForSession([mainFile, coreFile], session);
1469
1470            // Find all refs in coreFile
1471            session.executeCommandSeq<protocol.ReferencesRequest>({
1472                command: protocol.CommandTypes.References,
1473                arguments: protocolFileLocationFromSubstring(coreFile, `coreConst`)
1474            });
1475            baselineTsserverLogs("projectReferences", `when files from two projects are open and one project references`, session);
1476        });
1477
1478        describe("find refs to decl in other proj", () => {
1479            const indexA: File = {
1480                path: `${tscWatch.projectRoot}/a/index.ts`,
1481                content: `import { B } from "../b/lib";
1482
1483const b: B = new B();`
1484            };
1485
1486            const configB: File = {
1487                path: `${tscWatch.projectRoot}/b/tsconfig.json`,
1488                content: `{
1489"compilerOptions": {
1490    "declarationMap": true,
1491    "outDir": "lib",
1492    "composite": true
1493}
1494}`
1495            };
1496
1497            const indexB: File = {
1498                path: `${tscWatch.projectRoot}/b/index.ts`,
1499                content: `export class B {
1500    M() {}
1501}`
1502            };
1503
1504            const helperB: File = {
1505                path: `${tscWatch.projectRoot}/b/helper.ts`,
1506                content: `import { B } from ".";
1507
1508const b: B = new B();`
1509            };
1510
1511            const dtsB: File = {
1512                path: `${tscWatch.projectRoot}/b/lib/index.d.ts`,
1513                content: `export declare class B {
1514    M(): void;
1515}
1516//# sourceMappingURL=index.d.ts.map`
1517            };
1518
1519            const dtsMapB: File = {
1520                path: `${tscWatch.projectRoot}/b/lib/index.d.ts.map`,
1521                content: `{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,qBAAa,CAAC;IACV,CAAC;CACJ"}`
1522            };
1523
1524            function baselineDisableReferencedProjectLoad(
1525                projectAlreadyLoaded: boolean,
1526                disableReferencedProjectLoad: boolean,
1527                disableSourceOfProjectReferenceRedirect: boolean,
1528                dtsMapPresent: boolean) {
1529
1530                // Mangled to stay under windows path length limit
1531                const subScenario =
1532                    `when proj ${projectAlreadyLoaded ? "is" : "is not"} loaded` +
1533                    ` and refd proj loading is ${disableReferencedProjectLoad ? "disabled" : "enabled"}` +
1534                    ` and proj ref redirects are ${disableSourceOfProjectReferenceRedirect ? "disabled" : "enabled"}` +
1535                    ` and a decl map is ${dtsMapPresent ? "present" : "missing"}`;
1536                const compilerOptions: CompilerOptions = {
1537                    disableReferencedProjectLoad,
1538                    disableSourceOfProjectReferenceRedirect,
1539                    composite: true
1540                };
1541
1542                it(subScenario, () => {
1543                    const configA: File = {
1544                        path: `${tscWatch.projectRoot}/a/tsconfig.json`,
1545                        content: `{
1546        "compilerOptions": ${JSON.stringify(compilerOptions)},
1547        "references": [{ "path": "../b" }]
1548    }`
1549                    };
1550
1551                    const host = createServerHost([configA, indexA, configB, indexB, helperB, dtsB, ...(dtsMapPresent ? [dtsMapB] : [])]);
1552                    const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
1553                    openFilesForSession([indexA, ...(projectAlreadyLoaded ? [helperB] : [])], session);
1554
1555                    session.executeCommandSeq<protocol.ReferencesRequest>({
1556                        command: protocol.CommandTypes.References,
1557                        arguments: protocolFileLocationFromSubstring(indexA, `B`, { index: 1 })
1558                    });
1559                    baselineTsserverLogs("projectReferences", `find refs to decl in other proj ${subScenario}`, session);
1560                });
1561            }
1562
1563            /* eslint-disable local/boolean-trivia */
1564
1565            // Pre-loaded = A file from project B is already open when FAR is invoked
1566            // dRPL = Project A has disableReferencedProjectLoad
1567            // dSOPRR = Project A has disableSourceOfProjectReferenceRedirect
1568            // Map = The declaration map file b/lib/index.d.ts.map exists
1569            // B refs = files under directory b in which references are found (all scenarios find all references in a/index.ts)
1570
1571            //                                   Pre-loaded | dRPL   | dSOPRR | Map      | B state    | Notes        | B refs              | Notes
1572            //                                   -----------+--------+--------+----------+------------+--------------+---------------------+---------------------------------------------------
1573            baselineDisableReferencedProjectLoad(true,        true,    true,    true);  // Pre-loaded |              | index.ts, helper.ts | Via map and pre-loaded project
1574            baselineDisableReferencedProjectLoad(true,        true,    true,    false); // Pre-loaded |              | lib/index.d.ts      | Even though project is loaded
1575            baselineDisableReferencedProjectLoad(true,        true,    false,   true);  // Pre-loaded |              | index.ts, helper.ts |
1576            baselineDisableReferencedProjectLoad(true,        true,    false,   false); // Pre-loaded |              | index.ts, helper.ts |
1577            baselineDisableReferencedProjectLoad(true,        false,   true,    true);  // Pre-loaded |              | index.ts, helper.ts | Via map and pre-loaded project
1578            baselineDisableReferencedProjectLoad(true,        false,   true,    false); // Pre-loaded |              | lib/index.d.ts      | Even though project is loaded
1579            baselineDisableReferencedProjectLoad(true,        false,   false,   true);  // Pre-loaded |              | index.ts, helper.ts |
1580            baselineDisableReferencedProjectLoad(true,        false,   false,   false); // Pre-loaded |              | index.ts, helper.ts |
1581            baselineDisableReferencedProjectLoad(false,       true,    true,    true);  // Not loaded |              | lib/index.d.ts      | Even though map is present
1582            baselineDisableReferencedProjectLoad(false,       true,    true,    false); // Not loaded |              | lib/index.d.ts      |
1583            baselineDisableReferencedProjectLoad(false,       true,    false,   true);  // Not loaded |              | index.ts            | But not helper.ts, which is not referenced from a
1584            baselineDisableReferencedProjectLoad(false,       true,    false,   false); // Not loaded |              | index.ts            | But not helper.ts, which is not referenced from a
1585            baselineDisableReferencedProjectLoad(false,       false,   true,    true);  // Loaded     | Via map      | index.ts, helper.ts | Via map and newly loaded project
1586            baselineDisableReferencedProjectLoad(false,       false,   true,    false); // Not loaded |              | lib/index.d.ts      |
1587            baselineDisableReferencedProjectLoad(false,       false,   false,   true);  // Loaded     | Via redirect | index.ts, helper.ts |
1588            baselineDisableReferencedProjectLoad(false,       false,   false,   false); // Loaded     | Via redirect | index.ts, helper.ts |
1589
1590            /* eslint-enable local/boolean-trivia */
1591        });
1592    });
1593}
1594