Change builder to BuilderProgram so it is similar to operating on program
This commit is contained in:
parent
965f40f213
commit
dc62bb9abc
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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",
|
||||||
|
|
100
tests/baselines/reference/api/typescript.d.ts
vendored
100
tests/baselines/reference/api/typescript.d.ts
vendored
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue