Skip to content

Commit

Permalink
fix: Lazily create program and type checker when necessary.
Browse files Browse the repository at this point in the history
Implements #188.
  • Loading branch information
dsherret committed Dec 21, 2017
1 parent 79fbf4a commit 77b3889
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 25 deletions.
2 changes: 1 addition & 1 deletion src/GlobalContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class GlobalContainer {
if (opts.typeChecker != null) {
errors.throwIfTrue(opts.createLanguageService, "Cannot specify a type checker and create a language service.");
this._customTypeChecker = new TypeChecker(this);
this._customTypeChecker.reset(opts.typeChecker);
this._customTypeChecker.reset(() => opts.typeChecker!);
}
}

Expand Down
6 changes: 2 additions & 4 deletions src/compiler/tools/LanguageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export class LanguageService {
};

this._compilerObject = ts.createLanguageService(languageServiceHost);
this.program = new Program(this.global, this.global.compilerFactory.getSourceFilePaths(), this.compilerHost);

this.global.compilerFactory.onSourceFileAdded(() => this.resetProgram());
this.global.compilerFactory.onSourceFileRemoved(() => this.resetProgram());
Expand All @@ -90,16 +91,13 @@ export class LanguageService {
* @internal
*/
resetProgram() {
if (this.program != null)
this.program.reset(this.global.compilerFactory.getSourceFilePaths(), this.compilerHost);
this.program.reset(this.global.compilerFactory.getSourceFilePaths(), this.compilerHost);
}

/**
* Gets the language service's program.
*/
getProgram() {
if (this.program == null)
this.program = new Program(this.global, this.global.compilerFactory.getSourceFilePaths(), this.compilerHost);
return this.program;
}

Expand Down
28 changes: 12 additions & 16 deletions src/compiler/tools/Program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ export class Program {
/** @internal */
private readonly typeChecker: TypeChecker;
/** @internal */
private _compilerObject: ts.Program;
private _createdCompilerObject: ts.Program | undefined;
/** @internal */
private _getOrCreateCompilerObject: () => ts.Program;

/** @internal */
constructor(global: GlobalContainer, rootNames: string[], host: ts.CompilerHost) {
Expand All @@ -44,7 +46,7 @@ export class Program {
* Gets the underlying compiler program.
*/
get compilerObject() {
return this._compilerObject;
return this._getOrCreateCompilerObject();
}

/**
Expand All @@ -53,20 +55,14 @@ export class Program {
*/
reset(rootNames: string[], host: ts.CompilerHost) {
const compilerOptions = this.global.compilerOptions;
const oldProgram = this._compilerObject;
this._compilerObject = getNewProgram();
this.typeChecker.reset(this._compilerObject.getTypeChecker());

function getNewProgram() {
try {
// try to reuse the old compiler object
return ts.createProgram(rootNames, compilerOptions, host, oldProgram);
} catch (err) {
Logger.warn("Could not create new program from old program. " + err);
// if that fails, try without using the old program
return ts.createProgram(rootNames, compilerOptions, host);
}
}
this._getOrCreateCompilerObject = () => {
if (this._createdCompilerObject == null)
this._createdCompilerObject = ts.createProgram(rootNames, compilerOptions, host);
// this needs to be on a separate line in case the program was reset between the line above and here
return this._createdCompilerObject || this._getOrCreateCompilerObject();
};
this._createdCompilerObject = undefined;
this.typeChecker.reset(() => this.compilerObject.getTypeChecker());
}

/**
Expand Down
8 changes: 4 additions & 4 deletions src/compiler/tools/TypeChecker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class TypeChecker {
/** @internal */
private readonly global: GlobalContainer;
/** @internal */
private _compilerObject: ts.TypeChecker;
private _getCompilerObject: () => ts.TypeChecker;

/** @internal */
constructor(global: GlobalContainer) {
Expand All @@ -22,15 +22,15 @@ export class TypeChecker {
* Gets the compiler's TypeChecker.
*/
get compilerObject() {
return this._compilerObject;
return this._getCompilerObject();
}

/**
* Resets the type checker.
* @internal
*/
reset(typeChecker: ts.TypeChecker) {
this._compilerObject = typeChecker;
reset(getTypeChecker: () => ts.TypeChecker) {
this._getCompilerObject = getTypeChecker;
}

/**
Expand Down

0 comments on commit 77b3889

Please sign in to comment.