1import * as ts from "../_namespaces/ts"; 2import { checkResolvedModulesCache, checkResolvedTypeDirectivesCache, createTestCompilerHost, NamedSourceText, newLine, newProgram, ProgramWithSourceTexts, SourceText, TestCompilerHost, updateProgram, updateProgramText } from "./helpers"; 3 4describe("unittests:: Reuse program structure:: General", () => { 5 const target = ts.ScriptTarget.Latest; 6 const files: NamedSourceText[] = [ 7 { 8 name: "a.ts", text: SourceText.New( 9 ` 10/// <reference path='b.ts'/> 11/// <reference path='non-existing-file.ts'/> 12/// <reference types="typerefs" /> 13`, "", `var x = 1`) 14 }, 15 { name: "b.ts", text: SourceText.New(`/// <reference path='c.ts'/>`, "", `var y = 2`) }, 16 { name: "c.ts", text: SourceText.New("", "", `var z = 1;`) }, 17 { name: "types/typerefs/index.d.ts", text: SourceText.New("", "", `declare let z: number;`) }, 18 ]; 19 20 it("successful if change does not affect imports", () => { 21 const program1 = newProgram(files, ["a.ts"], { target }); 22 const program2 = updateProgram(program1, ["a.ts"], { target }, files => { 23 files[0].text = files[0].text.updateProgram("var x = 100"); 24 }); 25 assert.equal(program2.structureIsReused, ts.StructureIsReused.Completely); 26 const program1Diagnostics = program1.getSemanticDiagnostics(program1.getSourceFile("a.ts")); 27 const program2Diagnostics = program2.getSemanticDiagnostics(program2.getSourceFile("a.ts")); 28 assert.equal(program1Diagnostics.length, program2Diagnostics.length); 29 }); 30 31 it("successful if change does not affect type reference directives", () => { 32 const program1 = newProgram(files, ["a.ts"], { target }); 33 const program2 = updateProgram(program1, ["a.ts"], { target }, files => { 34 files[0].text = files[0].text.updateProgram("var x = 100"); 35 }); 36 assert.equal(program2.structureIsReused, ts.StructureIsReused.Completely); 37 const program1Diagnostics = program1.getSemanticDiagnostics(program1.getSourceFile("a.ts")); 38 const program2Diagnostics = program2.getSemanticDiagnostics(program2.getSourceFile("a.ts")); 39 assert.equal(program1Diagnostics.length, program2Diagnostics.length); 40 }); 41 42 it("successful if change affects a single module of a package", () => { 43 const files = [ 44 { name: "/a.ts", text: SourceText.New("", "import {b} from 'b'", "var a = b;") }, 45 { name: "/node_modules/b/index.d.ts", text: SourceText.New("", "export * from './internal';", "") }, 46 { name: "/node_modules/b/internal.d.ts", text: SourceText.New("", "", "export const b = 1;") }, 47 { name: "/node_modules/b/package.json", text: SourceText.New("", "", JSON.stringify({ name: "b", version: "1.2.3" })) }, 48 ]; 49 50 const options: ts.CompilerOptions = { target, moduleResolution: ts.ModuleResolutionKind.NodeJs }; 51 const program1 = newProgram(files, ["/a.ts"], options); 52 const program2 = updateProgram(program1, ["/a.ts"], options, files => { 53 files[2].text = files[2].text.updateProgram("export const b = 2;"); 54 }); 55 assert.equal(program2.structureIsReused, ts.StructureIsReused.Completely); 56 const program1Diagnostics = program1.getSemanticDiagnostics(program1.getSourceFile("a.ts")); 57 const program2Diagnostics = program2.getSemanticDiagnostics(program2.getSourceFile("a.ts")); 58 assert.equal(program1Diagnostics.length, program2Diagnostics.length); 59 }); 60 61 it("fails if change affects tripleslash references", () => { 62 const program1 = newProgram(files, ["a.ts"], { target }); 63 const program2 = updateProgram(program1, ["a.ts"], { target }, files => { 64 const newReferences = `/// <reference path='b.ts'/> 65 /// <reference path='c.ts'/> 66 `; 67 files[0].text = files[0].text.updateReferences(newReferences); 68 }); 69 assert.equal(program2.structureIsReused, ts.StructureIsReused.SafeModules); 70 }); 71 72 it("fails if change affects type references", () => { 73 const program1 = newProgram(files, ["a.ts"], { types: ["a"] }); 74 const program2 = updateProgram(program1, ["a.ts"], { types: ["b"] }, ts.noop); 75 assert.equal(program2.structureIsReused, ts.StructureIsReused.SafeModules); 76 }); 77 78 it("succeeds if change doesn't affect type references", () => { 79 const program1 = newProgram(files, ["a.ts"], { types: ["a"] }); 80 const program2 = updateProgram(program1, ["a.ts"], { types: ["a"] }, ts.noop); 81 assert.equal(program2.structureIsReused, ts.StructureIsReused.Completely); 82 }); 83 84 it("fails if change affects imports", () => { 85 const program1 = newProgram(files, ["a.ts"], { target }); 86 const program2 = updateProgram(program1, ["a.ts"], { target }, files => { 87 files[2].text = files[2].text.updateImportsAndExports("import x from 'b'"); 88 }); 89 assert.equal(program2.structureIsReused, ts.StructureIsReused.SafeModules); 90 }); 91 92 it("fails if change affects type directives", () => { 93 const program1 = newProgram(files, ["a.ts"], { target }); 94 const program2 = updateProgram(program1, ["a.ts"], { target }, files => { 95 const newReferences = ` 96/// <reference path='b.ts'/> 97/// <reference path='non-existing-file.ts'/> 98/// <reference types="typerefs1" />`; 99 files[0].text = files[0].text.updateReferences(newReferences); 100 }); 101 assert.equal(program2.structureIsReused, ts.StructureIsReused.SafeModules); 102 }); 103 104 it("fails if module kind changes", () => { 105 const program1 = newProgram(files, ["a.ts"], { target, module: ts.ModuleKind.CommonJS }); 106 const program2 = updateProgram(program1, ["a.ts"], { target, module: ts.ModuleKind.AMD }, ts.noop); 107 assert.equal(program2.structureIsReused, ts.StructureIsReused.Not); 108 }); 109 110 it("succeeds if rootdir changes", () => { 111 const program1 = newProgram(files, ["a.ts"], { target, module: ts.ModuleKind.CommonJS, rootDir: "/a/b" }); 112 const program2 = updateProgram(program1, ["a.ts"], { target, module: ts.ModuleKind.CommonJS, rootDir: "/a/c" }, ts.noop); 113 assert.equal(program2.structureIsReused, ts.StructureIsReused.Completely); 114 }); 115 116 it("fails if config path changes", () => { 117 const program1 = newProgram(files, ["a.ts"], { target, module: ts.ModuleKind.CommonJS, configFilePath: "/a/b/tsconfig.json" }); 118 const program2 = updateProgram(program1, ["a.ts"], { target, module: ts.ModuleKind.CommonJS, configFilePath: "/a/c/tsconfig.json" }, ts.noop); 119 assert.equal(program2.structureIsReused, ts.StructureIsReused.Not); 120 }); 121 122 it("succeeds if missing files remain missing", () => { 123 const options: ts.CompilerOptions = { target, noLib: true }; 124 125 const program1 = newProgram(files, ["a.ts"], options); 126 assert.notDeepEqual(ts.emptyArray, program1.getMissingFilePaths()); 127 128 const program2 = updateProgram(program1, ["a.ts"], options, ts.noop); 129 assert.deepEqual(program1.getMissingFilePaths(), program2.getMissingFilePaths()); 130 131 assert.equal(program2.structureIsReused, ts.StructureIsReused.Completely,); 132 }); 133 134 it("fails if missing file is created", () => { 135 const options: ts.CompilerOptions = { target, noLib: true }; 136 137 const program1 = newProgram(files, ["a.ts"], options); 138 assert.notDeepEqual(ts.emptyArray, program1.getMissingFilePaths()); 139 140 const newTexts: NamedSourceText[] = files.concat([{ name: "non-existing-file.ts", text: SourceText.New("", "", `var x = 1`) }]); 141 const program2 = updateProgram(program1, ["a.ts"], options, ts.noop, newTexts); 142 assert.lengthOf(program2.getMissingFilePaths(), 0); 143 144 assert.equal(program2.structureIsReused, ts.StructureIsReused.Not); 145 }); 146 147 it("resolution cache follows imports", () => { 148 (Error as any).stackTraceLimit = Infinity; 149 150 const files = [ 151 { name: "a.ts", text: SourceText.New("", "import {_} from 'b'", "var x = 1") }, 152 { name: "b.ts", text: SourceText.New("", "", "var y = 2") }, 153 ]; 154 const options: ts.CompilerOptions = { target }; 155 156 const program1 = newProgram(files, ["a.ts"], options); 157 checkResolvedModulesCache(program1, "a.ts", new ts.Map(ts.getEntries({ b: ts.createResolvedModule("b.ts") }))); 158 checkResolvedModulesCache(program1, "b.ts", /*expectedContent*/ undefined); 159 160 const program2 = updateProgram(program1, ["a.ts"], options, files => { 161 files[0].text = files[0].text.updateProgram("var x = 2"); 162 }); 163 assert.equal(program2.structureIsReused, ts.StructureIsReused.Completely); 164 165 // content of resolution cache should not change 166 checkResolvedModulesCache(program1, "a.ts", new ts.Map(ts.getEntries({ b: ts.createResolvedModule("b.ts") }))); 167 checkResolvedModulesCache(program1, "b.ts", /*expectedContent*/ undefined); 168 169 // imports has changed - program is not reused 170 const program3 = updateProgram(program2, ["a.ts"], options, files => { 171 files[0].text = files[0].text.updateImportsAndExports(""); 172 }); 173 assert.equal(program3.structureIsReused, ts.StructureIsReused.SafeModules); 174 checkResolvedModulesCache(program3, "a.ts", /*expectedContent*/ undefined); 175 176 const program4 = updateProgram(program3, ["a.ts"], options, files => { 177 const newImports = `import x from 'b' 178 import y from 'c' 179 `; 180 files[0].text = files[0].text.updateImportsAndExports(newImports); 181 }); 182 assert.equal(program4.structureIsReused, ts.StructureIsReused.SafeModules); 183 checkResolvedModulesCache(program4, "a.ts", new ts.Map(ts.getEntries({ b: ts.createResolvedModule("b.ts"), c: undefined }))); 184 }); 185 186 it("set the resolvedImports after re-using an ambient external module declaration", () => { 187 const files = [ 188 { name: "/a.ts", text: SourceText.New("", "", 'import * as a from "a";') }, 189 { name: "/types/zzz/index.d.ts", text: SourceText.New("", "", 'declare module "a" { }') }, 190 ]; 191 const options: ts.CompilerOptions = { target, typeRoots: ["/types"] }; 192 const program1 = newProgram(files, ["/a.ts"], options); 193 const program2 = updateProgram(program1, ["/a.ts"], options, files => { 194 files[0].text = files[0].text.updateProgram('import * as aa from "a";'); 195 }); 196 assert.isDefined(program2.getSourceFile("/a.ts")!.resolvedModules!.get("a", /*mode*/ undefined), "'a' is not an unresolved module after re-use"); 197 }); 198 199 it("works with updated SourceFiles", () => { 200 // adapted repro from https://github.com/Microsoft/TypeScript/issues/26166 201 const files = [ 202 { name: "/a.ts", text: SourceText.New("", "", 'import * as a from "a";a;') }, 203 { name: "/types/zzz/index.d.ts", text: SourceText.New("", "", 'declare module "a" { }') }, 204 ]; 205 const host = createTestCompilerHost(files, target); 206 const options: ts.CompilerOptions = { target, typeRoots: ["/types"] }; 207 const program1 = ts.createProgram(["/a.ts"], options, host); 208 let sourceFile = program1.getSourceFile("/a.ts")!; 209 assert.isDefined(sourceFile, "'/a.ts' is included in the program"); 210 sourceFile = ts.updateSourceFile(sourceFile, "'use strict';" + sourceFile.text, { newLength: "'use strict';".length, span: { start: 0, length: 0 } }); 211 assert.strictEqual(sourceFile.statements[2].getSourceFile(), sourceFile, "parent pointers are updated"); 212 const updateHost: TestCompilerHost = { 213 ...host, 214 getSourceFile(fileName) { 215 return fileName === sourceFile.fileName ? sourceFile : program1.getSourceFile(fileName); 216 } 217 }; 218 const program2 = ts.createProgram(["/a.ts"], options, updateHost, program1); 219 assert.isDefined(program2.getSourceFile("/a.ts")!.resolvedModules!.get("a", /*mode*/ undefined), "'a' is not an unresolved module after re-use"); 220 assert.strictEqual(sourceFile.statements[2].getSourceFile(), sourceFile, "parent pointers are not altered"); 221 }); 222 223 it("resolved type directives cache follows type directives", () => { 224 const files = [ 225 { name: "/a.ts", text: SourceText.New("/// <reference types='typedefs'/>", "", "var x = $") }, 226 { name: "/types/typedefs/index.d.ts", text: SourceText.New("", "", "declare var $: number") }, 227 ]; 228 const options: ts.CompilerOptions = { target, typeRoots: ["/types"] }; 229 230 const program1 = newProgram(files, ["/a.ts"], options); 231 checkResolvedTypeDirectivesCache(program1, "/a.ts", new ts.Map(ts.getEntries({ typedefs: { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }))); 232 checkResolvedTypeDirectivesCache(program1, "/types/typedefs/index.d.ts", /*expectedContent*/ undefined); 233 234 const program2 = updateProgram(program1, ["/a.ts"], options, files => { 235 files[0].text = files[0].text.updateProgram("var x = 2"); 236 }); 237 assert.equal(program2.structureIsReused, ts.StructureIsReused.Completely); 238 239 // content of resolution cache should not change 240 checkResolvedTypeDirectivesCache(program1, "/a.ts", new ts.Map(ts.getEntries({ typedefs: { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }))); 241 checkResolvedTypeDirectivesCache(program1, "/types/typedefs/index.d.ts", /*expectedContent*/ undefined); 242 243 // type reference directives has changed - program is not reused 244 const program3 = updateProgram(program2, ["/a.ts"], options, files => { 245 files[0].text = files[0].text.updateReferences(""); 246 }); 247 248 assert.equal(program3.structureIsReused, ts.StructureIsReused.SafeModules); 249 checkResolvedTypeDirectivesCache(program3, "/a.ts", /*expectedContent*/ undefined); 250 251 const program4 = updateProgram(program3, ["/a.ts"], options, files => { 252 const newReferences = `/// <reference types="typedefs"/> 253 /// <reference types="typedefs2"/> 254 `; 255 files[0].text = files[0].text.updateReferences(newReferences); 256 }); 257 assert.equal(program4.structureIsReused, ts.StructureIsReused.SafeModules); 258 checkResolvedTypeDirectivesCache(program1, "/a.ts", new ts.Map(ts.getEntries({ typedefs: { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }))); 259 }); 260 261 it("fetches imports after npm install", () => { 262 const file1Ts = { name: "file1.ts", text: SourceText.New("", `import * as a from "a";`, "const myX: number = a.x;") }; 263 const file2Ts = { name: "file2.ts", text: SourceText.New("", "", "") }; 264 const indexDTS = { name: "node_modules/a/index.d.ts", text: SourceText.New("", "export declare let x: number;", "") }; 265 const options: ts.CompilerOptions = { target: ts.ScriptTarget.ES2015, traceResolution: true, moduleResolution: ts.ModuleResolutionKind.NodeJs }; 266 const rootFiles = [file1Ts, file2Ts]; 267 const filesAfterNpmInstall = [file1Ts, file2Ts, indexDTS]; 268 269 const initialProgram = newProgram(rootFiles, rootFiles.map(f => f.name), options); 270 { 271 assert.deepEqual(initialProgram.host.getTrace(), 272 [ 273 "======== Resolving module 'a' from 'file1.ts'. ========", 274 "Explicitly specified module resolution kind: 'NodeJs'.", 275 "Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.", 276 "File 'node_modules/a/package.json' does not exist.", 277 "File 'node_modules/a.ts' does not exist.", 278 "File 'node_modules/a.tsx' does not exist.", 279 "File 'node_modules/a.d.ts' does not exist.", 280 "File 'node_modules/a.ets' does not exist.", 281 "File 'node_modules/a.d.ets' does not exist.", 282 "File 'node_modules/a/index.ts' does not exist.", 283 "File 'node_modules/a/index.tsx' does not exist.", 284 "File 'node_modules/a/index.d.ts' does not exist.", 285 "File 'node_modules/a/index.ets' does not exist.", 286 "File 'node_modules/a/index.d.ets' does not exist.", 287 "File 'node_modules/@types/a/package.json' does not exist.", 288 "File 'node_modules/@types/a.d.ts' does not exist.", 289 "File 'node_modules/@types/a/index.d.ts' does not exist.", 290 "Loading module 'a' from 'node_modules' folder, target file type 'JavaScript'.", 291 "File 'node_modules/a/package.json' does not exist according to earlier cached lookups.", 292 "File 'node_modules/a.js' does not exist.", 293 "File 'node_modules/a.jsx' does not exist.", 294 "File 'node_modules/a/index.js' does not exist.", 295 "File 'node_modules/a/index.jsx' does not exist.", 296 "======== Module name 'a' was not resolved. ========" 297 ], 298 "initialProgram: execute module resolution normally."); 299 300 const initialProgramDiagnostics = initialProgram.getSemanticDiagnostics(initialProgram.getSourceFile("file1.ts")); 301 assert.lengthOf(initialProgramDiagnostics, 1, `initialProgram: import should fail.`); 302 } 303 304 const afterNpmInstallProgram = updateProgram(initialProgram, rootFiles.map(f => f.name), options, f => { 305 f[1].text = f[1].text.updateReferences(`/// <reference no-default-lib="true"/>`); 306 }, filesAfterNpmInstall); 307 { 308 assert.deepEqual(afterNpmInstallProgram.host.getTrace(), 309 [ 310 "======== Resolving module 'a' from 'file1.ts'. ========", 311 "Explicitly specified module resolution kind: 'NodeJs'.", 312 "Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.", 313 "File 'node_modules/a/package.json' does not exist.", 314 "File 'node_modules/a.ts' does not exist.", 315 "File 'node_modules/a.tsx' does not exist.", 316 "File 'node_modules/a.d.ts' does not exist.", 317 "File 'node_modules/a.ets' does not exist.", 318 "File 'node_modules/a.d.ets' does not exist.", 319 "File 'node_modules/a/index.ts' does not exist.", 320 "File 'node_modules/a/index.tsx' does not exist.", 321 "File 'node_modules/a/index.d.ts' exist - use it as a name resolution result.", 322 "======== Module name 'a' was successfully resolved to 'node_modules/a/index.d.ts'. ========" 323 ], 324 "afterNpmInstallProgram: execute module resolution normally."); 325 326 const afterNpmInstallProgramDiagnostics = afterNpmInstallProgram.getSemanticDiagnostics(afterNpmInstallProgram.getSourceFile("file1.ts")); 327 assert.lengthOf(afterNpmInstallProgramDiagnostics, 0, `afterNpmInstallProgram: program is well-formed with import.`); 328 } 329 }); 330 331 it("can reuse ambient module declarations from non-modified files", () => { 332 const files = [ 333 { name: "/a/b/app.ts", text: SourceText.New("", "import * as fs from 'fs'", "") }, 334 { name: "/a/b/node.d.ts", text: SourceText.New("", "", "declare module 'fs' {}") } 335 ]; 336 const options = { target: ts.ScriptTarget.ES2015, traceResolution: true }; 337 const program = newProgram(files, files.map(f => f.name), options); 338 assert.deepEqual(program.host.getTrace(), 339 [ 340 "======== Resolving module 'fs' from '/a/b/app.ts'. ========", 341 "Module resolution kind is not specified, using 'Classic'.", 342 "File '/a/b/fs.ts' does not exist.", 343 "File '/a/b/fs.tsx' does not exist.", 344 "File '/a/b/fs.d.ts' does not exist.", 345 "File '/a/b/fs.ets' does not exist.", 346 "File '/a/b/fs.d.ets' does not exist.", 347 "File '/a/fs.ts' does not exist.", 348 "File '/a/fs.tsx' does not exist.", 349 "File '/a/fs.d.ts' does not exist.", 350 "File '/a/fs.ets' does not exist.", 351 "File '/a/fs.d.ets' does not exist.", 352 "File '/fs.ts' does not exist.", 353 "File '/fs.tsx' does not exist.", 354 "File '/fs.d.ts' does not exist.", 355 "File '/fs.ets' does not exist.", 356 "File '/fs.d.ets' does not exist.", 357 "File '/a/b/node_modules/@types/fs/package.json' does not exist.", 358 "File '/a/b/node_modules/@types/fs.d.ts' does not exist.", 359 "File '/a/b/node_modules/@types/fs/index.d.ts' does not exist.", 360 "File '/a/node_modules/@types/fs/package.json' does not exist.", 361 "File '/a/node_modules/@types/fs.d.ts' does not exist.", 362 "File '/a/node_modules/@types/fs/index.d.ts' does not exist.", 363 "File '/node_modules/@types/fs/package.json' does not exist.", 364 "File '/node_modules/@types/fs.d.ts' does not exist.", 365 "File '/node_modules/@types/fs/index.d.ts' does not exist.", 366 "File '/a/b/fs.js' does not exist.", 367 "File '/a/b/fs.jsx' does not exist.", 368 "File '/a/fs.js' does not exist.", 369 "File '/a/fs.jsx' does not exist.", 370 "File '/fs.js' does not exist.", 371 "File '/fs.jsx' does not exist.", 372 "======== Module name 'fs' was not resolved. ========", 373 ], "should look for 'fs'"); 374 375 const program2 = updateProgram(program, program.getRootFileNames(), options, f => { 376 f[0].text = f[0].text.updateProgram("var x = 1;"); 377 }); 378 assert.deepEqual(program2.host.getTrace(), [ 379 "Module 'fs' was resolved as ambient module declared in '/a/b/node.d.ts' since this file was not modified." 380 ], "should reuse 'fs' since node.d.ts was not changed"); 381 382 const program3 = updateProgram(program2, program2.getRootFileNames(), options, f => { 383 f[0].text = f[0].text.updateProgram("var y = 1;"); 384 f[1].text = f[1].text.updateProgram("declare var process: any"); 385 }); 386 assert.deepEqual(program3.host.getTrace(), 387 [ 388 "======== Resolving module 'fs' from '/a/b/app.ts'. ========", 389 "Module resolution kind is not specified, using 'Classic'.", 390 "File '/a/b/fs.ts' does not exist.", 391 "File '/a/b/fs.tsx' does not exist.", 392 "File '/a/b/fs.d.ts' does not exist.", 393 "File '/a/b/fs.ets' does not exist.", 394 "File '/a/b/fs.d.ets' does not exist.", 395 "File '/a/fs.ts' does not exist.", 396 "File '/a/fs.tsx' does not exist.", 397 "File '/a/fs.d.ts' does not exist.", 398 "File '/a/fs.ets' does not exist.", 399 "File '/a/fs.d.ets' does not exist.", 400 "File '/fs.ts' does not exist.", 401 "File '/fs.tsx' does not exist.", 402 "File '/fs.d.ts' does not exist.", 403 "File '/fs.ets' does not exist.", 404 "File '/fs.d.ets' does not exist.", 405 "File '/a/b/node_modules/@types/fs/package.json' does not exist.", 406 "File '/a/b/node_modules/@types/fs.d.ts' does not exist.", 407 "File '/a/b/node_modules/@types/fs/index.d.ts' does not exist.", 408 "File '/a/node_modules/@types/fs/package.json' does not exist.", 409 "File '/a/node_modules/@types/fs.d.ts' does not exist.", 410 "File '/a/node_modules/@types/fs/index.d.ts' does not exist.", 411 "File '/node_modules/@types/fs/package.json' does not exist.", 412 "File '/node_modules/@types/fs.d.ts' does not exist.", 413 "File '/node_modules/@types/fs/index.d.ts' does not exist.", 414 "File '/a/b/fs.js' does not exist.", 415 "File '/a/b/fs.jsx' does not exist.", 416 "File '/a/fs.js' does not exist.", 417 "File '/a/fs.jsx' does not exist.", 418 "File '/fs.js' does not exist.", 419 "File '/fs.jsx' does not exist.", 420 "======== Module name 'fs' was not resolved. ========", 421 ], "should look for 'fs' again since node.d.ts was changed"); 422 }); 423 424 it("can reuse module resolutions from non-modified files", () => { 425 const files = [ 426 { name: "a1.ts", text: SourceText.New("", "", "let x = 1;") }, 427 { name: "a2.ts", text: SourceText.New("", "", "let x = 1;") }, 428 { name: "b1.ts", text: SourceText.New("", "export class B { x: number; }", "") }, 429 { name: "b2.ts", text: SourceText.New("", "export class B { x: number; }", "") }, 430 { name: "node_modules/@types/typerefs1/index.d.ts", text: SourceText.New("", "", "declare let z: string;") }, 431 { name: "node_modules/@types/typerefs2/index.d.ts", text: SourceText.New("", "", "declare let z: string;") }, 432 { 433 name: "f1.ts", 434 text: 435 SourceText.New( 436 `/// <reference path="a1.ts"/>${newLine}/// <reference types="typerefs1"/>${newLine}/// <reference no-default-lib="true"/>`, 437 `import { B } from './b1';${newLine}export let BB = B;`, 438 "declare module './b1' { interface B { y: string; } }") 439 }, 440 { 441 name: "f2.ts", 442 text: SourceText.New( 443 `/// <reference path="a2.ts"/>${newLine}/// <reference types="typerefs2"/>`, 444 `import { B } from './b2';${newLine}import { BB } from './f1';`, 445 "(new BB).x; (new BB).y;") 446 }, 447 ]; 448 449 const options: ts.CompilerOptions = { target: ts.ScriptTarget.ES2015, traceResolution: true, moduleResolution: ts.ModuleResolutionKind.Classic }; 450 const program1 = newProgram(files, files.map(f => f.name), options); 451 let expectedErrors = 0; 452 { 453 assert.deepEqual(program1.host.getTrace(), 454 [ 455 "======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========", 456 "Resolving with primary search path 'node_modules/@types'.", 457 "File 'node_modules/@types/typerefs1/package.json' does not exist.", 458 "File 'node_modules/@types/typerefs1/index.d.ts' exist - use it as a name resolution result.", 459 "======== Type reference directive 'typerefs1' was successfully resolved to 'node_modules/@types/typerefs1/index.d.ts', primary: true. ========", 460 "======== Resolving module './b1' from 'f1.ts'. ========", 461 "Explicitly specified module resolution kind: 'Classic'.", 462 "File 'b1.ts' exist - use it as a name resolution result.", 463 "======== Module name './b1' was successfully resolved to 'b1.ts'. ========", 464 "======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========", 465 "Resolving with primary search path 'node_modules/@types'.", 466 "File 'node_modules/@types/typerefs2/package.json' does not exist.", 467 "File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.", 468 "======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========", 469 "======== Resolving module './b2' from 'f2.ts'. ========", 470 "Explicitly specified module resolution kind: 'Classic'.", 471 "File 'b2.ts' exist - use it as a name resolution result.", 472 "======== Module name './b2' was successfully resolved to 'b2.ts'. ========", 473 "======== Resolving module './f1' from 'f2.ts'. ========", 474 "Explicitly specified module resolution kind: 'Classic'.", 475 "File 'f1.ts' exist - use it as a name resolution result.", 476 "======== Module name './f1' was successfully resolved to 'f1.ts'. ========" 477 ], 478 "program1: execute module resolution normally."); 479 480 const program1Diagnostics = program1.getSemanticDiagnostics(program1.getSourceFile("f2.ts")); 481 assert.lengthOf(program1Diagnostics, expectedErrors, `initial program should be well-formed`); 482 } 483 const indexOfF1 = 6; 484 const program2 = updateProgram(program1, program1.getRootFileNames(), options, f => { 485 const newSourceText = f[indexOfF1].text.updateReferences(`/// <reference path="a1.ts"/>${newLine}/// <reference types="typerefs1"/>`); 486 f[indexOfF1] = { name: "f1.ts", text: newSourceText }; 487 }); 488 489 { 490 const program2Diagnostics = program2.getSemanticDiagnostics(program2.getSourceFile("f2.ts")); 491 assert.lengthOf(program2Diagnostics, expectedErrors, `removing no-default-lib shouldn't affect any types used.`); 492 493 assert.deepEqual(program2.host.getTrace(), [ 494 "======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========", 495 "Resolving with primary search path 'node_modules/@types'.", 496 "File 'node_modules/@types/typerefs1/package.json' does not exist.", 497 "File 'node_modules/@types/typerefs1/index.d.ts' exist - use it as a name resolution result.", 498 "======== Type reference directive 'typerefs1' was successfully resolved to 'node_modules/@types/typerefs1/index.d.ts', primary: true. ========", 499 "======== Resolving module './b1' from 'f1.ts'. ========", 500 "Explicitly specified module resolution kind: 'Classic'.", 501 "File 'b1.ts' exist - use it as a name resolution result.", 502 "======== Module name './b1' was successfully resolved to 'b1.ts'. ========", 503 "======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========", 504 "Resolving with primary search path 'node_modules/@types'.", 505 "File 'node_modules/@types/typerefs2/package.json' does not exist.", 506 "File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.", 507 "======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========", 508 "Reusing resolution of module './b2' from 'f2.ts' of old program, it was successfully resolved to 'b2.ts'.", 509 "Reusing resolution of module './f1' from 'f2.ts' of old program, it was successfully resolved to 'f1.ts'." 510 ], "program2: reuse module resolutions in f2 since it is unchanged"); 511 } 512 513 const program3 = updateProgram(program2, program2.getRootFileNames(), options, f => { 514 const newSourceText = f[indexOfF1].text.updateReferences(`/// <reference path="a1.ts"/>`); 515 f[indexOfF1] = { name: "f1.ts", text: newSourceText }; 516 }); 517 518 { 519 const program3Diagnostics = program3.getSemanticDiagnostics(program3.getSourceFile("f2.ts")); 520 assert.lengthOf(program3Diagnostics, expectedErrors, `typerefs2 was unused, so diagnostics should be unaffected.`); 521 522 assert.deepEqual(program3.host.getTrace(), [ 523 "======== Resolving module './b1' from 'f1.ts'. ========", 524 "Explicitly specified module resolution kind: 'Classic'.", 525 "File 'b1.ts' exist - use it as a name resolution result.", 526 "======== Module name './b1' was successfully resolved to 'b1.ts'. ========", 527 "======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========", 528 "Resolving with primary search path 'node_modules/@types'.", 529 "File 'node_modules/@types/typerefs2/package.json' does not exist.", 530 "File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.", 531 "======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========", 532 "Reusing resolution of module './b2' from 'f2.ts' of old program, it was successfully resolved to 'b2.ts'.", 533 "Reusing resolution of module './f1' from 'f2.ts' of old program, it was successfully resolved to 'f1.ts'." 534 ], "program3: reuse module resolutions in f2 since it is unchanged"); 535 } 536 537 538 const program4 = updateProgram(program3, program3.getRootFileNames(), options, f => { 539 const newSourceText = f[indexOfF1].text.updateReferences(""); 540 f[indexOfF1] = { name: "f1.ts", text: newSourceText }; 541 }); 542 543 { 544 const program4Diagnostics = program4.getSemanticDiagnostics(program4.getSourceFile("f2.ts")); 545 assert.lengthOf(program4Diagnostics, expectedErrors, `a1.ts was unused, so diagnostics should be unaffected.`); 546 547 assert.deepEqual(program4.host.getTrace(), [ 548 "======== Resolving module './b1' from 'f1.ts'. ========", 549 "Explicitly specified module resolution kind: 'Classic'.", 550 "File 'b1.ts' exist - use it as a name resolution result.", 551 "======== Module name './b1' was successfully resolved to 'b1.ts'. ========", 552 "======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========", 553 "Resolving with primary search path 'node_modules/@types'.", 554 "File 'node_modules/@types/typerefs2/package.json' does not exist.", 555 "File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.", 556 "======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========", 557 "Reusing resolution of module './b2' from 'f2.ts' of old program, it was successfully resolved to 'b2.ts'.", 558 "Reusing resolution of module './f1' from 'f2.ts' of old program, it was successfully resolved to 'f1.ts'.", 559 ], "program_4: reuse module resolutions in f2 since it is unchanged"); 560 } 561 562 const program5 = updateProgram(program4, program4.getRootFileNames(), options, f => { 563 const newSourceText = f[indexOfF1].text.updateImportsAndExports(`import { B } from './b1';`); 564 f[indexOfF1] = { name: "f1.ts", text: newSourceText }; 565 }); 566 567 { 568 const program5Diagnostics = program5.getSemanticDiagnostics(program5.getSourceFile("f2.ts")); 569 assert.lengthOf(program5Diagnostics, ++expectedErrors, `import of BB in f1 fails. BB is of type any. Add one error`); 570 571 assert.deepEqual(program5.host.getTrace(), [ 572 "======== Resolving module './b1' from 'f1.ts'. ========", 573 "Explicitly specified module resolution kind: 'Classic'.", 574 "File 'b1.ts' exist - use it as a name resolution result.", 575 "======== Module name './b1' was successfully resolved to 'b1.ts'. ========" 576 ], "program_5: exports do not affect program structure, so f2's resolutions are silently reused."); 577 } 578 579 const program6 = updateProgram(program5, program5.getRootFileNames(), options, f => { 580 const newSourceText = f[indexOfF1].text.updateProgram(""); 581 f[indexOfF1] = { name: "f1.ts", text: newSourceText }; 582 }); 583 584 { 585 const program6Diagnostics = program6.getSemanticDiagnostics(program6.getSourceFile("f2.ts")); 586 assert.lengthOf(program6Diagnostics, expectedErrors, `import of BB in f1 fails.`); 587 588 assert.deepEqual(program6.host.getTrace(), [ 589 "======== Resolving module './b1' from 'f1.ts'. ========", 590 "Explicitly specified module resolution kind: 'Classic'.", 591 "File 'b1.ts' exist - use it as a name resolution result.", 592 "======== Module name './b1' was successfully resolved to 'b1.ts'. ========", 593 "======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========", 594 "Resolving with primary search path 'node_modules/@types'.", 595 "File 'node_modules/@types/typerefs2/package.json' does not exist.", 596 "File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.", 597 "======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========", 598 "Reusing resolution of module './b2' from 'f2.ts' of old program, it was successfully resolved to 'b2.ts'.", 599 "Reusing resolution of module './f1' from 'f2.ts' of old program, it was successfully resolved to 'f1.ts'.", 600 ], "program_6: reuse module resolutions in f2 since it is unchanged"); 601 } 602 603 const program7 = updateProgram(program6, program6.getRootFileNames(), options, f => { 604 const newSourceText = f[indexOfF1].text.updateImportsAndExports(""); 605 f[indexOfF1] = { name: "f1.ts", text: newSourceText }; 606 }); 607 608 { 609 const program7Diagnostics = program7.getSemanticDiagnostics(program7.getSourceFile("f2.ts")); 610 assert.lengthOf(program7Diagnostics, expectedErrors, `removing import is noop with respect to program, so no change in diagnostics.`); 611 612 assert.deepEqual(program7.host.getTrace(), [ 613 "======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========", 614 "Resolving with primary search path 'node_modules/@types'.", 615 "File 'node_modules/@types/typerefs2/package.json' does not exist.", 616 "File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.", 617 "======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========", 618 "Reusing resolution of module './b2' from 'f2.ts' of old program, it was successfully resolved to 'b2.ts'.", 619 "Reusing resolution of module './f1' from 'f2.ts' of old program, it was successfully resolved to 'f1.ts'.", 620 ], "program_7 should reuse module resolutions in f2 since it is unchanged"); 621 } 622 }); 623 624 describe("redirects", () => { 625 const axIndex = "/node_modules/a/node_modules/x/index.d.ts"; 626 const axPackage = "/node_modules/a/node_modules/x/package.json"; 627 const bxIndex = "/node_modules/b/node_modules/x/index.d.ts"; 628 const bxPackage = "/node_modules/b/node_modules/x/package.json"; 629 const root = "/a.ts"; 630 const compilerOptions = { target, moduleResolution: ts.ModuleResolutionKind.NodeJs }; 631 632 function createRedirectProgram(useGetSourceFileByPath: boolean, options?: { bText: string, bVersion: string }): ProgramWithSourceTexts { 633 const files: NamedSourceText[] = [ 634 { 635 name: "/node_modules/a/index.d.ts", 636 text: SourceText.New("", 'import X from "x";', "export function a(x: X): void;"), 637 }, 638 { 639 name: axIndex, 640 text: SourceText.New("", "", "export default class X { private x: number; }"), 641 }, 642 { 643 name: axPackage, 644 text: SourceText.New("", "", JSON.stringify({ name: "x", version: "1.2.3" })), 645 }, 646 { 647 name: "/node_modules/b/index.d.ts", 648 text: SourceText.New("", 'import X from "x";', "export const b: X;"), 649 }, 650 { 651 name: bxIndex, 652 text: SourceText.New("", "", options ? options.bText : "export default class X { private x: number; }"), 653 }, 654 { 655 name: bxPackage, 656 text: SourceText.New("", "", JSON.stringify({ name: "x", version: options ? options.bVersion : "1.2.3" })), 657 }, 658 { 659 name: root, 660 text: SourceText.New("", 'import { a } from "a"; import { b } from "b";', "a(b)"), 661 }, 662 ]; 663 664 return newProgram(files, [root], compilerOptions, useGetSourceFileByPath); 665 } 666 667 function updateRedirectProgram(program: ProgramWithSourceTexts, updater: (files: NamedSourceText[]) => void, useGetSourceFileByPath: boolean): ProgramWithSourceTexts { 668 return updateProgram(program, [root], compilerOptions, updater, /*newTexts*/ undefined, useGetSourceFileByPath); 669 } 670 671 function verifyRedirects(useGetSourceFileByPath: boolean) { 672 it("No changes -> redirect not broken", () => { 673 const program1 = createRedirectProgram(useGetSourceFileByPath); 674 675 const program2 = updateRedirectProgram(program1, files => { 676 updateProgramText(files, root, "const x = 1;"); 677 }, useGetSourceFileByPath); 678 assert.equal(program2.structureIsReused, ts.StructureIsReused.Completely); 679 assert.lengthOf(program2.getSemanticDiagnostics(), 0); 680 }); 681 682 it("Target changes -> redirect broken", () => { 683 const program1 = createRedirectProgram(useGetSourceFileByPath); 684 assert.lengthOf(program1.getSemanticDiagnostics(), 0); 685 686 const program2 = updateRedirectProgram(program1, files => { 687 updateProgramText(files, axIndex, "export default class X { private x: number; private y: number; }"); 688 updateProgramText(files, axPackage, JSON.stringify('{ name: "x", version: "1.2.4" }')); 689 }, useGetSourceFileByPath); 690 assert.equal(program2.structureIsReused, ts.StructureIsReused.Not); 691 assert.lengthOf(program2.getSemanticDiagnostics(), 1); 692 }); 693 694 it("Underlying changes -> redirect broken", () => { 695 const program1 = createRedirectProgram(useGetSourceFileByPath); 696 697 const program2 = updateRedirectProgram(program1, files => { 698 updateProgramText(files, bxIndex, "export default class X { private x: number; private y: number; }"); 699 updateProgramText(files, bxPackage, JSON.stringify({ name: "x", version: "1.2.4" })); 700 }, useGetSourceFileByPath); 701 assert.equal(program2.structureIsReused, ts.StructureIsReused.Not); 702 assert.lengthOf(program2.getSemanticDiagnostics(), 1); 703 }); 704 705 it("Previously duplicate packages -> program structure not reused", () => { 706 const program1 = createRedirectProgram(useGetSourceFileByPath, { bVersion: "1.2.4", bText: "export = class X { private x: number; }" }); 707 708 const program2 = updateRedirectProgram(program1, files => { 709 updateProgramText(files, bxIndex, "export default class X { private x: number; }"); 710 updateProgramText(files, bxPackage, JSON.stringify({ name: "x", version: "1.2.3" })); 711 }, useGetSourceFileByPath); 712 assert.equal(program2.structureIsReused, ts.StructureIsReused.Not); 713 assert.deepEqual(program2.getSemanticDiagnostics(), []); 714 }); 715 } 716 717 describe("when host implements getSourceFile", () => { 718 verifyRedirects(/*useGetSourceFileByPath*/ false); 719 }); 720 describe("when host implements getSourceFileByPath", () => { 721 verifyRedirects(/*useGetSourceFileByPath*/ true); 722 }); 723 }); 724}); 725 726describe("unittests:: Reuse program structure:: host is optional", () => { 727 it("should work if host is not provided", () => { 728 ts.createProgram([], {}); 729 }); 730}); 731 732type File = ts.TestFSWithWatch.File; 733import createTestSystem = ts.TestFSWithWatch.createWatchedSystem; 734import libFile = ts.TestFSWithWatch.libFile; 735 736describe("unittests:: Reuse program structure:: isProgramUptoDate", () => { 737 function getWhetherProgramIsUptoDate( 738 program: ts.Program, 739 newRootFileNames: string[], 740 newOptions: ts.CompilerOptions 741 ) { 742 return ts.isProgramUptoDate( 743 program, newRootFileNames, newOptions, 744 path => program.getSourceFileByPath(path)!.version, /*fileExists*/ ts.returnFalse, 745 /*hasInvalidatedResolutions*/ ts.returnFalse, 746 /*hasChangedAutomaticTypeDirectiveNames*/ undefined, 747 /*getParsedCommandLine*/ ts.returnUndefined, 748 /*projectReferences*/ undefined 749 ); 750 } 751 752 function duplicate(options: ts.CompilerOptions): ts.CompilerOptions; 753 function duplicate(fileNames: string[]): string[]; 754 function duplicate(filesOrOptions: ts.CompilerOptions | string[]) { 755 return JSON.parse(JSON.stringify(filesOrOptions)); 756 } 757 758 describe("should return true when there is no change in compiler options and", () => { 759 function verifyProgramIsUptoDate( 760 program: ts.Program, 761 newRootFileNames: string[], 762 newOptions: ts.CompilerOptions 763 ) { 764 const actual = getWhetherProgramIsUptoDate(program, newRootFileNames, newOptions); 765 assert.isTrue(actual); 766 } 767 768 function verifyProgramWithoutConfigFile(system: ts.System, rootFiles: string[], options: ts.CompilerOptions) { 769 const program = ts.createWatchProgram(ts.createWatchCompilerHostOfFilesAndCompilerOptions({ 770 rootFiles, 771 options, 772 watchOptions: undefined, 773 system 774 })).getCurrentProgram().getProgram(); 775 verifyProgramIsUptoDate(program, duplicate(rootFiles), duplicate(options)); 776 } 777 778 function verifyProgramWithConfigFile(system: ts.System, configFileName: string) { 779 const program = ts.createWatchProgram(ts.createWatchCompilerHostOfConfigFile({ 780 configFileName, 781 system 782 })).getCurrentProgram().getProgram(); 783 const { fileNames, options } = ts.parseConfigFileWithSystem(configFileName, {}, /*extendedConfigCache*/ undefined, /*watchOptionsToExtend*/ undefined, system, ts.notImplemented)!; // TODO: GH#18217 784 verifyProgramIsUptoDate(program, fileNames, options); 785 } 786 787 function verifyProgram(files: File[], rootFiles: string[], options: ts.CompilerOptions, configFile: string) { 788 const system = createTestSystem(files); 789 verifyProgramWithoutConfigFile(system, rootFiles, options); 790 verifyProgramWithConfigFile(system, configFile); 791 } 792 793 it("has empty options", () => { 794 const file1: File = { 795 path: "/a/b/file1.ts", 796 content: "let x = 1" 797 }; 798 const file2: File = { 799 path: "/a/b/file2.ts", 800 content: "let y = 1" 801 }; 802 const configFile: File = { 803 path: "/a/b/tsconfig.json", 804 content: "{}" 805 }; 806 verifyProgram([file1, file2, libFile, configFile], [file1.path, file2.path], {}, configFile.path); 807 }); 808 809 it("has lib specified in the options", () => { 810 const compilerOptions: ts.CompilerOptions = { lib: ["es5", "es2015.promise"] }; 811 const app: File = { 812 path: "/src/app.ts", 813 content: "var x: Promise<string>;" 814 }; 815 const configFile: File = { 816 path: "/src/tsconfig.json", 817 content: JSON.stringify({ compilerOptions }) 818 }; 819 const es5Lib: File = { 820 path: "/compiler/lib.es5.d.ts", 821 content: "declare const eval: any" 822 }; 823 const es2015Promise: File = { 824 path: "/compiler/lib.es2015.promise.d.ts", 825 content: "declare class Promise<T> {}" 826 }; 827 828 verifyProgram([app, configFile, es5Lib, es2015Promise], [app.path], compilerOptions, configFile.path); 829 }); 830 831 it("has paths specified in the options", () => { 832 const compilerOptions: ts.CompilerOptions = { 833 baseUrl: ".", 834 paths: { 835 "*": [ 836 "packages/mail/data/*", 837 "packages/styles/*", 838 "*" 839 ] 840 } 841 }; 842 const app: File = { 843 path: "/src/packages/framework/app.ts", 844 content: 'import classc from "module1/lib/file1";\ 845 import classD from "module3/file3";\ 846 let x = new classc();\ 847 let y = new classD();' 848 }; 849 const module1: File = { 850 path: "/src/packages/mail/data/module1/lib/file1.ts", 851 content: 'import classc from "module2/file2";export default classc;', 852 }; 853 const module2: File = { 854 path: "/src/packages/mail/data/module1/lib/module2/file2.ts", 855 content: 'class classc { method2() { return "hello"; } }\nexport default classc', 856 }; 857 const module3: File = { 858 path: "/src/packages/styles/module3/file3.ts", 859 content: "class classD { method() { return 10; } }\nexport default classD;" 860 }; 861 const configFile: File = { 862 path: "/src/tsconfig.json", 863 content: JSON.stringify({ compilerOptions }) 864 }; 865 866 verifyProgram([app, module1, module2, module3, libFile, configFile], [app.path], compilerOptions, configFile.path); 867 }); 868 869 it("has include paths specified in tsconfig file", () => { 870 const compilerOptions: ts.CompilerOptions = { 871 baseUrl: ".", 872 paths: { 873 "*": [ 874 "packages/mail/data/*", 875 "packages/styles/*", 876 "*" 877 ] 878 } 879 }; 880 const app: File = { 881 path: "/src/packages/framework/app.ts", 882 content: 'import classc from "module1/lib/file1";\ 883 import classD from "module3/file3";\ 884 let x = new classc();\ 885 let y = new classD();' 886 }; 887 const module1: File = { 888 path: "/src/packages/mail/data/module1/lib/file1.ts", 889 content: 'import classc from "module2/file2";export default classc;', 890 }; 891 const module2: File = { 892 path: "/src/packages/mail/data/module1/lib/module2/file2.ts", 893 content: 'class classc { method2() { return "hello"; } }\nexport default classc', 894 }; 895 const module3: File = { 896 path: "/src/packages/styles/module3/file3.ts", 897 content: "class classD { method() { return 10; } }\nexport default classD;" 898 }; 899 const configFile: File = { 900 path: "/src/tsconfig.json", 901 content: JSON.stringify({ compilerOptions, include: ["packages/**/*.ts"] }) 902 }; 903 verifyProgramWithConfigFile(createTestSystem([app, module1, module2, module3, libFile, configFile]), configFile.path); 904 }); 905 it("has the same root file names", () => { 906 const module1: File = { 907 path: "/src/packages/mail/data/module1/lib/file1.ts", 908 content: 'import classc from "module2/file2";export default classc;', 909 }; 910 const module2: File = { 911 path: "/src/packages/mail/data/module1/lib/module2/file2.ts", 912 content: 'class classc { method2() { return "hello"; } }\nexport default classc', 913 }; 914 const module3: File = { 915 path: "/src/packages/styles/module3/file3.ts", 916 content: "class classD { method() { return 10; } }\nexport default classD;" 917 }; 918 const rootFiles = [module1.path, module2.path, module3.path]; 919 const system = createTestSystem([module1, module2, module3]); 920 const options = {}; 921 const program = ts.createWatchProgram(ts.createWatchCompilerHostOfFilesAndCompilerOptions({ 922 rootFiles, 923 options, 924 watchOptions: undefined, 925 system 926 })).getCurrentProgram().getProgram(); 927 verifyProgramIsUptoDate(program, duplicate(rootFiles), duplicate(options)); 928 }); 929 930 }); 931 describe("should return false when there is no change in compiler options but", () => { 932 function verifyProgramIsNotUptoDate( 933 program: ts.Program, 934 newRootFileNames: string[], 935 newOptions: ts.CompilerOptions 936 ) { 937 const actual = getWhetherProgramIsUptoDate(program, newRootFileNames, newOptions); 938 assert.isFalse(actual); 939 } 940 it("has more root file names", () => { 941 const module1: File = { 942 path: "/src/packages/mail/data/module1/lib/file1.ts", 943 content: 'import classc from "module2/file2";export default classc;', 944 }; 945 const module2: File = { 946 path: "/src/packages/mail/data/module1/lib/module2/file2.ts", 947 content: 'class classc { method2() { return "hello"; } }\nexport default classc', 948 }; 949 const module3: File = { 950 path: "/src/packages/styles/module3/file3.ts", 951 content: "class classD { method() { return 10; } }\nexport default classD;" 952 }; 953 const rootFiles = [module1.path, module2.path]; 954 const newRootFiles = [module1.path, module2.path, module3.path]; 955 const system = createTestSystem([module1, module2, module3]); 956 const options = {}; 957 const program = ts.createWatchProgram(ts.createWatchCompilerHostOfFilesAndCompilerOptions({ 958 rootFiles, 959 options, 960 watchOptions: undefined, 961 system 962 })).getCurrentProgram().getProgram(); 963 verifyProgramIsNotUptoDate(program, duplicate(newRootFiles), duplicate(options)); 964 }); 965 it("has one root file replaced by another", () => { 966 const module1: File = { 967 path: "/src/packages/mail/data/module1/lib/file1.ts", 968 content: 'import classc from "module2/file2";export default classc;', 969 }; 970 const module2: File = { 971 path: "/src/packages/mail/data/module1/lib/module2/file2.ts", 972 content: 'class classc { method2() { return "hello"; } }\nexport default classc', 973 }; 974 const module3: File = { 975 path: "/src/packages/styles/module3/file3.ts", 976 content: "class classD { method() { return 10; } }\nexport default classD;" 977 }; 978 const rootFiles = [module1.path, module2.path]; 979 const newRootFiles = [module2.path, module3.path]; 980 const system = createTestSystem([module1, module2, module3]); 981 const options = {}; 982 const program = ts.createWatchProgram(ts.createWatchCompilerHostOfFilesAndCompilerOptions({ 983 rootFiles, 984 options, 985 watchOptions: undefined, 986 system 987 })).getCurrentProgram().getProgram(); 988 verifyProgramIsNotUptoDate(program, duplicate(newRootFiles), duplicate(options)); 989 }); 990 }); 991}); 992