mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
Use builder state to emit instead
This commit is contained in:
parent
eb5797fd36
commit
a06f0c3d9f
@ -11,10 +11,8 @@ namespace ts {
|
||||
writeByteOrderMark: boolean;
|
||||
text: string;
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
/* @internal */
|
||||
export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean,
|
||||
cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput {
|
||||
const outputFiles: OutputFile[] = [];
|
||||
@ -26,213 +24,270 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export interface Builder {
|
||||
/** Called to inform builder about new program */
|
||||
updateProgram(newProgram: Program): void;
|
||||
|
||||
/** Gets the files affected by the file path */
|
||||
getFilesAffectedBy(program: Program, path: Path): ReadonlyArray<SourceFile>;
|
||||
|
||||
/** Emit the changed files and clear the cache of the changed files */
|
||||
emitChangedFiles(program: Program, writeFileCallback: WriteFileCallback): ReadonlyArray<EmitResult>;
|
||||
|
||||
/** When called gets the semantic diagnostics for the program. It also caches the diagnostics and manage them */
|
||||
getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
|
||||
|
||||
/** Called to reset the status of the builder */
|
||||
clear(): void;
|
||||
function hasSameKeys<T, U>(map1: ReadonlyMap<T> | undefined, map2: ReadonlyMap<U> | undefined) {
|
||||
if (map1 === undefined) {
|
||||
return map2 === undefined;
|
||||
}
|
||||
if (map2 === undefined) {
|
||||
return map1 === undefined;
|
||||
}
|
||||
// Has same size and every key is present in both maps
|
||||
return map1.size === map2.size && !forEachEntry(map1, (_value, key) => !map2.has(key));
|
||||
}
|
||||
|
||||
interface EmitHandler {
|
||||
/**
|
||||
* State on which you can query affected files (files to save) and get semantic diagnostics(with their cache managed in the object)
|
||||
* Note that it is only safe to pass BuilderState as old state when creating new state, when
|
||||
* - If iterator's next method to get next affected file is never called
|
||||
* - Iteration of single changed file and its dependencies (iteration through all of its affected files) is complete
|
||||
*/
|
||||
export interface BuilderState {
|
||||
/**
|
||||
* Called when sourceFile is added to the program
|
||||
* The map of file infos, where there is entry for each file in the program
|
||||
* The entry is signature of the file (from last emit) or empty string
|
||||
*/
|
||||
onAddSourceFile(program: Program, sourceFile: SourceFile): void;
|
||||
fileInfos: ReadonlyMap<Readonly<FileInfo>>;
|
||||
|
||||
/**
|
||||
* Called when sourceFile is removed from the program
|
||||
* Returns true if module gerneration is not ModuleKind.None
|
||||
*/
|
||||
onRemoveSourceFile(path: Path): void;
|
||||
isModuleEmit: boolean;
|
||||
|
||||
/**
|
||||
* For all source files, either "onUpdateSourceFile" or "onUpdateSourceFileWithSameVersion" will be called.
|
||||
* If the builder is sure that the source file needs an update, "onUpdateSourceFile" will be called;
|
||||
* otherwise "onUpdateSourceFileWithSameVersion" will be called.
|
||||
* Map of file referenced or undefined if it wasnt module emit
|
||||
* The entry is present only if file references other files
|
||||
* The key is path of file and value is referenced map for that file (for every file referenced, there is entry in the set)
|
||||
*/
|
||||
onUpdateSourceFile(program: Program, sourceFile: SourceFile): void;
|
||||
referencedMap: ReadonlyMap<ReferencedSet> | undefined;
|
||||
|
||||
/**
|
||||
* For all source files, either "onUpdateSourceFile" or "onUpdateSourceFileWithSameVersion" will be called.
|
||||
* If the builder is sure that the source file needs an update, "onUpdateSourceFile" will be called;
|
||||
* otherwise "onUpdateSourceFileWithSameVersion" will be called.
|
||||
* This function should return whether the source file should be marked as changed (meaning that something associated with file has changed, e.g. module resolution)
|
||||
* Set of source file's paths that have been changed, either in resolution or versions
|
||||
*/
|
||||
onUpdateSourceFileWithSameVersion(program: Program, sourceFile: SourceFile): boolean;
|
||||
changedFilesSet: ReadonlyMap<true>;
|
||||
|
||||
/**
|
||||
* Gets the files affected by the script info which has updated shape from the known one
|
||||
* Set of cached semantic diagnostics per file
|
||||
*/
|
||||
getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile): ReadonlyArray<SourceFile>;
|
||||
semanticDiagnosticsPerFile: ReadonlyMap<ReadonlyArray<Diagnostic>>;
|
||||
|
||||
/**
|
||||
* Returns true if this state is safe to use as oldState
|
||||
*/
|
||||
canCreateNewStateFrom(): boolean;
|
||||
|
||||
/**
|
||||
* Gets the files affected by the file path
|
||||
* This api is only for internal use
|
||||
*/
|
||||
/* @internal */
|
||||
getFilesAffectedBy(programOfThisState: Program, path: Path): ReadonlyArray<SourceFile>;
|
||||
|
||||
/**
|
||||
* Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
|
||||
*/
|
||||
emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined;
|
||||
|
||||
/**
|
||||
* 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, the file has been taken out of affected files
|
||||
*/
|
||||
getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
|
||||
}
|
||||
|
||||
interface FileInfo {
|
||||
/**
|
||||
* Information about the source file: Its version and optional signature from last emit
|
||||
*/
|
||||
export interface FileInfo {
|
||||
version: string;
|
||||
signature: string;
|
||||
}
|
||||
|
||||
export interface AffectedFileEmitResult extends EmitResult {
|
||||
affectedFile?: SourceFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Referenced files with values for the keys as referenced file's path to be true
|
||||
*/
|
||||
export type ReferencedSet = ReadonlyMap<true>;
|
||||
|
||||
export interface BuilderOptions {
|
||||
getCanonicalFileName: GetCanonicalFileName;
|
||||
computeHash: (data: string) => string;
|
||||
}
|
||||
|
||||
export function createBuilder(options: BuilderOptions): Builder {
|
||||
let isModuleEmit: boolean | undefined;
|
||||
export function createBuilderState(newProgram: Program, options: BuilderOptions, oldState?: Readonly<BuilderState>): BuilderState {
|
||||
const fileInfos = createMap<FileInfo>();
|
||||
const isModuleEmit = newProgram.getCompilerOptions().module !== ModuleKind.None;
|
||||
const referencedMap = isModuleEmit ? createMap<ReferencedSet>() : undefined;
|
||||
|
||||
const semanticDiagnosticsPerFile = createMap<ReadonlyArray<Diagnostic>>();
|
||||
/** The map has key by source file's path that has been changed */
|
||||
const changedFilesSet = createMap<true>();
|
||||
const hasShapeChanged = createMap<true>();
|
||||
let allFilesExcludingDefaultLibraryFile: ReadonlyArray<SourceFile> | undefined;
|
||||
let emitHandler: EmitHandler;
|
||||
|
||||
// Iterator datas
|
||||
let affectedFiles: ReadonlyArray<SourceFile> | undefined;
|
||||
let affectedFilesIndex = 0;
|
||||
const seenAffectedFiles = createMap<true>();
|
||||
const getEmitDependentFilesAffectedBy = isModuleEmit ?
|
||||
getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit;
|
||||
|
||||
const useOldState = oldState && oldState.isModuleEmit === isModuleEmit;
|
||||
if (useOldState) {
|
||||
Debug.assert(oldState.canCreateNewStateFrom(), "Cannot use this state as old state");
|
||||
Debug.assert(!forEachEntry(oldState.changedFilesSet, (_value, path) => oldState.semanticDiagnosticsPerFile.has(path)), "Semantic diagnostics shouldnt be available for changed files");
|
||||
|
||||
copyEntries(oldState.changedFilesSet, changedFilesSet);
|
||||
copyEntries(oldState.semanticDiagnosticsPerFile, semanticDiagnosticsPerFile);
|
||||
}
|
||||
|
||||
for (const sourceFile of newProgram.getSourceFiles()) {
|
||||
const version = sourceFile.version;
|
||||
let oldInfo: Readonly<FileInfo>;
|
||||
let oldReferences: ReferencedSet;
|
||||
const newReferences = referencedMap && getReferencedFiles(newProgram, sourceFile);
|
||||
|
||||
// Register changed file
|
||||
// if not using old state so every file is changed
|
||||
if (!useOldState ||
|
||||
// File wasnt present earlier
|
||||
!(oldInfo = oldState.fileInfos.get(sourceFile.path)) ||
|
||||
// versions dont match
|
||||
oldInfo.version !== version ||
|
||||
// Referenced files changed
|
||||
!hasSameKeys(newReferences, (oldReferences = oldState.referencedMap && oldState.referencedMap.get(sourceFile.path))) ||
|
||||
// Referenced file was deleted
|
||||
newReferences && forEachEntry(newReferences, (_value, path) => oldState.fileInfos.has(path) && !newProgram.getSourceFileByPath(path as Path))) {
|
||||
changedFilesSet.set(sourceFile.path, true);
|
||||
// All changed files need to re-evaluate its semantic diagnostics
|
||||
semanticDiagnosticsPerFile.delete(sourceFile.path);
|
||||
}
|
||||
|
||||
newReferences && referencedMap.set(sourceFile.path, newReferences);
|
||||
fileInfos.set(sourceFile.path, { version, signature: oldInfo && oldInfo.signature });
|
||||
}
|
||||
|
||||
// For removed files, remove the semantic diagnostics removed files as changed
|
||||
useOldState && oldState.fileInfos.forEach((_value, path) => !fileInfos.has(path) && semanticDiagnosticsPerFile.delete(path));
|
||||
|
||||
// Set the old state and program to undefined to ensure we arent keeping them alive hence forward
|
||||
oldState = undefined;
|
||||
newProgram = undefined;
|
||||
|
||||
return {
|
||||
updateProgram,
|
||||
fileInfos,
|
||||
isModuleEmit,
|
||||
referencedMap,
|
||||
changedFilesSet,
|
||||
semanticDiagnosticsPerFile,
|
||||
canCreateNewStateFrom,
|
||||
getFilesAffectedBy,
|
||||
emitChangedFiles,
|
||||
getSemanticDiagnostics,
|
||||
clear
|
||||
emitNextAffectedFile,
|
||||
getSemanticDiagnostics
|
||||
};
|
||||
|
||||
function createProgramGraph(program: Program) {
|
||||
const currentIsModuleEmit = program.getCompilerOptions().module !== ModuleKind.None;
|
||||
if (isModuleEmit !== currentIsModuleEmit) {
|
||||
isModuleEmit = currentIsModuleEmit;
|
||||
emitHandler = isModuleEmit ? getModuleEmitHandler() : getNonModuleEmitHandler();
|
||||
fileInfos.clear();
|
||||
semanticDiagnosticsPerFile.clear();
|
||||
}
|
||||
hasShapeChanged.clear();
|
||||
allFilesExcludingDefaultLibraryFile = undefined;
|
||||
mutateMap(
|
||||
fileInfos,
|
||||
arrayToMap(program.getSourceFiles(), sourceFile => sourceFile.path),
|
||||
{
|
||||
// Add new file info
|
||||
createNewValue: (_path, sourceFile) => addNewFileInfo(program, sourceFile),
|
||||
// Remove existing file info
|
||||
onDeleteValue: removeExistingFileInfo,
|
||||
// We will update in place instead of deleting existing value and adding new one
|
||||
onExistingValue: (existingInfo, sourceFile) => updateExistingFileInfo(program, existingInfo, sourceFile)
|
||||
}
|
||||
);
|
||||
/**
|
||||
* Can use this state as old State if we have iterated through all affected files present
|
||||
*/
|
||||
function canCreateNewStateFrom() {
|
||||
return !affectedFiles || affectedFiles.length <= affectedFilesIndex;
|
||||
}
|
||||
|
||||
function registerChangedFile(path: Path) {
|
||||
changedFilesSet.set(path, true);
|
||||
// All changed files need to re-evaluate its semantic diagnostics
|
||||
semanticDiagnosticsPerFile.delete(path);
|
||||
}
|
||||
|
||||
function addNewFileInfo(program: Program, sourceFile: SourceFile): FileInfo {
|
||||
registerChangedFile(sourceFile.path);
|
||||
emitHandler.onAddSourceFile(program, sourceFile);
|
||||
return { version: sourceFile.version, signature: undefined };
|
||||
}
|
||||
|
||||
function removeExistingFileInfo(_existingFileInfo: FileInfo, path: Path) {
|
||||
// Since we dont need to track removed file as changed file
|
||||
// We can just remove its diagnostics
|
||||
changedFilesSet.delete(path);
|
||||
semanticDiagnosticsPerFile.delete(path);
|
||||
emitHandler.onRemoveSourceFile(path);
|
||||
}
|
||||
|
||||
function updateExistingFileInfo(program: Program, existingInfo: FileInfo, sourceFile: SourceFile) {
|
||||
if (existingInfo.version !== sourceFile.version) {
|
||||
registerChangedFile(sourceFile.path);
|
||||
existingInfo.version = sourceFile.version;
|
||||
emitHandler.onUpdateSourceFile(program, sourceFile);
|
||||
}
|
||||
else if (emitHandler.onUpdateSourceFileWithSameVersion(program, sourceFile)) {
|
||||
registerChangedFile(sourceFile.path);
|
||||
}
|
||||
}
|
||||
|
||||
function ensureProgramGraph(program: Program) {
|
||||
if (!emitHandler) {
|
||||
createProgramGraph(program);
|
||||
}
|
||||
}
|
||||
|
||||
function updateProgram(newProgram: Program) {
|
||||
if (emitHandler) {
|
||||
createProgramGraph(newProgram);
|
||||
}
|
||||
}
|
||||
|
||||
function getFilesAffectedBy(program: Program, path: Path): ReadonlyArray<SourceFile> {
|
||||
ensureProgramGraph(program);
|
||||
|
||||
const sourceFile = program.getSourceFileByPath(path);
|
||||
/**
|
||||
* Gets the files affected by the path from the program
|
||||
*/
|
||||
function getFilesAffectedBy(programOfThisState: Program, path: Path): ReadonlyArray<SourceFile> {
|
||||
const sourceFile = programOfThisState.getSourceFileByPath(path);
|
||||
if (!sourceFile) {
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
if (!updateShapeSignature(program, sourceFile)) {
|
||||
if (!updateShapeSignature(programOfThisState, sourceFile)) {
|
||||
return [sourceFile];
|
||||
}
|
||||
return emitHandler.getFilesAffectedByUpdatedShape(program, sourceFile);
|
||||
|
||||
return getEmitDependentFilesAffectedBy(programOfThisState, sourceFile);
|
||||
}
|
||||
|
||||
function emitChangedFiles(program: Program, writeFileCallback: WriteFileCallback): ReadonlyArray<EmitResult> {
|
||||
ensureProgramGraph(program);
|
||||
const compilerOptions = program.getCompilerOptions();
|
||||
/**
|
||||
* 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): AffectedFileEmitResult | undefined {
|
||||
if (affectedFiles) {
|
||||
while (affectedFilesIndex < affectedFiles.length) {
|
||||
const affectedFile = affectedFiles[affectedFilesIndex];
|
||||
affectedFilesIndex++;
|
||||
if (!seenAffectedFiles.has(affectedFile.path)) {
|
||||
seenAffectedFiles.set(affectedFile.path, true);
|
||||
|
||||
if (!changedFilesSet.size) {
|
||||
return emptyArray;
|
||||
// Emit the affected file
|
||||
const result = programOfThisState.emit(affectedFile, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers) as AffectedFileEmitResult;
|
||||
result.affectedFile = affectedFile;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
affectedFiles = undefined;
|
||||
}
|
||||
|
||||
// Get next changed file
|
||||
const nextKey = changedFilesSet.keys().next();
|
||||
if (nextKey.done) {
|
||||
// Done
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const compilerOptions = programOfThisState.getCompilerOptions();
|
||||
// With --out or --outFile all outputs go into single file, do it only once
|
||||
if (compilerOptions.outFile || compilerOptions.out) {
|
||||
Debug.assert(semanticDiagnosticsPerFile.size === 0);
|
||||
changedFilesSet.clear();
|
||||
return [program.emit(/*targetSourceFile*/ undefined, writeFileCallback)];
|
||||
return programOfThisState.emit(/*targetSourceFile*/ undefined, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers);
|
||||
}
|
||||
|
||||
const seenFiles = createMap<true>();
|
||||
let result: EmitResult[] | undefined;
|
||||
changedFilesSet.forEach((_true, path) => {
|
||||
// Get the affected Files by this program
|
||||
const affectedFiles = getFilesAffectedBy(program, path as Path);
|
||||
affectedFiles.forEach(affectedFile => {
|
||||
// Affected files shouldnt have cached diagnostics
|
||||
semanticDiagnosticsPerFile.delete(affectedFile.path);
|
||||
// Get next batch of affected files
|
||||
changedFilesSet.delete(nextKey.value);
|
||||
affectedFilesIndex = 0;
|
||||
affectedFiles = getFilesAffectedBy(programOfThisState, nextKey.value as Path);
|
||||
|
||||
if (!seenFiles.has(affectedFile.path)) {
|
||||
seenFiles.set(affectedFile.path, true);
|
||||
// Clear the semantic diagnostic of affected files
|
||||
affectedFiles.forEach(affectedFile => semanticDiagnosticsPerFile.delete(affectedFile.path));
|
||||
|
||||
// Emit the affected file
|
||||
(result || (result = [])).push(program.emit(affectedFile, writeFileCallback));
|
||||
}
|
||||
});
|
||||
});
|
||||
changedFilesSet.clear();
|
||||
return result || emptyArray;
|
||||
return emitNextAffectedFile(programOfThisState, writeFileCallback, cancellationToken, customTransformers);
|
||||
}
|
||||
|
||||
function getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic> {
|
||||
ensureProgramGraph(program);
|
||||
Debug.assert(changedFilesSet.size === 0);
|
||||
|
||||
const compilerOptions = program.getCompilerOptions();
|
||||
/**
|
||||
* 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, the file has been taken out of affected files
|
||||
*/
|
||||
function getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic> {
|
||||
const compilerOptions = programOfThisState.getCompilerOptions();
|
||||
if (compilerOptions.outFile || compilerOptions.out) {
|
||||
Debug.assert(semanticDiagnosticsPerFile.size === 0);
|
||||
// We dont need to cache the diagnostics just return them from program
|
||||
return program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken);
|
||||
return programOfThisState.getSemanticDiagnostics(sourceFile, cancellationToken);
|
||||
}
|
||||
|
||||
if (sourceFile) {
|
||||
return getSemanticDiagnosticsOfFile(programOfThisState, sourceFile, cancellationToken);
|
||||
}
|
||||
|
||||
let diagnostics: Diagnostic[];
|
||||
for (const sourceFile of program.getSourceFiles()) {
|
||||
diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(program, sourceFile, cancellationToken));
|
||||
for (const sourceFile of programOfThisState.getSourceFiles()) {
|
||||
diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(programOfThisState, sourceFile, cancellationToken));
|
||||
}
|
||||
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 = semanticDiagnosticsPerFile.get(path);
|
||||
@ -247,15 +302,6 @@ namespace ts {
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
function clear() {
|
||||
isModuleEmit = undefined;
|
||||
emitHandler = undefined;
|
||||
fileInfos.clear();
|
||||
semanticDiagnosticsPerFile.clear();
|
||||
changedFilesSet.clear();
|
||||
hasShapeChanged.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* For script files that contains only ambient external modules, although they are not actually external module files,
|
||||
* they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore,
|
||||
@ -271,9 +317,10 @@ namespace ts {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean} indicates if the shape signature has changed since last update.
|
||||
*/
|
||||
/**
|
||||
* Returns if the shape of the signature has changed since last emit
|
||||
* Note that it also updates the current signature as the latest signature for the file
|
||||
*/
|
||||
function updateShapeSignature(program: Program, sourceFile: SourceFile) {
|
||||
Debug.assert(!!sourceFile);
|
||||
|
||||
@ -360,6 +407,15 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the files referenced by the the file path
|
||||
*/
|
||||
function getReferencedByPaths(referencedFilePath: Path) {
|
||||
return mapDefinedIter(referencedMap.entries(), ([filePath, referencesInFile]) =>
|
||||
referencesInFile.has(referencedFilePath) ? filePath as Path : undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all files of the program excluding the default library file
|
||||
*/
|
||||
@ -386,126 +442,53 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getNonModuleEmitHandler(): EmitHandler {
|
||||
return {
|
||||
onAddSourceFile: noop,
|
||||
onRemoveSourceFile: noop,
|
||||
onUpdateSourceFile: noop,
|
||||
onUpdateSourceFileWithSameVersion: returnFalse,
|
||||
getFilesAffectedByUpdatedShape
|
||||
};
|
||||
|
||||
function getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile): ReadonlyArray<SourceFile> {
|
||||
const options = program.getCompilerOptions();
|
||||
// If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project,
|
||||
// so returning the file itself is good enough.
|
||||
if (options && (options.out || options.outFile)) {
|
||||
return [sourceFile];
|
||||
}
|
||||
return getAllFilesExcludingDefaultLibraryFile(program, sourceFile);
|
||||
/**
|
||||
* When program emits non modular code, gets the files affected by the sourceFile whose shape has changed
|
||||
*/
|
||||
function getFilesAffectedByUpdatedShapeWhenNonModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile) {
|
||||
const compilerOptions = programOfThisState.getCompilerOptions();
|
||||
// If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project,
|
||||
// so returning the file itself is good enough.
|
||||
if (compilerOptions && (compilerOptions.out || compilerOptions.outFile)) {
|
||||
return [sourceFileWithUpdatedShape];
|
||||
}
|
||||
return getAllFilesExcludingDefaultLibraryFile(programOfThisState, sourceFileWithUpdatedShape);
|
||||
}
|
||||
|
||||
function getModuleEmitHandler(): EmitHandler {
|
||||
const references = createMap<Map<true>>();
|
||||
return {
|
||||
onAddSourceFile: setReferences,
|
||||
onRemoveSourceFile,
|
||||
onUpdateSourceFile: updateReferences,
|
||||
onUpdateSourceFileWithSameVersion: updateReferencesTrackingChangedReferences,
|
||||
getFilesAffectedByUpdatedShape
|
||||
};
|
||||
|
||||
function setReferences(program: Program, sourceFile: SourceFile) {
|
||||
const newReferences = getReferencedFiles(program, sourceFile);
|
||||
if (newReferences) {
|
||||
references.set(sourceFile.path, newReferences);
|
||||
}
|
||||
/**
|
||||
* When program emits modular code, gets the files affected by the sourceFile whose shape has changed
|
||||
*/
|
||||
function getFilesAffectedByUpdatedShapeWhenModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile) {
|
||||
if (!isExternalModule(sourceFileWithUpdatedShape) && !containsOnlyAmbientModules(sourceFileWithUpdatedShape)) {
|
||||
return getAllFilesExcludingDefaultLibraryFile(programOfThisState, sourceFileWithUpdatedShape);
|
||||
}
|
||||
|
||||
function updateReferences(program: Program, sourceFile: SourceFile) {
|
||||
const newReferences = getReferencedFiles(program, sourceFile);
|
||||
if (newReferences) {
|
||||
references.set(sourceFile.path, newReferences);
|
||||
}
|
||||
else {
|
||||
references.delete(sourceFile.path);
|
||||
}
|
||||
const compilerOptions = programOfThisState.getCompilerOptions();
|
||||
if (compilerOptions && (compilerOptions.isolatedModules || compilerOptions.out || compilerOptions.outFile)) {
|
||||
return [sourceFileWithUpdatedShape];
|
||||
}
|
||||
|
||||
function updateReferencesTrackingChangedReferences(program: Program, sourceFile: SourceFile) {
|
||||
const newReferences = getReferencedFiles(program, sourceFile);
|
||||
if (!newReferences) {
|
||||
// Changed if we had references
|
||||
return references.delete(sourceFile.path);
|
||||
}
|
||||
// Now we need to if each file in the referencedBy list has a shape change as well.
|
||||
// Because if so, its own referencedBy files need to be saved as well to make the
|
||||
// emitting result consistent with files on disk.
|
||||
const seenFileNamesMap = createMap<SourceFile>();
|
||||
|
||||
const oldReferences = references.get(sourceFile.path);
|
||||
references.set(sourceFile.path, newReferences);
|
||||
if (!oldReferences || oldReferences.size !== newReferences.size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If there are any new references that werent present previously there is change
|
||||
return forEachEntry(newReferences, (_true, referencedPath) => !oldReferences.delete(referencedPath)) ||
|
||||
// Otherwise its changed if there are more references previously than now
|
||||
!!oldReferences.size;
|
||||
}
|
||||
|
||||
function onRemoveSourceFile(removedFilePath: Path) {
|
||||
// Remove existing references
|
||||
references.forEach((referencesInFile, filePath) => {
|
||||
if (referencesInFile.has(removedFilePath)) {
|
||||
// add files referencing the removedFilePath, as changed files too
|
||||
const referencedByInfo = fileInfos.get(filePath);
|
||||
if (referencedByInfo) {
|
||||
registerChangedFile(filePath as Path);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Delete the entry for the removed file path
|
||||
references.delete(removedFilePath);
|
||||
}
|
||||
|
||||
function getReferencedByPaths(referencedFilePath: Path) {
|
||||
return mapDefinedIter(references.entries(), ([filePath, referencesInFile]) =>
|
||||
referencesInFile.has(referencedFilePath) ? filePath as Path : undefined
|
||||
);
|
||||
}
|
||||
|
||||
function getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile): ReadonlyArray<SourceFile> {
|
||||
if (!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile)) {
|
||||
return getAllFilesExcludingDefaultLibraryFile(program, sourceFile);
|
||||
}
|
||||
|
||||
const compilerOptions = program.getCompilerOptions();
|
||||
if (compilerOptions && (compilerOptions.isolatedModules || compilerOptions.out || compilerOptions.outFile)) {
|
||||
return [sourceFile];
|
||||
}
|
||||
|
||||
// Now we need to if each file in the referencedBy list has a shape change as well.
|
||||
// Because if so, its own referencedBy files need to be saved as well to make the
|
||||
// emitting result consistent with files on disk.
|
||||
const seenFileNamesMap = createMap<SourceFile>();
|
||||
|
||||
// Start with the paths this file was referenced by
|
||||
const path = sourceFile.path;
|
||||
seenFileNamesMap.set(path, sourceFile);
|
||||
const queue = getReferencedByPaths(path);
|
||||
while (queue.length > 0) {
|
||||
const currentPath = queue.pop();
|
||||
if (!seenFileNamesMap.has(currentPath)) {
|
||||
const currentSourceFile = program.getSourceFileByPath(currentPath);
|
||||
seenFileNamesMap.set(currentPath, currentSourceFile);
|
||||
if (currentSourceFile && updateShapeSignature(program, currentSourceFile)) {
|
||||
queue.push(...getReferencedByPaths(currentPath));
|
||||
}
|
||||
// Start with the paths this file was referenced by
|
||||
seenFileNamesMap.set(sourceFileWithUpdatedShape.path, sourceFileWithUpdatedShape);
|
||||
const queue = getReferencedByPaths(sourceFileWithUpdatedShape.path);
|
||||
while (queue.length > 0) {
|
||||
const currentPath = queue.pop();
|
||||
if (!seenFileNamesMap.has(currentPath)) {
|
||||
const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath);
|
||||
seenFileNamesMap.set(currentPath, currentSourceFile);
|
||||
if (currentSourceFile && updateShapeSignature(programOfThisState, currentSourceFile)) {
|
||||
queue.push(...getReferencedByPaths(currentPath));
|
||||
}
|
||||
}
|
||||
|
||||
// Return array of values that needs emit
|
||||
return flatMapIter(seenFileNamesMap.values(), value => value);
|
||||
}
|
||||
|
||||
// Return array of values that needs emit
|
||||
return flatMapIter(seenFileNamesMap.values(), value => value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,8 +155,8 @@ namespace ts {
|
||||
const watchingHost = ts.createWatchingSystemHost(/*pretty*/ undefined, sys, parseConfigFile, reportDiagnostic, reportWatchDiagnostic);
|
||||
watchingHost.beforeCompile = enableStatistics;
|
||||
const afterCompile = watchingHost.afterCompile;
|
||||
watchingHost.afterCompile = (host, program, builder) => {
|
||||
afterCompile(host, program, builder);
|
||||
watchingHost.afterCompile = (host, program) => {
|
||||
afterCompile(host, program);
|
||||
reportStatistics(program);
|
||||
};
|
||||
return watchingHost;
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
/// <reference path="builder.ts" />
|
||||
/// <reference path="resolutionCache.ts"/>
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export type DiagnosticReporter = (diagnostic: Diagnostic) => void;
|
||||
export type ParseConfigFile = (configFileName: string, optionsToExtend: CompilerOptions, system: DirectoryStructureHost, reportDiagnostic: DiagnosticReporter, reportWatchDiagnostic: DiagnosticReporter) => ParsedCommandLine;
|
||||
@ -19,7 +18,7 @@ namespace ts {
|
||||
|
||||
// Callbacks to do custom action before creating program and after creating program
|
||||
beforeCompile(compilerOptions: CompilerOptions): void;
|
||||
afterCompile(host: DirectoryStructureHost, program: Program, builder: Builder): void;
|
||||
afterCompile(host: DirectoryStructureHost, program: Program): void;
|
||||
}
|
||||
|
||||
const defaultFormatDiagnosticsHost: FormatDiagnosticsHost = sys ? {
|
||||
@ -133,6 +132,11 @@ namespace ts {
|
||||
reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system, pretty ? reportDiagnosticWithColorAndContext : reportDiagnosticSimply);
|
||||
reportWatchDiagnostic = reportWatchDiagnostic || createWatchDiagnosticReporter(system);
|
||||
parseConfigFile = parseConfigFile || ts.parseConfigFile;
|
||||
let builderState: Readonly<BuilderState> | undefined;
|
||||
const options: BuilderOptions = {
|
||||
getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames),
|
||||
computeHash: data => system.createHash ? system.createHash(data) : data
|
||||
};
|
||||
return {
|
||||
system,
|
||||
parseConfigFile,
|
||||
@ -142,7 +146,9 @@ namespace ts {
|
||||
afterCompile: compileWatchedProgram,
|
||||
};
|
||||
|
||||
function compileWatchedProgram(host: DirectoryStructureHost, program: Program, builder: Builder) {
|
||||
function compileWatchedProgram(host: DirectoryStructureHost, program: Program) {
|
||||
builderState = createBuilderState(program, options, builderState);
|
||||
|
||||
// First get and report any syntactic errors.
|
||||
const diagnostics = program.getSyntacticDiagnostics().slice();
|
||||
let reportSemanticDiagnostics = false;
|
||||
@ -163,22 +169,15 @@ namespace ts {
|
||||
let sourceMaps: SourceMapData[];
|
||||
let emitSkipped: boolean;
|
||||
|
||||
const result = builder.emitChangedFiles(program, writeFile);
|
||||
if (result.length === 0) {
|
||||
emitSkipped = true;
|
||||
}
|
||||
else {
|
||||
for (const emitOutput of result) {
|
||||
if (emitOutput.emitSkipped) {
|
||||
emitSkipped = true;
|
||||
}
|
||||
addRange(diagnostics, emitOutput.diagnostics);
|
||||
sourceMaps = concatenate(sourceMaps, emitOutput.sourceMaps);
|
||||
}
|
||||
let affectedEmitResult: AffectedFileEmitResult;
|
||||
while (affectedEmitResult = builderState.emitNextAffectedFile(program, writeFile)) {
|
||||
emitSkipped = emitSkipped || affectedEmitResult.emitSkipped;
|
||||
addRange(diagnostics, affectedEmitResult.diagnostics);
|
||||
sourceMaps = addRange(sourceMaps, affectedEmitResult.sourceMaps);
|
||||
}
|
||||
|
||||
if (reportSemanticDiagnostics) {
|
||||
addRange(diagnostics, builder.getSemanticDiagnostics(program));
|
||||
addRange(diagnostics, builderState.getSemanticDiagnostics(program));
|
||||
}
|
||||
return handleEmitOutputAndReportErrors(host, program, emittedFiles, emitSkipped,
|
||||
diagnostics, reportDiagnostic);
|
||||
@ -299,8 +298,6 @@ namespace ts {
|
||||
getDirectoryPath(getNormalizedAbsolutePath(configFileName, getCurrentDirectory())) :
|
||||
getCurrentDirectory()
|
||||
);
|
||||
// There is no extra check needed since we can just rely on the program to decide emit
|
||||
const builder = createBuilder({ getCanonicalFileName, computeHash });
|
||||
|
||||
synchronizeProgram();
|
||||
|
||||
@ -334,7 +331,6 @@ namespace ts {
|
||||
compilerHost.hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames;
|
||||
program = createProgram(rootFileNames, compilerOptions, compilerHost, program);
|
||||
resolutionCache.finishCachingPerDirectoryResolution();
|
||||
builder.updateProgram(program);
|
||||
|
||||
// Update watches
|
||||
updateMissingFilePathsWatch(program, missingFilesMap || (missingFilesMap = createMap()), watchMissingFilePath);
|
||||
@ -356,7 +352,7 @@ namespace ts {
|
||||
missingFilePathsRequestedForRelease = undefined;
|
||||
}
|
||||
|
||||
afterCompile(directoryStructureHost, program, builder);
|
||||
afterCompile(directoryStructureHost, program);
|
||||
reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes));
|
||||
}
|
||||
|
||||
@ -640,9 +636,5 @@ namespace ts {
|
||||
flags
|
||||
);
|
||||
}
|
||||
|
||||
function computeHash(data: string) {
|
||||
return system.createHash ? system.createHash(data) : data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,15 +44,16 @@ namespace ts {
|
||||
});
|
||||
|
||||
function makeAssertChanges(getProgram: () => Program): (fileNames: ReadonlyArray<string>) => void {
|
||||
const builder = createBuilder({
|
||||
let builderState: BuilderState;
|
||||
const builderOptions: BuilderOptions = {
|
||||
getCanonicalFileName: identity,
|
||||
computeHash: identity
|
||||
});
|
||||
};
|
||||
return fileNames => {
|
||||
const program = getProgram();
|
||||
builder.updateProgram(program);
|
||||
builderState = createBuilderState(program, builderOptions, builderState);
|
||||
const outputFileNames: string[] = [];
|
||||
builder.emitChangedFiles(program, fileName => outputFileNames.push(fileName));
|
||||
while (builderState.emitNextAffectedFile(program, fileName => outputFileNames.push(fileName))) { }
|
||||
assert.deepEqual(outputFileNames, fileNames);
|
||||
};
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@ namespace ts.server {
|
||||
/*@internal*/
|
||||
resolutionCache: ResolutionCache;
|
||||
|
||||
private builder: Builder;
|
||||
private builderState: BuilderState;
|
||||
/**
|
||||
* Set of files names that were updated since the last call to getChangesSinceVersion.
|
||||
*/
|
||||
@ -442,15 +442,6 @@ namespace ts.server {
|
||||
return this.languageService;
|
||||
}
|
||||
|
||||
private ensureBuilder() {
|
||||
if (!this.builder) {
|
||||
this.builder = createBuilder({
|
||||
getCanonicalFileName: this.projectService.toCanonicalFileName,
|
||||
computeHash: data => this.projectService.host.createHash(data)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private shouldEmitFile(scriptInfo: ScriptInfo) {
|
||||
return scriptInfo && !scriptInfo.isDynamicOrHasMixedContent();
|
||||
}
|
||||
@ -460,8 +451,11 @@ namespace ts.server {
|
||||
return [];
|
||||
}
|
||||
this.updateGraph();
|
||||
this.ensureBuilder();
|
||||
return mapDefined(this.builder.getFilesAffectedBy(this.program, scriptInfo.path),
|
||||
this.builderState = createBuilderState(this.program, {
|
||||
getCanonicalFileName: this.projectService.toCanonicalFileName,
|
||||
computeHash: data => this.projectService.host.createHash(data)
|
||||
}, this.builderState);
|
||||
return mapDefined(this.builderState.getFilesAffectedBy(this.program, scriptInfo.path),
|
||||
sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)) ? sourceFile.fileName : undefined);
|
||||
}
|
||||
|
||||
@ -497,6 +491,7 @@ namespace ts.server {
|
||||
}
|
||||
this.languageService.cleanupSemanticCache();
|
||||
this.languageServiceEnabled = false;
|
||||
this.builderState = undefined;
|
||||
this.resolutionCache.closeTypeRootsWatch();
|
||||
this.projectService.onUpdateLanguageServiceStateForProject(this, /*languageServiceEnabled*/ false);
|
||||
}
|
||||
@ -537,7 +532,7 @@ namespace ts.server {
|
||||
this.rootFilesMap = undefined;
|
||||
this.externalFiles = undefined;
|
||||
this.program = undefined;
|
||||
this.builder = undefined;
|
||||
this.builderState = undefined;
|
||||
this.resolutionCache.clear();
|
||||
this.resolutionCache = undefined;
|
||||
this.cachedUnresolvedImportsPerFile = undefined;
|
||||
@ -787,15 +782,9 @@ namespace ts.server {
|
||||
if (this.setTypings(cachedTypings)) {
|
||||
hasChanges = this.updateGraphWorker() || hasChanges;
|
||||
}
|
||||
if (this.builder) {
|
||||
this.builder.updateProgram(this.program);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.lastCachedUnresolvedImportsList = undefined;
|
||||
if (this.builder) {
|
||||
this.builder.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (hasChanges) {
|
||||
|
||||
@ -3771,6 +3771,70 @@ declare namespace ts {
|
||||
writeByteOrderMark: boolean;
|
||||
text: string;
|
||||
}
|
||||
/**
|
||||
* State on which you can query affected files (files to save) and get semantic diagnostics(with their cache managed in the object)
|
||||
* Note that it is only safe to pass BuilderState as old state when creating new state, when
|
||||
* - If iterator's next method to get next affected file is never called
|
||||
* - Iteration of single changed file and its dependencies (iteration through all of its affected files) is complete
|
||||
*/
|
||||
interface BuilderState {
|
||||
/**
|
||||
* The map of file infos, where there is entry for each file in the program
|
||||
* The entry is signature of the file (from last emit) or empty string
|
||||
*/
|
||||
fileInfos: ReadonlyMap<Readonly<FileInfo>>;
|
||||
/**
|
||||
* Returns true if module gerneration is not ModuleKind.None
|
||||
*/
|
||||
isModuleEmit: boolean;
|
||||
/**
|
||||
* Map of file referenced or undefined if it wasnt module emit
|
||||
* The entry is present only if file references other files
|
||||
* The key is path of file and value is referenced map for that file (for every file referenced, there is entry in the set)
|
||||
*/
|
||||
referencedMap: ReadonlyMap<ReferencedSet> | undefined;
|
||||
/**
|
||||
* Set of source file's paths that have been changed, either in resolution or versions
|
||||
*/
|
||||
changedFilesSet: ReadonlyMap<true>;
|
||||
/**
|
||||
* Set of cached semantic diagnostics per file
|
||||
*/
|
||||
semanticDiagnosticsPerFile: ReadonlyMap<ReadonlyArray<Diagnostic>>;
|
||||
/**
|
||||
* Returns true if this state is safe to use as oldState
|
||||
*/
|
||||
canCreateNewStateFrom(): boolean;
|
||||
/**
|
||||
* Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
|
||||
*/
|
||||
emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined;
|
||||
/**
|
||||
* 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, the file has been taken out of affected files
|
||||
*/
|
||||
getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
|
||||
}
|
||||
/**
|
||||
* Information about the source file: Its version and optional signature from last emit
|
||||
*/
|
||||
interface FileInfo {
|
||||
version: string;
|
||||
signature: string;
|
||||
}
|
||||
interface AffectedFileEmitResult extends EmitResult {
|
||||
affectedFile?: SourceFile;
|
||||
}
|
||||
/**
|
||||
* Referenced files with values for the keys as referenced file's path to be true
|
||||
*/
|
||||
type ReferencedSet = ReadonlyMap<true>;
|
||||
interface BuilderOptions {
|
||||
getCanonicalFileName: (fileName: string) => string;
|
||||
computeHash: (data: string) => string;
|
||||
}
|
||||
function createBuilderState(newProgram: Program, options: BuilderOptions, oldState?: Readonly<BuilderState>): BuilderState;
|
||||
}
|
||||
declare namespace ts {
|
||||
function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string;
|
||||
@ -7210,7 +7274,7 @@ declare namespace ts.server {
|
||||
languageServiceEnabled: boolean;
|
||||
readonly trace?: (s: string) => void;
|
||||
readonly realpath?: (path: string) => string;
|
||||
private builder;
|
||||
private builderState;
|
||||
/**
|
||||
* Set of files names that were updated since the last call to getChangesSinceVersion.
|
||||
*/
|
||||
@ -7271,7 +7335,6 @@ declare namespace ts.server {
|
||||
getGlobalProjectErrors(): ReadonlyArray<Diagnostic>;
|
||||
getAllProjectErrors(): ReadonlyArray<Diagnostic>;
|
||||
getLanguageService(ensureSynchronized?: boolean): LanguageService;
|
||||
private ensureBuilder();
|
||||
private shouldEmitFile(scriptInfo);
|
||||
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[];
|
||||
/**
|
||||
|
||||
64
tests/baselines/reference/api/typescript.d.ts
vendored
64
tests/baselines/reference/api/typescript.d.ts
vendored
@ -3718,6 +3718,70 @@ declare namespace ts {
|
||||
writeByteOrderMark: boolean;
|
||||
text: string;
|
||||
}
|
||||
/**
|
||||
* State on which you can query affected files (files to save) and get semantic diagnostics(with their cache managed in the object)
|
||||
* Note that it is only safe to pass BuilderState as old state when creating new state, when
|
||||
* - If iterator's next method to get next affected file is never called
|
||||
* - Iteration of single changed file and its dependencies (iteration through all of its affected files) is complete
|
||||
*/
|
||||
interface BuilderState {
|
||||
/**
|
||||
* The map of file infos, where there is entry for each file in the program
|
||||
* The entry is signature of the file (from last emit) or empty string
|
||||
*/
|
||||
fileInfos: ReadonlyMap<Readonly<FileInfo>>;
|
||||
/**
|
||||
* Returns true if module gerneration is not ModuleKind.None
|
||||
*/
|
||||
isModuleEmit: boolean;
|
||||
/**
|
||||
* Map of file referenced or undefined if it wasnt module emit
|
||||
* The entry is present only if file references other files
|
||||
* The key is path of file and value is referenced map for that file (for every file referenced, there is entry in the set)
|
||||
*/
|
||||
referencedMap: ReadonlyMap<ReferencedSet> | undefined;
|
||||
/**
|
||||
* Set of source file's paths that have been changed, either in resolution or versions
|
||||
*/
|
||||
changedFilesSet: ReadonlyMap<true>;
|
||||
/**
|
||||
* Set of cached semantic diagnostics per file
|
||||
*/
|
||||
semanticDiagnosticsPerFile: ReadonlyMap<ReadonlyArray<Diagnostic>>;
|
||||
/**
|
||||
* Returns true if this state is safe to use as oldState
|
||||
*/
|
||||
canCreateNewStateFrom(): boolean;
|
||||
/**
|
||||
* Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
|
||||
*/
|
||||
emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined;
|
||||
/**
|
||||
* 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, the file has been taken out of affected files
|
||||
*/
|
||||
getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
|
||||
}
|
||||
/**
|
||||
* Information about the source file: Its version and optional signature from last emit
|
||||
*/
|
||||
interface FileInfo {
|
||||
version: string;
|
||||
signature: string;
|
||||
}
|
||||
interface AffectedFileEmitResult extends EmitResult {
|
||||
affectedFile?: SourceFile;
|
||||
}
|
||||
/**
|
||||
* Referenced files with values for the keys as referenced file's path to be true
|
||||
*/
|
||||
type ReferencedSet = ReadonlyMap<true>;
|
||||
interface BuilderOptions {
|
||||
getCanonicalFileName: (fileName: string) => string;
|
||||
computeHash: (data: string) => string;
|
||||
}
|
||||
function createBuilderState(newProgram: Program, options: BuilderOptions, oldState?: Readonly<BuilderState>): BuilderState;
|
||||
}
|
||||
declare namespace ts {
|
||||
function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user