• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts.projectSystem {
2    describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectSystem CachingFileSystemInformation", () => {
3        enum CalledMapsWithSingleArg {
4            fileExists = "fileExists",
5            directoryExists = "directoryExists",
6            getDirectories = "getDirectories",
7            readFile = "readFile"
8        }
9        enum CalledMapsWithFiveArgs {
10            readDirectory = "readDirectory"
11        }
12        type CalledMaps = CalledMapsWithSingleArg | CalledMapsWithFiveArgs;
13        type CalledWithFiveArgs = [readonly string[], readonly string[], readonly string[], number];
14        function createLoggerTrackingHostCalls(host: TestServerHost) {
15            const calledMaps: Record<CalledMapsWithSingleArg, MultiMap<string, true>> & Record<CalledMapsWithFiveArgs, MultiMap<string, CalledWithFiveArgs>> = {
16                fileExists: setCallsTrackingWithSingleArgFn(CalledMapsWithSingleArg.fileExists),
17                directoryExists: setCallsTrackingWithSingleArgFn(CalledMapsWithSingleArg.directoryExists),
18                getDirectories: setCallsTrackingWithSingleArgFn(CalledMapsWithSingleArg.getDirectories),
19                readFile: setCallsTrackingWithSingleArgFn(CalledMapsWithSingleArg.readFile),
20                readDirectory: setCallsTrackingWithFiveArgFn(CalledMapsWithFiveArgs.readDirectory)
21            };
22
23            return logCacheAndClear;
24
25            function setCallsTrackingWithSingleArgFn(prop: CalledMapsWithSingleArg) {
26                const calledMap = createMultiMap<true>();
27                const cb = (host as any)[prop].bind(host);
28                (host as any)[prop] = (f: string) => {
29                    calledMap.add(f, /*value*/ true);
30                    return cb(f);
31                };
32                return calledMap;
33            }
34
35            function setCallsTrackingWithFiveArgFn<U, V, W, X>(prop: CalledMapsWithFiveArgs) {
36                const calledMap = createMultiMap<[U, V, W, X]>();
37                const cb = (host as any)[prop].bind(host);
38                (host as any)[prop] = (f: string, arg1?: U, arg2?: V, arg3?: W, arg4?: X) => {
39                    calledMap.add(f, [arg1!, arg2!, arg3!, arg4!]); // TODO: GH#18217
40                    return cb(f, arg1, arg2, arg3, arg4);
41                };
42                return calledMap;
43            }
44
45            function logCacheEntry(logger: Logger, callback: CalledMaps) {
46                const result = arrayFrom<[string, (true | CalledWithFiveArgs)[]], { key: string, count: number }>(calledMaps[callback].entries(), ([key, arr]) => ({ key, count: arr.length }));
47                logger.info(`${callback}:: ${JSON.stringify(result)}`);
48                calledMaps[callback].clear();
49            }
50
51            function logCacheAndClear(logger: Logger) {
52                logCacheEntry(logger, CalledMapsWithSingleArg.fileExists);
53                logCacheEntry(logger, CalledMapsWithSingleArg.directoryExists);
54                logCacheEntry(logger, CalledMapsWithSingleArg.getDirectories);
55                logCacheEntry(logger, CalledMapsWithSingleArg.readFile);
56                logCacheEntry(logger, CalledMapsWithFiveArgs.readDirectory);
57            }
58        }
59
60        function logSemanticDiagnostics(projectService: server.ProjectService, project: server.Project, file: File) {
61            const diags = project.getLanguageService().getSemanticDiagnostics(file.path);
62            projectService.logger.info(`getSemanticDiagnostics:: ${file.path}:: ${diags.length}`);
63            diags.forEach(d => projectService.logger.info(formatDiagnostic(d, project)));
64        }
65
66        it("works using legacy resolution logic", () => {
67            let rootContent = `import {x} from "f1"`;
68            const root: File = {
69                path: "/c/d/f0.ts",
70                content: rootContent
71            };
72
73            const imported: File = {
74                path: "/c/f1.ts",
75                content: `foo()`
76            };
77
78            const host = createServerHost([root, imported]);
79            const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
80            projectService.setCompilerOptionsForInferredProjects({ module: ModuleKind.AMD, noLib: true });
81            projectService.openClientFile(root.path);
82            const project = projectService.inferredProjects[0];
83            const rootScriptInfo = project.getRootScriptInfos()[0];
84            assert.equal(rootScriptInfo.fileName, root.path);
85
86            // ensure that imported file was found
87            logSemanticDiagnostics(projectService, project, imported);
88
89            const logCacheAndClear = createLoggerTrackingHostCalls(host);
90
91            // trigger synchronization to make sure that import will be fetched from the cache
92            // ensure file has correct number of errors after edit
93            editContent(`import {x} from "f1";
94                 var x: string = 1;`);
95            logSemanticDiagnostics(projectService, project, imported);
96            logCacheAndClear(projectService.logger);
97
98            // trigger synchronization to make sure that the host will try to find 'f2' module on disk
99            editContent(`import {x} from "f2"`);
100            try {
101                // trigger synchronization to make sure that the host will try to find 'f2' module on disk
102                logSemanticDiagnostics(projectService, project, imported);
103            }
104            catch (e) {
105                projectService.logger.info(e.message);
106            }
107            logCacheAndClear(projectService.logger);
108
109            editContent(`import {x} from "f1"`);
110            logSemanticDiagnostics(projectService, project, imported);
111            logCacheAndClear(projectService.logger);
112
113            // setting compiler options discards module resolution cache
114            projectService.setCompilerOptionsForInferredProjects({ module: ModuleKind.AMD, noLib: true, target: ScriptTarget.ES5 });
115            logSemanticDiagnostics(projectService, project, imported);
116            logCacheAndClear(projectService.logger);
117            baselineTsserverLogs("cachingFileSystemInformation", "works using legacy resolution logic", projectService);
118
119            function editContent(newContent: string) {
120                rootScriptInfo.editContent(0, rootContent.length, newContent);
121                rootContent = newContent;
122            }
123        });
124
125        it("loads missing files from disk", () => {
126            const root: File = {
127                path: "/c/foo.ts",
128                content: `import {y} from "bar"`
129            };
130
131            const imported: File = {
132                path: "/c/bar.d.ts",
133                content: `export var y = 1`
134            };
135
136            const host = createServerHost([root]);
137            const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
138            projectService.setCompilerOptionsForInferredProjects({ module: ModuleKind.AMD, noLib: true });
139            const logCacheAndClear = createLoggerTrackingHostCalls(host);
140            projectService.openClientFile(root.path);
141            const project = projectService.inferredProjects[0];
142            const rootScriptInfo = project.getRootScriptInfos()[0];
143            assert.equal(rootScriptInfo.fileName, root.path);
144
145            logSemanticDiagnostics(projectService, project, root);
146            logCacheAndClear(projectService.logger);
147
148            host.writeFile(imported.path, imported.content);
149            host.runQueuedTimeoutCallbacks();
150            logSemanticDiagnostics(projectService, project, root);
151            logCacheAndClear(projectService.logger);
152            baselineTsserverLogs("cachingFileSystemInformation", "loads missing files from disk", projectService);
153        });
154
155        it("when calling goto definition of module", () => {
156            const clientFile: File = {
157                path: "/a/b/controllers/vessels/client.ts",
158                content: `
159                    import { Vessel } from '~/models/vessel';
160                    const v = new Vessel();
161                `
162            };
163            const anotherModuleFile: File = {
164                path: "/a/b/utils/db.ts",
165                content: "export class Bookshelf { }"
166            };
167            const moduleFile: File = {
168                path: "/a/b/models/vessel.ts",
169                content: `
170                    import { Bookshelf } from '~/utils/db';
171                    export class Vessel extends Bookshelf {}
172                `
173            };
174            const tsconfigFile: File = {
175                path: "/a/b/tsconfig.json",
176                content: JSON.stringify({
177                    compilerOptions: {
178                        target: "es6",
179                        module: "es6",
180                        baseUrl: "./",  // all paths are relative to the baseUrl
181                        paths: {
182                            "~/*": ["*"]   // resolve any `~/foo/bar` to `<baseUrl>/foo/bar`
183                        }
184                    },
185                    exclude: [
186                        "api",
187                        "build",
188                        "node_modules",
189                        "public",
190                        "seeds",
191                        "sql_updates",
192                        "tests.build"
193                    ]
194                })
195            };
196            const projectFiles = [clientFile, anotherModuleFile, moduleFile, tsconfigFile];
197            const host = createServerHost(projectFiles);
198            const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
199            openFilesForSession([clientFile], session);
200            const logCacheAndClear = createLoggerTrackingHostCalls(host);
201
202            // Get definitions shouldnt make host requests
203            const getDefinitionRequest = makeSessionRequest<protocol.FileLocationRequestArgs>(protocol.CommandTypes.Definition, {
204                file: clientFile.path,
205                position: clientFile.content.indexOf("/vessel") + 1,
206                line: undefined!, // TODO: GH#18217
207                offset: undefined! // TODO: GH#18217
208            });
209            session.executeCommand(getDefinitionRequest);
210            logCacheAndClear(session.logger);
211
212            // Open the file should call only file exists on module directory and use cached value for parental directory
213            openFilesForSession([moduleFile], session);
214            logCacheAndClear(session.logger);
215
216            baselineTsserverLogs("cachingFileSystemInformation", "when calling goto definition of module", session);
217        });
218
219        describe("WatchDirectories for config file with", () => {
220            function verifyWatchDirectoriesCaseSensitivity(useCaseSensitiveFileNames: boolean) {
221                it(`watchDirectories for config file with case ${useCaseSensitiveFileNames ? "" : "in"}sensitive file system`, () => {
222                    const frontendDir = "/Users/someuser/work/applications/frontend";
223                    const file1: File = {
224                        path: `${frontendDir}/src/app/utils/Analytic.ts`,
225                        content: "export class SomeClass { };"
226                    };
227                    const file2: File = {
228                        path: `${frontendDir}/src/app/redux/configureStore.ts`,
229                        content: "export class configureStore { }"
230                    };
231                    const file3: File = {
232                        path: `${frontendDir}/src/app/utils/Cookie.ts`,
233                        content: "export class Cookie { }"
234                    };
235                    const es2016LibFile: File = {
236                        path: "/a/lib/lib.es2016.full.d.ts",
237                        content: libFile.content
238                    };
239                    const typeRoots = ["types", "node_modules/@types"];
240                    const types = ["node", "jest"];
241                    const tsconfigFile: File = {
242                        path: `${frontendDir}/tsconfig.json`,
243                        content: JSON.stringify({
244                            compilerOptions: {
245                                strict: true,
246                                strictNullChecks: true,
247                                target: "es2016",
248                                module: "commonjs",
249                                moduleResolution: "node",
250                                sourceMap: true,
251                                noEmitOnError: true,
252                                experimentalDecorators: true,
253                                emitDecoratorMetadata: true,
254                                types,
255                                noUnusedLocals: true,
256                                outDir: "./compiled",
257                                typeRoots,
258                                baseUrl: ".",
259                                paths: {
260                                    "*": [
261                                        "types/*"
262                                    ]
263                                }
264                            },
265                            include: [
266                                "src/**/*"
267                            ],
268                            exclude: [
269                                "node_modules",
270                                "compiled"
271                            ]
272                        })
273                    };
274                    const projectFiles = [file1, file2, es2016LibFile, tsconfigFile];
275                    const host = createServerHost(projectFiles, { useCaseSensitiveFileNames });
276                    const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
277                    projectService.openClientFile(file1.path);
278
279                    const logCacheAndClear = createLoggerTrackingHostCalls(host);
280
281                    // Create file cookie.ts
282                    host.writeFile(file3.path, file3.content);
283                    host.runQueuedTimeoutCallbacks();
284                    logCacheAndClear(projectService.logger);
285
286                    projectService.openClientFile(file3.path);
287                    logCacheAndClear(projectService.logger);
288                    baselineTsserverLogs("cachingFileSystemInformation", `watchDirectories for config file with case ${useCaseSensitiveFileNames ? "" : "in"}sensitive file system`, projectService);
289                });
290            }
291            verifyWatchDirectoriesCaseSensitivity(/*useCaseSensitiveFileNames*/ false);
292            verifyWatchDirectoriesCaseSensitivity(/*useCaseSensitiveFileNames*/ true);
293        });
294
295        describe("Subfolder invalidations correctly include parent folder failed lookup locations", () => {
296            function runFailedLookupTest(resolution: "Node" | "Classic") {
297                const projectLocation = "/proj";
298                const file1: File = {
299                    path: `${projectLocation}/foo/boo/app.ts`,
300                    content: `import * as debug from "debug"`
301                };
302                const file2: File = {
303                    path: `${projectLocation}/foo/boo/moo/app.ts`,
304                    content: `import * as debug from "debug"`
305                };
306                const tsconfig: File = {
307                    path: `${projectLocation}/tsconfig.json`,
308                    content: JSON.stringify({
309                        files: ["foo/boo/app.ts", "foo/boo/moo/app.ts"],
310                        moduleResolution: resolution
311                    })
312                };
313
314                const files = [file1, file2, tsconfig, libFile];
315                const host = createServerHost(files);
316                const service = createProjectService(host);
317                service.openClientFile(file1.path);
318
319                const project = service.configuredProjects.get(tsconfig.path)!;
320                checkProjectActualFiles(project, files.map(f => f.path));
321                assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(file1.path).map(diag => diag.messageText), ["Cannot find module 'debug' or its corresponding type declarations."]);
322                assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(file2.path).map(diag => diag.messageText), ["Cannot find module 'debug' or its corresponding type declarations."]);
323
324                const debugTypesFile: File = {
325                    path: `${projectLocation}/node_modules/debug/index.d.ts`,
326                    content: "export {}"
327                };
328                files.push(debugTypesFile);
329                host.writeFile(debugTypesFile.path, debugTypesFile.content);
330                host.runQueuedTimeoutCallbacks(); // Scheduled invalidation of resolutions
331                host.runQueuedTimeoutCallbacks(); // Actual update
332                checkProjectActualFiles(project, files.map(f => f.path));
333                assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(file1.path).map(diag => diag.messageText), []);
334                assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(file2.path).map(diag => diag.messageText), []);
335            }
336
337            it("Includes the parent folder FLLs in node module resolution mode", () => {
338                runFailedLookupTest("Node");
339            });
340            it("Includes the parent folder FLLs in classic module resolution mode", () => {
341                runFailedLookupTest("Classic");
342            });
343        });
344
345        describe("Verify npm install in directory with tsconfig file works when", () => {
346            function verifyNpmInstall(timeoutDuringPartialInstallation: boolean) {
347                const root = "/user/username/rootfolder/otherfolder";
348                const getRootedFileOrFolder = (fileOrFolder: File) => {
349                    fileOrFolder.path = root + fileOrFolder.path;
350                    return fileOrFolder;
351                };
352                const app: File = getRootedFileOrFolder({
353                    path: "/a/b/app.ts",
354                    content: "import _ from 'lodash';"
355                });
356                const tsconfigJson: File = getRootedFileOrFolder({
357                    path: "/a/b/tsconfig.json",
358                    content: '{ "compilerOptions": { } }'
359                });
360                const packageJson: File = getRootedFileOrFolder({
361                    path: "/a/b/package.json",
362                    content: `
363{
364  "name": "test",
365  "version": "1.0.0",
366  "description": "",
367  "main": "index.js",
368  "dependencies": {
369    "lodash",
370    "rxjs"
371  },
372  "devDependencies": {
373    "@types/lodash",
374    "typescript"
375  },
376  "scripts": {
377    "test": "echo \"Error: no test specified\" && exit 1"
378  },
379  "keywords": [],
380  "author": "",
381  "license": "ISC"
382}
383`
384                });
385                const host = createServerHost([app, libFile, tsconfigJson, packageJson]);
386                const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
387                projectService.setHostConfiguration({ preferences: { includePackageJsonAutoImports: "off" } });
388                projectService.openClientFile(app.path);
389
390                let npmInstallComplete = false;
391
392                // Simulate npm install
393                const filesAndFoldersToAdd: File[] = [
394                    { path: "/a/b/node_modules" },
395                    { path: "/a/b/node_modules/.staging/@types" },
396                    { path: "/a/b/node_modules/.staging/lodash-b0733faa" },
397                    { path: "/a/b/node_modules/.staging/@types/lodash-e56c4fe7" },
398                    { path: "/a/b/node_modules/.staging/symbol-observable-24bcbbff" },
399                    { path: "/a/b/node_modules/.staging/rxjs-22375c61" },
400                    { path: "/a/b/node_modules/.staging/typescript-8493ea5d" },
401                    { path: "/a/b/node_modules/.staging/symbol-observable-24bcbbff/package.json", content: "{\n  \"name\": \"symbol-observable\",\n  \"version\": \"1.0.4\",\n  \"description\": \"Symbol.observable ponyfill\",\n  \"license\": \"MIT\",\n  \"repository\": \"blesh/symbol-observable\",\n  \"author\": {\n    \"name\": \"Ben Lesh\",\n    \"email\": \"ben@benlesh.com\"\n  },\n  \"engines\": {\n    \"node\": \">=0.10.0\"\n  },\n  \"scripts\": {\n    \"test\": \"npm run build && mocha && tsc ./ts-test/test.ts && node ./ts-test/test.js && check-es3-syntax -p lib/ --kill\",\n    \"build\": \"babel es --out-dir lib\",\n    \"prepublish\": \"npm test\"\n  },\n  \"files\": [\n    \"" },
402                    { path: "/a/b/node_modules/.staging/lodash-b0733faa/package.json", content: "{\n  \"name\": \"lodash\",\n  \"version\": \"4.17.4\",\n  \"description\": \"Lodash modular utilities.\",\n  \"keywords\": \"modules, stdlib, util\",\n  \"homepage\": \"https://lodash.com/\",\n  \"repository\": \"lodash/lodash\",\n  \"icon\": \"https://lodash.com/icon.svg\",\n  \"license\": \"MIT\",\n  \"main\": \"lodash.js\",\n  \"author\": \"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)\",\n  \"contributors\": [\n    \"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)\",\n    \"Mathias Bynens <mathias@qiwi." },
403                    { path: "/a/b/node_modules/.staging/rxjs-22375c61/package.json", content: "{\n  \"name\": \"rxjs\",\n  \"version\": \"5.4.3\",\n  \"description\": \"Reactive Extensions for modern JavaScript\",\n  \"main\": \"Rx.js\",\n  \"config\": {\n    \"commitizen\": {\n      \"path\": \"cz-conventional-changelog\"\n    }\n  },\n  \"lint-staged\": {\n    \"*.@(js)\": [\n      \"eslint --fix\",\n      \"git add\"\n    ],\n    \"*.@(ts)\": [\n      \"eslint -c .eslintrc --ext .ts . --fix\",\n      \"git add\"\n    ]\n  },\n  \"scripts-info\": {\n    \"info\": \"List available script\",\n    \"build_all\": \"Build all packages (ES6, CJS, UMD) and generate packages\",\n    \"build_cjs\": \"Build CJS package with clean up existing build, copy source into dist\",\n    \"build_es6\": \"Build ES6 package with clean up existing build, copy source into dist\",\n    \"build_closure_core\": \"Minify Global core build using closure compiler\",\n    \"build_global\": \"Build Global package, then minify build\",\n    \"build_perf\": \"Build CJS & Global build, run macro performance test\",\n    \"build_test\": \"Build CJS package & test spec, execute mocha test runner\",\n    \"build_cover\": \"Run lint to current code, build CJS & test spec, execute test coverage\",\n    \"build_docs\": \"Build ES6 & global package, create documentation using it\",\n    \"build_spec\": \"Build test specs\",\n    \"check_circular_dependencies\": \"Check codebase has circular dependencies\",\n    \"clean_spec\": \"Clean up existing test spec build output\",\n    \"clean_dist_cjs\": \"Clean up existing CJS package output\",\n    \"clean_dist_es6\": \"Clean up existing ES6 package output\",\n    \"clean_dist_global\": \"Clean up existing Global package output\",\n    \"commit\": \"Run git commit wizard\",\n    \"compile_dist_cjs\": \"Compile codebase into CJS module\",\n    \"compile_module_es6\": \"Compile codebase into ES6\",\n    \"cover\": \"Execute test coverage\",\n    \"lint_perf\": \"Run lint against performance test suite\",\n    \"lint_spec\": \"Run lint against test spec\",\n    \"lint_src\": \"Run lint against source\",\n    \"lint\": \"Run lint against everything\",\n    \"perf\": \"Run macro performance benchmark\",\n    \"perf_micro\": \"Run micro performance benchmark\",\n    \"test_mocha\": \"Execute mocha test runner against existing test spec build\",\n    \"test_browser\": \"Execute mocha test runner on browser against existing test spec build\",\n    \"test\": \"Clean up existing test spec build, build test spec and execute mocha test runner\",\n    \"tests2png\": \"Generate marble diagram image from test spec\",\n    \"watch\": \"Watch codebase, trigger compile when source code changes\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git@github.com:ReactiveX/RxJS.git\"\n  },\n  \"keywords\": [\n    \"Rx\",\n    \"RxJS\",\n    \"ReactiveX\",\n    \"ReactiveExtensions\",\n    \"Streams\",\n    \"Observables\",\n    \"Observable\",\n    \"Stream\",\n    \"ES6\",\n    \"ES2015\"\n  ],\n  \"author\": \"Ben Lesh <ben@benlesh.com>\",\n  \"contributors\": [\n    {\n      \"name\": \"Ben Lesh\",\n      \"email\": \"ben@benlesh.com\"\n    },\n    {\n      \"name\": \"Paul Taylor\",\n      \"email\": \"paul.e.taylor@me.com\"\n    },\n    {\n      \"name\": \"Jeff Cross\",\n      \"email\": \"crossj@google.com\"\n    },\n    {\n      \"name\": \"Matthew Podwysocki\",\n      \"email\": \"matthewp@microsoft.com\"\n    },\n    {\n      \"name\": \"OJ Kwon\",\n      \"email\": \"kwon.ohjoong@gmail.com\"\n    },\n    {\n      \"name\": \"Andre Staltz\",\n      \"email\": \"andre@staltz.com\"\n    }\n  ],\n  \"license\": \"Apache-2.0\",\n  \"bugs\": {\n    \"url\": \"https://github.com/ReactiveX/RxJS/issues\"\n  },\n  \"homepage\": \"https://github.com/ReactiveX/RxJS\",\n  \"devDependencies\": {\n    \"babel-polyfill\": \"^6.23.0\",\n    \"benchmark\": \"^2.1.0\",\n    \"benchpress\": \"2.0.0-beta.1\",\n    \"chai\": \"^3.5.0\",\n    \"color\": \"^0.11.1\",\n    \"colors\": \"1.1.2\",\n    \"commitizen\": \"^2.8.6\",\n    \"coveralls\": \"^2.11.13\",\n    \"cz-conventional-changelog\": \"^1.2.0\",\n    \"danger\": \"^1.1.0\",\n    \"doctoc\": \"^1.0.0\",\n    \"escape-string-regexp\": \"^1.0.5 \",\n    \"esdoc\": \"^0.4.7\",\n    \"eslint\": \"^3.8.0\",\n    \"fs-extra\": \"^2.1.2\",\n    \"get-folder-size\": \"^1.0.0\",\n    \"glob\": \"^7.0.3\",\n    \"gm\": \"^1.22.0\",\n    \"google-closure-compiler-js\": \"^20170218.0.0\",\n    \"gzip-size\": \"^3.0.0\",\n    \"http-server\": \"^0.9.0\",\n    \"husky\": \"^0.13.3\",\n    \"lint-staged\": \"3.2.5\",\n    \"lodash\": \"^4.15.0\",\n    \"madge\": \"^1.4.3\",\n    \"markdown-doctest\": \"^0.9.1\",\n    \"minimist\": \"^1.2.0\",\n    \"mkdirp\": \"^0.5.1\",\n    \"mocha\": \"^3.0.2\",\n    \"mocha-in-sauce\": \"0.0.1\",\n    \"npm-run-all\": \"^4.0.2\",\n    \"npm-scripts-info\": \"^0.3.4\",\n    \"nyc\": \"^10.2.0\",\n    \"opn-cli\": \"^3.1.0\",\n    \"platform\": \"^1.3.1\",\n    \"promise\": \"^7.1.1\",\n    \"protractor\": \"^3.1.1\",\n    \"rollup\": \"0.36.3\",\n    \"rollup-plugin-inject\": \"^2.0.0\",\n    \"rollup-plugin-node-resolve\": \"^2.0.0\",\n    \"rx\": \"latest\",\n    \"rxjs\": \"latest\",\n    \"shx\": \"^0.2.2\",\n    \"sinon\": \"^2.1.0\",\n    \"sinon-chai\": \"^2.9.0\",\n    \"source-map-support\": \"^0.4.0\",\n    \"tslib\": \"^1.5.0\",\n    \"eslint\": \"^4.4.2\",\n    \"typescript\": \"~2.0.6\",\n    \"typings\": \"^2.0.0\",\n    \"validate-commit-msg\": \"^2.14.0\",\n    \"watch\": \"^1.0.1\",\n    \"webpack\": \"^1.13.1\",\n    \"xmlhttprequest\": \"1.8.0\"\n  },\n  \"engines\": {\n    \"npm\": \">=2.0.0\"\n  },\n  \"typings\": \"Rx.d.ts\",\n  \"dependencies\": {\n    \"symbol-observable\": \"^1.0.1\"\n  }\n}" },
404                    { path: "/a/b/node_modules/.staging/typescript-8493ea5d/package.json", content: "{\n    \"name\": \"typescript\",\n    \"author\": \"Microsoft Corp.\",\n    \"homepage\": \"http://typescriptlang.org/\",\n    \"version\": \"2.4.2\",\n    \"license\": \"Apache-2.0\",\n    \"description\": \"TypeScript is a language for application scale JavaScript development\",\n    \"keywords\": [\n        \"TypeScript\",\n        \"Microsoft\",\n        \"compiler\",\n        \"language\",\n        \"javascript\"\n    ],\n    \"bugs\": {\n        \"url\": \"https://github.com/Microsoft/TypeScript/issues\"\n    },\n    \"repository\": {\n        \"type\": \"git\",\n        \"url\": \"https://github.com/Microsoft/TypeScript.git\"\n    },\n    \"main\": \"./lib/typescript.js\",\n    \"typings\": \"./lib/typescript.d.ts\",\n    \"bin\": {\n        \"tsc\": \"./bin/tsc\",\n        \"tsserver\": \"./bin/tsserver\"\n    },\n    \"engines\": {\n        \"node\": \">=4.2.0\"\n    },\n    \"devDependencies\": {\n        \"@types/browserify\": \"latest\",\n        \"@types/chai\": \"latest\",\n        \"@types/convert-source-map\": \"latest\",\n        \"@types/del\": \"latest\",\n        \"@types/glob\": \"latest\",\n        \"@types/gulp\": \"latest\",\n        \"@types/gulp-concat\": \"latest\",\n        \"@types/gulp-help\": \"latest\",\n        \"@types/gulp-newer\": \"latest\",\n        \"@types/gulp-sourcemaps\": \"latest\",\n        \"@types/merge2\": \"latest\",\n        \"@types/minimatch\": \"latest\",\n        \"@types/minimist\": \"latest\",\n        \"@types/mkdirp\": \"latest\",\n        \"@types/mocha\": \"latest\",\n        \"@types/node\": \"latest\",\n        \"@types/q\": \"latest\",\n        \"@types/run-sequence\": \"latest\",\n        \"@types/through2\": \"latest\",\n        \"browserify\": \"latest\",\n        \"chai\": \"latest\",\n        \"convert-source-map\": \"latest\",\n        \"del\": \"latest\",\n        \"gulp\": \"latest\",\n        \"gulp-clone\": \"latest\",\n        \"gulp-concat\": \"latest\",\n        \"gulp-help\": \"latest\",\n        \"gulp-insert\": \"latest\",\n        \"gulp-newer\": \"latest\",\n        \"gulp-sourcemaps\": \"latest\",\n        \"gulp-typescript\": \"latest\",\n        \"into-stream\": \"latest\",\n        \"istanbul\": \"latest\",\n        \"jake\": \"latest\",\n        \"merge2\": \"latest\",\n        \"minimist\": \"latest\",\n        \"mkdirp\": \"latest\",\n        \"mocha\": \"latest\",\n        \"mocha-fivemat-progress-reporter\": \"latest\",\n        \"q\": \"latest\",\n        \"run-sequence\": \"latest\",\n        \"sorcery\": \"latest\",\n        \"through2\": \"latest\",\n        \"travis-fold\": \"latest\",\n        \"ts-node\": \"latest\",\n        \"eslint\": \"5.16.0\",\n        \"typescript\": \"^2.4\"\n    },\n    \"scripts\": {\n        \"pretest\": \"jake tests\",\n        \"test\": \"jake runtests-parallel\",\n        \"build\": \"npm run build:compiler && npm run build:tests\",\n        \"build:compiler\": \"jake local\",\n        \"build:tests\": \"jake tests\",\n        \"start\": \"node lib/tsc\",\n        \"clean\": \"jake clean\",\n        \"gulp\": \"gulp\",\n        \"jake\": \"jake\",\n        \"lint\": \"jake lint\",\n        \"setup-hooks\": \"node scripts/link-hooks.js\"\n    },\n    \"browser\": {\n        \"buffer\": false,\n        \"fs\": false,\n        \"os\": false,\n        \"path\": false\n    }\n}" },
405                    { path: "/a/b/node_modules/.staging/symbol-observable-24bcbbff/index.js", content: "module.exports = require('./lib/index');\n" },
406                    { path: "/a/b/node_modules/.staging/symbol-observable-24bcbbff/index.d.ts", content: "declare const observableSymbol: symbol;\nexport default observableSymbol;\n" },
407                    { path: "/a/b/node_modules/.staging/symbol-observable-24bcbbff/lib" },
408                    { path: "/a/b/node_modules/.staging/symbol-observable-24bcbbff/lib/index.js", content: "'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _ponyfill = require('./ponyfill');\n\nvar _ponyfill2 = _interopRequireDefault(_ponyfill);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }\n\nvar root; /* global window */\n\n\nif (typeof self !== 'undefined') {\n  root = self;\n} else if (typeof window !== 'undefined') {\n  root = window;\n} else if (typeof global !== 'undefined') {\n  root = global;\n} else if (typeof module !== 'undefined') {\n  root = module;\n} else {\n  root = Function('return this')();\n}\n\nvar result = (0, _ponyfill2['default'])(root);\nexports['default'] = result;" },
409                ].map(getRootedFileOrFolder);
410                verifyAfterPartialOrCompleteNpmInstall(2);
411
412                filesAndFoldersToAdd.push(...[
413                    { path: "/a/b/node_modules/.staging/typescript-8493ea5d/lib" },
414                    { path: "/a/b/node_modules/.staging/rxjs-22375c61/add/operator" },
415                    { path: "/a/b/node_modules/.staging/@types/lodash-e56c4fe7/package.json", content: "{\n    \"name\": \"@types/lodash\",\n    \"version\": \"4.14.74\",\n    \"description\": \"TypeScript definitions for Lo-Dash\",\n    \"license\": \"MIT\",\n    \"contributors\": [\n        {\n            \"name\": \"Brian Zengel\",\n            \"url\": \"https://github.com/bczengel\"\n        },\n        {\n            \"name\": \"Ilya Mochalov\",\n            \"url\": \"https://github.com/chrootsu\"\n        },\n        {\n            \"name\": \"Stepan Mikhaylyuk\",\n            \"url\": \"https://github.com/stepancar\"\n        },\n        {\n            \"name\": \"Eric L Anderson\",\n            \"url\": \"https://github.com/ericanderson\"\n        },\n        {\n            \"name\": \"AJ Richardson\",\n            \"url\": \"https://github.com/aj-r\"\n        },\n        {\n            \"name\": \"Junyoung Clare Jang\",\n            \"url\": \"https://github.com/ailrun\"\n        }\n    ],\n    \"main\": \"\",\n    \"repository\": {\n        \"type\": \"git\",\n        \"url\": \"https://www.github.com/DefinitelyTyped/DefinitelyTyped.git\"\n    },\n    \"scripts\": {},\n    \"dependencies\": {},\n    \"typesPublisherContentHash\": \"12af578ffaf8d86d2df37e591857906a86b983fa9258414326544a0fe6af0de8\",\n    \"typeScriptVersion\": \"2.2\"\n}" },
416                    { path: "/a/b/node_modules/.staging/lodash-b0733faa/index.js", content: "module.exports = require('./lodash');" },
417                    { path: "/a/b/node_modules/.staging/typescript-8493ea5d/package.json.3017591594", content: "" }
418                ].map(getRootedFileOrFolder));
419                // Since we added/removed in .staging no timeout
420                verifyAfterPartialOrCompleteNpmInstall(0);
421
422                // Remove file "/a/b/node_modules/.staging/typescript-8493ea5d/package.json.3017591594"
423                host.deleteFile(last(filesAndFoldersToAdd).path);
424                filesAndFoldersToAdd.length--;
425                verifyAfterPartialOrCompleteNpmInstall(0);
426
427                filesAndFoldersToAdd.push(...[
428                    { path: "/a/b/node_modules/.staging/rxjs-22375c61/bundles" },
429                    { path: "/a/b/node_modules/.staging/rxjs-22375c61/operator" },
430                    { path: "/a/b/node_modules/.staging/rxjs-22375c61/src/add/observable/dom" },
431                    { path: "/a/b/node_modules/.staging/@types/lodash-e56c4fe7/index.d.ts", content: "\n// Stub for lodash\nexport = _;\nexport as namespace _;\ndeclare var _: _.LoDashStatic;\ndeclare namespace _ {\n    interface LoDashStatic {\n        someProp: string;\n    }\n    class SomeClass {\n        someMethod(): void;\n    }\n}" }
432                ].map(getRootedFileOrFolder));
433                verifyAfterPartialOrCompleteNpmInstall(0);
434
435                filesAndFoldersToAdd.push(...[
436                    { path: "/a/b/node_modules/.staging/rxjs-22375c61/src/scheduler" },
437                    { path: "/a/b/node_modules/.staging/rxjs-22375c61/src/util" },
438                    { path: "/a/b/node_modules/.staging/rxjs-22375c61/symbol" },
439                    { path: "/a/b/node_modules/.staging/rxjs-22375c61/testing" },
440                    { path: "/a/b/node_modules/.staging/rxjs-22375c61/package.json.2252192041", content: "{\n  \"_args\": [\n    [\n      {\n        \"raw\": \"rxjs@^5.4.2\",\n        \"scope\": null,\n        \"escapedName\": \"rxjs\",\n        \"name\": \"rxjs\",\n        \"rawSpec\": \"^5.4.2\",\n        \"spec\": \">=5.4.2 <6.0.0\",\n        \"type\": \"range\"\n      },\n      \"C:\\\\Users\\\\shkamat\\\\Desktop\\\\app\"\n    ]\n  ],\n  \"_from\": \"rxjs@>=5.4.2 <6.0.0\",\n  \"_id\": \"rxjs@5.4.3\",\n  \"_inCache\": true,\n  \"_location\": \"/rxjs\",\n  \"_nodeVersion\": \"7.7.2\",\n  \"_npmOperationalInternal\": {\n    \"host\": \"s3://npm-registry-packages\",\n    \"tmp\": \"tmp/rxjs-5.4.3.tgz_1502407898166_0.6800217325799167\"\n  },\n  \"_npmUser\": {\n    \"name\": \"blesh\",\n    \"email\": \"ben@benlesh.com\"\n  },\n  \"_npmVersion\": \"5.3.0\",\n  \"_phantomChildren\": {},\n  \"_requested\": {\n    \"raw\": \"rxjs@^5.4.2\",\n    \"scope\": null,\n    \"escapedName\": \"rxjs\",\n    \"name\": \"rxjs\",\n    \"rawSpec\": \"^5.4.2\",\n    \"spec\": \">=5.4.2 <6.0.0\",\n    \"type\": \"range\"\n  },\n  \"_requiredBy\": [\n    \"/\"\n  ],\n  \"_resolved\": \"https://registry.npmjs.org/rxjs/-/rxjs-5.4.3.tgz\",\n  \"_shasum\": \"0758cddee6033d68e0fd53676f0f3596ce3d483f\",\n  \"_shrinkwrap\": null,\n  \"_spec\": \"rxjs@^5.4.2\",\n  \"_where\": \"C:\\\\Users\\\\shkamat\\\\Desktop\\\\app\",\n  \"author\": {\n    \"name\": \"Ben Lesh\",\n    \"email\": \"ben@benlesh.com\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/ReactiveX/RxJS/issues\"\n  },\n  \"config\": {\n    \"commitizen\": {\n      \"path\": \"cz-conventional-changelog\"\n    }\n  },\n  \"contributors\": [\n    {\n      \"name\": \"Ben Lesh\",\n      \"email\": \"ben@benlesh.com\"\n    },\n    {\n      \"name\": \"Paul Taylor\",\n      \"email\": \"paul.e.taylor@me.com\"\n    },\n    {\n      \"name\": \"Jeff Cross\",\n      \"email\": \"crossj@google.com\"\n    },\n    {\n      \"name\": \"Matthew Podwysocki\",\n      \"email\": \"matthewp@microsoft.com\"\n    },\n    {\n      \"name\": \"OJ Kwon\",\n      \"email\": \"kwon.ohjoong@gmail.com\"\n    },\n    {\n      \"name\": \"Andre Staltz\",\n      \"email\": \"andre@staltz.com\"\n    }\n  ],\n  \"dependencies\": {\n    \"symbol-observable\": \"^1.0.1\"\n  },\n  \"description\": \"Reactive Extensions for modern JavaScript\",\n  \"devDependencies\": {\n    \"babel-polyfill\": \"^6.23.0\",\n    \"benchmark\": \"^2.1.0\",\n    \"benchpress\": \"2.0.0-beta.1\",\n    \"chai\": \"^3.5.0\",\n    \"color\": \"^0.11.1\",\n    \"colors\": \"1.1.2\",\n    \"commitizen\": \"^2.8.6\",\n    \"coveralls\": \"^2.11.13\",\n    \"cz-conventional-changelog\": \"^1.2.0\",\n    \"danger\": \"^1.1.0\",\n    \"doctoc\": \"^1.0.0\",\n    \"escape-string-regexp\": \"^1.0.5 \",\n    \"esdoc\": \"^0.4.7\",\n    \"eslint\": \"^3.8.0\",\n    \"fs-extra\": \"^2.1.2\",\n    \"get-folder-size\": \"^1.0.0\",\n    \"glob\": \"^7.0.3\",\n    \"gm\": \"^1.22.0\",\n    \"google-closure-compiler-js\": \"^20170218.0.0\",\n    \"gzip-size\": \"^3.0.0\",\n    \"http-server\": \"^0.9.0\",\n    \"husky\": \"^0.13.3\",\n    \"lint-staged\": \"3.2.5\",\n    \"lodash\": \"^4.15.0\",\n    \"madge\": \"^1.4.3\",\n    \"markdown-doctest\": \"^0.9.1\",\n    \"minimist\": \"^1.2.0\",\n    \"mkdirp\": \"^0.5.1\",\n    \"mocha\": \"^3.0.2\",\n    \"mocha-in-sauce\": \"0.0.1\",\n    \"npm-run-all\": \"^4.0.2\",\n    \"npm-scripts-info\": \"^0.3.4\",\n    \"nyc\": \"^10.2.0\",\n    \"opn-cli\": \"^3.1.0\",\n    \"platform\": \"^1.3.1\",\n    \"promise\": \"^7.1.1\",\n    \"protractor\": \"^3.1.1\",\n    \"rollup\": \"0.36.3\",\n    \"rollup-plugin-inject\": \"^2.0.0\",\n    \"rollup-plugin-node-resolve\": \"^2.0.0\",\n    \"rx\": \"latest\",\n    \"rxjs\": \"latest\",\n    \"shx\": \"^0.2.2\",\n    \"sinon\": \"^2.1.0\",\n    \"sinon-chai\": \"^2.9.0\",\n    \"source-map-support\": \"^0.4.0\",\n    \"tslib\": \"^1.5.0\",\n    \"eslint\": \"^5.16.0\",\n    \"typescript\": \"~2.0.6\",\n    \"typings\": \"^2.0.0\",\n    \"validate-commit-msg\": \"^2.14.0\",\n    \"watch\": \"^1.0.1\",\n    \"webpack\": \"^1.13.1\",\n    \"xmlhttprequest\": \"1.8.0\"\n  },\n  \"directories\": {},\n  \"dist\": {\n    \"integrity\": \"sha512-fSNi+y+P9ss+EZuV0GcIIqPUK07DEaMRUtLJvdcvMyFjc9dizuDjere+A4V7JrLGnm9iCc+nagV/4QdMTkqC4A==\",\n    \"shasum\": \"0758cddee6033d68e0fd53676f0f3596ce3d483f\",\n    \"tarball\": \"https://registry.npmjs.org/rxjs/-/rxjs-5.4.3.tgz\"\n  },\n  \"engines\": {\n    \"npm\": \">=2.0.0\"\n  },\n  \"homepage\": \"https://github.com/ReactiveX/RxJS\",\n  \"keywords\": [\n    \"Rx\",\n    \"RxJS\",\n    \"ReactiveX\",\n    \"ReactiveExtensions\",\n    \"Streams\",\n    \"Observables\",\n    \"Observable\",\n    \"Stream\",\n    \"ES6\",\n    \"ES2015\"\n  ],\n  \"license\": \"Apache-2.0\",\n  \"lint-staged\": {\n    \"*.@(js)\": [\n      \"eslint --fix\",\n      \"git add\"\n    ],\n    \"*.@(ts)\": [\n      \"eslint -c .eslintrc --ext .ts . --fix\",\n      \"git add\"\n    ]\n  },\n  \"main\": \"Rx.js\",\n  \"maintainers\": [\n    {\n      \"name\": \"blesh\",\n      \"email\": \"ben@benlesh.com\"\n    }\n  ],\n  \"name\": \"rxjs\",\n  \"optionalDependencies\": {},\n  \"readme\": \"ERROR: No README data found!\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+ssh://git@github.com/ReactiveX/RxJS.git\"\n  },\n  \"scripts-info\": {\n    \"info\": \"List available script\",\n    \"build_all\": \"Build all packages (ES6, CJS, UMD) and generate packages\",\n    \"build_cjs\": \"Build CJS package with clean up existing build, copy source into dist\",\n    \"build_es6\": \"Build ES6 package with clean up existing build, copy source into dist\",\n    \"build_closure_core\": \"Minify Global core build using closure compiler\",\n    \"build_global\": \"Build Global package, then minify build\",\n    \"build_perf\": \"Build CJS & Global build, run macro performance test\",\n    \"build_test\": \"Build CJS package & test spec, execute mocha test runner\",\n    \"build_cover\": \"Run lint to current code, build CJS & test spec, execute test coverage\",\n    \"build_docs\": \"Build ES6 & global package, create documentation using it\",\n    \"build_spec\": \"Build test specs\",\n    \"check_circular_dependencies\": \"Check codebase has circular dependencies\",\n    \"clean_spec\": \"Clean up existing test spec build output\",\n    \"clean_dist_cjs\": \"Clean up existing CJS package output\",\n    \"clean_dist_es6\": \"Clean up existing ES6 package output\",\n    \"clean_dist_global\": \"Clean up existing Global package output\",\n    \"commit\": \"Run git commit wizard\",\n    \"compile_dist_cjs\": \"Compile codebase into CJS module\",\n    \"compile_module_es6\": \"Compile codebase into ES6\",\n    \"cover\": \"Execute test coverage\",\n    \"lint_perf\": \"Run lint against performance test suite\",\n    \"lint_spec\": \"Run lint against test spec\",\n    \"lint_src\": \"Run lint against source\",\n    \"lint\": \"Run lint against everything\",\n    \"perf\": \"Run macro performance benchmark\",\n    \"perf_micro\": \"Run micro performance benchmark\",\n    \"test_mocha\": \"Execute mocha test runner against existing test spec build\",\n    \"test_browser\": \"Execute mocha test runner on browser against existing test spec build\",\n    \"test\": \"Clean up existing test spec build, build test spec and execute mocha test runner\",\n    \"tests2png\": \"Generate marble diagram image from test spec\",\n    \"watch\": \"Watch codebase, trigger compile when source code changes\"\n  },\n  \"typings\": \"Rx.d.ts\",\n  \"version\": \"5.4.3\"\n}\n" }
441                ].map(getRootedFileOrFolder));
442                verifyAfterPartialOrCompleteNpmInstall(0);
443
444                // remove /a/b/node_modules/.staging/rxjs-22375c61/package.json.2252192041
445                host.deleteFile(last(filesAndFoldersToAdd).path);
446                filesAndFoldersToAdd.length--;
447                // and add few more folders/files
448                filesAndFoldersToAdd.push(...[
449                    { path: "/a/b/node_modules/symbol-observable" },
450                    { path: "/a/b/node_modules/@types" },
451                    { path: "/a/b/node_modules/@types/lodash" },
452                    { path: "/a/b/node_modules/lodash" },
453                    { path: "/a/b/node_modules/rxjs" },
454                    { path: "/a/b/node_modules/typescript" },
455                    { path: "/a/b/node_modules/.bin" }
456                ].map(getRootedFileOrFolder));
457                // From the type root update
458                verifyAfterPartialOrCompleteNpmInstall(2);
459
460                forEach(filesAndFoldersToAdd, f => {
461                    f.path = f.path
462                        .replace("/a/b/node_modules/.staging", "/a/b/node_modules")
463                        .replace(/[\-\.][\d\w][\d\w][\d\w][\d\w][\d\w][\d\w][\d\w][\d\w]/g, "");
464                });
465
466                host.deleteFolder(root + "/a/b/node_modules/.staging", /*recursive*/ true);
467                // npm installation complete, timeout after reload fs
468                npmInstallComplete = true;
469                verifyAfterPartialOrCompleteNpmInstall(2);
470
471                baselineTsserverLogs(
472                    "cachingFileSystemInformation",
473                    `npm install works when ${timeoutDuringPartialInstallation ? "timeout occurs inbetween installation" : "timeout occurs after installation"}`,
474                    projectService
475                );
476
477                function verifyAfterPartialOrCompleteNpmInstall(timeoutQueueLengthWhenRunningTimeouts: number) {
478                    filesAndFoldersToAdd.forEach(f => host.ensureFileOrFolder(f));
479                    if (npmInstallComplete || timeoutDuringPartialInstallation) {
480                        if (timeoutQueueLengthWhenRunningTimeouts) {
481                            // Expected project update
482                            host.checkTimeoutQueueLengthAndRun(timeoutQueueLengthWhenRunningTimeouts + 1); // Scheduled invalidation of resolutions
483                            host.runQueuedTimeoutCallbacks(); // Actual update
484                        }
485                        else {
486                            host.checkTimeoutQueueLengthAndRun(timeoutQueueLengthWhenRunningTimeouts);
487                        }
488                    }
489                    else {
490                        host.checkTimeoutQueueLength(3);
491                    }
492                }
493            }
494
495            it("timeouts occur inbetween installation", () => {
496                verifyNpmInstall(/*timeoutDuringPartialInstallation*/ true);
497            });
498            it("timeout occurs after installation", () => {
499                verifyNpmInstall(/*timeoutDuringPartialInstallation*/ false);
500            });
501        });
502
503        it("when node_modules dont receive event for the @types file addition", () => {
504            const projectLocation = "/user/username/folder/myproject";
505            const app: File = {
506                path: `${projectLocation}/app.ts`,
507                content: `import * as debug from "debug"`
508            };
509            const tsconfig: File = {
510                path: `${projectLocation}/tsconfig.json`,
511                content: ""
512            };
513
514            const files = [app, tsconfig, libFile];
515            const host = createServerHost(files);
516            const service = createProjectService(host);
517            service.openClientFile(app.path);
518
519            const project = service.configuredProjects.get(tsconfig.path)!;
520            checkProjectActualFiles(project, files.map(f => f.path));
521            assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(app.path).map(diag => diag.messageText), ["Cannot find module 'debug' or its corresponding type declarations."]);
522
523            const debugTypesFile: File = {
524                path: `${projectLocation}/node_modules/@types/debug/index.d.ts`,
525                content: "export {}"
526            };
527            files.push(debugTypesFile);
528            // Do not invoke recursive directory watcher for anything other than node_module/@types
529            const invoker = host.invokeFsWatchesRecursiveCallbacks;
530            host.invokeFsWatchesRecursiveCallbacks = (fullPath, eventName, entryFullPath) => {
531                if (fullPath.endsWith("@types")) {
532                    invoker.call(host, fullPath, eventName, entryFullPath);
533                }
534            };
535            host.writeFile(debugTypesFile.path, debugTypesFile.content);
536            host.runQueuedTimeoutCallbacks();
537            checkProjectActualFiles(project, files.map(f => f.path));
538            assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(app.path).map(diag => diag.messageText), []);
539        });
540
541        it("when creating new file in symlinked folder", () => {
542            const module1: File = {
543                path: `${tscWatch.projectRoot}/client/folder1/module1.ts`,
544                content: `export class Module1Class { }`
545            };
546            const module2: File = {
547                path: `${tscWatch.projectRoot}/folder2/module2.ts`,
548                content: `import * as M from "folder1/module1";`
549            };
550            const symlink: SymLink = {
551                path: `${tscWatch.projectRoot}/client/linktofolder2`,
552                symLink: `${tscWatch.projectRoot}/folder2`,
553            };
554            const config: File = {
555                path: `${tscWatch.projectRoot}/tsconfig.json`,
556                content: JSON.stringify({
557                    compilerOptions: {
558                        baseUrl: "client",
559                        paths: { "*": ["*"] },
560                    },
561                    include: ["client/**/*", "folder2"]
562                })
563            };
564            const host = createServerHost([module1, module2, symlink, config, libFile]);
565            const service = createProjectService(host);
566            service.openClientFile(`${symlink.path}/module2.ts`);
567            checkNumberOfProjects(service, { configuredProjects: 1 });
568            const project = Debug.checkDefined(service.configuredProjects.get(config.path));
569            checkProjectActualFiles(project, [module1.path, `${symlink.path}/module2.ts`, config.path, libFile.path]);
570            host.writeFile(`${symlink.path}/module3.ts`, `import * as M from "folder1/module1";`);
571            host.runQueuedTimeoutCallbacks();
572            checkNumberOfProjects(service, { configuredProjects: 1 });
573            checkProjectActualFiles(project, [module1.path, `${symlink.path}/module2.ts`, config.path, libFile.path, `${symlink.path}/module3.ts`]);
574        });
575    });
576}
577