Make any paths in buildInfoFile to be relative to it.

This commit is contained in:
Sheetal Nandi
2019-06-17 14:27:55 -07:00
parent 7ed3896a0a
commit d760cbb74b
20 changed files with 420 additions and 415 deletions
+53 -55
View File
@@ -10,18 +10,13 @@ namespace ts {
export interface ReusableDiagnosticRelatedInformation {
category: DiagnosticCategory;
code: number;
file: Path | undefined;
file: string | undefined;
start: number | undefined;
length: number | undefined;
messageText: string | ReusableDiagnosticMessageChain;
}
export interface ReusableDiagnosticMessageChain {
messageText: string;
category: DiagnosticCategory;
code: number;
next?: ReusableDiagnosticMessageChain;
}
export type ReusableDiagnosticMessageChain = DiagnosticMessageChain;
export interface ReusableBuilderProgramState extends ReusableBuilderState {
/**
@@ -227,7 +222,7 @@ namespace ts {
// Unchanged file copy diagnostics
const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath);
if (diagnostics) {
state.semanticDiagnosticsPerFile!.set(sourceFilePath, oldState!.hasReusableDiagnostic ? convertToDiagnostics(diagnostics as ReadonlyArray<ReusableDiagnostic>, newProgram) : diagnostics as ReadonlyArray<Diagnostic>);
state.semanticDiagnosticsPerFile!.set(sourceFilePath, oldState!.hasReusableDiagnostic ? convertToDiagnostics(diagnostics as ReadonlyArray<ReusableDiagnostic>, newProgram, getCanonicalFileName) : diagnostics as ReadonlyArray<Diagnostic>);
if (!state.semanticDiagnosticsFromOldState) {
state.semanticDiagnosticsFromOldState = createMap<true>();
}
@@ -246,37 +241,32 @@ namespace ts {
return state;
}
function convertToDiagnostics(diagnostics: ReadonlyArray<ReusableDiagnostic>, newProgram: Program): ReadonlyArray<Diagnostic> {
function convertToDiagnostics(diagnostics: ReadonlyArray<ReusableDiagnostic>, newProgram: Program, getCanonicalFileName: GetCanonicalFileName): ReadonlyArray<Diagnostic> {
if (!diagnostics.length) return emptyArray;
const buildInfoDirectory = getDirectoryPath(getOutputPathForBuildInfo(newProgram.getCompilerOptions())!);
return diagnostics.map(diagnostic => {
const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram);
const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram, toPath);
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
result.source = diagnostic.source;
const { relatedInformation } = diagnostic;
result.relatedInformation = relatedInformation ?
relatedInformation.length ?
relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, newProgram)) :
relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, newProgram, toPath)) :
emptyArray :
undefined;
return result;
});
function toPath(path: string) {
return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
}
}
function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, newProgram: Program): DiagnosticRelatedInformation {
const { file, messageText } = diagnostic;
function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, newProgram: Program, toPath: (path: string) => Path): DiagnosticRelatedInformation {
const { file } = diagnostic;
return {
...diagnostic,
file: file && newProgram.getSourceFileByPath(file),
messageText: messageText === undefined || isString(messageText) ?
messageText :
convertToDiagnosticMessageChain(messageText, newProgram)
};
}
function convertToDiagnosticMessageChain(diagnostic: ReusableDiagnosticMessageChain, newProgram: Program): DiagnosticMessageChain {
return {
...diagnostic,
next: diagnostic.next && convertToDiagnosticMessageChain(diagnostic.next, newProgram)
file: file ? newProgram.getSourceFileByPath(toPath(file)) : undefined
};
}
@@ -620,19 +610,20 @@ namespace ts {
/**
* Gets the program information to be emitted in buildInfo so that we can use it to create new program
*/
function getProgramBuildInfo(state: Readonly<ReusableBuilderProgramState>): ProgramBuildInfo | undefined {
function getProgramBuildInfo(state: Readonly<ReusableBuilderProgramState>, getCanonicalFileName: GetCanonicalFileName): ProgramBuildInfo | undefined {
if (state.compilerOptions.outFile || state.compilerOptions.out) return undefined;
const buildInfoDirectory = getDirectoryPath(getOutputPathForBuildInfo(state.compilerOptions)!);
const fileInfos: MapLike<BuilderState.FileInfo> = {};
state.fileInfos.forEach((value, key) => {
const signature = state.currentAffectedFilesSignatures && state.currentAffectedFilesSignatures.get(key);
fileInfos[key] = signature === undefined ? value : { version: value.version, signature };
fileInfos[relativeToBuildInfo(key)] = signature === undefined ? value : { version: value.version, signature };
});
const result: ProgramBuildInfo = { fileInfos, options: state.compilerOptions };
if (state.referencedMap) {
const referencedMap: MapLike<string[]> = {};
state.referencedMap.forEach((value, key) => {
referencedMap[key] = arrayFrom(value.keys());
referencedMap[relativeToBuildInfo(key)] = arrayFrom(value.keys(), relativeToBuildInfo);
});
result.referencedMap = referencedMap;
}
@@ -642,9 +633,9 @@ namespace ts {
state.exportedModulesMap.forEach((value, key) => {
const newValue = state.currentAffectedFilesExportedModulesMap && state.currentAffectedFilesExportedModulesMap.get(key);
// Not in temporary cache, use existing value
if (newValue === undefined) exportedModulesMap[key] = arrayFrom(value.keys());
if (newValue === undefined) exportedModulesMap[relativeToBuildInfo(key)] = arrayFrom(value.keys(), relativeToBuildInfo);
// Value in cache and has updated value map, use that
else if (newValue) exportedModulesMap[key] = arrayFrom(newValue.keys());
else if (newValue) exportedModulesMap[relativeToBuildInfo(key)] = arrayFrom(newValue.keys(), relativeToBuildInfo);
});
result.exportedModulesMap = exportedModulesMap;
}
@@ -655,50 +646,44 @@ namespace ts {
state.semanticDiagnosticsPerFile.forEach((value, key) => semanticDiagnosticsPerFile.push(
value.length ?
[
key,
relativeToBuildInfo(key),
state.hasReusableDiagnostic ?
value as ReadonlyArray<ReusableDiagnostic> :
convertToReusableDiagnostics(value as ReadonlyArray<Diagnostic>)
convertToReusableDiagnostics(value as ReadonlyArray<Diagnostic>, relativeToBuildInfo)
] :
key
relativeToBuildInfo(key)
));
result.semanticDiagnosticsPerFile = semanticDiagnosticsPerFile;
}
return result;
function relativeToBuildInfo(path: string) {
return ensurePathIsNonModuleName(getRelativePathFromDirectory(buildInfoDirectory, path, getCanonicalFileName));
}
}
function convertToReusableDiagnostics(diagnostics: ReadonlyArray<Diagnostic>): ReadonlyArray<ReusableDiagnostic> {
function convertToReusableDiagnostics(diagnostics: ReadonlyArray<Diagnostic>, relativeToBuildInfo: (path: string) => string): ReadonlyArray<ReusableDiagnostic> {
Debug.assert(!!diagnostics.length);
return diagnostics.map(diagnostic => {
const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic);
const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic, relativeToBuildInfo);
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
result.source = diagnostic.source;
const { relatedInformation } = diagnostic;
result.relatedInformation = relatedInformation ?
relatedInformation.length ?
relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r)) :
relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r, relativeToBuildInfo)) :
emptyArray :
undefined;
return result;
});
}
function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation): ReusableDiagnosticRelatedInformation {
const { file, messageText } = diagnostic;
function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation, relativeToBuildInfo: (path: string) => string): ReusableDiagnosticRelatedInformation {
const { file } = diagnostic;
return {
...diagnostic,
file: file && file.path,
messageText: messageText === undefined || isString(messageText) ?
messageText :
convertToReusableDiagnosticMessageChain(messageText)
};
}
function convertToReusableDiagnosticMessageChain(diagnostic: DiagnosticMessageChain): ReusableDiagnosticMessageChain {
return {
...diagnostic,
next: diagnostic.next && convertToReusableDiagnosticMessageChain(diagnostic.next)
file: file ? relativeToBuildInfo(file.path) : undefined
};
}
@@ -767,7 +752,7 @@ namespace ts {
const computeHash = host.createHash || generateDjb2Hash;
let state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState);
let backupState: BuilderProgramState | undefined;
newProgram.getProgramBuildInfo = () => getProgramBuildInfo(state);
newProgram.getProgramBuildInfo = () => getProgramBuildInfo(state, getCanonicalFileName);
// To ensure that we arent storing any references to old program or new program without state
newProgram = undefined!; // TODO: GH#18217
@@ -980,26 +965,35 @@ namespace ts {
}
}
function getMapOfReferencedSet(mapLike: MapLike<ReadonlyArray<string>> | undefined): ReadonlyMap<BuilderState.ReferencedSet> | undefined {
function getMapOfReferencedSet(mapLike: MapLike<ReadonlyArray<string>> | undefined, toPath: (path: string) => Path): ReadonlyMap<BuilderState.ReferencedSet> | undefined {
if (!mapLike) return undefined;
const map = createMap<BuilderState.ReferencedSet>();
// Copies keys/values from template. Note that for..in will not throw if
// template is undefined, and instead will just exit the loop.
for (const key in mapLike) {
if (hasProperty(mapLike, key)) {
map.set(key, arrayToSet(mapLike[key]));
map.set(toPath(key), arrayToSet(mapLike[key], toPath));
}
}
return map;
}
export function createBuildProgramUsingProgramBuildInfo(program: ProgramBuildInfo): EmitAndSemanticDiagnosticsBuilderProgram {
const fileInfos = createMapFromTemplate(program.fileInfos);
export function createBuildProgramUsingProgramBuildInfo(program: ProgramBuildInfo, buildInfoPath: string, useCaseSensitiveFileNames: boolean): EmitAndSemanticDiagnosticsBuilderProgram {
const buildInfoDirectory = getDirectoryPath(buildInfoPath);
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
const fileInfos = createMap<BuilderState.FileInfo>();
for (const key in program.fileInfos) {
if (hasProperty(program.fileInfos, key)) {
fileInfos.set(toPath(key), program.fileInfos[key]);
}
}
const state: ReusableBuilderProgramState = {
fileInfos,
compilerOptions: program.options,
referencedMap: getMapOfReferencedSet(program.referencedMap),
exportedModulesMap: getMapOfReferencedSet(program.exportedModulesMap),
referencedMap: getMapOfReferencedSet(program.referencedMap, toPath),
exportedModulesMap: getMapOfReferencedSet(program.exportedModulesMap, toPath),
semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => isString(value) ? value : value[0], value => isString(value) ? emptyArray : value[1]),
hasReusableDiagnostic: true
};
@@ -1025,6 +1019,10 @@ namespace ts {
emitNextAffectedFile: notImplemented,
getSemanticDiagnosticsOfNextAffectedFile: notImplemented,
};
function toPath(path: string) {
return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
}
}
export function createRedirectedBuilderProgram(state: { program: Program | undefined; compilerOptions: CompilerOptions; }, configFileParsingDiagnostics: ReadonlyArray<Diagnostic>): BuilderProgram {
+2 -2
View File
@@ -1298,11 +1298,11 @@ namespace ts {
}
}
function getOldProgram<T extends BuilderProgram>({ options, builderPrograms, readFileWithCache }: SolutionBuilderState<T>, proj: ResolvedConfigFilePath, parsed: ParsedCommandLine) {
function getOldProgram<T extends BuilderProgram>({ options, builderPrograms, readFileWithCache, host }: SolutionBuilderState<T>, proj: ResolvedConfigFilePath, parsed: ParsedCommandLine) {
if (options.force) return undefined;
const value = builderPrograms.get(proj);
if (value) return value;
return readBuilderProgram(parsed.options, readFileWithCache) as any as T;
return readBuilderProgram(parsed.options, readFileWithCache, host.useCaseSensitiveFileNames()) as any as T;
}
function afterProgramCreate<T extends BuilderProgram>({ host, watch, builderPrograms }: SolutionBuilderState<T>, proj: ResolvedConfigFilePath, program: T) {
+4 -4
View File
@@ -437,7 +437,7 @@ namespace ts {
}
namespace ts {
export function readBuilderProgram(compilerOptions: CompilerOptions, readFile: (path: string) => string | undefined) {
export function readBuilderProgram(compilerOptions: CompilerOptions, readFile: (path: string) => string | undefined, useCaseSensitiveFileNames: boolean) {
if (compilerOptions.out || compilerOptions.outFile) return undefined;
const buildInfoPath = getOutputPathForBuildInfo(compilerOptions);
if (!buildInfoPath) return undefined;
@@ -446,7 +446,7 @@ namespace ts {
const buildInfo = getBuildInfo(content);
if (buildInfo.version !== version) return undefined;
if (!buildInfo.program) return undefined;
return createBuildProgramUsingProgramBuildInfo(buildInfo.program);
return createBuildProgramUsingProgramBuildInfo(buildInfo.program, buildInfoPath, useCaseSensitiveFileNames);
}
export function createIncrementalCompilerHost(options: CompilerOptions, system = sys): CompilerHost {
@@ -471,7 +471,7 @@ namespace ts {
}: IncrementalProgramOptions<T>): T {
host = host || createIncrementalCompilerHost(options);
createProgram = createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>;
const oldProgram = readBuilderProgram(options, path => host!.readFile(path)) as any as T;
const oldProgram = readBuilderProgram(options, path => host!.readFile(path), host.useCaseSensitiveFileNames()) as any as T;
return createProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences);
}
@@ -747,7 +747,7 @@ namespace ts {
((typeDirectiveNames, containingFile, redirectedReference) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference));
const userProvidedResolution = !!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives;
builderProgram = readBuilderProgram(compilerOptions, path => compilerHost.readFile(path)) as any as T;
builderProgram = readBuilderProgram(compilerOptions, path => compilerHost.readFile(path), compilerHost.useCaseSensitiveFileNames()) as any as T;
synchronizeProgram();
// Update the wild card directory watch
@@ -129,6 +129,14 @@ namespace ts.tscWatch {
signature: Harness.mockHash(libFile.content)
};
const getCanonicalFileName = createGetCanonicalFileName(/*useCaseSensitiveFileNames*/ false);
function relativeToBuildInfo(buildInfoPath: string, path: string) {
return getRelativePathFromFile(buildInfoPath, path, getCanonicalFileName);
}
const buildInfoPath = `${project}/tsconfig.tsbuildinfo`;
const [libFilePath, file1Path, file2Path] = [libFile.path, `${project}/file1.ts`, `${project}/file2.ts`].map(path => relativeToBuildInfo(buildInfoPath, path));
describe("non module compilation", () => {
function getFileInfo(content: string): BuilderState.FileInfo {
return { version: Harness.mockHash(content), signature: Harness.mockHash(`declare ${content}\n`) };
@@ -150,7 +158,6 @@ namespace ts.tscWatch {
path: `${project}/file2.js`,
content: "var y = 20;\n"
};
describe("own file emit without errors", () => {
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
verifyIncrementalWatchEmit({
@@ -163,14 +170,14 @@ namespace ts.tscWatch {
content: getBuildInfoText({
program: {
fileInfos: {
[libFile.path]: libFileInfo,
[file1.path]: getFileInfo(file1.content),
[file2.path]: getFileInfo(file2.content)
[libFilePath]: libFileInfo,
[file1Path]: getFileInfo(file1.content),
[file2Path]: getFileInfo(file2.content)
},
options: { incremental: true, configFilePath: configFile.path },
referencedMap: {},
exportedModulesMap: {},
semanticDiagnosticsPerFile: [libFile.path, file1.path, file2.path]
semanticDiagnosticsPerFile: [libFilePath, file1Path, file2Path]
},
version
})
@@ -186,14 +193,14 @@ namespace ts.tscWatch {
content: getBuildInfoText({
program: {
fileInfos: {
[libFile.path]: libFileInfo,
[file1.path]: getFileInfo(file1.content),
[file2.path]: getFileInfo(modifiedFile2Content)
[libFilePath]: libFileInfo,
[file1Path]: getFileInfo(file1.content),
[file2Path]: getFileInfo(modifiedFile2Content)
},
options: { incremental: true, configFilePath: configFile.path },
referencedMap: {},
exportedModulesMap: {},
semanticDiagnosticsPerFile: [libFile.path, file1.path, file2.path]
semanticDiagnosticsPerFile: [libFilePath, file1Path, file2Path]
},
version
})
@@ -213,16 +220,16 @@ namespace ts.tscWatch {
signature: Harness.mockHash("declare const y: string;\n")
};
const file2ReuasableError: ProgramBuildInfoDiagnostic = [
file2.path, [
file2Path, [
{
file: file2.path,
file: file2Path,
start: 6,
length: 1,
code: Diagnostics.Type_0_is_not_assignable_to_type_1.code,
category: Diagnostics.Type_0_is_not_assignable_to_type_1.category,
messageText: "Type '20' is not assignable to type 'string'."
}
] as ReusableDiagnostic[]
]
];
const file2Errors = [
"file2.ts(1,7): error TS2322: Type '20' is not assignable to type 'string'.\n"
@@ -238,16 +245,16 @@ namespace ts.tscWatch {
content: getBuildInfoText({
program: {
fileInfos: {
[libFile.path]: libFileInfo,
[file1.path]: getFileInfo(file1.content),
[file2.path]: file2FileInfo
[libFilePath]: libFileInfo,
[file1Path]: getFileInfo(file1.content),
[file2Path]: file2FileInfo
},
options: { incremental: true, configFilePath: configFile.path },
referencedMap: {},
exportedModulesMap: {},
semanticDiagnosticsPerFile: [
libFile.path,
file1.path,
libFilePath,
file1Path,
file2ReuasableError
]
},
@@ -265,16 +272,16 @@ namespace ts.tscWatch {
content: getBuildInfoText({
program: {
fileInfos: {
[libFile.path]: libFileInfo,
[file1.path]: getFileInfo(modifiedFile1Content),
[file2.path]: file2FileInfo
[libFilePath]: libFileInfo,
[file1Path]: getFileInfo(modifiedFile1Content),
[file2Path]: file2FileInfo
},
options: { incremental: true, configFilePath: configFile.path },
referencedMap: {},
exportedModulesMap: {},
semanticDiagnosticsPerFile: [
libFile.path,
file1.path,
libFilePath,
file1Path,
file2ReuasableError
]
},
@@ -369,14 +376,14 @@ namespace ts.tscWatch {
content: getBuildInfoText({
program: {
fileInfos: {
[libFile.path]: libFileInfo,
[file1.path]: getFileInfo(file1.content),
[file2.path]: getFileInfo(file2.content)
[libFilePath]: libFileInfo,
[file1Path]: getFileInfo(file1.content),
[file2Path]: getFileInfo(file2.content)
},
options: { incremental: true, module: ModuleKind.AMD, configFilePath: configFile.path },
referencedMap: {},
exportedModulesMap: {},
semanticDiagnosticsPerFile: [libFile.path, file1.path, file2.path]
semanticDiagnosticsPerFile: [libFilePath, file1Path, file2Path]
},
version
})
@@ -391,14 +398,14 @@ namespace ts.tscWatch {
content: getBuildInfoText({
program: {
fileInfos: {
[libFile.path]: libFileInfo,
[file1.path]: getFileInfo(file1.content),
[file2.path]: getFileInfo(modifiedFile2Content)
[libFilePath]: libFileInfo,
[file1Path]: getFileInfo(file1.content),
[file2Path]: getFileInfo(modifiedFile2Content)
},
options: { incremental: true, module: ModuleKind.AMD, configFilePath: configFile.path },
referencedMap: {},
exportedModulesMap: {},
semanticDiagnosticsPerFile: [libFile.path, file1.path, file2.path]
semanticDiagnosticsPerFile: [libFilePath, file1Path, file2Path]
},
version
})
@@ -418,16 +425,16 @@ namespace ts.tscWatch {
signature: Harness.mockHash("export declare const y: string;\n")
};
const file2ReuasableError: ProgramBuildInfoDiagnostic = [
file2.path, [
file2Path, [
{
file: file2.path,
file: file2Path,
start: 13,
length: 1,
code: Diagnostics.Type_0_is_not_assignable_to_type_1.code,
category: Diagnostics.Type_0_is_not_assignable_to_type_1.category,
messageText: "Type '20' is not assignable to type 'string'."
}
] as ReusableDiagnostic[]
]
];
const file2Errors = [
"file2.ts(1,14): error TS2322: Type '20' is not assignable to type 'string'.\n"
@@ -443,16 +450,16 @@ namespace ts.tscWatch {
content: getBuildInfoText({
program: {
fileInfos: {
[libFile.path]: libFileInfo,
[file1.path]: getFileInfo(file1.content),
[file2.path]: file2FileInfo
[libFilePath]: libFileInfo,
[file1Path]: getFileInfo(file1.content),
[file2Path]: file2FileInfo
},
options: { incremental: true, module: ModuleKind.AMD, configFilePath: configFile.path },
referencedMap: {},
exportedModulesMap: {},
semanticDiagnosticsPerFile: [
libFile.path,
file1.path,
libFilePath,
file1Path,
file2ReuasableError
]
},
@@ -469,17 +476,17 @@ namespace ts.tscWatch {
content: getBuildInfoText({
program: {
fileInfos: {
[libFile.path]: libFileInfo,
[file1.path]: getFileInfo(modifiedFile1Content),
[file2.path]: file2FileInfo
[libFilePath]: libFileInfo,
[file1Path]: getFileInfo(modifiedFile1Content),
[file2Path]: file2FileInfo
},
options: { incremental: true, module: ModuleKind.AMD, configFilePath: configFile.path },
referencedMap: {},
exportedModulesMap: {},
semanticDiagnosticsPerFile: [
libFile.path,
libFilePath,
file2ReuasableError,
file1.path
file1Path
]
},
version