Skip to content

Commit c4f4675

Browse files
ayazhafizatscott
authored andcommitted
fix(language-service): Recover from error in analyzing Ng Modules (angular#37108)
In place of failing to return analyzed Ng Modules when the analyzer fails, return the previously-analyzed Ng Modules (which may be empty) and log an error. Closes angular/vscode-ng-language-service#777 PR Close angular#37108
1 parent 3482755 commit c4f4675

File tree

3 files changed

+55
-2
lines changed

3 files changed

+55
-2
lines changed

packages/language-service/src/typescript_host.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,15 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
175175
}
176176
};
177177
const programFiles = this.program.getSourceFiles().map(sf => sf.fileName);
178-
this.analyzedModules =
179-
analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver);
178+
179+
try {
180+
this.analyzedModules =
181+
analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver);
182+
} catch (e) {
183+
// Analyzing modules may throw; in that case, reuse the old modules.
184+
this.error(`Analyzing NgModules failed. ${e}`);
185+
return this.analyzedModules;
186+
}
180187

181188
// update template references and fileToComponent
182189
const urlResolver = createOfflineCompileUrlResolver();

packages/language-service/test/test_utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
101101
private readonly overrideDirectory = new Set<string>();
102102
private readonly existsCache = new Map<string, boolean>();
103103
private readonly fileCache = new Map<string, string|undefined>();
104+
errors: string[] = [];
104105

105106
constructor(
106107
private readonly scriptNames: string[],
@@ -398,6 +399,10 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
398399
}
399400
throw new Error(`Failed to find marker '${selector}' in ${fileName}`);
400401
}
402+
403+
error(msg: string) {
404+
this.errors.push(msg);
405+
}
401406
}
402407

403408
const locationMarker = /\~\{(\w+(-\w+)*)\}/g;

packages/language-service/test/typescript_host_spec.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import * as ngc from '@angular/compiler';
910
import * as ts from 'typescript';
1011

1112
import {TypeScriptServiceHost} from '../src/typescript_host';
@@ -216,4 +217,44 @@ describe('TypeScriptServiceHost', () => {
216217
// But the content should be exactly the same
217218
expect(newModules).toEqual(oldModules);
218219
});
220+
221+
it('should recover from error in analyzing ng modules', () => {
222+
// First create a TypescriptHost with empty script names
223+
const tsLSHost = new MockTypescriptHost([]);
224+
const tsLS = ts.createLanguageService(tsLSHost);
225+
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
226+
const oldModules = ngLSHost.getAnalyzedModules();
227+
expect(oldModules.ngModules).toEqual([]);
228+
// Now add a script, this would change the program
229+
let fileName = '/app/main.ts';
230+
let content = `
231+
import {CommonModule} from '@angular/common';
232+
import {NgModule} from '@angular/core';
233+
234+
@NgModule({
235+
entryComponents: [CommonModule],
236+
})
237+
export class AppModule {}
238+
`;
239+
tsLSHost.addScript(fileName, content);
240+
241+
// If analyzing modules throws, the old modules should be returned.
242+
let newModules = ngLSHost.getAnalyzedModules();
243+
expect(newModules.ngModules).toEqual([]);
244+
expect(tsLSHost.errors).toEqual([
245+
'Analyzing NgModules failed. Error: CommonModule cannot be used as an entry component.'
246+
]);
247+
248+
content = `
249+
import {CommonModule} from '@angular/common';
250+
import {NgModule} from '@angular/core';
251+
252+
@NgModule({})
253+
export class AppModule {}
254+
`;
255+
tsLSHost.override(fileName, content);
256+
// Check that analyzing modules successfully still works.
257+
newModules = ngLSHost.getAnalyzedModules();
258+
expect(newModules.ngModules.length).toBeGreaterThan(0);
259+
});
219260
});

0 commit comments

Comments
 (0)