Change builder to BuilderProgram so it is similar to operating on program

This commit is contained in:
Sheetal Nandi 2017-12-07 18:55:11 -08:00
parent 965f40f213
commit dc62bb9abc
6 changed files with 417 additions and 381 deletions

View file

@ -2,12 +2,10 @@
/*@internal*/ /*@internal*/
namespace ts { namespace ts {
export enum BuilderKind { /**
BuilderKindSemanticDiagnostics, * State to store the changed files, affected files and cache semantic diagnostics
BuilderKindEmitAndSemanticDiagnostics */
} export interface BuilderProgramState extends BuilderState {
interface BuilderStateWithChangedFiles extends BuilderState {
/** /**
* Cache of semantic diagnostics for files with their Path being the key * Cache of semantic diagnostics for files with their Path being the key
*/ */
@ -37,6 +35,10 @@ namespace ts {
* Already seen affected files * Already seen affected files
*/ */
seenAffectedFiles: Map<true> | undefined; seenAffectedFiles: Map<true> | undefined;
/**
* program corresponding to this state
*/
program: Program;
} }
function hasSameKeys<T, U>(map1: ReadonlyMap<T> | undefined, map2: ReadonlyMap<U> | undefined) { function hasSameKeys<T, U>(map1: ReadonlyMap<T> | undefined, map2: ReadonlyMap<U> | undefined) {
@ -53,8 +55,9 @@ namespace ts {
/** /**
* Create the state so that we can iterate on changedFiles/affected files * Create the state so that we can iterate on changedFiles/affected files
*/ */
function createBuilderStateWithChangedFiles(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly<BuilderStateWithChangedFiles>): BuilderStateWithChangedFiles { function createBuilderProgramState(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly<BuilderProgramState>): BuilderProgramState {
const state = BuilderState.create(newProgram, getCanonicalFileName, oldState) as BuilderStateWithChangedFiles; const state = BuilderState.create(newProgram, getCanonicalFileName, oldState) as BuilderProgramState;
state.program = newProgram;
const compilerOptions = newProgram.getCompilerOptions(); const compilerOptions = newProgram.getCompilerOptions();
if (!compilerOptions.outFile && !compilerOptions.out) { if (!compilerOptions.outFile && !compilerOptions.out) {
state.semanticDiagnosticsPerFile = createMap<ReadonlyArray<Diagnostic>>(); state.semanticDiagnosticsPerFile = createMap<ReadonlyArray<Diagnostic>>();
@ -107,9 +110,119 @@ namespace ts {
return state; return state;
} }
export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindSemanticDiagnostics): SemanticDiagnosticsBuilder; /**
export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindEmitAndSemanticDiagnostics): EmitAndSemanticDiagnosticsBuilder; * Verifies that source file is ok to be used in calls that arent handled by next
export function createBuilder(host: BuilderHost, builderKind: BuilderKind) { */
function assertSourceFileOkWithoutNextAffectedCall(state: BuilderProgramState, sourceFile: SourceFile | undefined) {
Debug.assert(!sourceFile || !state.affectedFiles || state.affectedFiles[state.affectedFilesIndex - 1] !== sourceFile || !state.semanticDiagnosticsPerFile.has(sourceFile.path));
}
/**
* This function returns the next affected file to be processed.
* Note that until doneAffected is called it would keep reporting same result
* This is to allow the callers to be able to actually remove affected file only when the operation is complete
* eg. if during diagnostics check cancellation token ends up cancelling the request, the affected file should be retained
*/
function getNextAffectedFile(state: BuilderProgramState, cancellationToken: CancellationToken | undefined, computeHash: BuilderState.ComputeHash): SourceFile | Program | undefined {
while (true) {
const { affectedFiles } = state;
if (affectedFiles) {
const { seenAffectedFiles, semanticDiagnosticsPerFile } = state;
let { affectedFilesIndex } = state;
while (affectedFilesIndex < affectedFiles.length) {
const affectedFile = affectedFiles[affectedFilesIndex];
if (!seenAffectedFiles.has(affectedFile.path)) {
// Set the next affected file as seen and remove the cached semantic diagnostics
state.affectedFilesIndex = affectedFilesIndex;
semanticDiagnosticsPerFile.delete(affectedFile.path);
return affectedFile;
}
seenAffectedFiles.set(affectedFile.path, true);
affectedFilesIndex++;
}
// Remove the changed file from the change set
state.changedFilesSet.delete(state.currentChangedFilePath);
state.currentChangedFilePath = undefined;
// Commit the changes in file signature
BuilderState.updateSignaturesFromCache(state, state.currentAffectedFilesSignatures);
state.currentAffectedFilesSignatures.clear();
state.affectedFiles = undefined;
}
// Get next changed file
const nextKey = state.changedFilesSet.keys().next();
if (nextKey.done) {
// Done
return undefined;
}
// With --out or --outFile all outputs go into single file
// so operations are performed directly on program, return program
const compilerOptions = state.program.getCompilerOptions();
if (compilerOptions.outFile || compilerOptions.out) {
Debug.assert(!state.semanticDiagnosticsPerFile);
return state.program;
}
// Get next batch of affected files
state.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures || createMap();
state.affectedFiles = BuilderState.getFilesAffectedBy(state, state.program, nextKey.value as Path, cancellationToken, computeHash, state.currentAffectedFilesSignatures);
state.currentChangedFilePath = nextKey.value as Path;
state.semanticDiagnosticsPerFile.delete(nextKey.value as Path);
state.affectedFilesIndex = 0;
state.seenAffectedFiles = state.seenAffectedFiles || createMap<true>();
}
}
/**
* This is called after completing operation on the next affected file.
* The operations here are postponed to ensure that cancellation during the iteration is handled correctly
*/
function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program) {
if (affected === state.program) {
state.changedFilesSet.clear();
}
else {
state.seenAffectedFiles.set((affected as SourceFile).path, true);
state.affectedFilesIndex++;
}
}
/**
* Returns the result with affected file
*/
function toAffectedFileResult<T>(state: BuilderProgramState, result: T, affected: SourceFile | Program): AffectedFileResult<T> {
doneWithAffectedFile(state, affected);
return { result, affected };
}
/**
* Gets the semantic diagnostics either from cache if present, or otherwise from program and caches it
* Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files/changed file set
*/
function getSemanticDiagnosticsOfFile(state: BuilderProgramState, sourceFile: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic> {
const path = sourceFile.path;
const cachedDiagnostics = state.semanticDiagnosticsPerFile.get(path);
// Report the semantic diagnostics from the cache if we already have those diagnostics present
if (cachedDiagnostics) {
return cachedDiagnostics;
}
// Diagnostics werent cached, get them from program, and cache the result
const diagnostics = state.program.getSemanticDiagnostics(sourceFile, cancellationToken);
state.semanticDiagnosticsPerFile.set(path, diagnostics);
return diagnostics;
}
export enum BuilderProgramKind {
SemanticDiagnosticsBuilderProgram,
EmitAndSemanticDiagnosticsBuilderProgram
}
export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BaseBuilderProgram | undefined, kind: BuilderProgramKind.SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram;
export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BaseBuilderProgram | undefined, kind: BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram;
export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BaseBuilderProgram | undefined, kind: BuilderProgramKind) {
/** /**
* Create the canonical file name for identity * Create the canonical file name for identity
*/ */
@ -118,183 +231,129 @@ namespace ts {
* Computing hash to for signature verification * Computing hash to for signature verification
*/ */
const computeHash = host.createHash || identity; const computeHash = host.createHash || identity;
let state: BuilderStateWithChangedFiles; const state = createBuilderProgramState(newProgram, getCanonicalFileName, oldProgram && oldProgram.getState());
switch (builderKind) { // To ensure that we arent storing any references to old program or new program without state
case BuilderKind.BuilderKindSemanticDiagnostics: newProgram = undefined;
return getSemanticDiagnosticsBuilder(); oldProgram = undefined;
case BuilderKind.BuilderKindEmitAndSemanticDiagnostics:
return getEmitAndSemanticDiagnosticsBuilder(); const result: BaseBuilderProgram = {
default: getState: () => state,
notImplemented(); getCompilerOptions: () => state.program.getCompilerOptions(),
getSourceFile: fileName => state.program.getSourceFile(fileName),
getSourceFiles: () => state.program.getSourceFiles(),
getOptionsDiagnostics: cancellationToken => state.program.getOptionsDiagnostics(cancellationToken),
getGlobalDiagnostics: cancellationToken => state.program.getGlobalDiagnostics(cancellationToken),
getSyntacticDiagnostics: (sourceFile, cancellationToken) => state.program.getSyntacticDiagnostics(sourceFile, cancellationToken),
getSemanticDiagnostics,
emit,
getAllDependencies: sourceFile => BuilderState.getAllDependencies(state, state.program, sourceFile)
};
if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) {
(result as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
}
else if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
(result as EmitAndSemanticDiagnosticsBuilderProgram).getCurrentDirectory = () => state.program.getCurrentDirectory();
(result as EmitAndSemanticDiagnosticsBuilderProgram).emitNextAffectedFile = emitNextAffectedFile;
}
else {
notImplemented();
} }
function getSemanticDiagnosticsBuilder(): SemanticDiagnosticsBuilder { return result;
return {
updateProgram,
getAllDependencies,
getSemanticDiagnosticsOfNextAffectedFile,
getSemanticDiagnostics
};
}
function getEmitAndSemanticDiagnosticsBuilder(): EmitAndSemanticDiagnosticsBuilder {
return {
updateProgram,
getAllDependencies,
emitNextAffectedFile,
getSemanticDiagnostics
};
}
/** /**
* Update current state to reflect new program * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
* Updates changed files, references, file infos etc which happens through the state callbacks * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
* in that order would be used to write the files
*/ */
function updateProgram(newProgram: Program) { function emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult> {
state = createBuilderStateWithChangedFiles(newProgram, getCanonicalFileName, state); const affected = getNextAffectedFile(state, cancellationToken, computeHash);
} if (!affected) {
/**
* This function returns the next affected file to be processed.
* Note that until doneAffected is called it would keep reporting same result
* This is to allow the callers to be able to actually remove affected file only when the operation is complete
* eg. if during diagnostics check cancellation token ends up cancelling the request, the affected file should be retained
*/
function getNextAffectedFile(programOfThisState: Program, cancellationToken: CancellationToken | undefined): SourceFile | Program | undefined {
while (true) {
const { affectedFiles } = state;
if (affectedFiles) {
const { seenAffectedFiles, semanticDiagnosticsPerFile } = state;
let { affectedFilesIndex } = state;
while (affectedFilesIndex < affectedFiles.length) {
const affectedFile = affectedFiles[affectedFilesIndex];
if (!seenAffectedFiles.has(affectedFile.path)) {
// Set the next affected file as seen and remove the cached semantic diagnostics
state.affectedFilesIndex = affectedFilesIndex;
semanticDiagnosticsPerFile.delete(affectedFile.path);
return affectedFile;
}
seenAffectedFiles.set(affectedFile.path, true);
affectedFilesIndex++;
}
// Remove the changed file from the change set
state.changedFilesSet.delete(state.currentChangedFilePath);
state.currentChangedFilePath = undefined;
// Commit the changes in file signature
BuilderState.updateSignaturesFromCache(state, state.currentAffectedFilesSignatures);
state.currentAffectedFilesSignatures.clear();
state.affectedFiles = undefined;
}
// Get next changed file
const nextKey = state.changedFilesSet.keys().next();
if (nextKey.done) {
// Done
return undefined;
}
const compilerOptions = programOfThisState.getCompilerOptions();
// With --out or --outFile all outputs go into single file
// so operations are performed directly on program, return program
if (compilerOptions.outFile || compilerOptions.out) {
Debug.assert(!state.semanticDiagnosticsPerFile);
return programOfThisState;
}
// Get next batch of affected files
state.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures || createMap();
state.affectedFiles = BuilderState.getFilesAffectedBy(state, programOfThisState, nextKey.value as Path, cancellationToken, computeHash, state.currentAffectedFilesSignatures);
state.currentChangedFilePath = nextKey.value as Path;
state.semanticDiagnosticsPerFile.delete(nextKey.value as Path);
state.affectedFilesIndex = 0;
state.seenAffectedFiles = state.seenAffectedFiles || createMap<true>();
}
}
/**
* This is called after completing operation on the next affected file.
* The operations here are postponed to ensure that cancellation during the iteration is handled correctly
*/
function doneWithAffectedFile(programOfThisState: Program, affected: SourceFile | Program) {
if (affected === programOfThisState) {
state.changedFilesSet.clear();
}
else {
state.seenAffectedFiles.set((affected as SourceFile).path, true);
state.affectedFilesIndex++;
}
}
/**
* Returns the result with affected file
*/
function toAffectedFileResult<T>(programOfThisState: Program, result: T, affected: SourceFile | Program): AffectedFileResult<T> {
doneWithAffectedFile(programOfThisState, affected);
return { result, affected };
}
/**
* Emits the next affected file, and returns the EmitResult along with source files emitted
* Returns undefined when iteration is complete
*/
function emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult> {
const affectedFile = getNextAffectedFile(programOfThisState, cancellationToken);
if (!affectedFile) {
// Done // Done
return undefined; return undefined;
} }
else if (affectedFile === programOfThisState) {
// When whole program is affected, do emit only once (eg when --out or --outFile is specified)
return toAffectedFileResult(
programOfThisState,
programOfThisState.emit(/*targetSourceFile*/ undefined, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers),
programOfThisState
);
}
// Emit the affected file
const targetSourceFile = affectedFile as SourceFile;
return toAffectedFileResult( return toAffectedFileResult(
programOfThisState, state,
programOfThisState.emit(targetSourceFile, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers), // When whole program is affected, do emit only once (eg when --out or --outFile is specified)
targetSourceFile // Otherwise just affected file
state.program.emit(affected === state.program ? undefined : affected as SourceFile, writeFile || host.writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers),
affected
); );
} }
/**
* Emits the JavaScript and declaration files.
* When targetSource file is specified, emits the files corresponding to that source file,
* otherwise for the whole program.
* In case of EmitAndSemanticDiagnosticsBuilderProgram, when targetSourceFile is specified,
* it is assumed that that file is handled from affected file list. If targetSourceFile is not specified,
* it will only emit all the affected files instead of whole program
*
* The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
* in that order would be used to write the files
*/
function emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult {
if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
assertSourceFileOkWithoutNextAffectedCall(state, targetSourceFile);
if (!targetSourceFile) {
// Emit and report any errors we ran into.
let sourceMaps: SourceMapData[] = [];
let emitSkipped: boolean;
let diagnostics: Diagnostic[];
let emittedFiles: string[] = [];
let affectedEmitResult: AffectedFileResult<EmitResult>;
while (affectedEmitResult = emitNextAffectedFile(writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers)) {
emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped;
diagnostics = addRange(diagnostics, affectedEmitResult.result.diagnostics);
emittedFiles = addRange(emittedFiles, affectedEmitResult.result.emittedFiles);
sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps);
}
return {
emitSkipped,
diagnostics: diagnostics || emptyArray,
emittedFiles,
sourceMaps
};
}
}
return state.program.emit(targetSourceFile, writeFile || host.writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
}
/** /**
* Return the semantic diagnostics for the next affected file or undefined if iteration is complete * Return the semantic diagnostics for the next affected file or undefined if iteration is complete
* If provided ignoreSourceFile would be called before getting the diagnostics and would ignore the sourceFile if the returned value was true * If provided ignoreSourceFile would be called before getting the diagnostics and would ignore the sourceFile if the returned value was true
*/ */
function getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult<ReadonlyArray<Diagnostic>> { function getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult<ReadonlyArray<Diagnostic>> {
while (true) { while (true) {
const affectedFile = getNextAffectedFile(programOfThisState, cancellationToken); const affected = getNextAffectedFile(state, cancellationToken, computeHash);
if (!affectedFile) { if (!affected) {
// Done // Done
return undefined; return undefined;
} }
else if (affectedFile === programOfThisState) { else if (affected === state.program) {
// When whole program is affected, get all semantic diagnostics (eg when --out or --outFile is specified) // When whole program is affected, get all semantic diagnostics (eg when --out or --outFile is specified)
return toAffectedFileResult( return toAffectedFileResult(
programOfThisState, state,
programOfThisState.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken), state.program.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken),
programOfThisState affected
); );
} }
// Get diagnostics for the affected file if its not ignored // Get diagnostics for the affected file if its not ignored
const targetSourceFile = affectedFile as SourceFile; if (ignoreSourceFile && ignoreSourceFile(affected as SourceFile)) {
if (ignoreSourceFile && ignoreSourceFile(targetSourceFile)) {
// Get next affected file // Get next affected file
doneWithAffectedFile(programOfThisState, targetSourceFile); doneWithAffectedFile(state, affected);
continue; continue;
} }
return toAffectedFileResult( return toAffectedFileResult(
programOfThisState, state,
getSemanticDiagnosticsOfFile(programOfThisState, targetSourceFile, cancellationToken), getSemanticDiagnosticsOfFile(state, affected as SourceFile, cancellationToken),
targetSourceFile affected
); );
} }
} }
@ -302,59 +361,46 @@ namespace ts {
/** /**
* Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
* The semantic diagnostics are cached and managed here * The semantic diagnostics are cached and managed here
* Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files * Note that it is assumed that when asked about semantic diagnostics through this API,
* the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
* In case of SemanticDiagnosticsBuilderProgram if the source file is not provided,
* it will iterate through all the affected files, to ensure that cache stays valid and yet provide a way to get all semantic diagnostics
*/ */
function getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic> { function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic> {
Debug.assert(!state.affectedFiles || state.affectedFiles[state.affectedFilesIndex - 1] !== sourceFile || !state.semanticDiagnosticsPerFile.has(sourceFile.path)); assertSourceFileOkWithoutNextAffectedCall(state, sourceFile);
const compilerOptions = programOfThisState.getCompilerOptions(); const compilerOptions = state.program.getCompilerOptions();
if (compilerOptions.outFile || compilerOptions.out) { if (compilerOptions.outFile || compilerOptions.out) {
Debug.assert(!state.semanticDiagnosticsPerFile); Debug.assert(!state.semanticDiagnosticsPerFile);
// We dont need to cache the diagnostics just return them from program // We dont need to cache the diagnostics just return them from program
return programOfThisState.getSemanticDiagnostics(sourceFile, cancellationToken); return state.program.getSemanticDiagnostics(sourceFile, cancellationToken);
} }
if (sourceFile) { if (sourceFile) {
return getSemanticDiagnosticsOfFile(programOfThisState, sourceFile, cancellationToken); return getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken);
}
if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) {
// When semantic builder asks for diagnostics of the whole program,
// ensure that all the affected files are handled
let affected: SourceFile | Program | undefined;
while (affected = getNextAffectedFile(state, cancellationToken, computeHash)) {
doneWithAffectedFile(state, affected);
}
} }
let diagnostics: Diagnostic[]; let diagnostics: Diagnostic[];
for (const sourceFile of programOfThisState.getSourceFiles()) { for (const sourceFile of state.program.getSourceFiles()) {
diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(programOfThisState, sourceFile, cancellationToken)); diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken));
} }
return diagnostics || emptyArray; return diagnostics || emptyArray;
} }
/**
* Gets the semantic diagnostics either from cache if present, or otherwise from program and caches it
* Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files/changed file set
*/
function getSemanticDiagnosticsOfFile(program: Program, sourceFile: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic> {
const path = sourceFile.path;
const cachedDiagnostics = state.semanticDiagnosticsPerFile.get(path);
// Report the semantic diagnostics from the cache if we already have those diagnostics present
if (cachedDiagnostics) {
return cachedDiagnostics;
}
// Diagnostics werent cached, get them from program, and cache the result
const diagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken);
state.semanticDiagnosticsPerFile.set(path, diagnostics);
return diagnostics;
}
/**
* Get all the dependencies of the sourceFile
*/
function getAllDependencies(programOfThisState: Program, sourceFile: SourceFile) {
return BuilderState.getAllDependencies(state, programOfThisState, sourceFile);
}
} }
} }
namespace ts { namespace ts {
export type AffectedFileResult<T> = { result: T; affected: SourceFile | Program; } | undefined; export type AffectedFileResult<T> = { result: T; affected: SourceFile | Program; } | undefined;
export interface BuilderHost { export interface BuilderProgramHost {
/** /**
* return true if file names are treated with case sensitivity * return true if file names are treated with case sensitivity
*/ */
@ -363,73 +409,110 @@ namespace ts {
* If provided this would be used this hash instead of actual file shape text for detecting changes * If provided this would be used this hash instead of actual file shape text for detecting changes
*/ */
createHash?: (data: string) => string; createHash?: (data: string) => string;
/**
* When emit or emitNextAffectedFile are called without writeFile,
* this callback if present would be used to write files
*/
writeFile?: WriteFileCallback;
} }
/** /**
* Builder to manage the program state changes * Builder to manage the program state changes
*/ */
export interface BaseBuilder { export interface BaseBuilderProgram {
/*@internal*/
getState(): BuilderProgramState;
/** /**
* Updates the program in the builder to represent new state * Get compiler options of the program
*/ */
updateProgram(newProgram: Program): void; getCompilerOptions(): CompilerOptions;
/**
* Get the source file in the program with file name
*/
getSourceFile(fileName: string): SourceFile | undefined;
/**
* Get a list of files in the program
*/
getSourceFiles(): ReadonlyArray<SourceFile>;
/**
* Get the diagnostics for compiler options
*/
getOptionsDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
/**
* Get the diagnostics that dont belong to any file
*/
getGlobalDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
/**
* Get the syntax diagnostics, for all source files if source file is not supplied
*/
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
/** /**
* Get all the dependencies of the file * Get all the dependencies of the file
*/ */
getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): ReadonlyArray<string>; getAllDependencies(sourceFile: SourceFile): ReadonlyArray<string>;
/**
* Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
* The semantic diagnostics are cached and managed here
* Note that it is assumed that when asked about semantic diagnostics through this API,
* the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
* In case of SemanticDiagnosticsBuilderProgram if the source file is not provided,
* it will iterate through all the affected files, to ensure that cache stays valid and yet provide a way to get all semantic diagnostics
*/
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
/**
* Emits the JavaScript and declaration files.
* When targetSource file is specified, emits the files corresponding to that source file,
* otherwise for the whole program.
* In case of EmitAndSemanticDiagnosticsBuilderProgram, when targetSourceFile is specified,
* it is assumed that that file is handled from affected file list. If targetSourceFile is not specified,
* it will only emit all the affected files instead of whole program
*
* The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
* in that order would be used to write the files
*/
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
} }
/** /**
* The builder that caches the semantic diagnostics for the program and handles the changed files and affected files * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files
*/ */
export interface SemanticDiagnosticsBuilder extends BaseBuilder { export interface SemanticDiagnosticsBuilderProgram extends BaseBuilderProgram {
/** /**
* Gets the semantic diagnostics from the program for the next affected file and caches it * Gets the semantic diagnostics from the program for the next affected file and caches it
* Returns undefined if the iteration is complete * Returns undefined if the iteration is complete
*/ */
getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult<ReadonlyArray<Diagnostic>>; getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult<ReadonlyArray<Diagnostic>>;
/**
* Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
* The semantic diagnostics are cached and managed here
* Note that it is assumed that the when asked about semantic diagnostics through this API,
* the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
*/
getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
} }
/** /**
* The builder that can handle the changes in program and iterate through changed file to emit the files * The builder that can handle the changes in program and iterate through changed file to emit the files
* The semantic diagnostics are cached per file and managed by clearing for the changed/affected files * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files
*/ */
export interface EmitAndSemanticDiagnosticsBuilder extends BaseBuilder { export interface EmitAndSemanticDiagnosticsBuilderProgram extends BaseBuilderProgram {
/**
* Get the current directory of the program
*/
getCurrentDirectory(): string;
/** /**
* Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
* The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
* in that order would be used to write the files
*/ */
emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult>; emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult>;
/**
* Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
* The semantic diagnostics are cached and managed here
* Note that it is assumed that the when asked about semantic diagnostics through this API,
* the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
*/
getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
} }
/** /**
* Create the builder to manage semantic diagnostics and cache them * Create the builder to manage semantic diagnostics and cache them
*/ */
export function createSemanticDiagnosticsBuilder(host: BuilderHost): SemanticDiagnosticsBuilder { export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram {
return createBuilder(host, BuilderKind.BuilderKindSemanticDiagnostics); return createBuilderProgram(newProgram, host, oldProgram, BuilderProgramKind.SemanticDiagnosticsBuilderProgram);
} }
/** /**
* Create the builder that can handle the changes in program and iterate through changed files * Create the builder that can handle the changes in program and iterate through changed files
* to emit the those files and manage semantic diagnostics cache as well * to emit the those files and manage semantic diagnostics cache as well
*/ */
export function createEmitAndSemanticDiagnosticsBuilder(host: BuilderHost): EmitAndSemanticDiagnosticsBuilder { export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram {
return createBuilder(host, BuilderKind.BuilderKindEmitAndSemanticDiagnostics); return createBuilderProgram(newProgram, host, oldProgram, BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram);
} }
} }

