mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-09 02:30:15 -06:00
Avoid double-resolving modified files
This commit is contained in:
parent
86d7031932
commit
c63d6d145a
@ -251,6 +251,15 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function zipToMap<T>(keys: string[], values: T[]): Map<T> {
|
||||
Debug.assert(keys.length === values.length);
|
||||
const map = createMap<T>();
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
map.set(keys[i], values[i]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through `array` by index and performs the callback on each element of array until the callback
|
||||
* returns a falsey value, then returns false.
|
||||
|
||||
@ -491,7 +491,24 @@ namespace ts {
|
||||
return resolveModuleNamesWorker(moduleNames, containingFile);
|
||||
}
|
||||
|
||||
// at this point we know at least one of the following hold:
|
||||
const oldSourceFile = oldProgramState.program && oldProgramState.program.getSourceFile(containingFile);
|
||||
if (oldSourceFile !== file && file.resolvedModules) {
|
||||
// `file` was created for the new program.
|
||||
//
|
||||
// We only set `file.resolvedModules` via work from the current function,
|
||||
// so it is defined iff we already called the current function on `file`.
|
||||
// That call happened no later than the creation of the `file` object,
|
||||
// which per above occured during the current program creation.
|
||||
// Since we model program creation as an atomic operation w/r/t IO,
|
||||
// it is safe to reuse resolutions from the earlier call.
|
||||
const result: ResolvedModuleFull[] = [];
|
||||
for (const moduleName of moduleNames) {
|
||||
const resolvedModule = file.resolvedModules.get(moduleName);
|
||||
result.push(resolvedModule);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// At this point, we know at least one of the following hold:
|
||||
// - file has local declarations for ambient modules
|
||||
// - old program state is available
|
||||
// With this information, we can infer some module resolutions without performing resolution.
|
||||
@ -511,7 +528,6 @@ namespace ts {
|
||||
/** A transient placeholder used to mark predicted resolution in the result list. */
|
||||
const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = <any>{};
|
||||
|
||||
const oldSourceFile = oldProgramState.program && oldProgramState.program.getSourceFile(containingFile);
|
||||
|
||||
for (let i = 0; i < moduleNames.length; i++) {
|
||||
const moduleName = moduleNames[i];
|
||||
@ -620,7 +636,7 @@ namespace ts {
|
||||
return oldProgram.structureIsReused = StructureIsReused.Not;
|
||||
}
|
||||
|
||||
Debug.assert(!(oldProgram.structureIsReused & (StructureIsReused.Completely | StructureIsReused.ModulesInUneditedFiles)));
|
||||
Debug.assert(!(oldProgram.structureIsReused & (StructureIsReused.Completely | StructureIsReused.SafeModules)));
|
||||
|
||||
// there is an old program, check if we can reuse its structure
|
||||
const oldRootNames = oldProgram.getRootFileNames();
|
||||
@ -653,32 +669,34 @@ namespace ts {
|
||||
filePaths.push(newSourceFile.path);
|
||||
|
||||
if (oldSourceFile !== newSourceFile) {
|
||||
// The `newSourceFile` object was created for the new program.
|
||||
|
||||
if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
|
||||
// value of no-default-lib has changed
|
||||
// this will affect if default library is injected into the list of files
|
||||
oldProgram.structureIsReused = StructureIsReused.ModulesInUneditedFiles;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
// check tripleslash references
|
||||
if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
|
||||
// tripleslash references has changed
|
||||
oldProgram.structureIsReused = StructureIsReused.ModulesInUneditedFiles;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
// check imports and module augmentations
|
||||
collectExternalModuleReferences(newSourceFile);
|
||||
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
|
||||
// imports has changed
|
||||
oldProgram.structureIsReused = StructureIsReused.ModulesInUneditedFiles;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) {
|
||||
// moduleAugmentations has changed
|
||||
oldProgram.structureIsReused = StructureIsReused.ModulesInUneditedFiles;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
|
||||
// 'types' references has changed
|
||||
oldProgram.structureIsReused = StructureIsReused.ModulesInUneditedFiles;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
// tentatively approve the file
|
||||
@ -695,7 +713,7 @@ namespace ts {
|
||||
|
||||
modifiedFilePaths = modifiedSourceFiles.map(f => f.newFile.path);
|
||||
// try to verify results of module resolution
|
||||
modifiedSourceFilesLoop: for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
|
||||
for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
|
||||
const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.fileName, currentDirectory);
|
||||
if (resolveModuleNamesWorker) {
|
||||
const moduleNames = map(concatenate(newSourceFile.imports, newSourceFile.moduleAugmentations), getTextOfLiteral);
|
||||
@ -704,8 +722,11 @@ namespace ts {
|
||||
// ensure that module resolution results are still correct
|
||||
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
|
||||
if (resolutionsChanged) {
|
||||
oldProgram.structureIsReused = StructureIsReused.ModulesInUneditedFiles;
|
||||
continue modifiedSourceFilesLoop;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
|
||||
}
|
||||
else {
|
||||
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
|
||||
}
|
||||
}
|
||||
if (resolveTypeReferenceDirectiveNamesWorker) {
|
||||
@ -714,13 +735,13 @@ namespace ts {
|
||||
// ensure that types resolutions are still correct
|
||||
const resolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, resolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
|
||||
if (resolutionsChanged) {
|
||||
oldProgram.structureIsReused = StructureIsReused.ModulesInUneditedFiles;
|
||||
continue modifiedSourceFilesLoop;
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
newSourceFile.resolvedTypeReferenceDirectiveNames = zipToMap(typesReferenceDirectives, resolutions);
|
||||
}
|
||||
else {
|
||||
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
|
||||
}
|
||||
}
|
||||
// pass the cache of module/types resolutions from the old source file
|
||||
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
|
||||
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
|
||||
}
|
||||
|
||||
if (oldProgram.structureIsReused !== StructureIsReused.Completely) {
|
||||
|
||||
@ -2412,7 +2412,7 @@ namespace ts {
|
||||
/* @internal */
|
||||
export const enum StructureIsReused {
|
||||
Completely,
|
||||
ModulesInUneditedFiles,
|
||||
SafeModules,
|
||||
Not
|
||||
}
|
||||
|
||||
|
||||
@ -261,7 +261,7 @@ namespace ts {
|
||||
`;
|
||||
files[0].text = files[0].text.updateReferences(newReferences);
|
||||
});
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.ModulesInUneditedFiles);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
|
||||
});
|
||||
|
||||
it("fails if change affects type references", () => {
|
||||
@ -281,7 +281,7 @@ namespace ts {
|
||||
updateProgram(program_1, ["a.ts"], { target }, files => {
|
||||
files[2].text = files[2].text.updateImportsAndExports("import x from 'b'");
|
||||
});
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.ModulesInUneditedFiles);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
|
||||
});
|
||||
|
||||
it("fails if change affects type directives", () => {
|
||||
@ -293,7 +293,7 @@ namespace ts {
|
||||
/// <reference types="typerefs1" />`;
|
||||
files[0].text = files[0].text.updateReferences(newReferences);
|
||||
});
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.ModulesInUneditedFiles);
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
|
||||
});
|
||||
|
||||
it("fails if module kind changes", () => {
|
||||
@ -340,7 +340,7 @@ namespace ts {
|
||||
const program_3 = updateProgram(program_2, ["a.ts"], options, files => {
|
||||
files[0].text = files[0].text.updateImportsAndExports("");
|
||||
});
|
||||
assert.isTrue(program_2.structureIsReused === StructureIsReused.ModulesInUneditedFiles);
|
||||
assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules);
|
||||
checkResolvedModulesCache(program_3, "a.ts", /*expectedContent*/ undefined);
|
||||
|
||||
const program_4 = updateProgram(program_3, ["a.ts"], options, files => {
|
||||
@ -349,7 +349,7 @@ namespace ts {
|
||||
`;
|
||||
files[0].text = files[0].text.updateImportsAndExports(newImports);
|
||||
});
|
||||
assert.isTrue(program_3.structureIsReused === StructureIsReused.ModulesInUneditedFiles);
|
||||
assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules);
|
||||
checkResolvedModulesCache(program_4, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts"), "c": undefined }));
|
||||
});
|
||||
|
||||
@ -378,7 +378,7 @@ namespace ts {
|
||||
files[0].text = files[0].text.updateReferences("");
|
||||
});
|
||||
|
||||
assert.isTrue(program_2.structureIsReused === StructureIsReused.ModulesInUneditedFiles);
|
||||
assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules);
|
||||
checkResolvedTypeDirectivesCache(program_3, "/a.ts", /*expectedContent*/ undefined);
|
||||
|
||||
updateProgram(program_3, ["/a.ts"], options, files => {
|
||||
@ -387,7 +387,7 @@ namespace ts {
|
||||
`;
|
||||
files[0].text = files[0].text.updateReferences(newReferences);
|
||||
});
|
||||
assert.isTrue(program_3.structureIsReused === StructureIsReused.ModulesInUneditedFiles);
|
||||
assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules);
|
||||
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user