Skip to content

Commit

Permalink
Always emit tsbuild info even if there are non syntax errors in tsc -…
Browse files Browse the repository at this point in the history
…-build mode
  • Loading branch information
sheetalkamat committed May 29, 2020
1 parent 62584b7 commit bafc3b7
Show file tree
Hide file tree
Showing 13 changed files with 1,296 additions and 107 deletions.
140 changes: 82 additions & 58 deletions src/compiler/tsbuildPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,18 @@ namespace ts {
};
}

enum BuildStep {
CreateProgram,
SyntaxDiagnostics,
SemanticDiagnostics,
Emit,
EmitBundle,
EmitBuildInfo,
BuildInvalidatedProjectOfBundle,
QueueReferencingProjects,
Done
}

function createBuildOrUpdateInvalidedProject<T extends BuilderProgram>(
kind: InvalidatedProjectKind.Build | InvalidatedProjectKind.UpdateBundle,
state: SolutionBuilderState<T>,
Expand All @@ -708,18 +720,7 @@ namespace ts {
config: ParsedCommandLine,
buildOrder: readonly ResolvedConfigFileName[],
): BuildInvalidedProject<T> | UpdateBundleProject<T> {
enum Step {
CreateProgram,
SyntaxDiagnostics,
SemanticDiagnostics,
Emit,
EmitBundle,
BuildInvalidatedProjectOfBundle,
QueueReferencingProjects,
Done
}

let step = kind === InvalidatedProjectKind.Build ? Step.CreateProgram : Step.EmitBundle;
let step = kind === InvalidatedProjectKind.Build ? BuildStep.CreateProgram : BuildStep.EmitBundle;
let program: T | undefined;
let buildResult: BuildResultFlags | undefined;
let invalidatedProjectOfBundle: BuildInvalidedProject<T> | undefined;
Expand Down Expand Up @@ -781,8 +782,11 @@ namespace ts {
program => program.emit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers)
);
}
executeSteps(Step.SemanticDiagnostics, cancellationToken);
if (step !== Step.Emit) return undefined;
executeSteps(BuildStep.SemanticDiagnostics, cancellationToken);
if (step === BuildStep.EmitBuildInfo) {
return emitBuildInfo(writeFile, cancellationToken);
}
if (step !== BuildStep.Emit) return undefined;
return emit(writeFile, cancellationToken, customTransformers);
},
done
Expand All @@ -795,19 +799,19 @@ namespace ts {
getCompilerOptions: () => config.options,
getCurrentDirectory: () => state.currentDirectory,
emit: (writeFile: WriteFileCallback | undefined, customTransformers: CustomTransformers | undefined) => {
if (step !== Step.EmitBundle) return invalidatedProjectOfBundle;
if (step !== BuildStep.EmitBundle) return invalidatedProjectOfBundle;
return emitBundle(writeFile, customTransformers);
},
done,
};

function done(cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, customTransformers?: CustomTransformers) {
executeSteps(Step.Done, cancellationToken, writeFile, customTransformers);
executeSteps(BuildStep.Done, cancellationToken, writeFile, customTransformers);
return doneInvalidatedProject(state, projectPath);
}

function withProgramOrUndefined<U>(action: (program: T) => U | undefined): U | undefined {
executeSteps(Step.CreateProgram);
executeSteps(BuildStep.CreateProgram);
return program && action(program);
}

