diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b7020344aab..fdffb813216 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -899,7 +899,7 @@ namespace ts { } } - let fileName = getResolvedModuleFileName(getSourceFile(location), moduleReferenceLiteral); + let fileName = getResolvedModuleFileName(getSourceFile(location), moduleReferenceLiteral.text); let sourceFile = fileName && host.getSourceFile(fileName); if (sourceFile) { if (sourceFile.symbol) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 3f19c28a9b2..7d1b0ce5fbf 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -163,10 +163,14 @@ namespace ts { let filesByName = createFileMap(fileName => host.getCanonicalFileName(fileName)); - // if old program was provided by has different target module kind - assume that it cannot be reused - // different module kind can lead to different way of resolving modules - if (oldProgram && oldProgram.getCompilerOptions().module !== options.module) { - oldProgram = undefined; + if (oldProgram) { + let oldOptions = oldProgram.getCompilerOptions(); + if ((oldOptions.module !== options.module) || + (oldOptions.noResolve !== options.noResolve) || + (oldOptions.target !== options.target) || + (oldOptions.noLib !== options.noLib)) { + oldProgram = undefined; + } } if (!tryReuseStructureFromOldProgram()) { @@ -222,10 +226,6 @@ namespace ts { } function tryReuseStructureFromOldProgram(): boolean { - if (!host.hasChanges) { - // host does not support method 'hasChanges' - return false; - } if (!oldProgram) { return false; } @@ -234,28 +234,19 @@ namespace ts { // there is an old program, check if we can reuse its structure let oldRootNames = oldProgram.getRootFileNames(); - if (rootNames.length !== oldRootNames.length) { - // different amount of root names - structure cannot be reused + if (!arrayIsEqualTo(oldRootNames, rootNames)) { return false; } - for (let i = 0; i < rootNames.length; i++) { - if (oldRootNames[i] !== rootNames[i]) { - // different order of root names - structure cannot be reused - return false; - } - } - // check if program source files has changed in the way that can affect structure of the program let newSourceFiles: SourceFile[] = []; for (let oldSourceFile of oldProgram.getSourceFiles()) { - let newSourceFile: SourceFile; - if (host.hasChanges(oldSourceFile)) { - newSourceFile = host.getSourceFile(oldSourceFile.fileName, options.target); - if (!newSourceFile) { - return false; - } - + let newSourceFile = host.getSourceFile(oldSourceFile.fileName, options.target); + if (!newSourceFile) { + return false; + } + + if (oldSourceFile !== newSourceFile) { // check tripleslash references if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) { // tripleslash references has changed @@ -420,7 +411,7 @@ namespace ts { } function moduleNameIsEqualTo(a: LiteralExpression, b: LiteralExpression): boolean { - return a.text ===b.text; + return a.text === b.text; } function collectExternalModuleReferences(file: SourceFile): void { @@ -603,7 +594,7 @@ namespace ts { checkImports: { if (file.resolvedModules) { for (let moduleName of file.imports) { - if (!hasResolvedModuleName(file, moduleName)) { + if (!hasResolvedModuleName(file, moduleName.text)) { break checkImports; } } @@ -640,7 +631,7 @@ namespace ts { if (existingResolutions && hasProperty(existingResolutions, moduleNameExpr.text)) { let fileName = existingResolutions[moduleNameExpr.text]; // use existing resolution - setResolvedModuleName(file, moduleNameExpr, fileName); + setResolvedModuleName(file, moduleNameExpr.text, fileName); if (fileName) { findModuleSourceFile(fileName, moduleNameExpr); } @@ -651,7 +642,7 @@ namespace ts { searchName = normalizePath(combinePaths(searchPath, moduleNameExpr.text)); let referencedSourceFile = forEach(supportedExtensions, extension => findModuleSourceFile(searchName + extension, moduleNameExpr)); if (referencedSourceFile) { - setResolvedModuleName(file, moduleNameExpr, referencedSourceFile.fileName); + setResolvedModuleName(file, moduleNameExpr.text, referencedSourceFile.fileName); return; } @@ -662,7 +653,7 @@ namespace ts { searchPath = parentPath; } // mark reference as non-resolved - setResolvedModuleName(file, moduleNameExpr, undefined); + setResolvedModuleName(file, moduleNameExpr.text, undefined); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1dbb3cabe09..79ac4e85dd8 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2075,7 +2075,6 @@ namespace ts { getCanonicalFileName(fileName: string): string; useCaseSensitiveFileNames(): boolean; getNewLine(): string; - hasChanges?(oldFile: SourceFile): boolean; } export interface TextSpan { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 41cf35e0483..150101b641b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -78,7 +78,7 @@ namespace ts { return node.end - node.pos; } - export function arrayIsEqualTo(arr1: T[], arr2: T[], comparer: (a: T, b: T) => boolean): boolean { + export function arrayIsEqualTo(arr1: T[], arr2: T[], comparer?: (a: T, b: T) => boolean): boolean { if (!arr1 || !arr2) { return arr1 === arr2; } @@ -88,7 +88,8 @@ namespace ts { } for (let i = 0; i < arr1.length; ++i) { - if (!comparer(arr1[i], arr2[i])) { + let equals = comparer ? comparer(arr1[i], arr2[i]) : arr1[i] === arr2[i]; + if (!equals) { return false; } } @@ -96,20 +97,20 @@ namespace ts { return true; } - export function hasResolvedModuleName(sourceFile: SourceFile, moduleName: LiteralExpression): boolean { - return sourceFile.resolvedModules && hasProperty(sourceFile.resolvedModules, moduleName.text); + export function hasResolvedModuleName(sourceFile: SourceFile, moduleNameText: string): boolean { + return sourceFile.resolvedModules && hasProperty(sourceFile.resolvedModules, moduleNameText); } - export function getResolvedModuleFileName(sourceFile: SourceFile, moduleName: LiteralExpression): string { - return sourceFile.resolvedModules && sourceFile.resolvedModules[moduleName.text]; + export function getResolvedModuleFileName(sourceFile: SourceFile, moduleNameText: string): string { + return hasResolvedModuleName(sourceFile, moduleNameText) ? sourceFile.resolvedModules[moduleNameText]: undefined; } - export function setResolvedModuleName(sourceFile: SourceFile, moduleName: LiteralExpression, resolvedFileName: string): void { + export function setResolvedModuleName(sourceFile: SourceFile, moduleNameText: string, resolvedFileName: string): void { if (!sourceFile.resolvedModules) { sourceFile.resolvedModules = {}; } - sourceFile.resolvedModules[moduleName.text] = resolvedFileName; + sourceFile.resolvedModules[moduleNameText] = resolvedFileName; } // Returns true if this node contains a parse error anywhere underneath it. diff --git a/src/services/services.ts b/src/services/services.ts index a27327b77b4..b8363fb4598 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1898,7 +1898,7 @@ namespace ts { let getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames); function getKeyFromCompilationSettings(settings: CompilerOptions): string { - return "_" + settings.target; // + "|" + settings.propagateEnumConstantoString() + return "_" + settings.target + "|" + settings.module + "|" + settings.noResolve; } function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): FileMap { @@ -2472,8 +2472,6 @@ namespace ts { let newSettings = hostCache.compilationSettings(); let changesInCompilationSettingsAffectSyntax = oldSettings && oldSettings.target !== newSettings.target; - let reusableOldProgram = changesInCompilationSettingsAffectSyntax ? undefined : program; - // Now create a new compiler let newProgram = createProgram(hostCache.getRootFileNames(), newSettings, { getSourceFile: getOrCreateSourceFile, @@ -2484,8 +2482,7 @@ namespace ts { getDefaultLibFileName: (options) => host.getDefaultLibFileName(options), writeFile: (fileName, data, writeByteOrderMark) => { }, getCurrentDirectory: () => host.getCurrentDirectory(), - hasChanges: oldFile => oldFile.version !== hostCache.getVersion(oldFile.fileName) - }, reusableOldProgram); + }, program); // Release any files we have acquired in the old program but are // not part of the new program. diff --git a/tests/cases/unittests/services/documentRegistry.ts b/tests/cases/unittests/services/documentRegistry.ts index 8fc466b857f..50a144d3056 100644 --- a/tests/cases/unittests/services/documentRegistry.ts +++ b/tests/cases/unittests/services/documentRegistry.ts @@ -30,10 +30,15 @@ describe("DocumentRegistry", () => { assert(f1 !== f3, "Changed target: Expected to have different instances of document"); - compilerOptions.module = ts.ModuleKind.CommonJS; + compilerOptions.preserveConstEnums = true; var f4 = documentRegistry.acquireDocument("file1.ts", compilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), /* version */ "1"); - assert(f3 === f4, "Changed module: Expected to have the same instance of the document"); + assert(f3 === f4, "Changed preserveConstEnums: Expected to have the same instance of the document"); + + compilerOptions.module = ts.ModuleKind.System; + var f5 = documentRegistry.acquireDocument("file1.ts", compilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), /* version */ "1"); + + assert(f4 !== f5, "Changed module: Expected to have different instances of the document"); }); it("Acquiring document gets correct version 1", () => {