mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-16 07:13:45 -05:00
Handle declaration emit errors in tsbuild mode by backing up builder state
This helps us revert to state where we pretend as if emit is not done (since we do not do emit if there are errors)
This commit is contained in:
@@ -154,6 +154,38 @@ namespace ts {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases program and other related not needed properties
|
||||
*/
|
||||
function releaseCache(state: BuilderProgramState) {
|
||||
BuilderState.releaseCache(state);
|
||||
state.program = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a clone of the state
|
||||
*/
|
||||
function cloneBuilderProgramState(state: Readonly<BuilderProgramState>): BuilderProgramState {
|
||||
const newState = BuilderState.clone(state) as BuilderProgramState;
|
||||
newState.semanticDiagnosticsPerFile = cloneMapOrUndefined(state.semanticDiagnosticsPerFile);
|
||||
newState.changedFilesSet = cloneMap(state.changedFilesSet);
|
||||
newState.affectedFiles = state.affectedFiles;
|
||||
newState.affectedFilesIndex = state.affectedFilesIndex;
|
||||
newState.currentChangedFilePath = state.currentChangedFilePath;
|
||||
newState.currentAffectedFilesSignatures = cloneMapOrUndefined(state.currentAffectedFilesSignatures);
|
||||
newState.currentAffectedFilesExportedModulesMap = cloneMapOrUndefined(state.currentAffectedFilesExportedModulesMap);
|
||||
newState.seenAffectedFiles = cloneMapOrUndefined(state.seenAffectedFiles);
|
||||
newState.cleanedDiagnosticsOfLibFiles = state.cleanedDiagnosticsOfLibFiles;
|
||||
newState.semanticDiagnosticsFromOldState = cloneMapOrUndefined(state.semanticDiagnosticsFromOldState);
|
||||
newState.program = state.program;
|
||||
newState.compilerOptions = state.compilerOptions;
|
||||
newState.affectedFilesPendingEmit = state.affectedFilesPendingEmit;
|
||||
newState.affectedFilesPendingEmitIndex = state.affectedFilesPendingEmitIndex;
|
||||
newState.seenEmittedFiles = cloneMapOrUndefined(state.seenEmittedFiles);
|
||||
newState.programEmitComplete = state.programEmitComplete;
|
||||
return newState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that source file is ok to be used in calls that arent handled by next
|
||||
*/
|
||||
@@ -458,7 +490,8 @@ namespace ts {
|
||||
* Computing hash to for signature verification
|
||||
*/
|
||||
const computeHash = host.createHash || generateDjb2Hash;
|
||||
const state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState);
|
||||
let state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState);
|
||||
let backupState: BuilderProgramState | undefined;
|
||||
|
||||
// To ensure that we arent storing any references to old program or new program without state
|
||||
newProgram = undefined!; // TODO: GH#18217
|
||||
@@ -467,9 +500,21 @@ namespace ts {
|
||||
|
||||
const result = createRedirectedBuilderProgram(state, configFileParsingDiagnostics);
|
||||
result.getState = () => state;
|
||||
result.backupCurrentState = () => {
|
||||
Debug.assert(backupState === undefined);
|
||||
backupState = cloneBuilderProgramState(state);
|
||||
};
|
||||
result.useBackupState = () => {
|
||||
state = Debug.assertDefined(backupState);
|
||||
backupState = undefined;
|
||||
};
|
||||
result.getAllDependencies = sourceFile => BuilderState.getAllDependencies(state, Debug.assertDefined(state.program), sourceFile);
|
||||
result.getSemanticDiagnostics = getSemanticDiagnostics;
|
||||
result.emit = emit;
|
||||
result.releaseProgram = () => {
|
||||
releaseCache(state);
|
||||
backupState = undefined;
|
||||
};
|
||||
|
||||
if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) {
|
||||
(result as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
|
||||
@@ -650,6 +695,8 @@ namespace ts {
|
||||
export function createRedirectedBuilderProgram(state: { program: Program | undefined; compilerOptions: CompilerOptions; }, configFileParsingDiagnostics: ReadonlyArray<Diagnostic>): BuilderProgram {
|
||||
return {
|
||||
getState: notImplemented,
|
||||
backupCurrentState: noop,
|
||||
useBackupState: noop,
|
||||
getProgram: () => Debug.assertDefined(state.program),
|
||||
getProgramOrUndefined: () => state.program,
|
||||
releaseProgram: () => state.program = undefined,
|
||||
@@ -694,6 +741,10 @@ namespace ts {
|
||||
export interface BuilderProgram {
|
||||
/*@internal*/
|
||||
getState(): BuilderProgramState;
|
||||
/*@internal*/
|
||||
backupCurrentState(): void;
|
||||
/*@internal*/
|
||||
useBackupState(): void;
|
||||
/**
|
||||
* Returns current program
|
||||
*/
|
||||
|
||||
@@ -50,11 +50,15 @@ namespace ts {
|
||||
/**
|
||||
* Cache of all files excluding default library file for the current program
|
||||
*/
|
||||
allFilesExcludingDefaultLibraryFile: ReadonlyArray<SourceFile> | undefined;
|
||||
allFilesExcludingDefaultLibraryFile?: ReadonlyArray<SourceFile>;
|
||||
/**
|
||||
* Cache of all the file names
|
||||
*/
|
||||
allFileNames: ReadonlyArray<string> | undefined;
|
||||
allFileNames?: ReadonlyArray<string>;
|
||||
}
|
||||
|
||||
export function cloneMapOrUndefined<T>(map: ReadonlyMap<T> | undefined) {
|
||||
return map ? cloneMap(map) : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,9 +234,32 @@ namespace ts.BuilderState {
|
||||
fileInfos,
|
||||
referencedMap,
|
||||
exportedModulesMap,
|
||||
hasCalledUpdateShapeSignature,
|
||||
allFilesExcludingDefaultLibraryFile: undefined,
|
||||
allFileNames: undefined
|
||||
hasCalledUpdateShapeSignature
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases needed properties
|
||||
*/
|
||||
export function releaseCache(state: BuilderState) {
|
||||
state.allFilesExcludingDefaultLibraryFile = undefined;
|
||||
state.allFileNames = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a clone of the state
|
||||
*/
|
||||
export function clone(state: Readonly<BuilderState>): BuilderState {
|
||||
const fileInfos = createMap<FileInfo>();
|
||||
state.fileInfos.forEach((value, key) => {
|
||||
fileInfos.set(key, { ...value });
|
||||
});
|
||||
// Dont need to backup allFiles info since its cache anyway
|
||||
return {
|
||||
fileInfos,
|
||||
referencedMap: cloneMapOrUndefined(state.referencedMap),
|
||||
exportedModulesMap: cloneMapOrUndefined(state.exportedModulesMap),
|
||||
hasCalledUpdateShapeSignature: cloneMap(state.hasCalledUpdateShapeSignature),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -382,8 +382,6 @@ namespace ts {
|
||||
host.reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system);
|
||||
host.reportSolutionBuilderStatus = reportSolutionBuilderStatus || createBuilderStatusReporter(system);
|
||||
return host;
|
||||
|
||||
// TODO after program create
|
||||
}
|
||||
|
||||
export function createSolutionBuilderHost<T extends BuilderProgram = BuilderProgram>(system = sys, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportErrorSummary?: ReportEmitErrorSummary) {
|
||||
@@ -499,9 +497,7 @@ namespace ts {
|
||||
clearMap(allWatchedWildcardDirectories, wildCardWatches => clearMap(wildCardWatches, closeFileWatcherOf));
|
||||
clearMap(allWatchedInputFiles, inputFileWatches => clearMap(inputFileWatches, closeFileWatcher));
|
||||
clearMap(allWatchedConfigFiles, closeFileWatcher);
|
||||
if (!options.watch) {
|
||||
builderPrograms.clear();
|
||||
}
|
||||
builderPrograms.clear();
|
||||
updateGetSourceFile();
|
||||
}
|
||||
|
||||
@@ -576,7 +572,7 @@ namespace ts {
|
||||
hostWithWatch,
|
||||
resolved,
|
||||
() => {
|
||||
invalidateProjectAndScheduleBuilds(resolved, ConfigFileProgramReloadLevel.Full);
|
||||
invalidateProjectAndScheduleBuilds(resolved, ConfigFileProgramReloadLevel.Full);
|
||||
},
|
||||
PollingInterval.High,
|
||||
WatchType.ConfigFile,
|
||||
@@ -1132,15 +1128,17 @@ namespace ts {
|
||||
return buildErrors(semanticDiagnostics, BuildResultFlags.TypeErrors, "Semantic");
|
||||
}
|
||||
|
||||
// Before emitting lets backup state, so we can revert it back if there are declaration errors to handle emit and declaration errors correctly
|
||||
program.backupCurrentState();
|
||||
let newestDeclarationFileContentChangedTime = minimumDate;
|
||||
let anyDtsChanged = false;
|
||||
let declDiagnostics: Diagnostic[] | undefined;
|
||||
const reportDeclarationDiagnostics = (d: Diagnostic) => (declDiagnostics || (declDiagnostics = [])).push(d);
|
||||
const outputFiles: OutputFile[] = [];
|
||||
// TODO:: handle declaration diagnostics in incremental build.
|
||||
emitFilesAndReportErrors(program, reportDeclarationDiagnostics, writeFileName, /*reportSummary*/ undefined, (name, text, writeByteOrderMark) => outputFiles.push({ name, text, writeByteOrderMark }));
|
||||
// Don't emit .d.ts if there are decl file errors
|
||||
if (declDiagnostics) {
|
||||
program.useBackupState();
|
||||
return buildErrors(declDiagnostics, BuildResultFlags.DeclarationEmitErrors, "Declaration file");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user