1namespace ts.projectSystem { 2 function getNumberOfWatchesInvokedForRecursiveWatches(recursiveWatchedDirs: string[], file: string) { 3 return countWhere(recursiveWatchedDirs, dir => file.length > dir.length && startsWith(file, dir) && file[dir.length] === directorySeparator); 4 } 5 6 describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectSystem CachingFileSystemInformation", () => { 7 enum CalledMapsWithSingleArg { 8 fileExists = "fileExists", 9 directoryExists = "directoryExists", 10 getDirectories = "getDirectories", 11 readFile = "readFile" 12 } 13 enum CalledMapsWithFiveArgs { 14 readDirectory = "readDirectory" 15 } 16 type CalledMaps = CalledMapsWithSingleArg | CalledMapsWithFiveArgs; 17 type CalledWithFiveArgs = [readonly string[], readonly string[], readonly string[], number]; 18 function createCallsTrackingHost(host: TestServerHost) { 19 const calledMaps: Record<CalledMapsWithSingleArg, MultiMap<string, true>> & Record<CalledMapsWithFiveArgs, MultiMap<string, CalledWithFiveArgs>> = { 20 fileExists: setCallsTrackingWithSingleArgFn(CalledMapsWithSingleArg.fileExists), 21 directoryExists: setCallsTrackingWithSingleArgFn(CalledMapsWithSingleArg.directoryExists), 22 getDirectories: setCallsTrackingWithSingleArgFn(CalledMapsWithSingleArg.getDirectories), 23 readFile: setCallsTrackingWithSingleArgFn(CalledMapsWithSingleArg.readFile), 24 readDirectory: setCallsTrackingWithFiveArgFn(CalledMapsWithFiveArgs.readDirectory) 25 }; 26 27 return { 28 verifyNoCall, 29 verifyCalledOnEachEntryNTimes, 30 verifyCalledOnEachEntry, 31 verifyNoHostCalls, 32 verifyNoHostCallsExceptFileExistsOnce, 33 verifyCalledOn, 34 clear 35 }; 36 37 function setCallsTrackingWithSingleArgFn(prop: CalledMapsWithSingleArg) { 38 const calledMap = createMultiMap<true>(); 39 const cb = (<any>host)[prop].bind(host); 40 (<any>host)[prop] = (f: string) => { 41 calledMap.add(f, /*value*/ true); 42 return cb(f); 43 }; 44 return calledMap; 45 } 46 47 function setCallsTrackingWithFiveArgFn<U, V, W, X>(prop: CalledMapsWithFiveArgs) { 48 const calledMap = createMultiMap<[U, V, W, X]>(); 49 const cb = (<any>host)[prop].bind(host); 50 (<any>host)[prop] = (f: string, arg1?: U, arg2?: V, arg3?: W, arg4?: X) => { 51 calledMap.add(f, [arg1!, arg2!, arg3!, arg4!]); // TODO: GH#18217 52 return cb(f, arg1, arg2, arg3, arg4); 53 }; 54 return calledMap; 55 } 56 57 function verifyCalledOn(callback: CalledMaps, name: string) { 58 const calledMap = calledMaps[callback]; 59 const result = calledMap.get(name); 60 assert.isTrue(result && !!result.length, `${callback} should be called with name: ${name}: ${arrayFrom(calledMap.keys())}`); 61 } 62 63 function verifyNoCall(callback: CalledMaps) { 64 const calledMap = calledMaps[callback]; 65 assert.equal(calledMap.size, 0, `${callback} shouldn't be called: ${arrayFrom(calledMap.keys())}`); 66 } 67 68 function verifyCalledOnEachEntry(callback: CalledMaps, expectedKeys: ESMap<string, number>) { 69 TestFSWithWatch.checkMap<true | CalledWithFiveArgs>(callback, calledMaps[callback], expectedKeys); 70 } 71 72 function verifyCalledOnEachEntryNTimes(callback: CalledMaps, expectedKeys: readonly string[], nTimes: number) { 73 TestFSWithWatch.checkMap<true | CalledWithFiveArgs>(callback, calledMaps[callback], expectedKeys, nTimes); 74 } 75 76 function verifyNoHostCalls() { 77 iterateOnCalledMaps(key => verifyNoCall(key)); 78 } 79 80 function verifyNoHostCallsExceptFileExistsOnce(expectedKeys: readonly string[]) { 81 verifyCalledOnEachEntryNTimes(CalledMapsWithSingleArg.fileExists, expectedKeys, 1); 82 verifyNoCall(CalledMapsWithSingleArg.directoryExists); 83 verifyNoCall(CalledMapsWithSingleArg.getDirectories); 84 verifyNoCall(CalledMapsWithSingleArg.readFile); 85 verifyNoCall(CalledMapsWithFiveArgs.readDirectory); 86 } 87 88 function clear() { 89 iterateOnCalledMaps(key => calledMaps[key].clear()); 90 } 91 92 function iterateOnCalledMaps(cb: (key: CalledMaps) => void) { 93 for (const key in CalledMapsWithSingleArg) { 94 cb(key as CalledMapsWithSingleArg); 95 } 96 for (const key in CalledMapsWithFiveArgs) { 97 cb(key as CalledMapsWithFiveArgs); 98 } 99 } 100 } 101 102 it("works using legacy resolution logic", () => { 103 let rootContent = `import {x} from "f1"`; 104 const root: File = { 105 path: "/c/d/f0.ts", 106 content: rootContent 107 }; 108 109 const imported: File = { 110 path: "/c/f1.ts", 111 content: `foo()` 112 }; 113 114 const host = createServerHost([root, imported]); 115 const projectService = createProjectService(host); 116 projectService.setCompilerOptionsForInferredProjects({ module: ModuleKind.AMD, noLib: true }); 117 projectService.openClientFile(root.path); 118 checkNumberOfProjects(projectService, { inferredProjects: 1 }); 119 const project = projectService.inferredProjects[0]; 120 const rootScriptInfo = project.getRootScriptInfos()[0]; 121 assert.equal(rootScriptInfo.fileName, root.path); 122 123 // ensure that imported file was found 124 verifyImportedDiagnostics(); 125 126 const callsTrackingHost = createCallsTrackingHost(host); 127 128 // trigger synchronization to make sure that import will be fetched from the cache 129 // ensure file has correct number of errors after edit 130 editContent(`import {x} from "f1"; 131 var x: string = 1;`); 132 verifyImportedDiagnostics(); 133 callsTrackingHost.verifyNoHostCalls(); 134 135 // trigger synchronization to make sure that the host will try to find 'f2' module on disk 136 editContent(`import {x} from "f2"`); 137 try { 138 // trigger synchronization to make sure that the host will try to find 'f2' module on disk 139 verifyImportedDiagnostics(); 140 assert.isTrue(false, `should not find file '${imported.path}'`); 141 } 142 catch (e) { 143 assert.isTrue(e.message.indexOf(`Could not find source file: '${imported.path}'.`) === 0, `Actual: ${e.message}`); 144 } 145 const f2Lookups = getLocationsForModuleLookup("f2"); 146 callsTrackingHost.verifyCalledOnEachEntryNTimes(CalledMapsWithSingleArg.fileExists, f2Lookups, 1); 147 const f2DirLookups = getLocationsForDirectoryLookup(); 148 callsTrackingHost.verifyCalledOnEachEntry(CalledMapsWithSingleArg.directoryExists, f2DirLookups); 149 callsTrackingHost.verifyNoCall(CalledMapsWithSingleArg.getDirectories); 150 callsTrackingHost.verifyNoCall(CalledMapsWithSingleArg.readFile); 151 callsTrackingHost.verifyNoCall(CalledMapsWithFiveArgs.readDirectory); 152 153 editContent(`import {x} from "f1"`); 154 verifyImportedDiagnostics(); 155 const f1Lookups = f2Lookups.map(s => s.replace("f2", "f1")); 156 f1Lookups.length = f1Lookups.indexOf(imported.path) + 1; 157 const f1DirLookups = ["/c/d", "/c", ...mapCombinedPathsInAncestor(getDirectoryPath(root.path), nodeModulesAtTypes, returnTrue)]; 158 vertifyF1Lookups(); 159 160 // setting compiler options discards module resolution cache 161 callsTrackingHost.clear(); 162 projectService.setCompilerOptionsForInferredProjects({ module: ModuleKind.AMD, noLib: true, target: ScriptTarget.ES5 }); 163 verifyImportedDiagnostics(); 164 vertifyF1Lookups(); 165 166 function vertifyF1Lookups() { 167 callsTrackingHost.verifyCalledOnEachEntryNTimes(CalledMapsWithSingleArg.fileExists, f1Lookups, 1); 168 callsTrackingHost.verifyCalledOnEachEntryNTimes(CalledMapsWithSingleArg.directoryExists, f1DirLookups, 1); 169 callsTrackingHost.verifyNoCall(CalledMapsWithSingleArg.getDirectories); 170 callsTrackingHost.verifyNoCall(CalledMapsWithSingleArg.readFile); 171 callsTrackingHost.verifyNoCall(CalledMapsWithFiveArgs.readDirectory); 172 } 173 174 function editContent(newContent: string) { 175 callsTrackingHost.clear(); 176 rootScriptInfo.editContent(0, rootContent.length, newContent); 177 rootContent = newContent; 178 } 179 180 function verifyImportedDiagnostics() { 181 const diags = project.getLanguageService().getSemanticDiagnostics(imported.path); 182 assert.equal(diags.length, 1); 183 const diag = diags[0]; 184 assert.equal(diag.code, Diagnostics.Cannot_find_name_0.code); 185 assert.equal(flattenDiagnosticMessageText(diag.messageText, "\n"), "Cannot find name 'foo'."); 186 } 187 188 function getLocationsForModuleLookup(module: string) { 189 const locations: string[] = []; 190 forEachAncestorDirectory(getDirectoryPath(root.path), ancestor => { 191 locations.push( 192 combinePaths(ancestor, `${module}.ts`), 193 combinePaths(ancestor, `${module}.tsx`), 194 combinePaths(ancestor, `${module}.d.ts`), 195 combinePaths(ancestor, `${module}.ets`) 196 ); 197 }); 198 forEachAncestorDirectory(getDirectoryPath(root.path), ancestor => { 199 locations.push( 200 combinePaths(ancestor, `${module}.js`), 201 combinePaths(ancestor, `${module}.jsx`) 202 ); 203 }); 204 return locations; 205 } 206 207 function getLocationsForDirectoryLookup() { 208 const result = new Map<string, number>(); 209 forEachAncestorDirectory(getDirectoryPath(root.path), ancestor => { 210 // To resolve modules 211 result.set(ancestor, 2); 212 // for type roots 213 result.set(combinePaths(ancestor, nodeModules), 1); 214 result.set(combinePaths(ancestor, nodeModulesAtTypes), 1); 215 }); 216 return result; 217 } 218 }); 219 220 it("loads missing files from disk", () => { 221 const root: File = { 222 path: "/c/foo.ts", 223 content: `import {y} from "bar"` 224 }; 225 226 const imported: File = { 227 path: "/c/bar.d.ts", 228 content: `export var y = 1` 229 }; 230 231 const host = createServerHost([root]); 232 const projectService = createProjectService(host); 233 projectService.setCompilerOptionsForInferredProjects({ module: ModuleKind.AMD, noLib: true }); 234 const callsTrackingHost = createCallsTrackingHost(host); 235 projectService.openClientFile(root.path); 236 checkNumberOfProjects(projectService, { inferredProjects: 1 }); 237 const project = projectService.inferredProjects[0]; 238 const rootScriptInfo = project.getRootScriptInfos()[0]; 239 assert.equal(rootScriptInfo.fileName, root.path); 240 241 let diags = project.getLanguageService().getSemanticDiagnostics(root.path); 242 assert.equal(diags.length, 1); 243 const diag = diags[0]; 244 assert.equal(diag.code, Diagnostics.Cannot_find_module_0_Did_you_mean_to_set_the_moduleResolution_option_to_node_or_to_add_aliases_to_the_paths_option.code); 245 assert.equal(flattenDiagnosticMessageText(diag.messageText, "\n"), "Cannot find module 'bar'. Did you mean to set the 'moduleResolution' option to 'node', or to add aliases to the 'paths' option?"); 246 callsTrackingHost.verifyCalledOn(CalledMapsWithSingleArg.fileExists, imported.path); 247 248 249 callsTrackingHost.clear(); 250 host.writeFile(imported.path, imported.content); 251 host.runQueuedTimeoutCallbacks(); 252 diags = project.getLanguageService().getSemanticDiagnostics(root.path); 253 assert.equal(diags.length, 0); 254 callsTrackingHost.verifyCalledOn(CalledMapsWithSingleArg.fileExists, imported.path); 255 }); 256 257 it("when calling goto definition of module", () => { 258 const clientFile: File = { 259 path: "/a/b/controllers/vessels/client.ts", 260 content: ` 261 import { Vessel } from '~/models/vessel'; 262 const v = new Vessel(); 263 ` 264 }; 265 const anotherModuleFile: File = { 266 path: "/a/b/utils/db.ts", 267 content: "export class Bookshelf { }" 268 }; 269 const moduleFile: File = { 270 path: "/a/b/models/vessel.ts", 271 content: ` 272 import { Bookshelf } from '~/utils/db'; 273 export class Vessel extends Bookshelf {} 274 ` 275 }; 276 const tsconfigFile: File = { 277 path: "/a/b/tsconfig.json", 278 content: JSON.stringify({ 279 compilerOptions: { 280 target: "es6", 281 module: "es6", 282 baseUrl: "./", // all paths are relative to the baseUrl 283 paths: { 284 "~/*": ["*"] // resolve any `~/foo/bar` to `<baseUrl>/foo/bar` 285 } 286 }, 287 exclude: [ 288 "api", 289 "build", 290 "node_modules", 291 "public", 292 "seeds", 293 "sql_updates", 294 "tests.build" 295 ] 296 }) 297 }; 298 const projectFiles = [clientFile, anotherModuleFile, moduleFile, tsconfigFile]; 299 const host = createServerHost(projectFiles); 300 const session = createSession(host); 301 const projectService = session.getProjectService(); 302 const { configFileName } = projectService.openClientFile(clientFile.path); 303 304 assert.isDefined(configFileName, `should find config`); 305 checkNumberOfConfiguredProjects(projectService, 1); 306 307 const project = projectService.configuredProjects.get(tsconfigFile.path)!; 308 checkProjectActualFiles(project, map(projectFiles, f => f.path)); 309 310 const callsTrackingHost = createCallsTrackingHost(host); 311 312 // Get definitions shouldnt make host requests 313 const getDefinitionRequest = makeSessionRequest<protocol.FileLocationRequestArgs>(protocol.CommandTypes.Definition, { 314 file: clientFile.path, 315 position: clientFile.content.indexOf("/vessel") + 1, 316 line: undefined!, // TODO: GH#18217 317 offset: undefined! // TODO: GH#18217 318 }); 319 const response = session.executeCommand(getDefinitionRequest).response as server.protocol.FileSpan[]; 320 assert.equal(response[0].file, moduleFile.path, "Should go to definition of vessel: response: " + JSON.stringify(response)); 321 callsTrackingHost.verifyNoHostCalls(); 322 323 // Open the file should call only file exists on module directory and use cached value for parental directory 324 const { configFileName: config2 } = projectService.openClientFile(moduleFile.path); 325 assert.equal(config2, configFileName); 326 callsTrackingHost.verifyNoHostCallsExceptFileExistsOnce(["/a/b/models/tsconfig.json", "/a/b/models/jsconfig.json"]); 327 328 checkNumberOfConfiguredProjects(projectService, 1); 329 assert.strictEqual(projectService.configuredProjects.get(tsconfigFile.path), project); 330 }); 331 332 describe("WatchDirectories for config file with", () => { 333 function verifyWatchDirectoriesCaseSensitivity(useCaseSensitiveFileNames: boolean) { 334 const frontendDir = "/Users/someuser/work/applications/frontend"; 335 const toCanonical: (s: string) => Path = useCaseSensitiveFileNames ? s => s as Path : s => s.toLowerCase() as Path; 336 const canonicalFrontendDir = toCanonical(frontendDir); 337 const file1: File = { 338 path: `${frontendDir}/src/app/utils/Analytic.ts`, 339 content: "export class SomeClass { };" 340 }; 341 const file2: File = { 342 path: `${frontendDir}/src/app/redux/configureStore.ts`, 343 content: "export class configureStore { }" 344 }; 345 const file3: File = { 346 path: `${frontendDir}/src/app/utils/Cookie.ts`, 347 content: "export class Cookie { }" 348 }; 349 const es2016LibFile: File = { 350 path: "/a/lib/lib.es2016.full.d.ts", 351 content: libFile.content 352 }; 353 const typeRoots = ["types", "node_modules/@types"]; 354 const types = ["node", "jest"]; 355 const tsconfigFile: File = { 356 path: `${frontendDir}/tsconfig.json`, 357 content: JSON.stringify({ 358 compilerOptions: { 359 strict: true, 360 strictNullChecks: true, 361 target: "es2016", 362 module: "commonjs", 363 moduleResolution: "node", 364 sourceMap: true, 365 noEmitOnError: true, 366 experimentalDecorators: true, 367 emitDecoratorMetadata: true, 368 types, 369 noUnusedLocals: true, 370 outDir: "./compiled", 371 typeRoots, 372 baseUrl: ".", 373 paths: { 374 "*": [ 375 "types/*" 376 ] 377 } 378 }, 379 include: [ 380 "src/**/*" 381 ], 382 exclude: [ 383 "node_modules", 384 "compiled" 385 ] 386 }) 387 }; 388 const projectFiles = [file1, file2, es2016LibFile, tsconfigFile]; 389 const host = createServerHost(projectFiles, { useCaseSensitiveFileNames }); 390 const projectService = createProjectService(host); 391 const canonicalConfigPath = toCanonical(tsconfigFile.path); 392 const { configFileName } = projectService.openClientFile(file1.path); 393 assert.equal(configFileName, tsconfigFile.path as server.NormalizedPath, `should find config`); 394 checkNumberOfConfiguredProjects(projectService, 1); 395 const watchingRecursiveDirectories = [`${canonicalFrontendDir}/src`, `${canonicalFrontendDir}/types`, `${canonicalFrontendDir}/node_modules`].concat(getNodeModuleDirectories(getDirectoryPath(canonicalFrontendDir))); 396 397 const project = projectService.configuredProjects.get(canonicalConfigPath)!; 398 verifyProjectAndWatchedDirectories(); 399 400 const callsTrackingHost = createCallsTrackingHost(host); 401 402 // Create file cookie.ts 403 projectFiles.push(file3); 404 host.writeFile(file3.path, file3.content); 405 host.runQueuedTimeoutCallbacks(); 406 407 const canonicalFile3Path = useCaseSensitiveFileNames ? file3.path : file3.path.toLocaleLowerCase(); 408 const numberOfTimesWatchInvoked = getNumberOfWatchesInvokedForRecursiveWatches(watchingRecursiveDirectories, canonicalFile3Path); 409 callsTrackingHost.verifyCalledOnEachEntryNTimes(CalledMapsWithSingleArg.fileExists, [canonicalFile3Path], numberOfTimesWatchInvoked); 410 callsTrackingHost.verifyCalledOnEachEntryNTimes(CalledMapsWithSingleArg.directoryExists, [canonicalFile3Path], numberOfTimesWatchInvoked); 411 callsTrackingHost.verifyNoCall(CalledMapsWithSingleArg.getDirectories); 412 callsTrackingHost.verifyCalledOnEachEntryNTimes(CalledMapsWithSingleArg.readFile, [file3.path], 1); 413 callsTrackingHost.verifyNoCall(CalledMapsWithFiveArgs.readDirectory); 414 415 checkNumberOfConfiguredProjects(projectService, 1); 416 assert.strictEqual(projectService.configuredProjects.get(canonicalConfigPath), project); 417 verifyProjectAndWatchedDirectories(); 418 419 callsTrackingHost.clear(); 420 421 const { configFileName: configFile2 } = projectService.openClientFile(file3.path); 422 assert.equal(configFile2, configFileName); 423 424 checkNumberOfConfiguredProjects(projectService, 1); 425 assert.strictEqual(projectService.configuredProjects.get(canonicalConfigPath), project); 426 verifyProjectAndWatchedDirectories(); 427 callsTrackingHost.verifyNoHostCalls(); 428 429 function getFilePathIfNotOpen(f: File) { 430 const path = toCanonical(f.path); 431 const info = projectService.getScriptInfoForPath(toCanonical(f.path)); 432 return info && info.isScriptOpen() ? undefined : path; 433 } 434 435 function verifyProjectAndWatchedDirectories() { 436 checkProjectActualFiles(project, map(projectFiles, f => f.path)); 437 checkWatchedFiles(host, mapDefined(projectFiles, getFilePathIfNotOpen)); 438 checkWatchedDirectories(host, watchingRecursiveDirectories, /*recursive*/ true); 439 checkWatchedDirectories(host, [], /*recursive*/ false); 440 } 441 } 442 443 it("case insensitive file system", () => { 444 verifyWatchDirectoriesCaseSensitivity(/*useCaseSensitiveFileNames*/ false); 445 }); 446 447 it("case sensitive file system", () => { 448 verifyWatchDirectoriesCaseSensitivity(/*useCaseSensitiveFileNames*/ true); 449 }); 450 }); 451 452 describe("Subfolder invalidations correctly include parent folder failed lookup locations", () => { 453 function runFailedLookupTest(resolution: "Node" | "Classic") { 454 const projectLocation = "/proj"; 455 const file1: File = { 456 path: `${projectLocation}/foo/boo/app.ts`, 457 content: `import * as debug from "debug"` 458 }; 459 const file2: File = { 460 path: `${projectLocation}/foo/boo/moo/app.ts`, 461 content: `import * as debug from "debug"` 462 }; 463 const tsconfig: File = { 464 path: `${projectLocation}/tsconfig.json`, 465 content: JSON.stringify({ 466 files: ["foo/boo/app.ts", "foo/boo/moo/app.ts"], 467 moduleResolution: resolution 468 }) 469 }; 470 471 const files = [file1, file2, tsconfig, libFile]; 472 const host = createServerHost(files); 473 const service = createProjectService(host); 474 service.openClientFile(file1.path); 475 476 const project = service.configuredProjects.get(tsconfig.path)!; 477 checkProjectActualFiles(project, files.map(f => f.path)); 478 assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(file1.path).map(diag => diag.messageText), ["Cannot find module 'debug' or its corresponding type declarations."]); 479 assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(file2.path).map(diag => diag.messageText), ["Cannot find module 'debug' or its corresponding type declarations."]); 480 481 const debugTypesFile: File = { 482 path: `${projectLocation}/node_modules/debug/index.d.ts`, 483 content: "export {}" 484 }; 485 files.push(debugTypesFile); 486 host.writeFile(debugTypesFile.path, debugTypesFile.content); 487 host.runQueuedTimeoutCallbacks(); // Scheduled invalidation of resolutions 488 host.runQueuedTimeoutCallbacks(); // Actual update 489 checkProjectActualFiles(project, files.map(f => f.path)); 490 assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(file1.path).map(diag => diag.messageText), []); 491 assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(file2.path).map(diag => diag.messageText), []); 492 } 493 494 it("Includes the parent folder FLLs in node module resolution mode", () => { 495 runFailedLookupTest("Node"); 496 }); 497 it("Includes the parent folder FLLs in classic module resolution mode", () => { 498 runFailedLookupTest("Classic"); 499 }); 500 }); 501 502 describe("Verify npm install in directory with tsconfig file works when", () => { 503 function verifyNpmInstall(timeoutDuringPartialInstallation: boolean) { 504 const root = "/user/username/rootfolder/otherfolder"; 505 const getRootedFileOrFolder = (fileOrFolder: File) => { 506 fileOrFolder.path = root + fileOrFolder.path; 507 return fileOrFolder; 508 }; 509 const app: File = getRootedFileOrFolder({ 510 path: "/a/b/app.ts", 511 content: "import _ from 'lodash';" 512 }); 513 const tsconfigJson: File = getRootedFileOrFolder({ 514 path: "/a/b/tsconfig.json", 515 content: '{ "compilerOptions": { } }' 516 }); 517 const packageJson: File = getRootedFileOrFolder({ 518 path: "/a/b/package.json", 519 content: ` 520{ 521 "name": "test", 522 "version": "1.0.0", 523 "description": "", 524 "main": "index.js", 525 "dependencies": { 526 "lodash", 527 "rxjs" 528 }, 529 "devDependencies": { 530 "@types/lodash", 531 "typescript" 532 }, 533 "scripts": { 534 "test": "echo \"Error: no test specified\" && exit 1" 535 }, 536 "keywords": [], 537 "author": "", 538 "license": "ISC" 539} 540` 541 }); 542 const appFolder = getDirectoryPath(app.path); 543 const projectFiles = [app, libFile, tsconfigJson]; 544 const typeRootDirectories = getTypeRootsFromLocation(getDirectoryPath(tsconfigJson.path)); 545 const otherFiles = [packageJson]; 546 const host = createServerHost(projectFiles.concat(otherFiles)); 547 const projectService = createProjectService(host); 548 projectService.setHostConfiguration({ preferences: { includePackageJsonAutoImports: "off" } }); 549 const { configFileName } = projectService.openClientFile(app.path); 550 assert.equal(configFileName, tsconfigJson.path as server.NormalizedPath, `should find config`); // TODO: GH#18217 551 const recursiveWatchedDirectories: string[] = [`${appFolder}`, `${appFolder}/node_modules`].concat(getNodeModuleDirectories(getDirectoryPath(appFolder))); 552 verifyProject(); 553 554 let npmInstallComplete = false; 555 556 // Simulate npm install 557 const filesAndFoldersToAdd: File[] = [ 558 { path: "/a/b/node_modules" }, 559 { path: "/a/b/node_modules/.staging/@types" }, 560 { path: "/a/b/node_modules/.staging/lodash-b0733faa" }, 561 { path: "/a/b/node_modules/.staging/@types/lodash-e56c4fe7" }, 562 { path: "/a/b/node_modules/.staging/symbol-observable-24bcbbff" }, 563 { path: "/a/b/node_modules/.staging/rxjs-22375c61" }, 564 { path: "/a/b/node_modules/.staging/typescript-8493ea5d" }, 565 { 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 \"" }, 566 { 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." }, 567 { 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}" }, 568 { 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}" }, 569 { path: "/a/b/node_modules/.staging/symbol-observable-24bcbbff/index.js", content: "module.exports = require('./lib/index');\n" }, 570 { path: "/a/b/node_modules/.staging/symbol-observable-24bcbbff/index.d.ts", content: "declare const observableSymbol: symbol;\nexport default observableSymbol;\n" }, 571 { path: "/a/b/node_modules/.staging/symbol-observable-24bcbbff/lib" }, 572 { 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;" }, 573 ].map(getRootedFileOrFolder); 574 verifyAfterPartialOrCompleteNpmInstall(2); 575 576 filesAndFoldersToAdd.push(...[ 577 { path: "/a/b/node_modules/.staging/typescript-8493ea5d/lib" }, 578 { path: "/a/b/node_modules/.staging/rxjs-22375c61/add/operator" }, 579 { 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}" }, 580 { path: "/a/b/node_modules/.staging/lodash-b0733faa/index.js", content: "module.exports = require('./lodash');" }, 581 { path: "/a/b/node_modules/.staging/typescript-8493ea5d/package.json.3017591594", content: "" } 582 ].map(getRootedFileOrFolder)); 583 // Since we added/removed in .staging no timeout 584 verifyAfterPartialOrCompleteNpmInstall(0); 585 586 // Remove file "/a/b/node_modules/.staging/typescript-8493ea5d/package.json.3017591594" 587 host.deleteFile(last(filesAndFoldersToAdd).path); 588 filesAndFoldersToAdd.length--; 589 verifyAfterPartialOrCompleteNpmInstall(0); 590 591 filesAndFoldersToAdd.push(...[ 592 { path: "/a/b/node_modules/.staging/rxjs-22375c61/bundles" }, 593 { path: "/a/b/node_modules/.staging/rxjs-22375c61/operator" }, 594 { path: "/a/b/node_modules/.staging/rxjs-22375c61/src/add/observable/dom" }, 595 { 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}" } 596 ].map(getRootedFileOrFolder)); 597 verifyAfterPartialOrCompleteNpmInstall(0); 598 599 filesAndFoldersToAdd.push(...[ 600 { path: "/a/b/node_modules/.staging/rxjs-22375c61/src/scheduler" }, 601 { path: "/a/b/node_modules/.staging/rxjs-22375c61/src/util" }, 602 { path: "/a/b/node_modules/.staging/rxjs-22375c61/symbol" }, 603 { path: "/a/b/node_modules/.staging/rxjs-22375c61/testing" }, 604 { 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" } 605 ].map(getRootedFileOrFolder)); 606 verifyAfterPartialOrCompleteNpmInstall(0); 607 608 // remove /a/b/node_modules/.staging/rxjs-22375c61/package.json.2252192041 609 host.deleteFile(last(filesAndFoldersToAdd).path); 610 filesAndFoldersToAdd.length--; 611 // and add few more folders/files 612 filesAndFoldersToAdd.push(...[ 613 { path: "/a/b/node_modules/symbol-observable" }, 614 { path: "/a/b/node_modules/@types" }, 615 { path: "/a/b/node_modules/@types/lodash" }, 616 { path: "/a/b/node_modules/lodash" }, 617 { path: "/a/b/node_modules/rxjs" }, 618 { path: "/a/b/node_modules/typescript" }, 619 { path: "/a/b/node_modules/.bin" } 620 ].map(getRootedFileOrFolder)); 621 // From the type root update 622 verifyAfterPartialOrCompleteNpmInstall(2); 623 624 forEach(filesAndFoldersToAdd, f => { 625 f.path = f.path 626 .replace("/a/b/node_modules/.staging", "/a/b/node_modules") 627 .replace(/[\-\.][\d\w][\d\w][\d\w][\d\w][\d\w][\d\w][\d\w][\d\w]/g, ""); 628 }); 629 630 host.deleteFolder(root + "/a/b/node_modules/.staging", /*recursive*/ true); 631 const lodashIndexPath = root + "/a/b/node_modules/@types/lodash/index.d.ts"; 632 projectFiles.push(find(filesAndFoldersToAdd, f => f.path === lodashIndexPath)!); 633 // we would now not have failed lookup in the parent of appFolder since lodash is available 634 recursiveWatchedDirectories.length = 2; 635 // npm installation complete, timeout after reload fs 636 npmInstallComplete = true; 637 verifyAfterPartialOrCompleteNpmInstall(2); 638 639 function verifyAfterPartialOrCompleteNpmInstall(timeoutQueueLengthWhenRunningTimeouts: number) { 640 filesAndFoldersToAdd.forEach(f => host.ensureFileOrFolder(f)); 641 if (npmInstallComplete || timeoutDuringPartialInstallation) { 642 if (timeoutQueueLengthWhenRunningTimeouts) { 643 // Expected project update 644 host.checkTimeoutQueueLengthAndRun(timeoutQueueLengthWhenRunningTimeouts + 1); // Scheduled invalidation of resolutions 645 host.runQueuedTimeoutCallbacks(); // Actual update 646 } 647 else { 648 host.checkTimeoutQueueLengthAndRun(timeoutQueueLengthWhenRunningTimeouts); 649 } 650 } 651 else { 652 host.checkTimeoutQueueLength(3); 653 } 654 verifyProject(); 655 } 656 657 function verifyProject() { 658 checkNumberOfConfiguredProjects(projectService, 1); 659 660 const project = projectService.configuredProjects.get(tsconfigJson.path)!; 661 const projectFilePaths = map(projectFiles, f => f.path); 662 checkProjectActualFiles(project, projectFilePaths); 663 664 const filesWatched = filter(projectFilePaths, p => p !== app.path && p.indexOf("/a/b/node_modules") === -1); 665 checkWatchedFiles(host, filesWatched); 666 checkWatchedDirectories(host, typeRootDirectories.concat(recursiveWatchedDirectories), /*recursive*/ true); 667 checkWatchedDirectories(host, [], /*recursive*/ false); 668 } 669 } 670 671 it("timeouts occur inbetween installation", () => { 672 verifyNpmInstall(/*timeoutDuringPartialInstallation*/ true); 673 }); 674 675 it("timeout occurs after installation", () => { 676 verifyNpmInstall(/*timeoutDuringPartialInstallation*/ false); 677 }); 678 }); 679 680 it("when node_modules dont receive event for the @types file addition", () => { 681 const projectLocation = "/user/username/folder/myproject"; 682 const app: File = { 683 path: `${projectLocation}/app.ts`, 684 content: `import * as debug from "debug"` 685 }; 686 const tsconfig: File = { 687 path: `${projectLocation}/tsconfig.json`, 688 content: "" 689 }; 690 691 const files = [app, tsconfig, libFile]; 692 const host = createServerHost(files); 693 const service = createProjectService(host); 694 service.openClientFile(app.path); 695 696 const project = service.configuredProjects.get(tsconfig.path)!; 697 checkProjectActualFiles(project, files.map(f => f.path)); 698 assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(app.path).map(diag => diag.messageText), ["Cannot find module 'debug' or its corresponding type declarations."]); 699 700 const debugTypesFile: File = { 701 path: `${projectLocation}/node_modules/@types/debug/index.d.ts`, 702 content: "export {}" 703 }; 704 files.push(debugTypesFile); 705 // Do not invoke recursive directory watcher for anything other than node_module/@types 706 const invoker = host.invokeFsWatchesRecursiveCallbacks; 707 host.invokeFsWatchesRecursiveCallbacks = (fullPath, eventName, entryFullPath) => { 708 if (fullPath.endsWith("@types")) { 709 invoker.call(host, fullPath, eventName, entryFullPath); 710 } 711 }; 712 host.writeFile(debugTypesFile.path, debugTypesFile.content); 713 host.runQueuedTimeoutCallbacks(); 714 checkProjectActualFiles(project, files.map(f => f.path)); 715 assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(app.path).map(diag => diag.messageText), []); 716 }); 717 }); 718} 719