Use SolutionBuilderHost instead of using compilerhost for solution builder

This commit is contained in:
Sheetal Nandi 2018-08-20 11:47:39 -07:00
parent dade3365d6
commit 0c4003e735
8 changed files with 47 additions and 58 deletions

View file

@ -189,10 +189,7 @@ namespace ts {
getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
getDirectories: (path: string) => system.getDirectories(path),
realpath,
readDirectory: (path, extensions, include, exclude, depth) => system.readDirectory(path, extensions, include, exclude, depth),
getModifiedTime: system.getModifiedTime && (path => system.getModifiedTime!(path)),
setModifiedTime: system.setModifiedTime && ((path, date) => system.setModifiedTime!(path, date)),
deleteFile: system.deleteFile && (path => system.deleteFile!(path))
readDirectory: (path, extensions, include, exclude, depth) => system.readDirectory(path, extensions, include, exclude, depth)
};
}

View file

@ -388,16 +388,27 @@ namespace ts {
};
}
export interface SolutionBuilderHost extends CompilerHost {
getModifiedTime(fileName: string): Date | undefined;
setModifiedTime(fileName: string, date: Date): void;
deleteFile(fileName: string): void;
}
export function createSolutionBuilderHost(system = sys) {
const host = createCompilerHost({}, /*setParentNodes*/ undefined, system) as SolutionBuilderHost;
host.getModifiedTime = system.getModifiedTime ? path => system.getModifiedTime!(path) : () => undefined;
host.setModifiedTime = system.setModifiedTime ? (path, date) => system.setModifiedTime!(path, date) : noop;
host.deleteFile = system.deleteFile ? path => system.deleteFile!(path) : noop;
return host;
}
/**
* A SolutionBuilder has an immutable set of rootNames that are the "entry point" projects, but
* can dynamically add/remove other projects based on changes on the rootNames' references
*/
export function createSolutionBuilder(compilerHost: CompilerHost, buildHost: BuildHost, rootNames: ReadonlyArray<string>, defaultOptions: BuildOptions, system?: System) {
if (!compilerHost.getModifiedTime || !compilerHost.setModifiedTime) {
throw new Error("Host must support timestamp APIs");
}
export function createSolutionBuilder(host: SolutionBuilderHost, buildHost: BuildHost, rootNames: ReadonlyArray<string>, defaultOptions: BuildOptions, system?: System) {
const configFileCache = createConfigFileCache(compilerHost);
const configFileCache = createConfigFileCache(host);
let context = createBuildContext(defaultOptions);
const existingWatchersForWildcards = createMap<WildcardDirectoryWatcher>();
@ -501,14 +512,14 @@ namespace ts {
let newestInputFileTime = minimumDate;
// Get timestamps of input files
for (const inputFile of project.fileNames) {
if (!compilerHost.fileExists(inputFile)) {
if (!host.fileExists(inputFile)) {
return {
type: UpToDateStatusType.Unbuildable,
reason: `${inputFile} does not exist`
};
}
const inputTime = compilerHost.getModifiedTime!(inputFile) || missingFileModifiedTime;
const inputTime = host.getModifiedTime(inputFile) || missingFileModifiedTime;
if (inputTime > newestInputFileTime) {
newestInputFileName = inputFile;
newestInputFileTime = inputTime;
@ -535,12 +546,12 @@ namespace ts {
for (const output of outputs) {
// Output is missing; can stop checking
// Don't immediately return because we can still be upstream-blocked, which is a higher-priority status
if (!compilerHost.fileExists(output)) {
if (!host.fileExists(output)) {
missingOutputFileName = output;
break;
}
const outputTime = compilerHost.getModifiedTime!(output) || missingFileModifiedTime;
const outputTime = host.getModifiedTime(output) || missingFileModifiedTime;
if (outputTime < oldestOutputFileTime) {
oldestOutputFileTime = outputTime;
oldestOutputFileName = output;
@ -568,7 +579,7 @@ namespace ts {
newestDeclarationFileContentChangedTime = newer(unchangedTime, newestDeclarationFileContentChangedTime);
}
else {
const outputModifiedTime = compilerHost.getModifiedTime!(output) || missingFileModifiedTime;
const outputModifiedTime = host.getModifiedTime(output) || missingFileModifiedTime;
newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, outputModifiedTime);
}
}
@ -580,7 +591,7 @@ namespace ts {
if (project.projectReferences) {
for (const ref of project.projectReferences) {
usesPrepend = usesPrepend || !!(ref.prepend);
const resolvedRef = resolveProjectReferencePath(compilerHost, ref);
const resolvedRef = resolveProjectReferencePath(host, ref);
const refStatus = getUpToDateStatus(configFileCache.parseConfigFile(resolvedRef));
// An upstream project is blocked
@ -809,7 +820,7 @@ namespace ts {
const programOptions: CreateProgramOptions = {
projectReferences: configFile.projectReferences,
host: compilerHost,
host,
rootNames: configFile.fileNames,
options: configFile.options
};
@ -858,18 +869,18 @@ namespace ts {
program.emit(/*targetSourceFile*/ undefined, (fileName, content, writeBom, onError) => {
let priorChangeTime: Date | undefined;
if (!anyDtsChanged && isDeclarationFile(fileName) && compilerHost.fileExists(fileName)) {
if (compilerHost.readFile(fileName) === content) {
if (!anyDtsChanged && isDeclarationFile(fileName) && host.fileExists(fileName)) {
if (host.readFile(fileName) === content) {
// Check for unchanged .d.ts files
resultFlags &= ~BuildResultFlags.DeclarationOutputUnchanged;
priorChangeTime = compilerHost.getModifiedTime && compilerHost.getModifiedTime(fileName);
priorChangeTime = host.getModifiedTime(fileName);
}
else {
anyDtsChanged = true;
}
}
compilerHost.writeFile(fileName, content, writeBom, onError, emptyArray);
host.writeFile(fileName, content, writeBom, onError, emptyArray);
if (priorChangeTime !== undefined) {
newestDeclarationFileContentChangedTime = newer(priorChangeTime, newestDeclarationFileContentChangedTime);
context.unchangedOutputs.setValue(fileName, priorChangeTime);
@ -898,10 +909,10 @@ namespace ts {
let priorNewestUpdateTime = minimumDate;
for (const file of outputs) {
if (isDeclarationFile(file)) {
priorNewestUpdateTime = newer(priorNewestUpdateTime, compilerHost.getModifiedTime!(file) || missingFileModifiedTime);
priorNewestUpdateTime = newer(priorNewestUpdateTime, host.getModifiedTime(file) || missingFileModifiedTime);
}
compilerHost.setModifiedTime!(file, now);
host.setModifiedTime(file, now);
}
context.projectStatus.setValue(proj.options.configFilePath!, { type: UpToDateStatusType.UpToDate, newestDeclarationFileContentChangedTime: priorNewestUpdateTime } as UpToDateStatus);
@ -924,7 +935,7 @@ namespace ts {
}
const outputs = getAllProjectOutputs(parsed);
for (const output of outputs) {
if (compilerHost.fileExists(output)) {
if (host.fileExists(output)) {
filesToDelete.push(output);
}
}
@ -958,25 +969,20 @@ namespace ts {
return ExitStatus.Success;
}
// Do this check later to allow --clean --dry to function even if the host can't delete files
if (!compilerHost.deleteFile) {
throw new Error("Host does not support deleting files");
}
for (const output of filesToDelete) {
compilerHost.deleteFile(output);
host.deleteFile(output);
}
return ExitStatus.Success;
}
function resolveProjectName(name: string): ResolvedConfigFileName | undefined {
const fullPath = resolvePath(compilerHost.getCurrentDirectory(), name);
if (compilerHost.fileExists(fullPath)) {
const fullPath = resolvePath(host.getCurrentDirectory(), name);
if (host.fileExists(fullPath)) {
return fullPath as ResolvedConfigFileName;
}
const fullPathWithTsconfig = combinePaths(fullPath, "tsconfig.json");
if (compilerHost.fileExists(fullPathWithTsconfig)) {
if (host.fileExists(fullPathWithTsconfig)) {
return fullPathWithTsconfig as ResolvedConfigFileName;
}
buildHost.error(Diagnostics.File_0_not_found, relName(fullPath));
@ -1058,7 +1064,7 @@ namespace ts {
}
function relName(path: string): string {
return convertToRelativePath(path, compilerHost.getCurrentDirectory(), f => compilerHost.getCanonicalFileName(f));
return convertToRelativePath(path, host.getCurrentDirectory(), f => host.getCanonicalFileName(f));
}
function reportVerbose(message: DiagnosticMessage, ...args: string[]) {
@ -1074,15 +1080,6 @@ namespace ts {
}
}
export interface UpToDateHost {
fileExists(fileName: string): boolean;
getModifiedTime(fileName: string): Date | undefined;
getUnchangedTime?(fileName: string): Date | undefined;
getLastStatus?(fileName: string): UpToDateStatus | undefined;
setLastStatus?(fileName: string, status: UpToDateStatus): void;
parseConfigFile?(configFilePath: ResolvedConfigFileName): ParsedCommandLine | undefined;
}
export function getAllProjectOutputs(project: ParsedCommandLine): ReadonlyArray<string> {
if (project.options.outFile) {
return getOutFileOutputs(project);

View file

@ -4845,10 +4845,6 @@ namespace ts {
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
createHash?(data: string): string;
getModifiedTime?(fileName: string): Date | undefined;
setModifiedTime?(fileName: string, date: Date): void;
deleteFile?(fileName: string): void;
}
/* @internal */

View file

@ -205,7 +205,7 @@ namespace fakes {
/**
* A fake `ts.CompilerHost` that leverages a virtual file system.
*/
export class CompilerHost implements ts.CompilerHost {
export class CompilerHost implements ts.CompilerHost, ts.SolutionBuilderHost {
public readonly sys: System;
public readonly defaultLibLocation: string;
public readonly outputs: documents.TextDocument[] = [];

View file

@ -1,8 +1,8 @@
namespace ts.tscWatch {
export import libFile = TestFSWithWatch.libFile;
function createSolutionBuilder(host: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
const compilerHost = createCompilerHost({}, /*setParentNodes*/ undefined, host);
const reportDiag = createDiagnosticReporter(host);
function createSolutionBuilder(system: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
const host = createSolutionBuilderHost(system);
const reportDiag = createDiagnosticReporter(system);
const report = (message: DiagnosticMessage, ...args: string[]) => reportDiag(createCompilerDiagnostic(message, ...args));
const buildHost: BuildHost = {
error: report,
@ -10,7 +10,7 @@ namespace ts.tscWatch {
message: report,
errorDiagnostic: d => reportDiag(d)
};
return ts.createSolutionBuilder(compilerHost, buildHost, rootNames, defaultOptions || { dry: false, force: false, verbose: false }, host);
return ts.createSolutionBuilder(host, buildHost, rootNames, defaultOptions || { dry: false, force: false, verbose: false }, system);
}
function createSolutionBuilderWithWatch(host: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {

View file

@ -243,6 +243,11 @@ namespace ts {
// Update to pretty if host supports it
updateReportDiagnostic({});
if (!sys.getModifiedTime || !sys.setModifiedTime || (buildOptions.clean && !sys.deleteFile)) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--build"));
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
// Nonsensical combinations
if (buildOptions.clean && buildOptions.force) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "force"));
@ -274,7 +279,7 @@ namespace ts {
errorDiagnostic: d => reportDiagnostic(d)
};
const builder = createSolutionBuilder(createCompilerHost({}), buildHost, projects, buildOptions);
const builder = createSolutionBuilder(createSolutionBuilderHost(), buildHost, projects, buildOptions);
if (buildOptions.clean) {
return builder.cleanAllProjects();
}

View file

@ -2690,9 +2690,6 @@ declare namespace ts {
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
getEnvironmentVariable?(name: string): string | undefined;
createHash?(data: string): string;
getModifiedTime?(fileName: string): Date | undefined;
setModifiedTime?(fileName: string, date: Date): void;
deleteFile?(fileName: string): void;
}
interface SourceMapRange extends TextRange {
source?: SourceMapSource;

View file

@ -2690,9 +2690,6 @@ declare namespace ts {
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
getEnvironmentVariable?(name: string): string | undefined;
createHash?(data: string): string;
getModifiedTime?(fileName: string): Date | undefined;
setModifiedTime?(fileName: string, date: Date): void;
deleteFile?(fileName: string): void;
}
interface SourceMapRange extends TextRange {
source?: SourceMapSource;