mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-11 19:27:35 -06:00
Combine buildHost methods into SolutionBuilderHost's reportDiagnostic and reportStatus
This commit is contained in:
parent
26b4b6c9ad
commit
dedb2aefc0
@ -392,21 +392,37 @@ namespace ts {
|
||||
getModifiedTime(fileName: string): Date | undefined;
|
||||
setModifiedTime(fileName: string, date: Date): void;
|
||||
deleteFile(fileName: string): void;
|
||||
|
||||
reportDiagnostic: DiagnosticReporter; // Technically we want to move it out and allow steps of actions on Solution, but for now just merge stuff in build host here
|
||||
reportSolutionBuilderStatus: DiagnosticReporter;
|
||||
}
|
||||
|
||||
export interface SolutionBuilderWithWatchHost extends SolutionBuilderHost, WatchHost {
|
||||
}
|
||||
|
||||
export function createSolutionBuilderHost(system = sys) {
|
||||
/**
|
||||
* Create a function that reports watch status by writing to the system and handles the formating of the diagnostic
|
||||
*/
|
||||
export function createBuilderStatusReporter(system: System, pretty?: boolean): DiagnosticReporter {
|
||||
return diagnostic => {
|
||||
let output = pretty ? `[${formatColorAndReset(new Date().toLocaleTimeString(), ForegroundColorEscapeSequences.Grey)}] ` : `${new Date().toLocaleTimeString()} - `;
|
||||
output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${system.newLine + system.newLine}`;
|
||||
system.write(output);
|
||||
};
|
||||
}
|
||||
|
||||
export function createSolutionBuilderHost(system = sys, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter) {
|
||||
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;
|
||||
host.reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system);
|
||||
host.reportSolutionBuilderStatus = reportSolutionBuilderStatus || createBuilderStatusReporter(system);
|
||||
return host;
|
||||
}
|
||||
|
||||
export function createSolutionBuilderWithWatchHost(system = sys, reportWatchStatus?: WatchStatusReporter) {
|
||||
const host = createSolutionBuilderHost(system) as SolutionBuilderWithWatchHost;
|
||||
export function createSolutionBuilderWithWatchHost(system = sys, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter) {
|
||||
const host = createSolutionBuilderHost(system, reportDiagnostic, reportSolutionBuilderStatus) as SolutionBuilderWithWatchHost;
|
||||
const watchHost = createWatchHost(system, reportWatchStatus);
|
||||
host.onWatchStatusChange = watchHost.onWatchStatusChange;
|
||||
host.watchFile = watchHost.watchFile;
|
||||
@ -422,7 +438,7 @@ namespace ts {
|
||||
* TODO: use SolutionBuilderWithWatchHost => watchedSolution
|
||||
* use SolutionBuilderHost => Solution
|
||||
*/
|
||||
export function createSolutionBuilder(host: SolutionBuilderHost, buildHost: BuildHost, rootNames: ReadonlyArray<string>, defaultOptions: BuildOptions) {
|
||||
export function createSolutionBuilder(host: SolutionBuilderHost, rootNames: ReadonlyArray<string>, defaultOptions: BuildOptions) {
|
||||
const hostWithWatch = host as SolutionBuilderWithWatchHost;
|
||||
const configFileCache = createConfigFileCache(host);
|
||||
let context = createBuildContext(defaultOptions);
|
||||
@ -445,6 +461,10 @@ namespace ts {
|
||||
startWatching
|
||||
};
|
||||
|
||||
function reportStatus(message: DiagnosticMessage, ...args: string[]) {
|
||||
host.reportSolutionBuilderStatus(createCompilerDiagnostic(message, ...args));
|
||||
}
|
||||
|
||||
function startWatching() {
|
||||
const graph = getGlobalDependencyGraph()!;
|
||||
if (!graph.buildQueue) {
|
||||
@ -743,7 +763,7 @@ namespace ts {
|
||||
verboseReportProjectStatus(next, status);
|
||||
|
||||
if (status.type === UpToDateStatusType.UpstreamBlocked) {
|
||||
if (context.options.verbose) buildHost.verbose(Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, resolved, status.upstreamProjectName);
|
||||
if (context.options.verbose) reportStatus(Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, resolved, status.upstreamProjectName);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -780,7 +800,7 @@ namespace ts {
|
||||
if (temporaryMarks[projPath]) {
|
||||
if (!inCircularContext) {
|
||||
hadError = true;
|
||||
buildHost.error(Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0, circularityReportStack.join("\r\n"));
|
||||
reportStatus(Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0, circularityReportStack.join("\r\n"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -812,11 +832,11 @@ namespace ts {
|
||||
|
||||
function buildSingleProject(proj: ResolvedConfigFileName): BuildResultFlags {
|
||||
if (context.options.dry) {
|
||||
buildHost.message(Diagnostics.A_non_dry_build_would_build_project_0, proj);
|
||||
reportStatus(Diagnostics.A_non_dry_build_would_build_project_0, proj);
|
||||
return BuildResultFlags.Success;
|
||||
}
|
||||
|
||||
if (context.options.verbose) buildHost.verbose(Diagnostics.Building_project_0, proj);
|
||||
if (context.options.verbose) reportStatus(Diagnostics.Building_project_0, proj);
|
||||
|
||||
let resultFlags = BuildResultFlags.None;
|
||||
resultFlags |= BuildResultFlags.DeclarationOutputUnchanged;
|
||||
@ -850,7 +870,7 @@ namespace ts {
|
||||
if (syntaxDiagnostics.length) {
|
||||
resultFlags |= BuildResultFlags.SyntaxErrors;
|
||||
for (const diag of syntaxDiagnostics) {
|
||||
buildHost.errorDiagnostic(diag);
|
||||
host.reportDiagnostic(diag);
|
||||
}
|
||||
context.projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: "Syntactic errors" });
|
||||
return resultFlags;
|
||||
@ -862,7 +882,7 @@ namespace ts {
|
||||
if (declDiagnostics.length) {
|
||||
resultFlags |= BuildResultFlags.DeclarationEmitErrors;
|
||||
for (const diag of declDiagnostics) {
|
||||
buildHost.errorDiagnostic(diag);
|
||||
host.reportDiagnostic(diag);
|
||||
}
|
||||
context.projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: "Declaration file errors" });
|
||||
return resultFlags;
|
||||
@ -874,7 +894,7 @@ namespace ts {
|
||||
if (semanticDiagnostics.length) {
|
||||
resultFlags |= BuildResultFlags.TypeErrors;
|
||||
for (const diag of semanticDiagnostics) {
|
||||
buildHost.errorDiagnostic(diag);
|
||||
host.reportDiagnostic(diag);
|
||||
}
|
||||
context.projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: "Semantic errors" });
|
||||
return resultFlags;
|
||||
@ -913,11 +933,11 @@ namespace ts {
|
||||
|
||||
function updateOutputTimestamps(proj: ParsedCommandLine) {
|
||||
if (context.options.dry) {
|
||||
return buildHost.message(Diagnostics.A_non_dry_build_would_build_project_0, proj.options.configFilePath!);
|
||||
return reportStatus(Diagnostics.A_non_dry_build_would_build_project_0, proj.options.configFilePath!);
|
||||
}
|
||||
|
||||
if (context.options.verbose) {
|
||||
buildHost.verbose(Diagnostics.Updating_output_timestamps_of_project_0, proj.options.configFilePath!);
|
||||
reportStatus(Diagnostics.Updating_output_timestamps_of_project_0, proj.options.configFilePath!);
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
@ -970,18 +990,18 @@ namespace ts {
|
||||
function cleanAllProjects() {
|
||||
const resolvedNames: ReadonlyArray<ResolvedConfigFileName> | undefined = getAllProjectsInScope();
|
||||
if (resolvedNames === undefined) {
|
||||
buildHost.message(Diagnostics.Skipping_clean_because_not_all_projects_could_be_located);
|
||||
reportStatus(Diagnostics.Skipping_clean_because_not_all_projects_could_be_located);
|
||||
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
|
||||
}
|
||||
|
||||
const filesToDelete = getFilesToClean(resolvedNames);
|
||||
if (filesToDelete === undefined) {
|
||||
buildHost.message(Diagnostics.Skipping_clean_because_not_all_projects_could_be_located);
|
||||
reportStatus(Diagnostics.Skipping_clean_because_not_all_projects_could_be_located);
|
||||
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
|
||||
}
|
||||
|
||||
if (context.options.dry) {
|
||||
buildHost.message(Diagnostics.A_non_dry_build_would_delete_the_following_files_Colon_0, filesToDelete.map(f => `\r\n * ${f}`).join(""));
|
||||
reportStatus(Diagnostics.A_non_dry_build_would_delete_the_following_files_Colon_0, filesToDelete.map(f => `\r\n * ${f}`).join(""));
|
||||
return ExitStatus.Success;
|
||||
}
|
||||
|
||||
@ -1001,7 +1021,7 @@ namespace ts {
|
||||
if (host.fileExists(fullPathWithTsconfig)) {
|
||||
return fullPathWithTsconfig as ResolvedConfigFileName;
|
||||
}
|
||||
buildHost.error(Diagnostics.File_0_not_found, relName(fullPath));
|
||||
host.reportDiagnostic(createCompilerDiagnostic(Diagnostics.File_0_not_found, relName(fullPath)));
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -1039,7 +1059,7 @@ namespace ts {
|
||||
// Up to date, skip
|
||||
if (defaultOptions.dry) {
|
||||
// In a dry build, inform the user of this fact
|
||||
buildHost.message(Diagnostics.Project_0_is_up_to_date, projName);
|
||||
reportStatus(Diagnostics.Project_0_is_up_to_date, projName);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -1051,7 +1071,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (status.type === UpToDateStatusType.UpstreamBlocked) {
|
||||
if (context.options.verbose) buildHost.verbose(Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, projName, status.upstreamProjectName);
|
||||
if (context.options.verbose) reportStatus(Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, projName, status.upstreamProjectName);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1076,23 +1096,19 @@ namespace ts {
|
||||
for (const name of graph.buildQueue) {
|
||||
names.push(name);
|
||||
}
|
||||
if (context.options.verbose) buildHost.verbose(Diagnostics.Projects_in_this_build_Colon_0, names.map(s => "\r\n * " + relName(s)).join(""));
|
||||
if (context.options.verbose) reportStatus(Diagnostics.Projects_in_this_build_Colon_0, names.map(s => "\r\n * " + relName(s)).join(""));
|
||||
}
|
||||
|
||||
function relName(path: string): string {
|
||||
return convertToRelativePath(path, host.getCurrentDirectory(), f => host.getCanonicalFileName(f));
|
||||
}
|
||||
|
||||
function reportVerbose(message: DiagnosticMessage, ...args: string[]) {
|
||||
buildHost.verbose(message, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the up-to-date status of a project if we're in verbose mode
|
||||
*/
|
||||
function verboseReportProjectStatus(configFileName: string, status: UpToDateStatus) {
|
||||
if (!context.options.verbose) return;
|
||||
return formatUpToDateStatus(configFileName, status, relName, reportVerbose);
|
||||
return formatUpToDateStatus(configFileName, status, relName, reportStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -205,7 +205,7 @@ namespace fakes {
|
||||
/**
|
||||
* A fake `ts.CompilerHost` that leverages a virtual file system.
|
||||
*/
|
||||
export class CompilerHost implements ts.CompilerHost, ts.SolutionBuilderHost {
|
||||
export class CompilerHost implements ts.CompilerHost {
|
||||
public readonly sys: System;
|
||||
public readonly defaultLibLocation: string;
|
||||
public readonly outputs: documents.TextDocument[] = [];
|
||||
@ -374,5 +374,41 @@ namespace fakes {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
export class SolutionBuilderHost extends CompilerHost implements ts.SolutionBuilderHost {
|
||||
diagnostics: ts.Diagnostic[] = [];
|
||||
|
||||
reportDiagnostic(diagnostic: ts.Diagnostic) {
|
||||
this.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
reportSolutionBuilderStatus(diagnostic: ts.Diagnostic) {
|
||||
this.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
clearDiagnostics() {
|
||||
this.diagnostics.length = 0;
|
||||
}
|
||||
|
||||
assertDiagnosticMessages(...expected: ts.DiagnosticMessage[]) {
|
||||
const actual = this.diagnostics.slice();
|
||||
if (actual.length !== expected.length) {
|
||||
assert.fail<any>(actual, expected, `Diagnostic arrays did not match - got\r\n${actual.map(a => " " + a.messageText).join("\r\n")}\r\nexpected\r\n${expected.map(e => " " + e.message).join("\r\n")}`);
|
||||
}
|
||||
for (let i = 0; i < actual.length; i++) {
|
||||
if (actual[i].code !== expected[i].code) {
|
||||
assert.fail(actual[i].messageText, expected[i].message, `Mismatched error code - expected diagnostic ${i} "${actual[i].messageText}" to match ${expected[i].message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printDiagnostics(header = "== Diagnostics ==") {
|
||||
const out = ts.createDiagnosticReporter(ts.sys);
|
||||
ts.sys.write(header + "\r\n");
|
||||
for (const d of this.diagnostics) {
|
||||
out(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,15 +1,5 @@
|
||||
namespace ts {
|
||||
let currentTime = 100;
|
||||
let lastDiagnostics: Diagnostic[] = [];
|
||||
const reportDiagnostic: DiagnosticReporter = diagnostic => lastDiagnostics.push(diagnostic);
|
||||
const report = (message: DiagnosticMessage, ...args: string[]) => reportDiagnostic(createCompilerDiagnostic(message, ...args));
|
||||
const buildHost: BuildHost = {
|
||||
error: report,
|
||||
verbose: report,
|
||||
message: report,
|
||||
errorDiagnostic: d => reportDiagnostic(d)
|
||||
};
|
||||
|
||||
export namespace Sample1 {
|
||||
tick();
|
||||
const projFs = loadProjectFromDisk("tests/projects/sample1");
|
||||
@ -21,12 +11,12 @@ namespace ts {
|
||||
describe("tsbuild - sanity check of clean build of 'sample1' project", () => {
|
||||
it("can build the sample project 'sample1' without error", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
|
||||
clearDiagnostics();
|
||||
host.clearDiagnostics();
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(/*empty*/);
|
||||
host.assertDiagnosticMessages(/*empty*/);
|
||||
|
||||
// Check for outputs. Not an exhaustive list
|
||||
for (const output of allExpectedOutputs) {
|
||||
@ -37,12 +27,11 @@ namespace ts {
|
||||
|
||||
describe("tsbuild - dry builds", () => {
|
||||
it("doesn't write any files in a dry build", () => {
|
||||
clearDiagnostics();
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: true, force: false, verbose: false });
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0);
|
||||
host.assertDiagnosticMessages(Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0);
|
||||
|
||||
// Check for outputs to not be written. Not an exhaustive list
|
||||
for (const output of allExpectedOutputs) {
|
||||
@ -51,28 +40,26 @@ namespace ts {
|
||||
});
|
||||
|
||||
it("indicates that it would skip builds during a dry build", () => {
|
||||
clearDiagnostics();
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
|
||||
let builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
let builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
tick();
|
||||
|
||||
clearDiagnostics();
|
||||
builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: true, force: false, verbose: false });
|
||||
host.clearDiagnostics();
|
||||
builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date);
|
||||
host.assertDiagnosticMessages(Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsbuild - clean builds", () => {
|
||||
it("removes all files it built", () => {
|
||||
clearDiagnostics();
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
// Verify they exist
|
||||
for (const output of allExpectedOutputs) {
|
||||
@ -91,9 +78,9 @@ namespace ts {
|
||||
describe("tsbuild - force builds", () => {
|
||||
it("always builds under --force", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: true, verbose: false });
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: true, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
let currentTime = time();
|
||||
checkOutputTimestamps(currentTime);
|
||||
@ -116,14 +103,14 @@ namespace ts {
|
||||
|
||||
describe("tsbuild - can detect when and what to rebuild", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: true });
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: true });
|
||||
|
||||
it("Builds the project", () => {
|
||||
clearDiagnostics();
|
||||
host.clearDiagnostics();
|
||||
builder.resetBuildContext();
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
|
||||
Diagnostics.Building_project_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
|
||||
@ -135,10 +122,10 @@ namespace ts {
|
||||
|
||||
// All three projects are up to date
|
||||
it("Detects that all projects are up to date", () => {
|
||||
clearDiagnostics();
|
||||
host.clearDiagnostics();
|
||||
builder.resetBuildContext();
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2);
|
||||
@ -147,12 +134,12 @@ namespace ts {
|
||||
|
||||
// Update a file in the leaf node (tests), only it should rebuild the last one
|
||||
it("Only builds the leaf node project", () => {
|
||||
clearDiagnostics();
|
||||
host.clearDiagnostics();
|
||||
fs.writeFileSync("/src/tests/index.ts", "const m = 10;");
|
||||
builder.resetBuildContext();
|
||||
builder.buildAllProjects();
|
||||
|
||||
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
|
||||
Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2,
|
||||
@ -162,12 +149,12 @@ namespace ts {
|
||||
|
||||
// Update a file in the parent (without affecting types), should get fast downstream builds
|
||||
it("Detects type-only changes in upstream projects", () => {
|
||||
clearDiagnostics();
|
||||
host.clearDiagnostics();
|
||||
replaceText(fs, "/src/core/index.ts", "HELLO WORLD", "WELCOME PLANET");
|
||||
builder.resetBuildContext();
|
||||
builder.buildAllProjects();
|
||||
|
||||
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2,
|
||||
Diagnostics.Building_project_0,
|
||||
Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies,
|
||||
@ -180,15 +167,13 @@ namespace ts {
|
||||
describe("tsbuild - downstream-blocked compilations", () => {
|
||||
it("won't build downstream projects if upstream projects have errors", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: true });
|
||||
|
||||
clearDiagnostics();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: true });
|
||||
|
||||
// Induce an error in the middle project
|
||||
replaceText(fs, "/src/logic/index.ts", "c.multiply(10, 15)", `c.muitply()`);
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(
|
||||
host.assertDiagnosticMessages(
|
||||
Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
|
||||
Diagnostics.Building_project_0,
|
||||
@ -204,12 +189,11 @@ namespace ts {
|
||||
describe("tsbuild - project invalidation", () => {
|
||||
it("invalidates projects correctly", () => {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
|
||||
|
||||
clearDiagnostics();
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(/*empty*/);
|
||||
host.assertDiagnosticMessages(/*empty*/);
|
||||
|
||||
// Update a timestamp in the middle project
|
||||
tick();
|
||||
@ -240,11 +224,10 @@ namespace ts {
|
||||
|
||||
function verifyProjectWithResolveJsonModule(configFile: string, ...expectedDiagnosticMessages: DiagnosticMessage[]) {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, [configFile], { dry: false, force: false, verbose: false });
|
||||
clearDiagnostics();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const builder = createSolutionBuilder(host, [configFile], { dry: false, force: false, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(...expectedDiagnosticMessages);
|
||||
host.assertDiagnosticMessages(...expectedDiagnosticMessages);
|
||||
if (!expectedDiagnosticMessages.length) {
|
||||
// Check for outputs. Not an exhaustive list
|
||||
for (const output of allExpectedOutputs) {
|
||||
@ -274,11 +257,11 @@ namespace ts {
|
||||
let fs: vfs.FileSystem | undefined;
|
||||
before(() => {
|
||||
fs = outFileFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/third"], { dry: false, force: false, verbose: false });
|
||||
clearDiagnostics();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/third"], { dry: false, force: false, verbose: false });
|
||||
host.clearDiagnostics();
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(/*none*/);
|
||||
host.assertDiagnosticMessages(/*none*/);
|
||||
});
|
||||
after(() => {
|
||||
fs = undefined;
|
||||
@ -293,25 +276,24 @@ namespace ts {
|
||||
describe("tsbuild - downstream prepend projects always get rebuilt", () => {
|
||||
it("", () => {
|
||||
const fs = outFileFs.shadow();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
const builder = createSolutionBuilder(host, buildHost, ["/src/third"], { dry: false, force: false, verbose: false });
|
||||
clearDiagnostics();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/third"], { dry: false, force: false, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(/*none*/);
|
||||
host.assertDiagnosticMessages(/*none*/);
|
||||
assert.equal(fs.statSync("src/third/thirdjs/output/third-output.js").mtimeMs, time(), "First build timestamp is correct");
|
||||
tick();
|
||||
replaceText(fs, "src/first/first_PART1.ts", "Hello", "Hola");
|
||||
tick();
|
||||
builder.resetBuildContext();
|
||||
builder.buildAllProjects();
|
||||
assertDiagnosticMessages(/*none*/);
|
||||
host.assertDiagnosticMessages(/*none*/);
|
||||
assert.equal(fs.statSync("src/third/thirdjs/output/third-output.js").mtimeMs, time(), "Second build timestamp is correct");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe("tsbuild - graph-ordering", () => {
|
||||
let host: fakes.CompilerHost | undefined;
|
||||
let host: fakes.SolutionBuilderHost | undefined;
|
||||
const deps: [string, string][] = [
|
||||
["A", "B"],
|
||||
["B", "C"],
|
||||
@ -324,7 +306,7 @@ namespace ts {
|
||||
|
||||
before(() => {
|
||||
const fs = new vfs.FileSystem(false);
|
||||
host = new fakes.CompilerHost(fs);
|
||||
host = new fakes.SolutionBuilderHost(fs);
|
||||
writeProjects(fs, ["A", "B", "C", "D", "E", "F", "G"], deps);
|
||||
});
|
||||
|
||||
@ -349,7 +331,7 @@ namespace ts {
|
||||
});
|
||||
|
||||
function checkGraphOrdering(rootNames: string[], expectedBuildSet: string[]) {
|
||||
const builder = createSolutionBuilder(host!, buildHost, rootNames, { dry: true, force: false, verbose: false });
|
||||
const builder = createSolutionBuilder(host!, rootNames, { dry: true, force: false, verbose: false });
|
||||
|
||||
const projFileNames = rootNames.map(getProjectFileName);
|
||||
const graph = builder.getBuildGraph(projFileNames);
|
||||
@ -404,30 +386,6 @@ namespace ts {
|
||||
fs.writeFileSync(path, newContent, "utf-8");
|
||||
}
|
||||
|
||||
function assertDiagnosticMessages(...expected: DiagnosticMessage[]) {
|
||||
const actual = lastDiagnostics.slice();
|
||||
if (actual.length !== expected.length) {
|
||||
assert.fail<any>(actual, expected, `Diagnostic arrays did not match - got\r\n${actual.map(a => " " + a.messageText).join("\r\n")}\r\nexpected\r\n${expected.map(e => " " + e.message).join("\r\n")}`);
|
||||
}
|
||||
for (let i = 0; i < actual.length; i++) {
|
||||
if (actual[i].code !== expected[i].code) {
|
||||
assert.fail(actual[i].messageText, expected[i].message, `Mismatched error code - expected diagnostic ${i} "${actual[i].messageText}" to match ${expected[i].message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearDiagnostics() {
|
||||
lastDiagnostics = [];
|
||||
}
|
||||
|
||||
export function printDiagnostics(header = "== Diagnostics ==") {
|
||||
const out = createDiagnosticReporter(sys);
|
||||
sys.write(header + "\r\n");
|
||||
for (const d of lastDiagnostics) {
|
||||
out(d);
|
||||
}
|
||||
}
|
||||
|
||||
function tick() {
|
||||
currentTime += 60_000;
|
||||
}
|
||||
|
||||
@ -2,15 +2,7 @@ namespace ts.tscWatch {
|
||||
export import libFile = TestFSWithWatch.libFile;
|
||||
function createSolutionBuilder(system: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
|
||||
const host = createSolutionBuilderWithWatchHost(system);
|
||||
const reportDiag = createDiagnosticReporter(system);
|
||||
const report = (message: DiagnosticMessage, ...args: string[]) => reportDiag(createCompilerDiagnostic(message, ...args));
|
||||
const buildHost: BuildHost = {
|
||||
error: report,
|
||||
verbose: report,
|
||||
message: report,
|
||||
errorDiagnostic: d => reportDiag(d)
|
||||
};
|
||||
return ts.createSolutionBuilder(host, buildHost, rootNames, defaultOptions || { dry: false, force: false, verbose: false, watch: true });
|
||||
return ts.createSolutionBuilder(host, rootNames, defaultOptions || { dry: false, force: false, verbose: false, watch: true });
|
||||
}
|
||||
|
||||
function createSolutionBuilderWithWatch(host: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
|
||||
|
||||
@ -13,7 +13,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
let reportDiagnostic = createDiagnosticReporter(sys);
|
||||
function updateReportDiagnostic(options: CompilerOptions) {
|
||||
function updateReportDiagnostic(options?: CompilerOptions) {
|
||||
if (shouldBePretty(options)) {
|
||||
reportDiagnostic = createDiagnosticReporter(sys, /*pretty*/ true);
|
||||
}
|
||||
@ -23,8 +23,8 @@ namespace ts {
|
||||
return !!sys.writeOutputIsTTY && sys.writeOutputIsTTY();
|
||||
}
|
||||
|
||||
function shouldBePretty(options: CompilerOptions) {
|
||||
if (typeof options.pretty === "undefined") {
|
||||
function shouldBePretty(options?: CompilerOptions) {
|
||||
if (!options || typeof options.pretty === "undefined") {
|
||||
return defaultIsPretty();
|
||||
}
|
||||
return options.pretty;
|
||||
@ -241,7 +241,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Update to pretty if host supports it
|
||||
updateReportDiagnostic({});
|
||||
updateReportDiagnostic();
|
||||
|
||||
if (!sys.getModifiedTime || !sys.setModifiedTime || (buildOptions.clean && !sys.deleteFile)) {
|
||||
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--build"));
|
||||
@ -274,16 +274,8 @@ namespace ts {
|
||||
addProject(".");
|
||||
}
|
||||
|
||||
const report = (message: DiagnosticMessage, ...args: string[]) => reportDiagnostic(createCompilerDiagnostic(message, ...args));
|
||||
const buildHost: BuildHost = {
|
||||
error: report,
|
||||
verbose: report,
|
||||
message: report,
|
||||
errorDiagnostic: d => reportDiagnostic(d)
|
||||
};
|
||||
|
||||
// TODO: change this to host if watch => watchHost otherwiue without wathc
|
||||
const builder = createSolutionBuilder(createSolutionBuilderWithWatchHost(), buildHost, projects, buildOptions);
|
||||
const builder = createSolutionBuilder(createSolutionBuilderWithWatchHost(sys, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty()), createWatchStatusReporter()), projects, buildOptions);
|
||||
if (buildOptions.clean) {
|
||||
return builder.cleanAllProjects();
|
||||
}
|
||||
@ -300,7 +292,7 @@ namespace ts {
|
||||
const fileName = resolvePath(sys.getCurrentDirectory(), projectSpecification);
|
||||
const refPath = resolveProjectReferencePath(sys, { path: fileName });
|
||||
if (!sys.fileExists(refPath)) {
|
||||
return buildHost.error(Diagnostics.File_0_does_not_exist, fileName);
|
||||
return reportDiagnostic(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, fileName));
|
||||
}
|
||||
projects.push(refPath);
|
||||
}
|
||||
@ -339,7 +331,7 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
function createWatchStatusReporter(options: CompilerOptions) {
|
||||
function createWatchStatusReporter(options?: CompilerOptions) {
|
||||
return ts.createWatchStatusReporter(sys, shouldBePretty(options));
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user