Use source files instead of .d.ts files from project references

This commit is contained in:
Sheetal Nandi 2019-06-21 13:11:39 -07:00
parent d0282b75a1
commit 0adab8934a
7 changed files with 119 additions and 18 deletions

View File

@ -813,6 +813,8 @@ namespace ts {
let resolvedProjectReferences: ReadonlyArray<ResolvedProjectReference | undefined> | undefined;
let projectReferenceRedirects: Map<ResolvedProjectReference | false> | undefined;
let mapFromFileToProjectReferenceRedirects: Map<Path> | undefined;
let mapFromToProjectReferenceRedirectSource: Map<SourceOfProjectReferenceRedirect> | undefined;
const useSourceOfReference = host.useSourceInsteadOfReferenceRedirect && host.useSourceInsteadOfReferenceRedirect();
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
const structuralIsReused = tryReuseStructureFromOldProgram();
@ -824,17 +826,29 @@ namespace ts {
if (!resolvedProjectReferences) {
resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
}
if (host.setGetSourceOfProjectReferenceRedirect) {
host.setGetSourceOfProjectReferenceRedirect(getSourceOfProjectReferenceRedirect);
}
if (rootNames.length) {
for (const parsedRef of resolvedProjectReferences) {
if (!parsedRef) continue;
const out = parsedRef.commandLine.options.outFile || parsedRef.commandLine.options.out;
if (out) {
processSourceFile(changeExtension(out, ".d.ts"), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
if (useSourceOfReference) {
if (out || getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
for (const fileName of parsedRef.commandLine.fileNames) {
processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
}
}
}
else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
for (const fileName of parsedRef.commandLine.fileNames) {
if (!fileExtensionIs(fileName, Extension.Dts) && hasTSFileExtension(fileName)) {
processSourceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames()), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
else {
if (out) {
processSourceFile(changeExtension(out, ".d.ts"), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
}
else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
for (const fileName of parsedRef.commandLine.fileNames) {
if (!fileExtensionIs(fileName, Extension.Dts) && hasTSFileExtension(fileName)) {
processSourceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames()), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
}
}
}
}
@ -1212,6 +1226,9 @@ namespace ts {
}
if (projectReferences) {
resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
if (host.setGetSourceOfProjectReferenceRedirect) {
host.setGetSourceOfProjectReferenceRedirect(getSourceOfProjectReferenceRedirect);
}
}
// check if program source files has changed in the way that can affect structure of the program
@ -2220,6 +2237,14 @@ namespace ts {
// Get source file from normalized fileName
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, refFile: SourceFile, refPos: number, refEnd: number, packageId: PackageId | undefined): SourceFile | undefined {
if (useSourceOfReference) {
const source = getSourceOfProjectReferenceRedirect(fileName);
if (source) {
return isString(source) ?
findSourceFile(source, toPath(source), isDefaultLib, ignoreNoDefaultLib, refFile, refPos, refEnd, packageId) :
undefined;
}
}
const originalFileName = fileName;
if (filesByName.has(path)) {
const file = filesByName.get(path);
@ -2267,7 +2292,7 @@ namespace ts {
}
let redirectedPath: Path | undefined;
if (refFile) {
if (refFile && !useSourceOfReference) {
const redirectProject = getProjectReferenceRedirectProject(fileName);
if (redirectProject) {
if (redirectProject.commandLine.options.outFile || redirectProject.commandLine.options.out) {
@ -2286,15 +2311,20 @@ namespace ts {
}
// We haven't looked for this file, do so now and cache result
const file = host.getSourceFile(fileName, options.target!, hostErrorMessage => { // TODO: GH#18217
if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) {
fileProcessingDiagnostics.add(createFileDiagnostic(refFile, refPos, refEnd - refPos,
Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
}
else {
fileProcessingDiagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
}
}, shouldCreateNewSourceFile);
const file = host.getSourceFile(
fileName,
options.target!,
hostErrorMessage => { // TODO: GH#18217
if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) {
fileProcessingDiagnostics.add(createFileDiagnostic(refFile, refPos, refEnd - refPos,
Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
}
else {
fileProcessingDiagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
}
},
shouldCreateNewSourceFile
);
if (packageId) {
const packageIdKey = packageIdToString(packageId);
@ -2424,6 +2454,30 @@ namespace ts {
});
}
function getSourceOfProjectReferenceRedirect(file: string) {
if (!isDeclarationFileName(file)) return undefined;
if (mapFromToProjectReferenceRedirectSource === undefined) {
mapFromToProjectReferenceRedirectSource = createMap();
forEachResolvedProjectReference(resolvedRef => {
if (resolvedRef) {
const out = resolvedRef.commandLine.options.outFile || resolvedRef.commandLine.options.out;
if (out) {
// Dont know which source file it means so return true?
const outputDts = changeExtension(out, Extension.Dts);
mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true);
}
else {
forEach(resolvedRef.commandLine.fileNames, fileName => {
const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, host.useCaseSensitiveFileNames());
mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName);
});
}
}
});
}
return mapFromToProjectReferenceRedirectSource.get(toPath(file));
}
function forEachProjectReference<T>(
projectReferences: ReadonlyArray<ProjectReference> | undefined,
resolvedProjectReferences: ReadonlyArray<ResolvedProjectReference | undefined> | undefined,

View File

@ -5166,11 +5166,20 @@ namespace ts {
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
createHash?(data: string): string;
getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined;
/* @internal */ setGetSourceOfProjectReferenceRedirect?(getSource: GetSourceOfProjectReferenceRedirect): void;
/* @internal */ useSourceInsteadOfReferenceRedirect?(): boolean;
// TODO: later handle this in better way in builder host instead once the api for tsbuild finalizes and doesn't use compilerHost as base
/*@internal*/createDirectory?(directory: string): void;
}
/** true if --out otherwise source file name */
/*@internal*/
export type SourceOfProjectReferenceRedirect = string | true ;
/*@internal*/
export type GetSourceOfProjectReferenceRedirect = (fileName: string) => SourceOfProjectReferenceRedirect | undefined;
/* @internal */
export const enum TransformFlags {
None = 0,

View File

@ -1798,7 +1798,7 @@ namespace ts.server {
let scriptInfo: ScriptInfo | NormalizedPath;
let path: Path;
// Use the project's fileExists so that it can use caching instead of reaching to disk for the query
if (!isDynamic && !project.fileExists(newRootFile)) {
if (!isDynamic && !project.fileExistsWithCache(newRootFile)) {
path = normalizedPathToPath(normalizedPath, this.currentDirectory, this.toCanonicalFileName);
const existingValue = projectRootFilesMap.get(path)!;
if (isScriptInfo(existingValue)) {
@ -1831,7 +1831,7 @@ namespace ts.server {
projectRootFilesMap.forEach((value, path) => {
if (!newRootScriptInfoMap.has(path)) {
if (isScriptInfo(value)) {
project.removeFile(value, project.fileExists(path), /*detachFromProject*/ true);
project.removeFile(value, project.fileExistsWithCache(path), /*detachFromProject*/ true);
}
else {
projectRootFilesMap.delete(path);

View File

@ -381,6 +381,11 @@ namespace ts.server {
}
fileExists(file: string): boolean {
return this.fileExistsWithCache(file);
}
/* @internal */
fileExistsWithCache(file: string): boolean {
// As an optimization, don't hit the disks for files we already know don't exist
// (because we're watching for their creation).
const path = this.toPath(file);
@ -1369,6 +1374,7 @@ namespace ts.server {
configFileWatcher: FileWatcher | undefined;
private directoriesWatchedForWildcards: Map<WildcardDirectoryWatcher> | undefined;
readonly canonicalConfigFilePath: NormalizedPath;
private getSourceOfProjectReferenceRedirect: GetSourceOfProjectReferenceRedirect | undefined;
/* @internal */
pendingReload: ConfigFileProgramReloadLevel | undefined;
@ -1414,6 +1420,25 @@ namespace ts.server {
this.canonicalConfigFilePath = asNormalizedPath(projectService.toCanonicalFileName(configFileName));
}
/* @internal */
setGetSourceOfProjectReferenceRedirect(getSource: GetSourceOfProjectReferenceRedirect) {
this.getSourceOfProjectReferenceRedirect = getSource;
}
/* @internal */
useSourceInsteadOfReferenceRedirect() {
return true;
}
fileExists(file: string): boolean {
// Project references go to source file instead of .d.ts file
if (this.getSourceOfProjectReferenceRedirect) {
const source = this.getSourceOfProjectReferenceRedirect(file);
if (source) return isString(source) ? super.fileExists(source) : true;
}
return super.fileExists(file);
}
/**
* If the project has reload from disk pending, it reloads (and then updates graph as part of that) instead of just updating the graph
* @returns: true if set of files in the project stays the same and false - otherwise.
@ -1436,6 +1461,7 @@ namespace ts.server {
default:
result = super.updateGraph();
}
this.getSourceOfProjectReferenceRedirect = undefined;
this.projectService.sendProjectLoadingFinishEvent(this);
this.projectService.sendProjectTelemetry(this);
return result;

View File

@ -1245,6 +1245,12 @@ namespace ts {
return host.resolveTypeReferenceDirectives!(typeReferenceDirectiveNames, containingFile, redirectedReference);
};
}
if (host.setGetSourceOfProjectReferenceRedirect) {
compilerHost.setGetSourceOfProjectReferenceRedirect = getSource => host.setGetSourceOfProjectReferenceRedirect!(getSource);
}
if (host.useSourceInsteadOfReferenceRedirect) {
compilerHost.useSourceInsteadOfReferenceRedirect = () => host.useSourceInsteadOfReferenceRedirect!();
}
const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings);
const options: CreateProgramOptions = {

View File

@ -236,6 +236,10 @@ namespace ts {
getDocumentPositionMapper?(generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined;
/* @internal */
getSourceFileLike?(fileName: string): SourceFileLike | undefined;
/* @internal */
setGetSourceOfProjectReferenceRedirect?(getSource: GetSourceOfProjectReferenceRedirect): void;
/* @internal */
useSourceInsteadOfReferenceRedirect?(): boolean;
}
/* @internal */

View File

@ -8546,11 +8546,13 @@ declare namespace ts.server {
private typeAcquisition;
private directoriesWatchedForWildcards;
readonly canonicalConfigFilePath: NormalizedPath;
private getSourceOfProjectReferenceRedirect;
/** Ref count to the project when opened from external project */
private externalProjectRefCount;
private projectErrors;
private projectReferences;
protected isInitialLoadPending: () => boolean;
fileExists(file: string): boolean;
/**
* If the project has reload from disk pending, it reloads (and then updates graph as part of that) instead of just updating the graph
* @returns: true if set of files in the project stays the same and false - otherwise.