From 7ac7e284fd65dde642e5e9fa316950a50c5bf647 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 28 Aug 2018 12:08:07 -0700 Subject: [PATCH] Use SolutionBuilderHost instead of using compilerhost for solution builder --- src/compiler/program.ts | 5 +- src/compiler/tsbuild.ts | 74 +++++++++---------- src/compiler/types.ts | 4 - src/harness/fakes.ts | 2 +- src/testRunner/unittests/tsbuildWatchMode.ts | 8 +- src/tsc/tsc.ts | 7 +- .../reference/api/tsserverlibrary.d.ts | 3 - tests/baselines/reference/api/typescript.d.ts | 3 - 8 files changed, 48 insertions(+), 58 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index fd97ce8fcc6..8fa736b3f68 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -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) }; } diff --git a/src/compiler/tsbuild.ts b/src/compiler/tsbuild.ts index b381527b6f7..ff4d2077b83 100644 --- a/src/compiler/tsbuild.ts +++ b/src/compiler/tsbuild.ts @@ -387,16 +387,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, 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, defaultOptions: BuildOptions, system?: System) { - const configFileCache = createConfigFileCache(compilerHost); + const configFileCache = createConfigFileCache(host); let context = createBuildContext(defaultOptions); const existingWatchersForWildcards = createMap(); @@ -500,14 +511,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; @@ -534,12 +545,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; @@ -567,7 +578,7 @@ namespace ts { newestDeclarationFileContentChangedTime = newer(unchangedTime, newestDeclarationFileContentChangedTime); } else { - const outputModifiedTime = compilerHost.getModifiedTime!(output) || missingFileModifiedTime; + const outputModifiedTime = host.getModifiedTime(output) || missingFileModifiedTime; newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, outputModifiedTime); } } @@ -579,7 +590,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 @@ -808,7 +819,7 @@ namespace ts { const programOptions: CreateProgramOptions = { projectReferences: configFile.projectReferences, - host: compilerHost, + host, rootNames: configFile.fileNames, options: configFile.options }; @@ -857,18 +868,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); @@ -894,9 +905,10 @@ namespace ts { let priorNewestUpdateTime = minimumDate; for (const file of outputs) { if (isDeclarationFile(file)) { - priorNewestUpdateTime = newer(priorNewestUpdateTime, compilerHost.getModifiedTime!(file)); + 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); @@ -919,7 +931,7 @@ namespace ts { } const outputs = getAllProjectOutputs(parsed); for (const output of outputs) { - if (compilerHost.fileExists(output)) { + if (host.fileExists(output)) { filesToDelete.push(output); } } @@ -953,25 +965,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)); @@ -1053,7 +1060,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[]) { @@ -1069,15 +1076,6 @@ namespace ts { } } - export interface UpToDateHost { - fileExists(fileName: string): boolean; - getModifiedTime(fileName: string): Date; - 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 { if (project.options.outFile) { return getOutFileOutputs(project); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8fb78dd7b11..a487a010da9 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4811,10 +4811,6 @@ namespace ts { /* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution; /* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean; createHash?(data: string): string; - - getModifiedTime?(fileName: string): Date; - setModifiedTime?(fileName: string, date: Date): void; - deleteFile?(fileName: string): void; } /* @internal */ diff --git a/src/harness/fakes.ts b/src/harness/fakes.ts index 1bb358698a2..b4dd97878be 100644 --- a/src/harness/fakes.ts +++ b/src/harness/fakes.ts @@ -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[] = []; diff --git a/src/testRunner/unittests/tsbuildWatchMode.ts b/src/testRunner/unittests/tsbuildWatchMode.ts index 9f608180d03..dd6a95f0f12 100644 --- a/src/testRunner/unittests/tsbuildWatchMode.ts +++ b/src/testRunner/unittests/tsbuildWatchMode.ts @@ -1,8 +1,8 @@ namespace ts.tscWatch { export import libFile = TestFSWithWatch.libFile; - function createSolutionBuilder(host: WatchedSystem, rootNames: ReadonlyArray, defaultOptions?: BuildOptions) { - const compilerHost = createCompilerHost({}, /*setParentNodes*/ undefined, host); - const reportDiag = createDiagnosticReporter(host); + function createSolutionBuilder(system: WatchedSystem, rootNames: ReadonlyArray, 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, defaultOptions?: BuildOptions) { diff --git a/src/tsc/tsc.ts b/src/tsc/tsc.ts index a2413d132bb..d53aa1f56ff 100644 --- a/src/tsc/tsc.ts +++ b/src/tsc/tsc.ts @@ -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(); } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 28082885bf3..ad69cb3ab74 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2677,9 +2677,6 @@ declare namespace ts { resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; getEnvironmentVariable?(name: string): string | undefined; createHash?(data: string): string; - getModifiedTime?(fileName: string): Date; - setModifiedTime?(fileName: string, date: Date): void; - deleteFile?(fileName: string): void; } interface SourceMapRange extends TextRange { source?: SourceMapSource; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3f8b5131058..64a7ef53aad 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2677,9 +2677,6 @@ declare namespace ts { resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; getEnvironmentVariable?(name: string): string | undefined; createHash?(data: string): string; - getModifiedTime?(fileName: string): Date; - setModifiedTime?(fileName: string, date: Date): void; - deleteFile?(fileName: string): void; } interface SourceMapRange extends TextRange { source?: SourceMapSource;