View file

@ -38,6 +38,7 @@
"emitter.ts", "emitter.ts",
"watchUtilities.ts", "watchUtilities.ts",
"program.ts", "program.ts",
"builderState.ts",
"builder.ts", "builder.ts",
"resolutionCache.ts", "resolutionCache.ts",
"watch.ts", "watch.ts",

View file

@ -146,62 +146,23 @@ namespace ts {
return ExitStatus.Success; return ExitStatus.Success;
} }
/**
* Creates the function that emits files and reports errors when called with program
*/
function createEmitFilesAndReportErrorsWithBuilderUsingSystem(system: System, reportDiagnostic: DiagnosticReporter) {
const emitErrorsAndReportErrorsWithBuilder = createEmitFilesAndReportErrorsWithBuilder({
useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames,
createHash: system.createHash && (s => system.createHash(s)),
writeFile,
reportDiagnostic,
writeFileName: s => system.write(s + system.newLine)
});
let host: CachedDirectoryStructureHost | undefined;
return {
emitFilesAndReportError: (program: Program) => emitErrorsAndReportErrorsWithBuilder(program),
setHost: (cachedDirectoryStructureHost: CachedDirectoryStructureHost) => host = cachedDirectoryStructureHost
};
function getHost() {
return host || system;
}
function ensureDirectoriesExist(directoryPath: string) {
if (directoryPath.length > getRootLength(directoryPath) && !getHost().directoryExists(directoryPath)) {
const parentDirectory = getDirectoryPath(directoryPath);
ensureDirectoriesExist(parentDirectory);
getHost().createDirectory(directoryPath);
}
}
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) {
try {
performance.mark("beforeIOWrite");
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
getHost().writeFile(fileName, text, writeByteOrderMark);
performance.mark("afterIOWrite");
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
}
catch (e) {
if (onError) {
onError(e.message);
}
}
}
}
const noopFileWatcher: FileWatcher = { close: noop }; const noopFileWatcher: FileWatcher = { close: noop };
/** /**
* Creates the watch compiler host that can be extended with config file or root file names and options host * Creates the watch compiler host that can be extended with config file or root file names and options host
*/ */
function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter): WatchCompilerHost { function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter): WatchCompilerHost {
const { emitFilesAndReportError, setHost } = createEmitFilesAndReportErrorsWithBuilderUsingSystem(system, reportDiagnostic); let host: DirectoryStructureHost = system;
const host: WatchCompilerHost = { const useCaseSensitiveFileNames = () => system.useCaseSensitiveFileNames;
useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, const writeFileName = (s: string) => system.write(s + system.newLine);
const builderProgramHost: BuilderProgramHost = {
useCaseSensitiveFileNames,
createHash: system.createHash && (s => system.createHash(s)),
writeFile
};
let builderProgram: EmitAndSemanticDiagnosticsBuilderProgram | undefined;
return {
useCaseSensitiveFileNames,
getNewLine: () => system.newLine, getNewLine: () => system.newLine,
getCurrentDirectory: () => system.getCurrentDirectory(), getCurrentDirectory: () => system.getCurrentDirectory(),
getDefaultLibLocation, getDefaultLibLocation,
@ -220,10 +181,9 @@ namespace ts {
onWatchStatusChange, onWatchStatusChange,
createDirectory: path => system.createDirectory(path), createDirectory: path => system.createDirectory(path),
writeFile: (path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark), writeFile: (path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark),
onCachedDirectoryStructureHostCreate: host => setHost(host), onCachedDirectoryStructureHostCreate: cacheHost => host = cacheHost || system,
afterProgramCreate: emitFilesAndReportError, afterProgramCreate: emitFilesAndReportErrorUsingBuilder,
}; };
return host;
function getDefaultLibLocation() { function getDefaultLibLocation() {
return getDirectoryPath(normalizePath(system.getExecutingFilePath())); return getDirectoryPath(normalizePath(system.getExecutingFilePath()));
@ -235,6 +195,36 @@ namespace ts {
} }
system.write(`${new Date().toLocaleTimeString()} - ${flattenDiagnosticMessageText(diagnostic.messageText, newLine)}${newLine + newLine + newLine}`); system.write(`${new Date().toLocaleTimeString()} - ${flattenDiagnosticMessageText(diagnostic.messageText, newLine)}${newLine + newLine + newLine}`);
} }
function emitFilesAndReportErrorUsingBuilder(program: Program) {
builderProgram = createEmitAndSemanticDiagnosticsBuilderProgram(program, builderProgramHost, builderProgram);
emitFilesAndReportErrors(builderProgram, reportDiagnostic, writeFileName);
}
function ensureDirectoriesExist(directoryPath: string) {
if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists(directoryPath)) {
const parentDirectory = getDirectoryPath(directoryPath);
ensureDirectoriesExist(parentDirectory);
host.createDirectory(directoryPath);
}
}
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) {
try {
performance.mark("beforeIOWrite");
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
host.writeFile(fileName, text, writeByteOrderMark);
performance.mark("afterIOWrite");
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
}
catch (e) {
if (onError) {
onError(e.message);
}
}
}
} }
/** /**
@ -272,73 +262,6 @@ namespace ts {
namespace ts { namespace ts {
export type DiagnosticReporter = (diagnostic: Diagnostic) => void; export type DiagnosticReporter = (diagnostic: Diagnostic) => void;
interface BuilderProgram extends ProgramToEmitFilesAndReportErrors {
updateProgram(program: Program): void;
}
function createBuilderProgram(host: BuilderEmitHost): BuilderProgram {
const builder = createEmitAndSemanticDiagnosticsBuilder(host);
let program: Program;
return {
getCurrentDirectory: () => program.getCurrentDirectory(),
getCompilerOptions: () => program.getCompilerOptions(),
getSourceFiles: () => program.getSourceFiles(),
getSyntacticDiagnostics: () => program.getSyntacticDiagnostics(),
getOptionsDiagnostics: () => program.getOptionsDiagnostics(),
getGlobalDiagnostics: () => program.getGlobalDiagnostics(),
getSemanticDiagnostics: () => builder.getSemanticDiagnostics(program),
emit,
updateProgram
};
function updateProgram(p: Program) {
program = p;
builder.updateProgram(p);
}
function emit(): EmitResult {
// Emit and report any errors we ran into.
let sourceMaps: SourceMapData[];
let emitSkipped: boolean;
let diagnostics: Diagnostic[];
let emittedFiles: string[];
let affectedEmitResult: AffectedFileResult<EmitResult>;
while (affectedEmitResult = builder.emitNextAffectedFile(program, host.writeFile)) {
emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped;
diagnostics = addRange(diagnostics, affectedEmitResult.result.diagnostics);
emittedFiles = addRange(emittedFiles, affectedEmitResult.result.emittedFiles);
sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps);
}
return {
emitSkipped,
diagnostics,
emittedFiles,
sourceMaps
};
}
}
/**
* Host needed to emit files and report errors using builder
*/
export interface BuilderEmitHost extends BuilderHost {
writeFile: WriteFileCallback;
reportDiagnostic: DiagnosticReporter;
writeFileName?: (s: string) => void;
}
/**
* Creates the function that reports the program errors and emit files every time it is called with argument as program
*/
export function createEmitFilesAndReportErrorsWithBuilder(host: BuilderEmitHost) {
const builderProgram = createBuilderProgram(host);
return (program: Program) => {
builderProgram.updateProgram(program);
emitFilesAndReportErrors(builderProgram, host.reportDiagnostic, host.writeFileName);
};
}
export interface WatchCompilerHost { export interface WatchCompilerHost {
/** If provided, callback to invoke before each program creation */ /** If provided, callback to invoke before each program creation */
beforeProgramCreate?(compilerOptions: CompilerOptions): void; beforeProgramCreate?(compilerOptions: CompilerOptions): void;

View file

@ -81,20 +81,22 @@ namespace ts {
}); });
function makeAssertChanges(getProgram: () => Program): (fileNames: ReadonlyArray<string>) => void { function makeAssertChanges(getProgram: () => Program): (fileNames: ReadonlyArray<string>) => void {
const builder = createEmitAndSemanticDiagnosticsBuilder({ useCaseSensitiveFileNames: returnTrue, }); const host: BuilderProgramHost = { useCaseSensitiveFileNames: returnTrue };
let builderProgram: EmitAndSemanticDiagnosticsBuilderProgram | undefined;
return fileNames => { return fileNames => {
const program = getProgram(); const program = getProgram();
builder.updateProgram(program); builderProgram = createEmitAndSemanticDiagnosticsBuilderProgram(program, host, builderProgram);
const outputFileNames: string[] = []; const outputFileNames: string[] = [];
// tslint:disable-next-line no-empty // tslint:disable-next-line no-empty
while (builder.emitNextAffectedFile(program, fileName => outputFileNames.push(fileName))) { while (builderProgram.emitNextAffectedFile(fileName => outputFileNames.push(fileName))) {
} }
assert.deepEqual(outputFileNames, fileNames); assert.deepEqual(outputFileNames, fileNames);
}; };
} }
function makeAssertChangesWithCancellationToken(getProgram: () => Program): (fileNames: ReadonlyArray<string>, cancelAfterEmitLength?: number) => void { function makeAssertChangesWithCancellationToken(getProgram: () => Program): (fileNames: ReadonlyArray<string>, cancelAfterEmitLength?: number) => void {
const builder = createEmitAndSemanticDiagnosticsBuilder({ useCaseSensitiveFileNames: returnTrue, }); const host: BuilderProgramHost = { useCaseSensitiveFileNames: returnTrue };
let builderProgram: EmitAndSemanticDiagnosticsBuilderProgram | undefined;
let cancel = false; let cancel = false;
const cancellationToken: CancellationToken = { const cancellationToken: CancellationToken = {
isCancellationRequested: () => cancel, isCancellationRequested: () => cancel,
@ -108,7 +110,7 @@ namespace ts {
cancel = false; cancel = false;
let operationWasCancelled = false; let operationWasCancelled = false;
const program = getProgram(); const program = getProgram();
builder.updateProgram(program); builderProgram = createEmitAndSemanticDiagnosticsBuilderProgram(program, host, builderProgram);
const outputFileNames: string[] = []; const outputFileNames: string[] = [];
try { try {
// tslint:disable-next-line no-empty // tslint:disable-next-line no-empty
@ -117,7 +119,7 @@ namespace ts {
if (outputFileNames.length === cancelAfterEmitLength) { if (outputFileNames.length === cancelAfterEmitLength) {
cancel = true; cancel = true;
} }
} while (builder.emitNextAffectedFile(program, fileName => outputFileNames.push(fileName), cancellationToken)); } while (builderProgram.emitNextAffectedFile(fileName => outputFileNames.push(fileName), cancellationToken));
} }
catch (e) { catch (e) {
assert.isFalse(operationWasCancelled); assert.isFalse(operationWasCancelled);

View file

@ -37,6 +37,7 @@
"../compiler/declarationEmitter.ts", "../compiler/declarationEmitter.ts",
"../compiler/emitter.ts", "../compiler/emitter.ts",
"../compiler/program.ts", "../compiler/program.ts",
"../compiler/builderState.ts",
"../compiler/builder.ts", "../compiler/builder.ts",
"../compiler/resolutionCache.ts", "../compiler/resolutionCache.ts",
"../compiler/watch.ts", "../compiler/watch.ts",

View file

@ -3749,7 +3749,7 @@ declare namespace ts {
result: T; result: T;
affected: SourceFile | Program; affected: SourceFile | Program;
} | undefined; } | undefined;
interface BuilderHost { interface BuilderProgramHost {
/** /**
* return true if file names are treated with case sensitivity * return true if file names are treated with case sensitivity
*/ */
@ -3758,78 +3758,104 @@ declare namespace ts {
* If provided this would be used this hash instead of actual file shape text for detecting changes * If provided this would be used this hash instead of actual file shape text for detecting changes
*/ */
createHash?: (data: string) => string; createHash?: (data: string) => string;
/**
* When emit or emitNextAffectedFile are called without writeFile,
* this callback if present would be used to write files
*/
writeFile?: WriteFileCallback;
} }
/** /**
* Builder to manage the program state changes * Builder to manage the program state changes
*/ */
interface BaseBuilder { interface BaseBuilderProgram {
/** /**
* Updates the program in the builder to represent new state * Get compiler options of the program
*/ */
updateProgram(newProgram: Program): void; getCompilerOptions(): CompilerOptions;
/**
* Get the source file in the program with file name
*/
getSourceFile(fileName: string): SourceFile | undefined;
/**
* Get a list of files in the program
*/
getSourceFiles(): ReadonlyArray<SourceFile>;
/**
* Get the diagnostics for compiler options
*/
getOptionsDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
/**
* Get the diagnostics that dont belong to any file
*/
getGlobalDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
/**
* Get the syntax diagnostics, for all source files if source file is not supplied
*/
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
/** /**
* Get all the dependencies of the file * Get all the dependencies of the file
*/ */
getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): ReadonlyArray<string>; getAllDependencies(sourceFile: SourceFile): ReadonlyArray<string>;
/**
* Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
* The semantic diagnostics are cached and managed here
* Note that it is assumed that when asked about semantic diagnostics through this API,
* the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
* In case of SemanticDiagnosticsBuilderProgram if the source file is not provided,
* it will iterate through all the affected files, to ensure that cache stays valid and yet provide a way to get all semantic diagnostics
*/
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
/**
* Emits the JavaScript and declaration files.
* When targetSource file is specified, emits the files corresponding to that source file,
* otherwise for the whole program.
* In case of EmitAndSemanticDiagnosticsBuilderProgram, when targetSourceFile is specified,
* it is assumed that that file is handled from affected file list. If targetSourceFile is not specified,
* it will only emit all the affected files instead of whole program
*
* The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
* in that order would be used to write the files
*/
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
} }
/** /**
* The builder that caches the semantic diagnostics for the program and handles the changed files and affected files * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files
*/ */
interface SemanticDiagnosticsBuilder extends BaseBuilder { interface SemanticDiagnosticsBuilderProgram extends BaseBuilderProgram {
/** /**
* Gets the semantic diagnostics from the program for the next affected file and caches it * Gets the semantic diagnostics from the program for the next affected file and caches it
* Returns undefined if the iteration is complete * Returns undefined if the iteration is complete
*/ */
getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult<ReadonlyArray<Diagnostic>>; getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult<ReadonlyArray<Diagnostic>>;
/**
* Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
* The semantic diagnostics are cached and managed here
* Note that it is assumed that the when asked about semantic diagnostics through this API,
* the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
*/
getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
} }
/** /**
* The builder that can handle the changes in program and iterate through changed file to emit the files * The builder that can handle the changes in program and iterate through changed file to emit the files
* The semantic diagnostics are cached per file and managed by clearing for the changed/affected files * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files
*/ */
interface EmitAndSemanticDiagnosticsBuilder extends BaseBuilder { interface EmitAndSemanticDiagnosticsBuilderProgram extends BaseBuilderProgram {
/**
* Get the current directory of the program
*/
getCurrentDirectory(): string;
/** /**
* Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
* The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
* in that order would be used to write the files
*/ */
emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult>; emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult>;
/**
* Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
* The semantic diagnostics are cached and managed here
* Note that it is assumed that the when asked about semantic diagnostics through this API,
* the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
*/
getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
} }
/** /**
* Create the builder to manage semantic diagnostics and cache them * Create the builder to manage semantic diagnostics and cache them
*/ */
function createSemanticDiagnosticsBuilder(host: BuilderHost): SemanticDiagnosticsBuilder; function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram;
/** /**
* Create the builder that can handle the changes in program and iterate through changed files * Create the builder that can handle the changes in program and iterate through changed files
* to emit the those files and manage semantic diagnostics cache as well * to emit the those files and manage semantic diagnostics cache as well
*/ */
function createEmitAndSemanticDiagnosticsBuilder(host: BuilderHost): EmitAndSemanticDiagnosticsBuilder; function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram;
} }
declare namespace ts { declare namespace ts {
type DiagnosticReporter = (diagnostic: Diagnostic) => void; type DiagnosticReporter = (diagnostic: Diagnostic) => void;
/**
* Host needed to emit files and report errors using builder
*/
interface BuilderEmitHost extends BuilderHost {
writeFile: WriteFileCallback;
reportDiagnostic: DiagnosticReporter;
writeFileName?: (s: string) => void;
}
/**
* Creates the function that reports the program errors and emit files every time it is called with argument as program
*/
function createEmitFilesAndReportErrorsWithBuilder(host: BuilderEmitHost): (program: Program) => void;
interface WatchCompilerHost { interface WatchCompilerHost {
/** If provided, callback to invoke before each program creation */ /** If provided, callback to invoke before each program creation */
beforeProgramCreate?(compilerOptions: CompilerOptions): void; beforeProgramCreate?(compilerOptions: CompilerOptions): void;