Expand All @@ -821,7 +825,7 @@ namespace ts {
if (state.options.dry) {
reportStatus(state, Diagnostics.A_non_dry_build_would_build_project_0, project);
buildResult = BuildResultFlags.Success;
step = Step.QueueReferencingProjects;
step = BuildStep.QueueReferencingProjects;
return;
}

Expand All @@ -831,7 +835,7 @@ namespace ts {
reportAndStoreErrors(state, projectPath, getConfigFileParsingDiagnostics(config));
// Nothing to build - must be a solution file, basically
buildResult = BuildResultFlags.None;
step = Step.QueueReferencingProjects;
step = BuildStep.QueueReferencingProjects;
return;
}

Expand All @@ -854,16 +858,15 @@ namespace ts {

function handleDiagnostics(diagnostics: readonly Diagnostic[], errorFlags: BuildResultFlags, errorType: string) {
if (diagnostics.length) {
buildResult = buildErrors(
({ buildResult, step } = buildErrors(
state,
projectPath,
program,
config,
diagnostics,
errorFlags,
errorType
);
step = Step.QueueReferencingProjects;
));
}
else {
step++;
Expand Down Expand Up @@ -894,7 +897,7 @@ namespace ts {

function emit(writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitResult {
Debug.assertIsDefined(program);
Debug.assert(step === Step.Emit);
Debug.assert(step === BuildStep.Emit);
// Before emitting lets backup state, so we can revert it back if there are declaration errors to handle emit and declaration errors correctly
program.backupState();
let declDiagnostics: Diagnostic[] | undefined;
Expand All @@ -913,16 +916,15 @@ namespace ts {
// Don't emit .d.ts if there are decl file errors
if (declDiagnostics) {
program.restoreState();
buildResult = buildErrors(
({ buildResult, step } = buildErrors(
state,
projectPath,
program,
config,
declDiagnostics,
BuildResultFlags.DeclarationEmitErrors,
"Declaration file"
);
step = Step.QueueReferencingProjects;
));
return {
emitSkipped: true,
diagnostics: emitResult.diagnostics
Expand Down Expand Up @@ -967,6 +969,24 @@ namespace ts {
return emitResult;
}

function emitBuildInfo(writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult {
Debug.assertIsDefined(program);
Debug.assert(step === BuildStep.EmitBuildInfo);
const emitResult = program.emitBuildInfo(writeFileCallback, cancellationToken);
if (emitResult.diagnostics.length) {
reportErrors(state, emitResult.diagnostics);
state.diagnostics.set(projectPath, [...state.diagnostics.get(projectPath)!, ...emitResult.diagnostics]);
buildResult = BuildResultFlags.EmitErrors & buildResult!;
}

if (emitResult.emittedFiles && state.writeFileName) {
emitResult.emittedFiles.forEach(name => listEmittedFile(state, config, name));
}
afterProgramDone(state, projectPath, program, config);
step = BuildStep.QueueReferencingProjects;
return emitResult;
}

function finishEmit(
emitterDiagnostics: DiagnosticCollection,
emittedOutputs: FileMap<string>,
Expand All @@ -977,22 +997,20 @@ namespace ts {
) {
const emitDiagnostics = emitterDiagnostics.getDiagnostics();
if (emitDiagnostics.length) {
buildResult = buildErrors(
({ buildResult, step } = buildErrors(
state,
projectPath,
program,
config,
emitDiagnostics,
BuildResultFlags.EmitErrors,
"Emit"
);
step = Step.QueueReferencingProjects;
));
return emitDiagnostics;
}

if (state.writeFileName) {
emittedOutputs.forEach(name => listEmittedFile(state, config, name));
if (program) listFiles(program, state.writeFileName);
}

// Update time stamps for rest of the outputs
Expand All @@ -1006,8 +1024,7 @@ namespace ts {
oldestOutputFileName
});
afterProgramDone(state, projectPath, program, config);
state.projectCompilerOptions = state.baseCompilerOptions;
step = Step.QueueReferencingProjects;
step = BuildStep.QueueReferencingProjects;
buildResult = resultFlags;
return emitDiagnostics;
}
Expand All @@ -1017,7 +1034,7 @@ namespace ts {
if (state.options.dry) {
reportStatus(state, Diagnostics.A_non_dry_build_would_update_output_of_project_0, project);
buildResult = BuildResultFlags.Success;
step = Step.QueueReferencingProjects;
step = BuildStep.QueueReferencingProjects;
return undefined;
}

Expand All @@ -1038,7 +1055,7 @@ namespace ts {

if (isString(outputFiles)) {
reportStatus(state, Diagnostics.Cannot_update_output_of_project_0_because_there_was_error_reading_file_1, project, relName(state, outputFiles));
step = Step.BuildInvalidatedProjectOfBundle;
step = BuildStep.BuildInvalidatedProjectOfBundle;
return invalidatedProjectOfBundle = createBuildOrUpdateInvalidedProject(
InvalidatedProjectKind.Build,
state,
Expand Down Expand Up @@ -1070,44 +1087,48 @@ namespace ts {
return { emitSkipped: false, diagnostics: emitDiagnostics };
}

function executeSteps(till: Step, cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, customTransformers?: CustomTransformers) {
while (step <= till && step < Step.Done) {
function executeSteps(till: BuildStep, cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, customTransformers?: CustomTransformers) {
while (step <= till && step < BuildStep.Done) {
const currentStep = step;
switch (step) {
case Step.CreateProgram:
case BuildStep.CreateProgram:
createProgram();
break;

case Step.SyntaxDiagnostics:
case BuildStep.SyntaxDiagnostics:
getSyntaxDiagnostics(cancellationToken);
break;

case Step.SemanticDiagnostics:
case BuildStep.SemanticDiagnostics:
getSemanticDiagnostics(cancellationToken);
break;

case Step.Emit:
case BuildStep.Emit:
emit(writeFile, cancellationToken, customTransformers);
break;

case Step.EmitBundle:
case BuildStep.EmitBuildInfo:
emitBuildInfo(writeFile, cancellationToken);
break;

case BuildStep.EmitBundle:
emitBundle(writeFile, customTransformers);
break;

case Step.BuildInvalidatedProjectOfBundle:
case BuildStep.BuildInvalidatedProjectOfBundle:
Debug.checkDefined(invalidatedProjectOfBundle).done(cancellationToken);
step = Step.Done;
step = BuildStep.Done;
break;

case Step.QueueReferencingProjects:
case BuildStep.QueueReferencingProjects:
queueReferencingProjects(state, project, projectPath, projectIndex, config, buildOrder, Debug.checkDefined(buildResult));
step++;
break;

// Should never be done
case Step.Done:
case BuildStep.Done:
default:
assertType<Step.Done>(step);
assertType<BuildStep.Done>(step);

}
Debug.assert(step > currentStep);
Expand Down Expand Up @@ -1247,23 +1268,25 @@ namespace ts {
}

function afterProgramDone<T extends BuilderProgram>(
{ host, watch, builderPrograms }: SolutionBuilderState<T>,
state: SolutionBuilderState<T>,
proj: ResolvedConfigFilePath,
program: T | undefined,
config: ParsedCommandLine
) {
if (program) {
if (host.afterProgramEmitAndDiagnostics) {
host.afterProgramEmitAndDiagnostics(program);
if (program && state.writeFileName) listFiles(program, state.writeFileName);
if (state.host.afterProgramEmitAndDiagnostics) {
state.host.afterProgramEmitAndDiagnostics(program);
}
if (watch) {
if (state.watch) {
program.releaseProgram();
builderPrograms.set(proj, program);
state.builderPrograms.set(proj, program);
}
}
else if (host.afterEmitBundle) {
host.afterEmitBundle(config);
else if (state.host.afterEmitBundle) {
state.host.afterEmitBundle(config);
}
state.projectCompilerOptions = state.baseCompilerOptions;
}

function buildErrors<T extends BuilderProgram>(
Expand All @@ -1272,16 +1295,17 @@ namespace ts {
program: T | undefined,
config: ParsedCommandLine,
diagnostics: readonly Diagnostic[],
errorFlags: BuildResultFlags,
errorType: string
buildResult: BuildResultFlags,
errorType: string,
) {
const canEmitBuildInfo = !(buildResult & BuildResultFlags.SyntaxErrors) && program && !outFile(program.getCompilerOptions());

reportAndStoreErrors(state, resolvedPath, diagnostics);
// List files if any other build error using program (emit errors already report files)
if (program && state.writeFileName) listFiles(program, state.writeFileName);
state.projectStatus.set(resolvedPath, { type: UpToDateStatusType.Unbuildable, reason: `${errorType} errors` });
if (canEmitBuildInfo) return { buildResult, step: BuildStep.EmitBuildInfo };
afterProgramDone(state, resolvedPath, program, config);
state.projectCompilerOptions = state.baseCompilerOptions;
return errorFlags;
return { buildResult, step: BuildStep.QueueReferencingProjects };
}

function updateModuleResolutionCache(
Expand Down
Loading

0 comments on commit bafc3b7

Please sign in to comment.